diff --git a/README.md b/README.md index 0a590ca..ae3a186 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,26 @@ You can convert it to ISBN-10 or ISBN-13: > isbn.isbn13 => "9780000000002" -And you can break it up into its GS1 prefix, group identifier, prefix/publisher code, +You can break it up into its GS1 prefix, group identifier, prefix/publisher code, item number and check digit: > isbn.parts => ["978", "0", "00", "000000", "2"] - + +You can generate the complete range of ISBNs for a registrant element, using the publisher_range object: + + > isbn.publisher_range.isbn13s + => ["9786017002008", "9786017002015", "9786017002022", .. "9786017002992"] + +You can create a publication range object directly: + + > Lisbn::PublicationRange.new("9786017002008") + +You can see the range as a string representation: + + > Lisbn::PublicationRange.new("9786017002008").to_s + => "978-601-7002-00-8..978-601-7002-99-2" + ## Updating You can update the ISBN ranges by replacing the RangeMessage.xml file with an diff --git a/lib/lisbn.rb b/lib/lisbn.rb index 96faa01..4870f16 100644 --- a/lib/lisbn.rb +++ b/lib/lisbn.rb @@ -3,3 +3,4 @@ require_relative "lisbn/cache_method" require_relative "lisbn/lisbn" require_relative "lisbn/ranges" +require_relative "lisbn/publication_range" diff --git a/lib/lisbn/lisbn.rb b/lib/lisbn/lisbn.rb index dffb22f..dcbb8f8 100644 --- a/lib/lisbn/lisbn.rb +++ b/lib/lisbn/lisbn.rb @@ -45,6 +45,16 @@ def isbn13 '978' + isbn[0..-2] + isbn_13_checksum end + def isbn13_checksum_corrected + return nil unless isbn.length == 13 + isbn[0..-2] + isbn_13_checksum + end + + def publication_range + return unless isbn13 + PublicationRange.new(isbn13) + end + # Returns an Array with the 'parts' of the ISBN-13 in left-to-right order. # The parts of an ISBN are as follows: # - GS1 prefix diff --git a/lib/lisbn/publication_range.rb b/lib/lisbn/publication_range.rb new file mode 100644 index 0000000..45a1af7 --- /dev/null +++ b/lib/lisbn/publication_range.rb @@ -0,0 +1,57 @@ +class Lisbn < String + class PublicationRange + include Enumerable + def initialize(seed_isbn13) + @seed_isbn13 = Lisbn.new(seed_isbn13) + end + + def parts + seed_isbn13.parts[0..2] + end + + def prefix + parts.join + end + + def to_s + "#{Lisbn.new(from).isbn_with_dash}..#{Lisbn.new(to).isbn_with_dash}" + end + + def number_of_publications + 10 ** (13 - prefix.size - 1) + end + + def publication_numbers + (0..(number_of_publications-1)) + end + + def each + (from_prefix..to_prefix).each do |isbn| + yield Lisbn.new((isbn*10).to_s).isbn13_checksum_corrected + end + end + + private + attr_reader :seed_isbn13 + + def multiplier + (10 ** (12 - prefix.size)) + end + + def from_prefix + prefix.to_i * multiplier + end + + def to_prefix + (prefix.to_i+1) * multiplier - 1 + end + + def from + Lisbn.new((from_prefix*10).to_s).isbn13_checksum_corrected + end + + def to + Lisbn.new((to_prefix*10).to_s).isbn13_checksum_corrected + end + end +end diff --git a/spec/lisbn_spec.rb b/spec/lisbn_spec.rb index 9e11b35..0efdfc0 100644 --- a/spec/lisbn_spec.rb +++ b/spec/lisbn_spec.rb @@ -127,6 +127,16 @@ lisbn.should_not_receive(:isbn_13_checksum) lisbn.isbn13.should == "9780000000002" end + + it "provides a corrected checksum" do + lisbn = Lisbn.new("9780000000001") + lisbn.isbn13_checksum_corrected.should == "9780000000002" + end + + it "can generate a publication range" do + lisbn = Lisbn.new("9786017002015") + lisbn.publication_range.to_s.should == "978-601-7002-00-8..978-601-7002-99-2" + end end describe "#parts" do @@ -147,6 +157,138 @@ end end + describe "#publication_range" do + subject { Lisbn::PublicationRange.new("9786017002015") } + + it "returns a PublicationRange object" do + subject.class.should == Lisbn::PublicationRange + end + + it "returns the three parts of a PublicationRange" do + subject.parts.should == ["978", "601", "7002"] + end + + it "returns the number of publication in the range" do + subject.number_of_publications.should == 100 + end + + it "returns the range of publication numbers" do + subject.publication_numbers.should == (0..99) + end + + it "returns a string representation of the range of ISBN13s" do + subject.to_s.should == "978-601-7002-00-8..978-601-7002-99-2" + end + + it "returns an enumerable of isbns" do + subject.min.should == "9786017002008" + subject.max.should == "9786017002992" + + subject.map{|x| x}.should == [ + "9786017002008", + "9786017002015", + "9786017002022", + "9786017002039", + "9786017002046", + "9786017002053", + "9786017002060", + "9786017002077", + "9786017002084", + "9786017002091", + "9786017002107", + "9786017002114", + "9786017002121", + "9786017002138", + "9786017002145", + "9786017002152", + "9786017002169", + "9786017002176", + "9786017002183", + "9786017002190", + "9786017002206", + "9786017002213", + "9786017002220", + "9786017002237", + "9786017002244", + "9786017002251", + "9786017002268", + "9786017002275", + "9786017002282", + "9786017002299", + "9786017002305", + "9786017002312", + "9786017002329", + "9786017002336", + "9786017002343", + "9786017002350", + "9786017002367", + "9786017002374", + "9786017002381", + "9786017002398", + "9786017002404", + "9786017002411", + "9786017002428", + "9786017002435", + "9786017002442", + "9786017002459", + "9786017002466", + "9786017002473", + "9786017002480", + "9786017002497", + "9786017002503", + "9786017002510", + "9786017002527", + "9786017002534", + "9786017002541", + "9786017002558", + "9786017002565", + "9786017002572", + "9786017002589", + "9786017002596", + "9786017002602", + "9786017002619", + "9786017002626", + "9786017002633", + "9786017002640", + "9786017002657", + "9786017002664", + "9786017002671", + "9786017002688", + "9786017002695", + "9786017002701", + "9786017002718", + "9786017002725", + "9786017002732", + "9786017002749", + "9786017002756", + "9786017002763", + "9786017002770", + "9786017002787", + "9786017002794", + "9786017002800", + "9786017002817", + "9786017002824", + "9786017002831", + "9786017002848", + "9786017002855", + "9786017002862", + "9786017002879", + "9786017002886", + "9786017002893", + "9786017002909", + "9786017002916", + "9786017002923", + "9786017002930", + "9786017002947", + "9786017002954", + "9786017002961", + "9786017002978", + "9786017002985", + "9786017002992" + ] + end + end + describe "ranges" do it "skips over invalid '0-length' ranges" do Lisbn::RANGES.values.flatten.map {|v| v[:length]}.should_not include(0) @@ -157,7 +299,8 @@ subject { Lisbn.new("9780000000002") } it "#splits" do - subject.split("7").should == ["9", "80000000002"] + subject.split("7").should == ["9", + "80000000002"] end end end