Class: Identifiers::ISBN

Inherits:
Object
  • Object
show all
Defined in:
lib/identifiers/isbn.rb

Constant Summary collapse

REGEX_13 =
/\b97[89]\d{10}\b/
REGEX_10 =
/\b\d{9}(?:\d|X)\b/
REGEX_A =
%r{\b(?<=10\.)97[89]\.\d{2,8}/\d{1,7}\b}

Class Method Summary collapse

Class Method Details

.digits_of(isbn) ⇒ Object



56
57
58
# File 'lib/identifiers/isbn.rb', line 56

def self.digits_of(isbn)
  isbn.each_char.map { |char| char == 'X' ? 10 : Integer(char) }.to_enum
end

.extract(str) ⇒ Object



7
8
9
# File 'lib/identifiers/isbn.rb', line 7

def self.extract(str)
  extract_isbn_as(str) + extract_thirteen_digit_isbns(str) + extract_ten_digit_isbns(str)
end

.extract_isbn_as(str) ⇒ Object



11
12
13
# File 'lib/identifiers/isbn.rb', line 11

def self.extract_isbn_as(str)
  extract_thirteen_digit_isbns(str.scan(REGEX_A).join("\n").tr('/.', ''))
end

.extract_ten_digit_isbns(str) ⇒ Object



19
20
21
22
23
24
25
26
27
# File 'lib/identifiers/isbn.rb', line 19

def self.extract_ten_digit_isbns(str)
  str.gsub(/(?<=\d)[- ](?=[\dX])/i, '').scan(REGEX_10).select { |isbn| valid_isbn_10?(isbn) }.map { |isbn|
    isbn.chop!
    isbn.prepend('978')
    isbn << isbn_13_check_digit(isbn).to_s

    isbn
  }
end

.extract_thirteen_digit_isbns(str) ⇒ Object



15
16
17
# File 'lib/identifiers/isbn.rb', line 15

def self.extract_thirteen_digit_isbns(str)
  str.gsub(/(?<=\d)[- ](?=\d)/, '').scan(REGEX_13).select { |isbn| valid_isbn_13?(isbn) }
end

.isbn_13_check_digit(isbn) ⇒ Object



29
30
31
32
33
34
35
36
37
38
# File 'lib/identifiers/isbn.rb', line 29

def self.isbn_13_check_digit(isbn)
  sum = digits_of(isbn).zip([1, 3].cycle).map { |digit, weight| digit * weight }.reduce(:+)
  check_digit = 10 - (sum % 10)

  if check_digit == 10
    0
  else
    check_digit
  end
end

.valid_isbn_10?(isbn) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
54
# File 'lib/identifiers/isbn.rb', line 48

def self.valid_isbn_10?(isbn)
  return false unless isbn =~ REGEX_10

  result = digits_of(isbn).with_index.map { |digit, weight| digit * weight.succ }.reduce(:+)

  (result % 11).zero?
end

.valid_isbn_13?(isbn) ⇒ Boolean

Returns:

  • (Boolean)


40
41
42
43
44
45
46
# File 'lib/identifiers/isbn.rb', line 40

def self.valid_isbn_13?(isbn)
  return false unless isbn =~ REGEX_13

  result = digits_of(isbn).zip([1, 3].cycle).map { |digit, weight| digit * weight }.reduce(:+)

  (result % 10).zero?
end