active_validation 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +14 -0
  4. data/.rspec +4 -0
  5. data/.travis.yml +13 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +1099 -0
  9. data/Rakefile +1 -0
  10. data/active_validation.gemspec +29 -0
  11. data/config/locales/en.yml +109 -0
  12. data/lib/active_validation/matchers/ensure_valid_alpha_format_of.rb +26 -0
  13. data/lib/active_validation/matchers/ensure_valid_alpha_numeric_format_of.rb +26 -0
  14. data/lib/active_validation/matchers/ensure_valid_base64_format_of.rb +26 -0
  15. data/lib/active_validation/matchers/ensure_valid_boolean_format_of.rb +26 -0
  16. data/lib/active_validation/matchers/ensure_valid_credit_card_format_of.rb +26 -0
  17. data/lib/active_validation/matchers/ensure_valid_currency_format_of.rb +26 -0
  18. data/lib/active_validation/matchers/ensure_valid_cusip_format_of.rb +26 -0
  19. data/lib/active_validation/matchers/ensure_valid_email_format_of.rb +26 -0
  20. data/lib/active_validation/matchers/ensure_valid_equality_matcher_of.rb +40 -0
  21. data/lib/active_validation/matchers/ensure_valid_hex_format_of.rb +26 -0
  22. data/lib/active_validation/matchers/ensure_valid_imei_format_of.rb +26 -0
  23. data/lib/active_validation/matchers/ensure_valid_ip_format_of.rb +26 -0
  24. data/lib/active_validation/matchers/ensure_valid_isbn_format_of.rb +26 -0
  25. data/lib/active_validation/matchers/ensure_valid_isin_format_of.rb +26 -0
  26. data/lib/active_validation/matchers/ensure_valid_latitude_format_of.rb +26 -0
  27. data/lib/active_validation/matchers/ensure_valid_longitude_format_of.rb +26 -0
  28. data/lib/active_validation/matchers/ensure_valid_mac_address_format_of.rb +26 -0
  29. data/lib/active_validation/matchers/ensure_valid_name_format_of.rb +26 -0
  30. data/lib/active_validation/matchers/ensure_valid_password_format_of.rb +26 -0
  31. data/lib/active_validation/matchers/ensure_valid_phone_format_of.rb +26 -0
  32. data/lib/active_validation/matchers/ensure_valid_sedol_format_of.rb +26 -0
  33. data/lib/active_validation/matchers/ensure_valid_slug_format_of.rb +26 -0
  34. data/lib/active_validation/matchers/ensure_valid_ssn_format_of.rb +26 -0
  35. data/lib/active_validation/matchers/ensure_valid_url_format_of.rb +26 -0
  36. data/lib/active_validation/matchers/ensure_valid_username_format_of.rb +26 -0
  37. data/lib/active_validation/matchers/ensure_valid_uuid_format_of.rb +26 -0
  38. data/lib/active_validation/validators/alpha_numeric_validator.rb +30 -0
  39. data/lib/active_validation/validators/alpha_validator.rb +31 -0
  40. data/lib/active_validation/validators/base64_validator.rb +9 -0
  41. data/lib/active_validation/validators/boolean_validator.rb +9 -0
  42. data/lib/active_validation/validators/credit_card_validator.rb +133 -0
  43. data/lib/active_validation/validators/currency_validator.rb +23 -0
  44. data/lib/active_validation/validators/cusip_validator.rb +33 -0
  45. data/lib/active_validation/validators/email_validator.rb +25 -0
  46. data/lib/active_validation/validators/equality_validator.rb +27 -0
  47. data/lib/active_validation/validators/hex_validator.rb +9 -0
  48. data/lib/active_validation/validators/imei_validator.rb +37 -0
  49. data/lib/active_validation/validators/ip_validator.rb +9 -0
  50. data/lib/active_validation/validators/isbn_validator.rb +24 -0
  51. data/lib/active_validation/validators/isin_validator.rb +38 -0
  52. data/lib/active_validation/validators/latitude_validator.rb +9 -0
  53. data/lib/active_validation/validators/longitude_validator.rb +9 -0
  54. data/lib/active_validation/validators/mac_address_validator.rb +24 -0
  55. data/lib/active_validation/validators/name_validator.rb +9 -0
  56. data/lib/active_validation/validators/password_validator.rb +23 -0
  57. data/lib/active_validation/validators/phone_validator.rb +9 -0
  58. data/lib/active_validation/validators/sedol_validator.rb +32 -0
  59. data/lib/active_validation/validators/slug_validator.rb +9 -0
  60. data/lib/active_validation/validators/ssn_validator.rb +9 -0
  61. data/lib/active_validation/validators/url_validator.rb +36 -0
  62. data/lib/active_validation/validators/username_validator.rb +9 -0
  63. data/lib/active_validation/validators/uuid_validator.rb +28 -0
  64. data/lib/active_validation/version.rb +3 -0
  65. data/lib/active_validation.rb +91 -0
  66. data/spec/lib/alpha_numeric_validator_spec.rb +91 -0
  67. data/spec/lib/alpha_validator_spec.rb +182 -0
  68. data/spec/lib/base64_validator_spec.rb +33 -0
  69. data/spec/lib/boolean_validator_spec.rb +35 -0
  70. data/spec/lib/credit_card_validator_spec.rb +686 -0
  71. data/spec/lib/currency_validator_spec.rb +63 -0
  72. data/spec/lib/cusip_validator_spec.rb +27 -0
  73. data/spec/lib/email_validator_spec.rb +109 -0
  74. data/spec/lib/equality_validator_spec.rb +334 -0
  75. data/spec/lib/hex_validator_spec.rb +73 -0
  76. data/spec/lib/imei_validator_spec.rb +41 -0
  77. data/spec/lib/ip_validator_spec.rb +33 -0
  78. data/spec/lib/isbn_validator_spec.rb +41 -0
  79. data/spec/lib/isin_validator_spec.rb +35 -0
  80. data/spec/lib/latitude_validator_spec.rb +31 -0
  81. data/spec/lib/longitude_validator_spec.rb +31 -0
  82. data/spec/lib/mac_address_validator_spec.rb +54 -0
  83. data/spec/lib/name_validator_spec.rb +39 -0
  84. data/spec/lib/password_validator_spec.rb +85 -0
  85. data/spec/lib/phone_validator_spec.rb +42 -0
  86. data/spec/lib/sedol_validator_spec.rb +31 -0
  87. data/spec/lib/slug_validator_spec.rb +41 -0
  88. data/spec/lib/ssn_validator_spec.rb +36 -0
  89. data/spec/lib/url_validator_spec.rb +106 -0
  90. data/spec/lib/username_validator_spec.rb +37 -0
  91. data/spec/lib/uuid_validator_spec.rb +157 -0
  92. data/spec/spec_helper.rb +12 -0
  93. metadata +260 -0
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_password_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "pass word")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.password'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_password_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_password_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_phone_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "555-123n")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.phone'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_phone_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_phone_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_sedol_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "1234")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.sedol'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_sedol_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_sedol_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_slug_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "sl ug")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.slug'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_slug_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_slug_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_ssn_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "555-0s-1234")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.ssn'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_ssn_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_ssn_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_url_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", ".")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.url'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_url_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_url_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_username_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "user name")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.username'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_username_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_username_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :ensure_valid_uuid_format_of do |attribute|
2
+ match do |model|
3
+ model.send("#{attribute}=", "1234uuid")
4
+ model.valid?
5
+
6
+ if model.errors.has_key?(attribute)
7
+ model.errors[attribute].include?(I18n.t('active_validation.errors.messages.uuid'))
8
+ end
9
+ end
10
+
11
+ failure_message do |model|
12
+ I18n.t(
13
+ 'active_validation.matchers.ensure_valid_uuid_format_of.failure_message_for_should',
14
+ attr: attribute.inspect,
15
+ model: model.class.name
16
+ )
17
+ end
18
+
19
+ failure_message_when_negated do |model|
20
+ I18n.t(
21
+ 'active_validation.matchers.ensure_valid_uuid_format_of.failure_message_for_should_not',
22
+ attr: attribute.inspect,
23
+ model: model.class.name
24
+ )
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ class AlphaNumericValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value, options)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.alpha_numeric'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value, options)
12
+ case options[:case]
13
+ when :lower
14
+ options[:strict] ? (value =~ /^[a-z0-9]+$/) : (value =~ /^[a-z0-9 ]+$/)
15
+ when :upper
16
+ options[:strict] ? (value =~ /^[A-Z0-9]+$/) : (value =~ /^[A-Z0-9 ]+$/)
17
+ else
18
+ options[:strict] ? (value =~ /^[A-Za-z0-9]+$/i) : (value =~ /^[A-Za-z0-9 ]+$/i)
19
+ end
20
+ end
21
+
22
+ def valid_length?(value)
23
+ value != ' '
24
+ end
25
+
26
+ def valid?(value, options)
27
+ valid_format?(value, options) && valid_length?(value)
28
+ end
29
+
30
+ end
@@ -0,0 +1,31 @@
1
+ class AlphaValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value, options)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.alpha'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value, options)
12
+ case options[:case]
13
+ when :lower
14
+ options[:strict] ? (value =~ /^[a-z]+$/) : (value =~ /^[a-z ]+$/)
15
+ when :upper
16
+ options[:strict] ? (value =~ /^[A-Z]+$/) : (value =~ /^[A-Z ]+$/)
17
+ else
18
+ options[:strict] ? (value =~ /^[A-Za-z]+$/i) : (value =~ /^[A-Za-z ]+$/i)
19
+ end
20
+ end
21
+
22
+ def valid_length?(value)
23
+ value != ' '
24
+ end
25
+
26
+ def valid?(value, options)
27
+ valid_format?(value, options) &&
28
+ valid_length?(value)
29
+ end
30
+
31
+ end
@@ -0,0 +1,9 @@
1
+ class Base64Validator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless value =~ /^(?:[A-Za-z0-9+$]{4})*(?:[A-Za-z0-9+$]{2}==|[A-Za-z0-9+$]{3}=|[A-Za-z0-9+$]{4})$/
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.base64'))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ class BooleanValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless [true, false, 1, 0].include?(value)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.boolean'))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,133 @@
1
+ class CreditCardValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value, options)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.credit_card'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ DEFAULT_LENGTHS = {
12
+ american_express: [15],
13
+ diners_club: [14, 16],
14
+ discover: [16],
15
+ jcb: [16],
16
+ laser: [16, 17, 18, 19],
17
+ maestro: [12, 13, 14, 15, 16, 17, 18, 19],
18
+ mastercard: [16],
19
+ solo: [16, 18, 19],
20
+ unionpay: [16, 17, 18, 19],
21
+ visa: [16]
22
+ }
23
+
24
+ DEFAULT_PREFIXES = {
25
+ american_express: ['34', '37'],
26
+ diners_club: ['300', '301', '302', '303', '304', '305', '36', '54', '55'],
27
+ discover: [
28
+ '6011', '622126', '622127', '622128', '622129', '62213',
29
+ '62214', '62215', '62216', '62217', '62218', '62219',
30
+ '6222', '6223', '6224', '6225', '6226', '6227', '6228',
31
+ '62290', '62291', '622920', '622921', '622922', '622923',
32
+ '622924', '622925', '644', '645', '646', '647', '648',
33
+ '649', '65'
34
+ ],
35
+ jcb: ['3528', '3529', '353', '354', '355', '356', '357', '358'],
36
+ laser: ['6304', '6706', '6771', '6709'],
37
+ maestro: [
38
+ '5018', '5020', '5038', '6304', '6759', '6761', '6762',
39
+ '6763', '6764', '6765', '6766'
40
+ ],
41
+ mastercard: ['51', '52', '53', '54', '55'],
42
+ solo: ['6334', '6767'],
43
+ unionpay: [
44
+ '622126', '622127', '622128', '622129', '62213', '62214',
45
+ '62215', '62216', '62217', '62218', '62219', '6222', '6223',
46
+ '6224', '6225', '6226', '6227', '6228', '62290', '62291',
47
+ '622920', '622921', '622922', '622923', '622924', '622925'
48
+ ],
49
+ visa: ['4']
50
+ }
51
+
52
+ def valid_format?(value, options)
53
+ if options[:strict]
54
+ value =~ /^[0-9]+$/
55
+ else
56
+ value =~ /^[0-9 -.]+$/
57
+ end
58
+ end
59
+
60
+ def valid_length?(value, options)
61
+ value_size_range = DEFAULT_LENGTHS.values.flatten.uniq.sort
62
+ value_size = value.size
63
+
64
+ case options[:card]
65
+ when :american_express, :amex
66
+ DEFAULT_LENGTHS[:american_express].include?(value_size)
67
+ when :diners_club
68
+ DEFAULT_LENGTHS[:diners_club].include?(value_size)
69
+ when :discover
70
+ DEFAULT_LENGTHS[:discover].include?(value_size)
71
+ when :jcb
72
+ DEFAULT_LENGTHS[:jcb].include?(value_size)
73
+ when :laser
74
+ DEFAULT_LENGTHS[:laser].include?(value_size)
75
+ when :maestro
76
+ DEFAULT_LENGTHS[:maestro].include?(value_size)
77
+ when :mastercard
78
+ DEFAULT_LENGTHS[:mastercard].include?(value_size)
79
+ when :solo
80
+ DEFAULT_LENGTHS[:solo].include?(value_size)
81
+ when :unionpay
82
+ DEFAULT_LENGTHS[:unionpay].include?(value_size)
83
+ when :visa
84
+ DEFAULT_LENGTHS[:visa].include?(value_size)
85
+ else
86
+ value_size.between?(value_size_range.first, value_size_range.last)
87
+ end
88
+ end
89
+
90
+ def valid_prefix?(value, options)
91
+ value_size = value.size
92
+
93
+ case options[:card]
94
+ when :american_express, :amex
95
+ DEFAULT_PREFIXES[:american_express].any? { |prefix| value.start_with?(prefix) }
96
+ when :diners_club
97
+ DEFAULT_PREFIXES[:diners_club].any? { |prefix| value.start_with?(prefix) }
98
+ when :discover
99
+ DEFAULT_PREFIXES[:discover].any? { |prefix| value.start_with?(prefix) }
100
+ when :jcb
101
+ DEFAULT_PREFIXES[:jcb].any? { |prefix| value.start_with?(prefix) }
102
+ when :laser
103
+ DEFAULT_PREFIXES[:laser].any? { |prefix| value.start_with?(prefix) }
104
+ when :maestro
105
+ DEFAULT_PREFIXES[:maestro].any? { |prefix| value.start_with?(prefix) }
106
+ when :mastercard
107
+ DEFAULT_PREFIXES[:mastercard].any? { |prefix| value.start_with?(prefix) }
108
+ when :solo
109
+ DEFAULT_PREFIXES[:solo].any? { |prefix| value.start_with?(prefix) }
110
+ when :unionpay
111
+ DEFAULT_PREFIXES[:unionpay].any? { |prefix| value.start_with?(prefix) }
112
+ when :visa
113
+ DEFAULT_PREFIXES[:visa].any? { |prefix| value.start_with?(prefix) }
114
+ else
115
+ responses = []
116
+ DEFAULT_LENGTHS.each do |key, values|
117
+ if values.include?(value_size)
118
+ responses << DEFAULT_PREFIXES[key].any? { |prefix| value.start_with?(prefix) }
119
+ end
120
+ end
121
+ responses.include?(true)
122
+ end
123
+ end
124
+
125
+ def valid?(value, options)
126
+ striped_value = value.to_s.gsub(/\D/, '')
127
+
128
+ valid_format?(value, options) &&
129
+ valid_length?(striped_value, options) &&
130
+ valid_prefix?(striped_value, options)
131
+ end
132
+
133
+ end
@@ -0,0 +1,23 @@
1
+ class CurrencyValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value, options)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.currency'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value, options)
12
+ if options[:strict]
13
+ value =~ /^\d+(\.\d{2})$/
14
+ else
15
+ value =~ /^\d*+(\.\d{1,2})$/
16
+ end
17
+ end
18
+
19
+ def valid?(value, options)
20
+ valid_format?(value, options)
21
+ end
22
+
23
+ end
@@ -0,0 +1,33 @@
1
+ class CusipValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value.to_s)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.cusip'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value)
12
+ value =~ /^[0-9A-Z]{9}$/
13
+ end
14
+
15
+ def valid_checksum?(value)
16
+ digits = value.split('').map { |i| i.match(/[A-Z]/) ? (i.ord - 55) : i.to_i }
17
+ even_values = digits.values_at(* digits.each_index.select { |i| i.even? })
18
+ odd_values = digits.values_at(* digits.each_index.select { |i| i.odd? })
19
+ values = odd_values.map { |i| i * 2 }.zip(even_values).flatten
20
+
21
+ values = values.inject(0) do |sum, i|
22
+ sum += (i / 10) + i % 10
23
+ end
24
+
25
+ ((10 - values) % 10) % 10
26
+ end
27
+
28
+ def valid?(value)
29
+ valid_format?(value) &&
30
+ valid_checksum?(value)
31
+ end
32
+
33
+ end
@@ -0,0 +1,25 @@
1
+ class EmailValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value, options)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.email'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value)
12
+ value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
13
+ end
14
+
15
+ def valid_domain?(value, options)
16
+ value_downcased = value.downcase
17
+ options.empty? || options.any? { |domain| value_downcased.end_with?(".#{domain.downcase}") }
18
+ end
19
+
20
+ def valid?(email, options)
21
+ valid_format?(email) &&
22
+ valid_domain?(email, [*(options[:domain])])
23
+ end
24
+
25
+ end
@@ -0,0 +1,27 @@
1
+ class EqualityValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ to_value = options[:to]
5
+ operator = OPERATORS.fetch(options[:operator])
6
+
7
+ if (options[:operator] == :less_than) || (options[:operator] == :greater_than)
8
+ raise Exception if to_value.nil? || operator.nil?
9
+ end
10
+
11
+ unless value.send(operator, record.send(to_value.to_sym))
12
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.equality', attr: to_value, operator: operator))
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ OPERATORS = {
19
+ less_than: :<,
20
+ less_than_or_equal_to: :<=,
21
+ greater_than: :>,
22
+ greater_than_or_equal_to: :>=,
23
+ equal_to: :==,
24
+ not_equal_to: :!=
25
+ }
26
+
27
+ end
@@ -0,0 +1,9 @@
1
+ class HexValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless value =~ /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.hex'))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,37 @@
1
+ class ImeiValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value.to_s)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.imei'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value)
12
+ value =~ /\A[\d\.\:\-\s]+\z/i
13
+ end
14
+
15
+ def valid_luhn?(value)
16
+ numbers = value.gsub(/\D/, '').reverse
17
+
18
+ sum, i = 0, 0
19
+
20
+ numbers.each_char do |ch|
21
+ n = ch.to_i
22
+ n *= 2 if i.odd?
23
+ n = 1 + (n - 10) if n >= 10
24
+
25
+ sum += n
26
+ i += 1
27
+ end
28
+
29
+ (sum % 10).zero?
30
+ end
31
+
32
+ def valid?(value)
33
+ valid_format?(value) &&
34
+ valid_luhn?(value)
35
+ end
36
+
37
+ end
@@ -0,0 +1,9 @@
1
+ class IpValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless value =~ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.ip'))
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,24 @@
1
+ class IsbnValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value.to_s)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.isbn'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value)
12
+ value = '' if value.nil?
13
+ value.gsub!(/-| /, '')
14
+ value.downcase!
15
+
16
+ [10, 13].include?(value.size) &&
17
+ value.chars.all? { |char| ['0','1','2','3','4','5','6','7','8','9','0','x'].include?(char) }
18
+ end
19
+
20
+ def valid?(value)
21
+ valid_format?(value)
22
+ end
23
+
24
+ end
@@ -0,0 +1,38 @@
1
+ class IsinValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless valid?(value)
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.isin'))
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def valid_format?(value)
12
+ value =~ /^((AF|AX|AL|DZ|AS|AD|AO|AI|AQ|AG|AR|AM|AW|AU|AT|AZ|BS|BH|BD|BB|BY|BE|BZ|BJ|BM|BT|BO|BQ|BA|BW|BV|BR|IO|BN|BG|BF|BI|KH|CM|CA|CV|KY|CF|TD|CL|CN|CX|CC|CO|KM|CG|CD|CK|CR|CI|HR|CU|CW|CY|CZ|DK|DJ|DM|DO|EC|EG|SV|GQ|ER|EE|ET|FK|FO|FJ|FI|FR|GF|PF|TF|GA|GM|GE|DE|GH|GI|GR|GL|GD|GP|GU|GT|GG|GN|GW|GY|HT|HM|VA|HN|HK|HU|IS|IN|ID|IR|IQ|IE|IM|IL|IT|JM|JP|JE|JO|KZ|KE|KI|KP|KR|KW|KG|LA|LV|LB|LS|LR|LY|LI|LT|LU|MO|MK|MG|MW|MY|MV|ML|MT|MH|MQ|MR|MU|YT|MX|FM|MD|MC|MN|ME|MS|MA|MZ|MM|NA|NR|NP|NL|NC|NZ|NI|NE|NG|NU|NF|MP|NO|OM|PK|PW|PS|PA|PG|PY|PE|PH|PN|PL|PT|PR|QA|RE|RO|RU|RW|BL|SH|KN|LC|MF|PM|VC|WS|SM|ST|SA|SN|RS|SC|SL|SG|SX|SK|SI|SB|SO|ZA|GS|SS|ES|LK|SD|SR|SJ|SZ|SE|CH|SY|TW|TJ|TZ|TH|TL|TG|TK|TO|TT|TN|TR|TM|TC|TV|UG|UA|AE|GB|US|UM|UY|UZ|VU|VE|VN|VG|VI|WF|EH|YE|ZM|ZW{2})([A-Z0-9]{9}))(\d{1})$/
13
+ end
14
+
15
+ def valid_checksum?(value)
16
+ digits = value.split('').map { |i| i.match(/[A-Z]/) ? (i.ord - 55) : i.to_i }
17
+ even_values = digits.values_at(* digits.each_index.select { |i| i.even? })
18
+ odd_values = digits.values_at(* digits.each_index.select { |i| i.odd? })
19
+
20
+ longest, shortest = if even_values.last == value.split('').map(&:to_i)
21
+ [even_values, odd_values]
22
+ else
23
+ [odd_values, even_values]
24
+ end
25
+
26
+ longest = longest.map { |i| i * 2 }
27
+ values = (longest.concat(shortest)).to_s.scan(/./).map(&:to_i)
28
+ sum = values.inject(&:+)
29
+
30
+ (10 - (sum % 10)) % 10
31
+ end
32
+
33
+ def valid?(value)
34
+ valid_format?(value) &&
35
+ valid_checksum?(value)
36
+ end
37
+
38
+ end
@@ -0,0 +1,9 @@
1
+ class LatitudeValidator < ActiveModel::EachValidator
2
+
3
+ def validate_each(record, attribute, value)
4
+ unless value.present? && value >= -90 && value <= 90
5
+ record.errors[attribute] << (options[:message] || I18n.t('active_validation.errors.messages.latitude'))
6
+ end
7
+ end
8
+
9
+ end