As of the beginning of 2007, ISBN10 is dead. Now we’re in a world that allows “979″ prefixes, though the following code doesn’t expect them yet…
Here’s some stuff to turn your 10-digit ISBNs into 13-digit ISBNs, naively assuming “978″, following an the API post doing the same from LibraryThing. There’s another one at isbn.org for humans.
Code from O’Reilly’s internal stuff:
module Isbn
def self.dash_isbn(isbn)
raise ArgumentError.new("ISBN argument must be string") unless isbn.is_a?(String)
if isbn.length == 10
return isbn[/^./] + "-" + isbn[1..3] + "-" + isbn[4..8] + "-" + isbn[/.$/]
elsif isbn.length == 13
return isbn[0..2] + "-" + isbn[3].chr + "-" + isbn[4..6] + "-" + isbn[7..11] + "-" + isbn[/.$/]
else
raise ArgumentError.new("ISBN must be 10 or 13 characters")
end
end
def self.isbn10toisbn13(isbn)
raise ArgumentError.new("ISBN argument must be string") unless isbn.is_a?(String)
raise ArgumentError.new("ISBN must be of length 10") unless isbn.length == 10
prefix = "978"
isbn12 = prefix + isbn[0...-1]
return isbn12 + check_digit_13(isbn12).to_s
end
def self.check_digit_13(isbn_12)
# http://www.barcodeisland.com/ean13.phtml
# need to subtract remainder from 10
# and do exemption for zero LMS 08.29.2006
raise ArgumentError.new("ISBN must be of length 12") unless isbn_12.length == 12
sum = 0
odds = 0
evens = 0
isbn_12.scan(/\d/).each_with_index {|d, i|
if (i % 2) == 0
evens = evens + (d.to_i * 1)
else
odds = odds + (d.to_i * 3)
end
}
sum = evens + odds
digit = sum % 10
if digit.zero?
return 0
else
return 10 - digit
end
end
end # of module Isbns
The last test method relies on my database, which you won’t have. Replace it with something else you trust or drop it.
#!/usr/bin/env ruby
require 'test/unit'
require 'pdb'
require 'isbn'
class IsbnTest < Test::Unit::TestCase
def setup
@isbn10 = "059610123X"
@isbn13 = "978059610123X"
end
def test_dash_isbn
# must be a String
assert_raise ArgumentError do Isbn.dash_isbn(123) end
# must be 10 or 13 characters
assert_raise ArgumentError do Isbn.dash_isbn("123") end
assert_equal("0-596-10123-X", Isbn.dash_isbn(@isbn10))
assert_equal("978-0-596-10123-X", Isbn.dash_isbn(@isbn13))
end
def test_isbn10toisbn13
# must be a String
assert_raise ArgumentError do Isbn.isbn10toisbn13(123) end
# must be 10 characters
assert_raise ArgumentError do Isbn.isbn10toisbn13(@isbn13) end
assert_equal("9780596101237", Isbn.isbn10toisbn13(@isbn10))
end
def test_check_digit_13
# must be 12 characters
assert_raise ArgumentError do Isbn.check_digit_13(@isbn13) end
assert_equal(7, Isbn.check_digit_13(@isbn13[0..-2]))
assert_equal(0, Isbn.check_digit_13("123456789018"))
end
def test_isbn13
["0596008627", "1565929470", "0596002734", "0596004001", "0596527357",
"0596101635", "0596005059", "0596527063", "1565926374", "0596526946",
"1565926420", "0596101805", "059652742X", "156592455X", "0596006446",
"0596008473", "0596009607", "0596100582", "0596100493", "0596004427",
"1565925890", "1565927141", "059651610X"].each {|isbn|
puts "Testing #{isbn}"
assert_equal(PDB::ProdDB.new(isbn).isbn13, Isbn.isbn10toisbn13(isbn), "Bad checksum!")
}
end
end