app/models/identifier/global/isbn.rb
# Officially all ISBNs are now 13 digits - however anything published prior to 2007 may have a 10 digit format that
# requires special processing (see isbn.org) to convert a 10 digit ISBN to a 13 digit ISBN. For our purposes we will
# allow either a 10 digit or a 13 digit ISBN. - 1/31/2014
#
# From the International ISBN Agency (http://isbn-international.org/faqs) on 1/31/2014:
#
# Each ISBN consists of 5 parts with each section being separated by spaces or hyphens.
# Three of the five elements may be of varying length:
# Prefix element – currently this can only be either 978 or 979 (it is always 3 digits).
# Registration group element – this identifies the particular country, geographical region, or language area
# participating in the ISBN system. This element may be between 1 and 5 digits in length.
# Registrant element - this identifies the particular publisher or imprint. This may be up to 7 digits in length.
# Publication element – this identifies the particular edition and format of a specific title. This may be up to 6
# digits in length
# Check digit – this is always the final single digit that mathematically validates the rest of the number.
# It is calculated using a Modulus 10 system with alternate weights of 1 and 3.
#
# All of this means that it is difficult to validate an ISBN.
#
# validations contains at least 10 digit & only digits, dashes, or spaces
# validates :identifier, :format => {:with => /\A[\d\s-]*\z/, :message => 'Only digits, spaces and dashes allowed.'}
# doesn't allow blank identifier, because can only create an object if it has an identifier.
#
class Identifier::Global::Isbn < Identifier::Global
validate :using_isbn_class
def using_isbn_class
unless identifier.nil?
isbn = identifier.upcase
# 'ISBN-13: 978-0-596-52068-7'
isbn.gsub!('ISBN-10', '')
type = $&
isbn.gsub!('ISBN-13', '')
type ||= $&
type.gsub!('ISBN-', '') unless type.nil?
isbn.gsub!('ISBN', '')
# ': 978-0-596-52068-7'
isbn.gsub!(':', '')
# ' 978-0-596-52068-7'
isbn.strip!
# '978-0-596-52068-7' or '978 0 596 52068 7'
# if there are spaces or hyphens, check to see that they are in the right places
/^(\d{3}[ -]{0,1}){0,1}\d{1,5}[ -]{0,1}\d{1,5}[ -]{0,1}\d{1,5}[ -]{0,1}(?<last>.)$/ =~ isbn
isbn.gsub!(' ', '')
isbn.gsub!('-', '')
# if there are *any* non-digits left, it is improperly formed
# NB! 'X' is a valid digit in this case
/[^\dX]/ =~ isbn
unless $&.nil?
errors.add(:identifier, "'#{identifier}' is an improperly formed ISBN.")
return
end
# the final (checksum) digit has alreaqdy been identified, so we need 12 or 9 here
/^(?<isbn_13>\d{12})\d$|^(?<isbn_10>\d{9}).$/ =~ isbn
if ($&.nil?) or
(isbn_10.nil? and isbn_13.nil?) or
((isbn_10.nil? and type == '10') or (isbn_13.nil? and type == '13'))
errors.add(:identifier, "'#{identifier}' has the wrong number of digits.")
return
end
data = isbn.slice(0, isbn.size - 1)
sum = 0
index = 0
# last = 10 if last == 'X'
if data.size == 9
data.reverse!
data.each_char { |c|
factor = (index + 2)
sum += factor * c.to_i
index += 1
}
check_byte = 11 - (sum % 11)
if check_byte == 10
check_byte = 'X'
else
if check_byte == 11
check_byte = 0
end
end
else
data.each_char { |c|
factor = (((index % 2) * 2) + 1)
sum += factor * c.to_i
index += 1
}
check_byte = 10 - (sum % 10)
if check_byte == 10
check_byte = 0;
end
end
# check_byte and/or last might be an 'X'
if check_byte.to_s != last
errors.add(:identifier, "'#{identifier}' has bad check digit.")
return
end
end
end
def ten_or_thirteen_digits?
# @todo beth will talk to Jim when she gets back from vacation on how to correctly extract only the digits from the string.
#count = 0
#carray = self.chars
#carray.each do |c|
# if c.is_digit?
#end
end
end