active_validation 2.6.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -1
- data/.gitignore +1 -1
- data/.rspec +1 -1
- data/.travis.yml +1 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +2 -2
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/active_validation.gemspec +14 -15
- data/bin/console +1 -1
- data/bin/rake +6 -6
- data/bin/setup +1 -1
- data/lib/active_validation/matchers/ensure_valid_alpha_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_alpha_numeric_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_base64_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_boolean_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_coordinate_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_credit_card_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_currency_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_cusip_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_email_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_equality_matcher_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_hex_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_imei_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_ip_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_isbn_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_isin_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_mac_address_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_name_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_password_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_phone_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_sedol_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_slug_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_ssn_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_tracking_number_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_type_format_of.rb +3 -3
- data/lib/active_validation/matchers/ensure_valid_url_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_username_format_of.rb +4 -4
- data/lib/active_validation/matchers/ensure_valid_uuid_format_of.rb +4 -4
- data/lib/active_validation/validators/alpha_numeric_validator.rb +3 -4
- data/lib/active_validation/validators/alpha_validator.rb +3 -4
- data/lib/active_validation/validators/base64_validator.rb +3 -4
- data/lib/active_validation/validators/boolean_validator.rb +4 -4
- data/lib/active_validation/validators/coordinate_validator.rb +11 -12
- data/lib/active_validation/validators/credit_card_validator.rb +31 -39
- data/lib/active_validation/validators/currency_validator.rb +3 -4
- data/lib/active_validation/validators/cusip_validator.rb +7 -9
- data/lib/active_validation/validators/email_validator.rb +5 -5
- data/lib/active_validation/validators/equality_validator.rb +9 -13
- data/lib/active_validation/validators/hex_validator.rb +3 -4
- data/lib/active_validation/validators/imei_validator.rb +6 -8
- data/lib/active_validation/validators/ip_validator.rb +3 -4
- data/lib/active_validation/validators/isbn_validator.rb +8 -8
- data/lib/active_validation/validators/isin_validator.rb +15 -13
- data/lib/active_validation/validators/mac_address_validator.rb +8 -12
- data/lib/active_validation/validators/name_validator.rb +4 -5
- data/lib/active_validation/validators/password_validator.rb +3 -4
- data/lib/active_validation/validators/phone_validator.rb +3 -4
- data/lib/active_validation/validators/sedol_validator.rb +6 -8
- data/lib/active_validation/validators/slug_validator.rb +3 -4
- data/lib/active_validation/validators/ssn_validator.rb +3 -4
- data/lib/active_validation/validators/tracking_number_validator.rb +39 -46
- data/lib/active_validation/validators/type_validator.rb +1 -1
- data/lib/active_validation/validators/url_validator.rb +6 -6
- data/lib/active_validation/validators/username_validator.rb +3 -4
- data/lib/active_validation/validators/uuid_validator.rb +3 -4
- data/lib/active_validation/version.rb +1 -1
- data/lib/active_validation.rb +60 -61
- metadata +3 -17
@@ -2,7 +2,7 @@ class EmailValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s, options)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.email"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -21,9 +21,9 @@ class EmailValidator < ActiveModel::EachValidator
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def valid?(value, options)
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
options = [*(options.fetch(:domain, nil))]
|
25
|
+
|
26
|
+
valid_length?(value) && valid_format?(value) && valid_domain?(value, options)
|
27
27
|
end
|
28
28
|
|
29
|
-
end
|
29
|
+
end
|
@@ -7,28 +7,24 @@ class EqualityValidator < ActiveModel::EachValidator
|
|
7
7
|
"ArgumentError: missing ':to' attribute for comparison."
|
8
8
|
end
|
9
9
|
|
10
|
-
operator
|
10
|
+
operator = options.fetch(:operator, nil)
|
11
11
|
operators = OPERATORS.keys
|
12
12
|
unless operators.include?(operator)
|
13
13
|
raise ArgumentError,
|
14
|
-
"Unknown operator: #{operator.inspect}. Valid operators are: #{operators.map(&:inspect).join(
|
14
|
+
"Unknown operator: #{operator.inspect}. Valid operators are: #{operators.map(&:inspect).join(", ")}"
|
15
15
|
end
|
16
|
-
operator
|
16
|
+
operator = OPERATORS.fetch(operator)
|
17
17
|
|
18
|
-
unless value.send(operator
|
19
|
-
record.errors[attribute] <<
|
18
|
+
unless value.send(operator, record.send(to))
|
19
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.equality", attr: to, operator: operator))
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
25
|
OPERATORS = {
|
26
|
-
less_than: :<,
|
27
|
-
|
28
|
-
|
29
|
-
greater_than_or_equal_to: :>=,
|
30
|
-
equal_to: :==,
|
31
|
-
not_equal_to: :!=
|
32
|
-
}.freeze
|
26
|
+
less_than: :<, less_than_or_equal_to: :<=, greater_than: :>,
|
27
|
+
greater_than_or_equal_to: :>=, equal_to: :==, not_equal_to: :!=
|
28
|
+
}
|
33
29
|
|
34
|
-
end
|
30
|
+
end
|
@@ -2,7 +2,7 @@ class HexValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.hex"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class HexValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,7 +2,7 @@ class ImeiValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.imei"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,23 +17,21 @@ class ImeiValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid_luhn?(value)
|
20
|
-
number = value.gsub(/\D/,
|
20
|
+
number = value.gsub(/\D/, "").reverse
|
21
21
|
|
22
22
|
total = 0
|
23
|
-
number.chars.
|
23
|
+
number.chars.each_with_index do |c, i|
|
24
24
|
result = c.to_i
|
25
25
|
result *= 2 if i.odd?
|
26
26
|
result = (1 + (result - 10)) if (result >= 10)
|
27
|
-
total
|
27
|
+
total += result
|
28
28
|
end
|
29
29
|
|
30
30
|
(total % 10).zero?
|
31
31
|
end
|
32
32
|
|
33
33
|
def valid?(value)
|
34
|
-
valid_length?(value) &&
|
35
|
-
valid_format?(value) &&
|
36
|
-
valid_luhn?(value)
|
34
|
+
valid_length?(value) && valid_format?(value) && valid_luhn?(value)
|
37
35
|
end
|
38
36
|
|
39
|
-
end
|
37
|
+
end
|
@@ -2,7 +2,7 @@ class IpValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.ip"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class IpValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,18 +2,19 @@ class IsbnValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.isbn"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
+
CHARS_VALUES = ["0", "1", "2" , "3", "4", "5", "6", "7", "8", "9", "0", "x"]
|
12
|
+
|
11
13
|
def valid_format?(value)
|
12
|
-
|
13
|
-
value = value.gsub(/-| /,
|
14
|
+
return(false) if value.empty?
|
15
|
+
value = value.gsub(/-| /, "").downcase.chars
|
14
16
|
|
15
|
-
[10, 13].
|
16
|
-
value.all? { |c| ['0', '1', '2' , '3', '4', '5', '6', '7', '8', '9', '0', 'x'].freeze.include?(c) }
|
17
|
+
[10, 13].include?(value.size) && value.all? { |c| CHARS_VALUES.include?(c) }
|
17
18
|
end
|
18
19
|
|
19
20
|
def valid_length?(value)
|
@@ -21,8 +22,7 @@ class IsbnValidator < ActiveModel::EachValidator
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def valid?(value)
|
24
|
-
valid_length?(value) &&
|
25
|
-
valid_format?(value)
|
25
|
+
valid_length?(value) && valid_format?(value)
|
26
26
|
end
|
27
27
|
|
28
|
-
end
|
28
|
+
end
|
@@ -2,23 +2,27 @@ class IsinValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.isin"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def valid_checksum?(value)
|
12
|
-
characters
|
13
|
-
digits
|
14
|
-
even_values = digits.values_at(*
|
15
|
-
odd_values
|
16
|
-
|
17
|
-
longest, shortest =
|
12
|
+
characters = value.chars
|
13
|
+
digits = characters.map { |c| c.match(/[A-Z]/) ? (c.ord - 55) : c.to_i }
|
14
|
+
even_values = digits.values_at(*digits.each_index.select { |i| i.even? })
|
15
|
+
odd_values = digits.values_at(*digits.each_index.select { |i| i.odd? })
|
16
|
+
|
17
|
+
longest, shortest = if even_values.last == characters.map(&:to_i)
|
18
|
+
[even_values, odd_values]
|
19
|
+
else
|
20
|
+
[odd_values, even_values]
|
21
|
+
end
|
18
22
|
|
19
23
|
longest = longest.map { |i| i * 2 }
|
20
|
-
values
|
21
|
-
total
|
24
|
+
values = longest.concat(shortest).to_s.scan(/./).map(&:to_i)
|
25
|
+
total = values.inject(&:+)
|
22
26
|
|
23
27
|
(10 - (total % 10)) % 10
|
24
28
|
end
|
@@ -32,9 +36,7 @@ class IsinValidator < ActiveModel::EachValidator
|
|
32
36
|
end
|
33
37
|
|
34
38
|
def valid?(value)
|
35
|
-
valid_length?(value) &&
|
36
|
-
valid_format?(value) &&
|
37
|
-
valid_checksum?(value)
|
39
|
+
valid_length?(value) && valid_format?(value) && valid_checksum?(value)
|
38
40
|
end
|
39
41
|
|
40
|
-
end
|
42
|
+
end
|
@@ -2,24 +2,21 @@ class MacAddressValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.mac_address"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
11
|
DEFAULT_FORMATS = [
|
12
|
-
/^([\h]{2}:){5}[\h]{2}?$/i,
|
13
|
-
/^([\h]{
|
14
|
-
/^([\h]{
|
15
|
-
|
16
|
-
/^([\h]{4}[-|\.|\s]){2}[\h]{4}?$/i,
|
17
|
-
/^[\h]{12}?$/i
|
18
|
-
].freeze
|
12
|
+
/^([\h]{2}:){5}[\h]{2}?$/i, /^([\h]{2}[-|\.|\s]){5}[\h]{2}?$/i,
|
13
|
+
/^([\h]{6})[-|\.][\h]{6}?$/i, /^([\h]{6}):[\h]{6}?$/i,
|
14
|
+
/^([\h]{4}[-|\.|\s]){2}[\h]{4}?$/i, /^[\h]{12}?$/i
|
15
|
+
]
|
19
16
|
|
20
17
|
def valid_format?(value)
|
21
18
|
result = false
|
22
|
-
DEFAULT_FORMATS.
|
19
|
+
DEFAULT_FORMATS.each { |p| break if result = (value =~ p) }
|
23
20
|
result
|
24
21
|
end
|
25
22
|
|
@@ -28,8 +25,7 @@ class MacAddressValidator < ActiveModel::EachValidator
|
|
28
25
|
end
|
29
26
|
|
30
27
|
def valid?(value)
|
31
|
-
valid_length?(value) &&
|
32
|
-
valid_format?(value)
|
28
|
+
valid_length?(value) && valid_format?(value)
|
33
29
|
end
|
34
30
|
|
35
|
-
end
|
31
|
+
end
|
@@ -2,14 +2,14 @@ class NameValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.name"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def valid_format?(value)
|
12
|
-
value =~ /\A([a-zA-Z
|
12
|
+
value =~ /\A([a-zA-Z"-]+\s+){1,4}[a-zA-Z"-]*\z/i
|
13
13
|
end
|
14
14
|
|
15
15
|
def valid_length?(value)
|
@@ -17,8 +17,7 @@ class NameValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,7 +2,7 @@ class PasswordValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s, options)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.password"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class PasswordValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value, options)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value, options)
|
20
|
+
valid_length?(value) && valid_format?(value, options)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,7 +2,7 @@ class PhoneValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.phone"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class PhoneValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,18 +2,18 @@ class SedolValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.sedol"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def valid_checksum?(value)
|
12
|
-
digits
|
13
|
-
weights = [1, 3, 1, 7, 3, 9, 1]
|
12
|
+
digits = value.chars.map { |d| d.match(/[A-Z]/) ? (d.ord - 55) : d.to_i }
|
13
|
+
weights = [1, 3, 1, 7, 3, 9, 1]
|
14
14
|
|
15
15
|
total = 0
|
16
|
-
digits.
|
16
|
+
digits.each_with_index { |d, i| total += (weights[i] * d) }
|
17
17
|
|
18
18
|
(10 - total % 10) % 10
|
19
19
|
end
|
@@ -27,9 +27,7 @@ class SedolValidator < ActiveModel::EachValidator
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def valid?(value)
|
30
|
-
valid_length?(value) &&
|
31
|
-
valid_format?(value) &&
|
32
|
-
valid_checksum?(value)
|
30
|
+
valid_length?(value) && valid_format?(value) && valid_checksum?(value)
|
33
31
|
end
|
34
32
|
|
35
|
-
end
|
33
|
+
end
|
@@ -2,7 +2,7 @@ class SlugValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.slug"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class SlugValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,7 +2,7 @@ class SsnValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.ssn"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -17,8 +17,7 @@ class SsnValidator < ActiveModel::EachValidator
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def valid?(value)
|
20
|
-
valid_length?(value) &&
|
21
|
-
valid_format?(value)
|
20
|
+
valid_length?(value) && valid_format?(value)
|
22
21
|
end
|
23
22
|
|
24
|
-
end
|
23
|
+
end
|
@@ -2,32 +2,28 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
4
|
unless valid?(value.to_s, options)
|
5
|
-
record.errors[attribute] <<
|
5
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.tracking_number"))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
11
|
DEFAULT_CARRIERS_AND_SERVICES = {
|
12
|
-
dhl: {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
express: /^([0-9]{11,11})([0-9])$/,
|
18
|
-
ground: /^([0-9]{14,14})([0-9])$/,
|
19
|
-
ground18: /^[0-9]{2,2}([0-9]{15,15})([0-9])$/,
|
20
|
-
ground96: /^96[0-9]{5,5}([0-9]{14,14})([0-9])$/,
|
12
|
+
dhl: { express: /^([0-9]{9,9})([0-9])$/, express_air: /^([0-9]{10,10})([0-9])$/ },
|
13
|
+
fedex: {
|
14
|
+
express: /^([0-9]{11,11})([0-9])$/, ground: /^([0-9]{14,14})([0-9])$/,
|
15
|
+
ground18: /^[0-9]{2,2}([0-9]{15,15})([0-9])$/,
|
16
|
+
ground96: /^96[0-9]{5,5}([0-9]{14,14})([0-9])$/,
|
21
17
|
smart_post: /^((?:92)?[0-9]{5}[0-9]{14})([0-9])$/
|
22
18
|
},
|
23
19
|
ontrac: { express: /^(C[0-9]{13,13})([0-9])$/ },
|
24
|
-
ups:
|
25
|
-
usps:
|
20
|
+
ups: { express: /^1Z(\w{15,15})(\w)$/ },
|
21
|
+
usps: {
|
26
22
|
usps13: /^([A-Z]{2,2})([0-9]{9,9})([A-Z]{2,2})$/,
|
27
23
|
usps20: /^([0-9]{2,2})([0-9]{9,9})([0-9]{8,8})([0-9])$/,
|
28
24
|
usps91: /^(?:420\d{5})?(9[1-5](?:[0-9]{19}|[0-9]{23}))([0-9])$/
|
29
25
|
}
|
30
|
-
}
|
26
|
+
}
|
31
27
|
|
32
28
|
# DHL
|
33
29
|
DEFAULT_CARRIERS_AND_SERVICES.fetch(:dhl).each do |service, pattern|
|
@@ -50,26 +46,26 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
50
46
|
sequence, check_digit = formula
|
51
47
|
|
52
48
|
total = 0
|
53
|
-
sequence.chars.zip([3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1]
|
49
|
+
sequence.chars.zip([3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1]).each do |char1, char2|
|
54
50
|
total += (char1.to_i * char2)
|
55
51
|
end
|
56
52
|
|
57
53
|
(total % 11 % 10) == check_digit.to_i
|
58
54
|
end
|
59
55
|
|
60
|
-
DEFAULT_CARRIERS_AND_SERVICES.fetch(:fedex).
|
56
|
+
DEFAULT_CARRIERS_AND_SERVICES.fetch(:fedex).select { |k, v| [:ground, :ground18, :ground96].include?(k) }.each_with_index do |(service, pattern), i|
|
61
57
|
define_method("valid_fedex_#{service}_checksum?") do |value|
|
62
|
-
return(false) unless value.size == [15, 18, 22].
|
58
|
+
return(false) unless value.size == [15, 18, 22].at(i)
|
63
59
|
|
64
60
|
formula = value.scan(pattern).flatten.compact
|
65
61
|
return(false) if formula.empty?
|
66
62
|
sequence, check_digit = formula
|
67
63
|
|
68
64
|
total = 0
|
69
|
-
sequence.chars.reverse.
|
65
|
+
sequence.chars.reverse.each_with_index do |character, i|
|
70
66
|
result = character.to_i
|
71
67
|
result *= 3 if i.even?
|
72
|
-
total
|
68
|
+
total += result
|
73
69
|
end
|
74
70
|
|
75
71
|
check = total % 10
|
@@ -88,10 +84,10 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
88
84
|
sequence, check_digit = formula
|
89
85
|
|
90
86
|
total = 0
|
91
|
-
sequence.chars.reverse.
|
87
|
+
sequence.chars.reverse.each_with_index do |character, i|
|
92
88
|
result = character.to_i
|
93
89
|
result *= 3 if i.even?
|
94
|
-
total
|
90
|
+
total += result
|
95
91
|
end
|
96
92
|
|
97
93
|
check = total % 10
|
@@ -100,20 +96,20 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
100
96
|
end
|
101
97
|
|
102
98
|
# Ontrac & UPS
|
103
|
-
DEFAULT_CARRIERS_AND_SERVICES.
|
99
|
+
DEFAULT_CARRIERS_AND_SERVICES.select { |k, v| [:ontrac, :ups].include?(k) }.each_with_index do |(carrier, services), i|
|
104
100
|
services.each do |service, pattern|
|
105
101
|
define_method("valid_#{carrier}_#{service}_checksum?") do |value|
|
106
|
-
return(false) unless value.size == [15, 18].
|
102
|
+
return(false) unless value.size == [15, 18].at(i)
|
107
103
|
|
108
104
|
formula = value.scan(pattern).flatten.compact
|
109
105
|
return(false) if formula.empty?
|
110
106
|
sequence, check_digit = formula
|
111
107
|
|
112
108
|
total = 0
|
113
|
-
sequence.chars.
|
109
|
+
sequence.chars.each_with_index do |character, i|
|
114
110
|
result = character[/[0-9]/] ? character.to_i : ((character[0].ord - 3) % 10)
|
115
111
|
result *= 2 if i.odd?
|
116
|
-
total
|
112
|
+
total += result
|
117
113
|
end
|
118
114
|
|
119
115
|
check = total % 10
|
@@ -127,24 +123,22 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
127
123
|
def valid_usps_usps13_checksum?(value)
|
128
124
|
return(false) unless value.size == 13
|
129
125
|
|
130
|
-
pattern
|
126
|
+
pattern = DEFAULT_CARRIERS_AND_SERVICES.fetch(:usps).fetch(:usps13)
|
131
127
|
sequence = value.scan(pattern).flatten.compact
|
132
128
|
return(false) if sequence.empty?
|
133
129
|
|
134
|
-
characters
|
130
|
+
characters = sequence[1].chars
|
135
131
|
check_digit = characters.pop.to_i
|
136
132
|
|
137
133
|
total = 0
|
138
|
-
characters.zip([8, 6, 4, 2, 3, 5, 9, 7]
|
134
|
+
characters.zip([8, 6, 4, 2, 3, 5, 9, 7]).each do |pair|
|
139
135
|
total += (pair[0].to_i * pair[1].to_i)
|
140
136
|
end
|
141
137
|
|
142
138
|
remainder = total % 11
|
143
|
-
check
|
144
|
-
when 1
|
145
|
-
|
146
|
-
when 0
|
147
|
-
5
|
139
|
+
check = case remainder
|
140
|
+
when 1 then 0;
|
141
|
+
when 0 then 5;
|
148
142
|
else
|
149
143
|
11 - remainder
|
150
144
|
end
|
@@ -155,18 +149,18 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
155
149
|
def valid_usps_usps20_checksum?(value)
|
156
150
|
return(false) unless value.size == 20
|
157
151
|
|
158
|
-
pattern
|
152
|
+
pattern = DEFAULT_CARRIERS_AND_SERVICES.fetch(:usps).fetch(:usps20)
|
159
153
|
sequence = value.scan(pattern).flatten.compact
|
160
154
|
return(false) if sequence.empty?
|
161
155
|
|
162
|
-
characters
|
156
|
+
characters = sequence.first(3).join.chars
|
163
157
|
check_digit = sequence.last.to_i
|
164
158
|
|
165
159
|
total = 0
|
166
|
-
characters.reverse.
|
160
|
+
characters.reverse.each_with_index do |character, i|
|
167
161
|
result = character.to_i
|
168
162
|
result *= 3 if i.even?
|
169
|
-
total
|
163
|
+
total += result
|
170
164
|
end
|
171
165
|
|
172
166
|
check = total % 10
|
@@ -178,18 +172,18 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
178
172
|
value = "91#{value}" unless value =~ /^(420\d{5})?9[1-5]/
|
179
173
|
return(false) unless value.size == 22
|
180
174
|
|
181
|
-
pattern
|
175
|
+
pattern = DEFAULT_CARRIERS_AND_SERVICES.fetch(:usps).fetch(:usps91)
|
182
176
|
sequence = value.scan(pattern).flatten.compact
|
183
177
|
return(false) if sequence.empty?
|
184
178
|
|
185
|
-
characters
|
179
|
+
characters = sequence.first.chars
|
186
180
|
check_digit = sequence.last.to_i
|
187
181
|
|
188
182
|
total = 0
|
189
|
-
characters.reverse.
|
183
|
+
characters.reverse.each_with_index do |character, i|
|
190
184
|
result = character.to_i
|
191
185
|
result *= 3 if i.even?
|
192
|
-
total
|
186
|
+
total += result
|
193
187
|
end
|
194
188
|
|
195
189
|
check = total % 10
|
@@ -199,9 +193,9 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
199
193
|
|
200
194
|
# Base
|
201
195
|
def valid_checksum?(value, options)
|
202
|
-
carrier = options
|
203
|
-
service = options
|
204
|
-
result
|
196
|
+
carrier = options.fetch(:carrier, nil)
|
197
|
+
service = options.fetch(:service, nil)
|
198
|
+
result = false
|
205
199
|
|
206
200
|
if carrier.nil? && service.nil?
|
207
201
|
DEFAULT_CARRIERS_AND_SERVICES.each do |carrier, services|
|
@@ -228,8 +222,7 @@ class TrackingNumberValidator < ActiveModel::EachValidator
|
|
228
222
|
end
|
229
223
|
|
230
224
|
def valid?(value, options)
|
231
|
-
valid_length?(value) &&
|
232
|
-
valid_checksum?(value, options)
|
225
|
+
valid_length?(value) && valid_checksum?(value, options)
|
233
226
|
end
|
234
227
|
|
235
|
-
end
|
228
|
+
end
|
@@ -6,7 +6,7 @@ class TypeValidator < ActiveModel::EachValidator
|
|
6
6
|
|
7
7
|
def validate_each(record, attribute, value)
|
8
8
|
unless valid?(value, options)
|
9
|
-
record.errors[attribute] <<
|
9
|
+
record.errors[attribute] << options.fetch(:message, I18n.t("active_validation.errors.messages.type"))
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|