active_validation 2.6.0 → 3.0.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.
- 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
|
|