Module: StdNum::ISBN

Extended by:
Helpers
Defined in:
lib/library_stdnums.rb

Overview

Validate, convert, and normalize ISBNs (10-digit or 13-digit)

Constant Summary collapse

TEN_TO_THIRTEEN_PREFIX =
'978'.freeze
VALID_THIRTEEN_PREFIX =
/\A97[89]/

Constants included from Helpers

Helpers::STDNUMPAT, Helpers::STDNUMPAT_MULTIPLE

Class Method Summary collapse

Methods included from Helpers

extractNumber, extract_multiple_numbers, reduce_to_basics

Class Method Details

.allNormalizedValues(isbn) ⇒ Array<String,String>?

Return an array of the ISBN13 and ISBN10 (in that order) for the passed in value. You'll only get one value back if it's a 13-digit ISBN that can't be converted to an ISBN10. it can't be recognized.

Examples:

Get the normalized values and index them (if valid) or original value (if not)

norms = StdNum::ISBN.allNormalizedValues(rawisbn)
doc['isbn'] = norms ? norms : [rawisbn]

Parameters:

  • isbn (String)

    The original ISBN, in 10-character or 13-digit format

Returns:

  • (Array<String,String>, nil)

    Either the (one or two) normalized ISBNs, or nil if



180
181
182
183
184
185
186
187
188
189
# File 'lib/library_stdnums.rb', line 180

def self.allNormalizedValues isbn
  isbn = reduce_to_basics isbn, [10,13]
  return [] unless isbn
  case isbn.size
  when 10
    return [self.convert_to_13(isbn), isbn]
  when 13
    return [isbn, self.convert_to_10(isbn)].compact
  end
end

.at_least_trying?(isbn) ⇒ Boolean

Does it even look like an ISBN?

Returns:

  • (Boolean)


71
72
73
# File 'lib/library_stdnums.rb', line 71

def self.at_least_trying? isbn
  reduce_to_basics(isbn, [10,13]) ? true : false
end

.checkdigit(isbn, preprocessed = false) ⇒ String?

Compute check digits for 10 or 13-digit ISBNs. See algorithm at http://en.wikipedia.org/wiki/International_Standard_Book_Number

Parameters:

  • isbn (String)

    The ISBN (we'll try to clean it up if possible)

  • preprocessed (Boolean) (defaults to: false)

    Set to true if the ISBN has already been through reduce_to_basics

Returns:

  • (String, nil)

    the one-character checkdigit, or nil if it's not an ISBN string



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/library_stdnums.rb', line 81

def self.checkdigit isbn, preprocessed = false
  isbn = reduce_to_basics isbn, [10,13] unless preprocessed
  return nil unless isbn

  checkdigit = 0
  if isbn.size == 10
    digits = isbn[0..8].split(//).map {|i| i.to_i}
    (1..9).each do |i|
      checkdigit += digits[i-1] * i
    end
    checkdigit = checkdigit % 11
    return 'X' if checkdigit == 10
    return checkdigit.to_s
  else # size == 13
    checkdigit = 0
    digits = isbn[0..11].split(//).map {|i| i.to_i}
    6.times do
      checkdigit += digits.shift
      checkdigit += digits.shift * 3
    end
    check = 10 - (checkdigit % 10)
    check = 0 if check == 10
    return check.to_s
  end
end

.convert_to_10(isbn) ⇒ String

Convert to 10 if it's 13 digits and the first three digits are 978. Pass through anything 10-digits, and return nil for everything else.

Parameters:

  • isbn (String)

    The ISBN (we'll try to clean it up if possible)

Returns:

  • (String)

    The converted 10-character ISBN, nil if something looks wrong, or whatever was passed in if it already looked like a 10-digit ISBN



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/library_stdnums.rb', line 157

def self.convert_to_10 isbn
  isbn = reduce_to_basics isbn, [10,13]

  # Already 10 digits? Just return
  return isbn if isbn.size == 10

  # Can't be converted to ISBN-10? Bail
  return nil unless isbn[0..2] == TEN_TO_THIRTEEN_PREFIX

  prefix = isbn[3..11]
  return prefix + self.checkdigit(prefix + '0')
end

.convert_to_13(isbn) ⇒ String?

To convert to an ISBN13, throw a '978' on the front and compute the checkdigit We leave 13-digit numbers alone, figuring they're already ok. NO CHECKSUM CHECK IS DONE FOR 13-DIGIT ISBNS! and return nil on anything that's not the right length

Parameters:

  • isbn (String)

    The ISBN (we'll try to clean it up if possible)

Returns:

  • (String, nil)

    The converted 13-character ISBN, nil if something looks wrong, or whatever was passed in if it already looked like a 13-digit ISBN



143
144
145
146
147
148
149
150
# File 'lib/library_stdnums.rb', line 143

def self.convert_to_13 isbn
  isbn = reduce_to_basics isbn, [10,13]
  return nil unless isbn
  return nil unless valid?(isbn, true)
  return isbn if isbn.size == 13
  prefix = TEN_TO_THIRTEEN_PREFIX + isbn[0..8]
  return prefix + self.checkdigit(prefix + '0', true)
end

.normalize(rawisbn) ⇒ String?

For an ISBN, normalizing it is the same as converting to ISBN 13 and making sure it's valid

Parameters:

  • rawisbn (String)

    The ISBN to normalize

Returns:

  • (String, nil)

    the normalized (to 13 digit) ISBN, or nil on failure



128
129
130
131
132
133
134
135
# File 'lib/library_stdnums.rb', line 128

def self.normalize rawisbn
  isbn = convert_to_13 rawisbn
  if isbn
    return isbn
  else
    return nil
  end
end

.valid?(isbn, preprocessed = false) ⇒ Boolean

Check to see if the checkdigit is correct

Parameters:

  • isbn (String)

    The ISBN (we'll try to clean it up if possible)

  • preprocessed (Boolean) (defaults to: false)

    Set to true if the ISBN has already been through reduce_to_basics

Returns:

  • (Boolean)

    Whether or not the checkdigit is correct. Sneakily, return 'nil' for values that don't even look like ISBNs, and 'false' for those that look possible but don't normalize / have bad checkdigits



113
114
115
116
117
118
119
120
# File 'lib/library_stdnums.rb', line 113

def self.valid? isbn, preprocessed = false
  return nil if isbn.nil?
  isbn = reduce_to_basics(isbn, [10,13]) unless preprocessed
  return nil unless isbn
  return false unless isbn[-1..-1] == self.checkdigit(isbn, true)
  return false unless isbn.size == 10 || valid_isbn13_prefix?(isbn)
  return true
end

.valid_isbn13_prefix?(isbn13) ⇒ Boolean

Checks for a valid ISBN13 prefix ISBN13 always starts with 978 or 979. For example: 1000000000012 has a valid check digit, but is not a valid ISBN13.

Parameters:

  • isbn13 (String)

    The ISBN13 to be checked.

Returns:

  • (Boolean)

    If true then the prefix is valid



196
197
198
# File 'lib/library_stdnums.rb', line 196

def self.valid_isbn13_prefix?(isbn13)
  isbn13.size == 13 and !!VALID_THIRTEEN_PREFIX.match(isbn13)
end