isbn_validation 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -3
- data/lib/isbn_validation.rb +64 -55
- data/lib/isbn_validation/version.rb +1 -1
- data/test/isbn_validation_test.rb +68 -45
- data/test/models.rb +8 -3
- metadata +14 -3
data/README.md
CHANGED
@@ -21,9 +21,9 @@ support, please use v0.1.2.
|
|
21
21
|
## Example
|
22
22
|
|
23
23
|
class Book < ActiveRecord::Base
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
validates :isbn, :isbn_format => true
|
25
|
+
validates :isbn10, :isbn_format => { :with => :isbn10 }
|
26
|
+
validates :isbn13, :isbn_format => { :with => :isbn13 }
|
27
27
|
end
|
28
28
|
|
29
29
|
------
|
data/lib/isbn_validation.rb
CHANGED
@@ -6,9 +6,9 @@ require "isbn_validation/version"
|
|
6
6
|
# but this behavior can be modified using configuration options (as shown below).
|
7
7
|
#
|
8
8
|
# class Book < ActiveRecord::Base
|
9
|
-
#
|
10
|
-
# #validates_isbn :isbn10, :with => :isbn10
|
11
|
-
# #validates_isbn :isbn13, :with => :isbn13
|
9
|
+
# validates :isbn, :isbn_format => true
|
10
|
+
# # validates_isbn :isbn10, :isbn_format => { :with => :isbn10 }
|
11
|
+
# # validates_isbn :isbn13, :isbn_format => { :with => :isbn13 }
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# Configuration options:
|
@@ -23,75 +23,84 @@ require "isbn_validation/version"
|
|
23
23
|
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
|
24
24
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
25
25
|
# method, proc or string should return or evaluate to a true or false value.
|
26
|
-
module
|
27
|
-
module
|
28
|
-
|
29
|
-
|
30
|
-
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/
|
26
|
+
module ValidationExtensions
|
27
|
+
module IsbnValidation
|
28
|
+
ISBN10_REGEX = /^(?:\d[\ |-]?){9}[\d|X]$/
|
29
|
+
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/
|
31
30
|
|
32
|
-
|
33
|
-
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
class IsbnFormatValidator < ActiveModel::EachValidator
|
36
|
+
def initialize(options)
|
37
|
+
options[:message] ||= "is not a valid ISBN code"
|
38
|
+
options[:allow_nil] ||= false
|
39
|
+
options[:allow_blank] ||= false
|
40
|
+
super(options)
|
34
41
|
end
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
def validate_each(record, attribute, value)
|
44
|
+
valid_isbn = case options[:with]
|
45
|
+
when :isbn10
|
46
|
+
validate_with_isbn10(value)
|
47
|
+
when :isbn13
|
48
|
+
validate_with_isbn13(value)
|
49
|
+
else
|
50
|
+
validate_with_isbn10(value) || validate_with_isbn13(value)
|
51
|
+
end
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
valid = case configuration[:with]
|
45
|
-
when :isbn10
|
46
|
-
validate_with_isbn10(value)
|
47
|
-
when :isbn13
|
48
|
-
validate_with_isbn13(value)
|
49
|
-
else
|
50
|
-
validate_with_isbn10(value) || validate_with_isbn13(value)
|
51
|
-
end
|
52
|
-
record.errors.add(attr_name, configuration[:message]) unless valid
|
53
|
-
end
|
53
|
+
unless valid_isbn || (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
54
|
+
record.errors.add(attribute, options[:message])
|
54
55
|
end
|
56
|
+
end
|
55
57
|
|
56
|
-
|
57
|
-
if (isbn || '').match(ISBN10_REGEX)
|
58
|
-
isbn_values = isbn.upcase.gsub(/\ |-/, '').split('')
|
59
|
-
check_digit = isbn_values.pop # last digit is check digit
|
60
|
-
check_digit = (check_digit == 'X') ? 10 : check_digit.to_i
|
58
|
+
private
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
60
|
+
def validate_with_isbn10(isbn) #:nodoc:
|
61
|
+
if (isbn || '').match(ISBN10_REGEX)
|
62
|
+
isbn_values = isbn.upcase.gsub(/\ |-/, '').split('')
|
63
|
+
check_digit = isbn_values.pop # last digit is check digit
|
64
|
+
check_digit = (check_digit == 'X') ? 10 : check_digit.to_i
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
sum = 0
|
67
|
+
isbn_values.each_with_index do |value, index|
|
68
|
+
sum += (index + 1) * value.to_i
|
70
69
|
end
|
70
|
+
|
71
|
+
(sum % 11) == check_digit
|
72
|
+
else
|
73
|
+
false
|
71
74
|
end
|
75
|
+
end
|
72
76
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
def validate_with_isbn13(isbn) #:nodoc:
|
78
|
+
if (isbn || '').match(ISBN13_REGEX)
|
79
|
+
isbn_values = isbn.upcase.gsub(/\ |-/, '').split('')
|
80
|
+
check_digit = isbn_values.pop.to_i # last digit is check digit
|
77
81
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
82
|
+
sum = 0
|
83
|
+
isbn_values.each_with_index do |value, index|
|
84
|
+
multiplier = (index % 2 == 0) ? 1 : 3
|
85
|
+
sum += multiplier * value.to_i
|
86
|
+
end
|
83
87
|
|
84
|
-
|
85
|
-
|
88
|
+
result = (10 - (sum % 10))
|
89
|
+
result = 0 if result == 10
|
86
90
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
+
result == check_digit
|
92
|
+
else
|
93
|
+
false
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
97
|
+
|
98
|
+
module ClassMethods
|
99
|
+
def validates_isbn(*attr_names)
|
100
|
+
validates_with IsbnFormatValidator, _merge_attributes(attr_names)
|
101
|
+
end
|
102
|
+
end
|
94
103
|
end
|
95
104
|
end
|
96
105
|
|
97
|
-
ActiveRecord::Base.send(:include,
|
106
|
+
ActiveRecord::Base.send(:include, ValidationExtensions::IsbnValidation)
|
@@ -2,96 +2,119 @@ require File.dirname(__FILE__) + '/test_helper'
|
|
2
2
|
require File.dirname(__FILE__) + '/models'
|
3
3
|
|
4
4
|
class IsbnValidationTest < Test::Unit::TestCase
|
5
|
-
def setup
|
6
|
-
@book = Book.new
|
7
|
-
end
|
8
|
-
|
9
5
|
def test_isbn10_should_match_regex
|
10
6
|
isbn = '1590599934'
|
11
|
-
assert isbn.match(
|
7
|
+
assert isbn.match(ValidationExtensions::IsbnValidation::ISBN10_REGEX)
|
12
8
|
end
|
13
9
|
|
14
10
|
def test_isbn10_should_not_match_regex
|
15
11
|
isbn = 'abc123ab3344'
|
16
|
-
assert !isbn.match(
|
12
|
+
assert !isbn.match(ValidationExtensions::IsbnValidation::ISBN10_REGEX)
|
17
13
|
end
|
18
14
|
|
19
15
|
def test_isbn10_with_dashes_and_spaces_should_match_regex
|
20
16
|
isbn = '159-059 9934'
|
21
|
-
assert isbn.match(
|
17
|
+
assert isbn.match(ValidationExtensions::IsbnValidation::ISBN10_REGEX)
|
22
18
|
end
|
23
19
|
|
24
20
|
def test_isbn13_should_match_regex
|
25
21
|
isbn = '9781590599938'
|
26
|
-
assert isbn.match(
|
22
|
+
assert isbn.match(ValidationExtensions::IsbnValidation::ISBN13_REGEX)
|
27
23
|
end
|
28
24
|
|
29
25
|
def test_isbn13_should_not_match_regex
|
30
26
|
isbn = '9991a9010599938'
|
31
|
-
assert !isbn.match(
|
27
|
+
assert !isbn.match(ValidationExtensions::IsbnValidation::ISBN13_REGEX)
|
32
28
|
end
|
33
29
|
|
34
30
|
def test_isbn13_with_dashes_and_spaces_should_match_regex
|
35
31
|
isbn = '978-159059 9938'
|
36
|
-
assert isbn.match(
|
32
|
+
assert isbn.match(ValidationExtensions::IsbnValidation::ISBN13_REGEX)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_isbn_should_match_either_isbn10_or_isbn13
|
36
|
+
book = Book.new
|
37
|
+
book.isbn = 'invalid'
|
38
|
+
assert !book.valid?
|
39
|
+
book.isbn = '1590599934'
|
40
|
+
assert book.valid?
|
41
|
+
book.isbn = '9781590599938'
|
42
|
+
assert book.valid?
|
37
43
|
end
|
38
44
|
|
39
45
|
def test_isbn10_should_pass_check_digit_verification
|
40
|
-
|
41
|
-
|
46
|
+
book = Book10.new
|
47
|
+
book.isbn = '159059993-4'
|
48
|
+
assert book.valid?
|
42
49
|
end
|
43
50
|
|
44
51
|
def test_isbn10_should_fail_check_digit_verification
|
45
|
-
|
46
|
-
|
52
|
+
book = Book10.new
|
53
|
+
book.isbn = '159059993-0'
|
54
|
+
assert !book.valid?
|
47
55
|
end
|
48
56
|
|
49
57
|
def test_isbn10_should_handle_x_character_checksum
|
50
|
-
|
51
|
-
|
58
|
+
book = Book10.new
|
59
|
+
book.isbn = '0-9722051-1-X'
|
60
|
+
assert book.valid?
|
52
61
|
end
|
53
62
|
|
54
63
|
def test_isbn13_should_pass_check_digit_verification
|
55
|
-
|
56
|
-
|
64
|
+
book = Book13.new
|
65
|
+
book.isbn = '978-1590599938'
|
66
|
+
assert book.valid?
|
57
67
|
end
|
58
68
|
|
59
69
|
def test_isbn13_should_fail_check_digit_verification
|
60
|
-
|
61
|
-
|
70
|
+
book = Book13.new
|
71
|
+
book.isbn = '978-1590599934'
|
72
|
+
assert !book.valid?
|
62
73
|
end
|
63
74
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
assert @book.valid?
|
75
|
+
def test_isbn13_with_zero_check_digit_should_validate
|
76
|
+
book = Book13.new
|
77
|
+
book.isbn = '978-1-60746-006-0'
|
78
|
+
assert book.valid?
|
69
79
|
end
|
70
80
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
assert
|
75
|
-
@book.isbn = '1590599934'
|
76
|
-
assert @book.valid?
|
81
|
+
def test_isbn_should_be_valid_to_isbn10_by_default
|
82
|
+
book = Book.new
|
83
|
+
book.isbn = '1590599934'
|
84
|
+
assert book.valid?
|
77
85
|
end
|
78
86
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
assert @book.valid?
|
87
|
+
def test_should_have_custom_error_message
|
88
|
+
book = Book.new
|
89
|
+
book.isbn = '978-159059AAAAAA'
|
90
|
+
book.valid?
|
91
|
+
assert_equal 'is too fantastical!', book.errors[:isbn].first
|
85
92
|
end
|
86
93
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
94
|
+
def test_blank_should_not_be_valid_by_default
|
95
|
+
book = Book13.new
|
96
|
+
book.isbn = ''
|
97
|
+
book.valid?
|
98
|
+
assert_equal 'is not a valid ISBN code', book.errors[:isbn].first
|
91
99
|
end
|
92
|
-
|
93
|
-
def
|
94
|
-
|
95
|
-
|
100
|
+
|
101
|
+
def test_should_have_an_option_to_allow_nil
|
102
|
+
book = Book13.new
|
103
|
+
book.isbn = nil
|
104
|
+
assert book.valid?
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_should_have_an_option_to_allow_blank
|
108
|
+
book = Book10.new
|
109
|
+
book.isbn = ''
|
110
|
+
assert book.valid?
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_should_support_old_syntax
|
114
|
+
book = OldBook.new
|
115
|
+
book.isbn = ''
|
116
|
+
assert !book.valid?
|
117
|
+
book.isbn = '1590599934'
|
118
|
+
assert book.valid?
|
96
119
|
end
|
97
120
|
end
|
data/test/models.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
class Book < ActiveRecord::Base
|
2
|
-
|
2
|
+
validates :isbn, :isbn_format => { :message => 'is too fantastical!' }
|
3
3
|
end
|
4
4
|
|
5
5
|
class Book10 < ActiveRecord::Base
|
6
6
|
set_table_name 'books'
|
7
|
-
|
7
|
+
validates :isbn, :isbn_format => { :with => :isbn10, :allow_blank => true }
|
8
8
|
end
|
9
9
|
|
10
10
|
class Book13 < ActiveRecord::Base
|
11
11
|
set_table_name 'books'
|
12
|
-
|
12
|
+
validates :isbn, :isbn_format => { :with => :isbn13, :allow_nil => true }
|
13
|
+
end
|
14
|
+
|
15
|
+
class OldBook < ActiveRecord::Base
|
16
|
+
set_table_name 'books'
|
17
|
+
validates_isbn :isbn
|
13
18
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: isbn_validation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,7 +14,7 @@ default_executable:
|
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
17
|
-
requirement: &
|
17
|
+
requirement: &2153258500 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,7 +22,18 @@ dependencies:
|
|
22
22
|
version: '3'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2153258500
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: sqlite3
|
28
|
+
requirement: &2153258080 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2153258080
|
26
37
|
description: isbn_validation adds an isbn validation routine to active record models.
|
27
38
|
email: nap@zerosum.org
|
28
39
|
executables: []
|