active_validation 1.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.
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