can_has_validations 1.0.2 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +81 -4
- data/lib/can_has_validations.rb +1 -1
- data/lib/can_has_validations/locale/en.yml +3 -1
- data/lib/can_has_validations/locale/es.yml +12 -0
- data/lib/can_has_validations/validators/array_validator.rb +29 -5
- data/lib/can_has_validations/validators/email_validator.rb +1 -1
- data/lib/can_has_validations/validators/grandparent_validator.rb +3 -2
- data/lib/can_has_validations/validators/hash_keys_validator.rb +30 -0
- data/lib/can_has_validations/validators/hash_values_validator.rb +55 -0
- data/lib/can_has_validations/validators/hostname_validator.rb +7 -4
- data/lib/can_has_validations/validators/ipaddr_validator.rb +91 -0
- data/lib/can_has_validations/validators/ordering_validator.rb +2 -2
- data/lib/can_has_validations/validators/url_validator.rb +1 -1
- data/lib/can_has_validations/validators/write_once_validator.rb +12 -6
- data/lib/can_has_validations/version.rb +1 -1
- metadata +16 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ebceb28bd42a889a2d7b4d8d589dcdc25bf722d2a1f028de00dabeb92beb826
|
4
|
+
data.tar.gz: 6acf11da982dddf4702db8c386af8d0780d940f9b8c4273e881e18fb0a4bc12b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89327c90cb07c1663fbe9cfa245eb62d964499e3b05220de5bc5ed5443f153ea5613249f9e1afec65e5b29c345a43484dd7cafd1988d76a633c97c5ac62819b3
|
7
|
+
data.tar.gz: 177ce2aedb07dd299209d8099b52c3344cfa7f157682a3edc67ea27adce8853d51779138f191bb12171de5ab72976c5bd512c80eaa42b1f6575919c580b636a7
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,10 @@ Validations provided:
|
|
9
9
|
* Email
|
10
10
|
* Existence
|
11
11
|
* Grandparent
|
12
|
+
* Hash Keys
|
13
|
+
* Hash Values
|
12
14
|
* Hostname
|
15
|
+
* IP address
|
13
16
|
* Ordering
|
14
17
|
* URL
|
15
18
|
* Write Once
|
@@ -134,6 +137,42 @@ or the database foreign key (`:user_id`). You can also use any other field. The
|
|
134
137
|
test is merely that they match, not that they are associations.
|
135
138
|
|
136
139
|
|
140
|
+
## Hash Keys validator ##
|
141
|
+
|
142
|
+
Many databases now allow storing hashes. This validates the keys of those
|
143
|
+
hashes. It is conceptually the same as using the Array validator to validate
|
144
|
+
`hash_attribute.keys`.
|
145
|
+
|
146
|
+
It is able to use most existing validators that themselves work on individual
|
147
|
+
attribute values (including standard Rails validators, others that are part of
|
148
|
+
this gem, and likely many from other gems too).
|
149
|
+
|
150
|
+
By default it reports only one validation error per sub-validator, regardless
|
151
|
+
of how many keys fail validation. Use `multiple_errors: true` to report all
|
152
|
+
errors. See Array validator for more details.
|
153
|
+
|
154
|
+
validates :subjects,
|
155
|
+
hash_keys: {
|
156
|
+
format: /\A[a-z]+\z/,
|
157
|
+
# multiple_errors: true
|
158
|
+
}
|
159
|
+
|
160
|
+
|
161
|
+
## Hash Values validator ##
|
162
|
+
|
163
|
+
This is the companion to the Hash Keys validator and validates hash values
|
164
|
+
instead. It is conceptually the same as using the Array validator to validate
|
165
|
+
`hash_attribute.values`.
|
166
|
+
|
167
|
+
See Hash Keys validator for more details.
|
168
|
+
|
169
|
+
validates :subjects,
|
170
|
+
hash_values: {
|
171
|
+
length: 3..100,
|
172
|
+
# multiple_errors: true
|
173
|
+
}
|
174
|
+
|
175
|
+
|
137
176
|
## Hostname validator ##
|
138
177
|
|
139
178
|
Ensures an attribute is generally formatted as a hostname. It allows for any
|
@@ -147,6 +186,9 @@ TLD, so as to not fail as ICANN continues to add TLDs.
|
|
147
186
|
# allows '_abc.example.com'
|
148
187
|
validates :domain, hostname: {allow_underscore: true}
|
149
188
|
|
189
|
+
# allows '4.0/25.3.2.1.example.com'
|
190
|
+
validates :domain, hostname: {allow_slash: true}
|
191
|
+
|
150
192
|
# allows 'a.example.com', but not 'example.com'
|
151
193
|
validates :domain, hostname: {segments: 3..100}
|
152
194
|
|
@@ -158,6 +200,34 @@ TLD, so as to not fail as ICANN continues to add TLDs.
|
|
158
200
|
# use 4 or 6 for ipv4 or ipv6 only
|
159
201
|
|
160
202
|
|
203
|
+
## IP address validator ##
|
204
|
+
|
205
|
+
Ensures an attribute is generally formatted as a IP or IP block.
|
206
|
+
|
207
|
+
# allows '1.2.3.4' or '::1'
|
208
|
+
validates :ip, ipaddr: true
|
209
|
+
|
210
|
+
# allows '1.2.3.0/24' or '2001:db8::/64'
|
211
|
+
validates :cidr, ipaddr: {allow_block: true}
|
212
|
+
|
213
|
+
# if an ip block, the attribute must be fully contained within an allowed block.
|
214
|
+
# allows '10.0.0.1' and '10.0.0.0/24', but not '10.0.0.0/15'
|
215
|
+
validates :private_ip, ipaddr: {
|
216
|
+
allow_block: true,
|
217
|
+
within: [IPAddr.new('10.0.0.0/16'), '127.0.0.1']
|
218
|
+
# allowed IPs and blocks may be IPAddrs or Strings
|
219
|
+
}
|
220
|
+
|
221
|
+
# the inverse of :within
|
222
|
+
validates :public_ip6, ipaddr: {without: ['fc00::/7']]}
|
223
|
+
|
224
|
+
# :within and :without may also be procs or method names
|
225
|
+
validates :ip, ipaddr: {
|
226
|
+
within: :some_method,
|
227
|
+
without: ->(record){ ... }
|
228
|
+
}
|
229
|
+
|
230
|
+
|
161
231
|
## Ordering validators ##
|
162
232
|
|
163
233
|
Ensures two attribute values maintain a relative order to one another. This is
|
@@ -183,9 +253,9 @@ Always skips over nil values; use `:presence` to validate those.
|
|
183
253
|
|
184
254
|
## URL validator ##
|
185
255
|
|
186
|
-
|
256
|
+
Ensures an attribute is generally formatted as a URL. If `addressable/uri` is
|
187
257
|
already loaded, it will be used to parse IDN's. Additionally, allowed schemes
|
188
|
-
can be specified; they default to ['http','https']
|
258
|
+
can be specified; they default to `['http','https']`.
|
189
259
|
|
190
260
|
validates :website, url: true
|
191
261
|
validates :secure_url, url: {scheme: 'https'}
|
@@ -205,8 +275,8 @@ can be specified; they default to ['http','https'].
|
|
205
275
|
|
206
276
|
## Write Once validator ##
|
207
277
|
|
208
|
-
|
209
|
-
for this.
|
278
|
+
Ensures that once a value is written, it becomes readonly. There are a few
|
279
|
+
uses for this.
|
210
280
|
|
211
281
|
The first is as an equivalent to `attr_readonly :user_id` except that it also
|
212
282
|
produces a validation error instead of silently ignoring the change as
|
@@ -221,6 +291,10 @@ sorts.
|
|
221
291
|
|
222
292
|
validates :user_id, allow_nil: true, write_once: true
|
223
293
|
|
294
|
+
The third use is to allow a nil value, and treat the nil also as write-once.
|
295
|
+
|
296
|
+
validates :source, write_once: {immutable_nil: true}
|
297
|
+
|
224
298
|
|
225
299
|
## Error messages
|
226
300
|
|
@@ -232,6 +306,9 @@ Default messages are as follows:
|
|
232
306
|
messages:
|
233
307
|
invalid_email: "is an invalid email"
|
234
308
|
invalid_hostname: "is an invalid hostname"
|
309
|
+
invalid_ip: "is an invalid IP"
|
310
|
+
ip_not_allowed: "is not an allowed IP"
|
311
|
+
single_ip_required: "must be a single IP"
|
235
312
|
invalid_url: "is an invalid URL"
|
236
313
|
unchangeable: "cannot be changed"
|
237
314
|
before: "must be before %{attribute2}"
|
data/lib/can_has_validations.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'active_model/validations'
|
2
2
|
|
3
|
-
%w(array email existence grandparent hostname ordering url write_once).each do |validator|
|
3
|
+
%w(array email existence grandparent hash_keys hash_values hostname ipaddr ordering url write_once).each do |validator|
|
4
4
|
require "can_has_validations/validators/#{validator}_validator"
|
5
5
|
end
|
6
6
|
|
@@ -3,8 +3,10 @@ en:
|
|
3
3
|
messages:
|
4
4
|
invalid_email: "is an invalid email"
|
5
5
|
invalid_hostname: "is an invalid hostname"
|
6
|
+
invalid_ip: "is an invalid IP"
|
7
|
+
ip_not_allowed: "is not an allowed IP"
|
8
|
+
single_ip_required: "must be a single IP"
|
6
9
|
invalid_url: "is an invalid URL"
|
7
10
|
unchangeable: "cannot be changed"
|
8
11
|
before: "must be before %{attribute2}"
|
9
12
|
after: "must be after %{attribute2}"
|
10
|
-
|
@@ -0,0 +1,12 @@
|
|
1
|
+
es:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
invalid_email: "es un correo electrónico no válido"
|
5
|
+
invalid_hostname: "es un nombre de sistema no válido"
|
6
|
+
invalid_ip: "es una IP no válida"
|
7
|
+
ip_not_allowed: "no es una IP permitida"
|
8
|
+
single_ip_required: "debe ser solo una IP"
|
9
|
+
invalid_url: "es una URL no válida"
|
10
|
+
unchangeable: "no se puede cambiar"
|
11
|
+
before: "debe ser antes de %{attribute2}"
|
12
|
+
after: "debe ser después de %{attribute2}"
|
@@ -19,6 +19,18 @@
|
|
19
19
|
# multiple_errors: true,
|
20
20
|
# format: /\A[^aeiou]*\z/
|
21
21
|
# }
|
22
|
+
#
|
23
|
+
# the :if, :unless, and :on conditionals are not supported on sub-validators,
|
24
|
+
# but do work as normal on the :array validator itself.
|
25
|
+
#
|
26
|
+
# validates :permissions, if: :this_condition_works,
|
27
|
+
# array: {
|
28
|
+
# if: :this_condition_applies_to_permissions_but_not_each_element,
|
29
|
+
# inclusion: {
|
30
|
+
# in: %w(one two),
|
31
|
+
# unless: :conditions_on_subvalidators_are_ignored
|
32
|
+
# }
|
33
|
+
# }
|
22
34
|
|
23
35
|
module ActiveModel
|
24
36
|
module Validations
|
@@ -33,12 +45,17 @@ module ActiveModel
|
|
33
45
|
defaults = @options.dup
|
34
46
|
validations = defaults.slice!(*record_class.send(:_validates_default_keys), :attributes)
|
35
47
|
|
36
|
-
raise ArgumentError, "You need to supply at least one
|
48
|
+
raise ArgumentError, "You need to supply at least one validation for :#{kind}" if validations.empty?
|
37
49
|
|
38
50
|
defaults[:attributes] = attributes
|
39
51
|
|
40
52
|
@validators = validations.map do |key, sub_options|
|
41
53
|
next unless sub_options
|
54
|
+
|
55
|
+
if (cond_keys = _parse_validates_options(sub_options).keys & %i(if on unless)).any?
|
56
|
+
raise ArgumentError, ":#{kind} does not support conditionals on sub-validators - found on #{key}: #{cond_keys.map(&:inspect).join(', ')}"
|
57
|
+
end
|
58
|
+
|
42
59
|
key = "#{key.to_s.camelize}Validator"
|
43
60
|
|
44
61
|
begin
|
@@ -47,7 +64,7 @@ module ActiveModel
|
|
47
64
|
raise ArgumentError, "Unknown validator: '#{key}'"
|
48
65
|
end
|
49
66
|
|
50
|
-
klass.new(defaults.merge(_parse_validates_options(sub_options)))
|
67
|
+
klass.new(defaults.merge(_parse_validates_options(sub_options)).except(:if, :on, :unless))
|
51
68
|
end
|
52
69
|
end
|
53
70
|
|
@@ -56,7 +73,7 @@ module ActiveModel
|
|
56
73
|
error_count = count_errors(record)
|
57
74
|
|
58
75
|
Array(array_values).each do |value|
|
59
|
-
validator
|
76
|
+
validate_one(validator, record, attribute, value)
|
60
77
|
|
61
78
|
# to avoid repeating error messages, stop after a single error
|
62
79
|
unless validator.options[:multiple_errors]
|
@@ -66,14 +83,21 @@ module ActiveModel
|
|
66
83
|
end
|
67
84
|
end
|
68
85
|
|
86
|
+
def validate_one(validator, record, attribute, value)
|
87
|
+
unless validator.is_a?(ExistenceValidator)
|
88
|
+
return if (value.nil? && validator.options[:allow_nil]) || (value.blank? && validator.options[:allow_blank])
|
89
|
+
end
|
90
|
+
validator.validate_each(record, attribute, value)
|
91
|
+
end
|
92
|
+
|
69
93
|
|
70
94
|
private
|
71
95
|
|
72
96
|
def count_errors(record)
|
73
|
-
|
74
|
-
record.errors.messages.sum{|key, val| val.blank? ? 0 : val.size }
|
97
|
+
record.errors.count
|
75
98
|
end
|
76
99
|
|
100
|
+
# copied from active_model/validations/validates.rb
|
77
101
|
def _parse_validates_options(options)
|
78
102
|
case options
|
79
103
|
when TrueClass
|
@@ -13,7 +13,7 @@ module ActiveModel::Validations
|
|
13
13
|
|
14
14
|
def validate_each(record, attribute, value)
|
15
15
|
unless email_valid?(value)
|
16
|
-
record.errors.add(attribute, :invalid_email, options.merge(value: value))
|
16
|
+
record.errors.add(attribute, :invalid_email, **options.merge(value: value))
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -16,11 +16,12 @@ module ActiveModel::Validations
|
|
16
16
|
if cousin.nil?
|
17
17
|
options[:allow_nil]
|
18
18
|
else
|
19
|
-
association
|
19
|
+
association &&
|
20
|
+
association.send(options[:parent]) == cousin.send(options[:parent])
|
20
21
|
end
|
21
22
|
end
|
22
23
|
unless all_match
|
23
|
-
record.errors.add(attribute, :invalid, options.except(:allow_nil, :parent, :scope))
|
24
|
+
record.errors.add(attribute, :invalid, **options.except(:allow_nil, :parent, :scope))
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# validates each key of a hash attribute
|
2
|
+
#
|
3
|
+
# by default only allows the first error per validator, regardless of how many
|
4
|
+
# keys fail validation. this improves performance and avoids a bunch of
|
5
|
+
# repeating error messages.
|
6
|
+
# use `multiple_errors: true` on :hash_keys or a single sub-validator to
|
7
|
+
# enable the full set of errors. this is potentially useful if each error
|
8
|
+
# message will vary based upon each hash key.
|
9
|
+
#
|
10
|
+
# the :if, :unless, and :on conditionals are not supported on sub-validators,
|
11
|
+
# but do work as normal on the :hash_keys validator itself.
|
12
|
+
#
|
13
|
+
# usage:
|
14
|
+
# validates :subjects,
|
15
|
+
# hash_keys: {
|
16
|
+
# format: /\A[a-z]+\z/,
|
17
|
+
# # multiple_errors: true
|
18
|
+
# }
|
19
|
+
|
20
|
+
module ActiveModel
|
21
|
+
module Validations
|
22
|
+
class HashKeysValidator < ArrayValidator
|
23
|
+
|
24
|
+
def validate_each(record, attribute, hash)
|
25
|
+
super(record, attribute, Hash(hash).keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# validates each value of a hash attribute
|
2
|
+
#
|
3
|
+
# by default only allows the first error per validator, regardless of how many
|
4
|
+
# values fail validation. this improves performance and avoids a bunch of
|
5
|
+
# repeating error messages.
|
6
|
+
# use `multiple_errors: true` on :hash_values or a single sub-validator to
|
7
|
+
# enable the full set of errors. this is potentially useful if each error
|
8
|
+
# message will vary based upon each hash value.
|
9
|
+
#
|
10
|
+
# the :if, :unless, and :on conditionals are not supported on sub-validators,
|
11
|
+
# but do work as normal on the :hash_values validator itself.
|
12
|
+
#
|
13
|
+
# usage:
|
14
|
+
# validates :subjects,
|
15
|
+
# hash_values: {
|
16
|
+
# length: 3..100,
|
17
|
+
# # multiple_errors: true
|
18
|
+
# }
|
19
|
+
|
20
|
+
module ActiveModel
|
21
|
+
module Validations
|
22
|
+
class HashValuesValidator < ArrayValidator
|
23
|
+
|
24
|
+
def initialize(options)
|
25
|
+
record_class = options[:class]
|
26
|
+
super
|
27
|
+
record_class.include HashValidatorKey
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_each(record, attribute, hash)
|
31
|
+
super(record, attribute, Array(Hash(hash)))
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_one(validator, record, attribute, key_and_value)
|
35
|
+
key, value = key_and_value
|
36
|
+
record.hash_validator_key = key
|
37
|
+
super(validator, record, attribute, value)
|
38
|
+
ensure
|
39
|
+
record.hash_validator_key = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
module HashValidatorKey
|
44
|
+
def hash_validator_key
|
45
|
+
@_hash_validator_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash_validator_key=(v)
|
49
|
+
@_hash_validator_key = v
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -18,6 +18,8 @@
|
|
18
18
|
# allows '*.example.com'
|
19
19
|
# validates :domain, hostname: {allow_underscore: true}
|
20
20
|
# allows '_abc.example.com'
|
21
|
+
# validates :domain, hostname: {allow_slash: true}
|
22
|
+
# allows '4.0/25.3.2.1.example.com' # rfc2317
|
21
23
|
# validates :domain, hostname: {segments: 3..100}
|
22
24
|
# allows 'a.example.com', but not 'example.com'
|
23
25
|
# validates :domain, hostname: {allow_ip: true} # or 4 or 6 for ipv4 or ipv6 only
|
@@ -30,9 +32,9 @@ require 'resolv'
|
|
30
32
|
module ActiveModel::Validations
|
31
33
|
class HostnameValidator < ActiveModel::EachValidator
|
32
34
|
|
33
|
-
LABEL_REGEXP =
|
34
|
-
FINAL_LABEL_REGEXP =
|
35
|
-
RESERVED_OPTIONS = %i(allow_ip allow_underscore allow_wildcard)
|
35
|
+
LABEL_REGEXP = %r{\A([a-zA-Z0-9_]([a-zA-Z0-9_/-]+)?)?[a-zA-Z0-9]\z}
|
36
|
+
FINAL_LABEL_REGEXP = %r{\A(xn--[a-zA-Z0-9]{2,}|[a-zA-Z]{2,})\z}
|
37
|
+
RESERVED_OPTIONS = %i(allow_ip allow_slash allow_underscore allow_wildcard)
|
36
38
|
|
37
39
|
def validate_each(record, attribute, value)
|
38
40
|
case options[:allow_ip]
|
@@ -55,6 +57,7 @@ module ActiveModel::Validations
|
|
55
57
|
is_valid &&= value.length <= 255
|
56
58
|
is_valid &&= value !~ /\.\./
|
57
59
|
is_valid &&= value !~ /_/ unless options[:allow_underscore]
|
60
|
+
is_valid &&= value !~ %r{/} unless options[:allow_slash]
|
58
61
|
is_valid &&= labels.size.in? segments
|
59
62
|
labels.each_with_index do |label, idx|
|
60
63
|
is_valid &&= label.length <= 63
|
@@ -68,7 +71,7 @@ module ActiveModel::Validations
|
|
68
71
|
end
|
69
72
|
|
70
73
|
unless is_valid
|
71
|
-
record.errors.add(attribute, :invalid_hostname, options.except(*RESERVED_OPTIONS).merge!(value: value))
|
74
|
+
record.errors.add(attribute, :invalid_hostname, **options.except(*RESERVED_OPTIONS).merge!(value: value))
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Ensure an attribute is generally formatted as a IP or IP block.
|
2
|
+
# eg: validates :ip, ipaddr: true
|
3
|
+
# validates :cidr, ipaddr: {allow_block: true}
|
4
|
+
# validates :private_ip, ipaddr: {within: [IPAddr.new('10.0.0.0/8'), '127.0.0.1']}
|
5
|
+
# ip must be within any one of the provided ips/blocks
|
6
|
+
# if ip is block, it must be fully contained within any one of the provided blocks
|
7
|
+
# validates :public_ip6, ipaddr: {without: ['fc00::/7']]}
|
8
|
+
# ip must be outside all of the provided ips/blocks
|
9
|
+
# if ip is block, it must be fully outside all of the provided blocks
|
10
|
+
|
11
|
+
require 'ipaddr'
|
12
|
+
|
13
|
+
module ActiveModel::Validations
|
14
|
+
class IpaddrValidator < ActiveModel::EachValidator
|
15
|
+
|
16
|
+
def initialize(options)
|
17
|
+
options[:within] = normalize_within options[:within], :within
|
18
|
+
options[:without] = normalize_within options[:without], :without
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_each(record, attribute, value)
|
23
|
+
allowed_ips = resolve_array record, options[:within]
|
24
|
+
disallowed_ips = resolve_array record, options[:without]
|
25
|
+
|
26
|
+
ip = case value
|
27
|
+
when IPAddr
|
28
|
+
ip
|
29
|
+
when String
|
30
|
+
IPAddr.new(value) rescue nil
|
31
|
+
end
|
32
|
+
unless ip
|
33
|
+
record.errors.add(attribute, :invalid_ip, **options.merge(value: value))
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
if !options[:allow_block] && (ip.ipv4? && ip.prefix!=32 or ip.ipv6? && ip.prefix!=128)
|
38
|
+
record.errors.add(attribute, :single_ip_required, **options.merge(value: value))
|
39
|
+
end
|
40
|
+
if allowed_ips && allowed_ips.none?{|blk| blk.include? ip}
|
41
|
+
record.errors.add(attribute, :ip_not_allowed, **options.merge(value: value))
|
42
|
+
elsif disallowed_ips && disallowed_ips.any?{|blk| blk.include? ip}
|
43
|
+
record.errors.add(attribute, :ip_not_allowed, **options.merge(value: value))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def normalize_within(val, key)
|
51
|
+
if val.nil? || val.respond_to?(:call) || val.is_a?(Symbol)
|
52
|
+
val
|
53
|
+
else
|
54
|
+
Array(val).flatten.map do |i|
|
55
|
+
case i
|
56
|
+
when IPAddr
|
57
|
+
i
|
58
|
+
when String
|
59
|
+
IPAddr.new i
|
60
|
+
else
|
61
|
+
raise "Unexpected value for #{key.inspect} : #{i}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def resolve_array(record, val)
|
68
|
+
res = if val.respond_to?(:call)
|
69
|
+
val.call(record)
|
70
|
+
elsif val.is_a?(Symbol)
|
71
|
+
record.send(val)
|
72
|
+
else
|
73
|
+
val
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# tests for & fixes broken IPAddr <= 1.2.2
|
81
|
+
if IPAddr.new('192.168.2.0/32').include? '192.168.2.0/24'
|
82
|
+
# warn 'IPAddr <= 1.2.2 is broken; monkey-patching'
|
83
|
+
class IPAddr
|
84
|
+
def include?(other)
|
85
|
+
range = to_range
|
86
|
+
other = coerce_other(other).to_range
|
87
|
+
range.begin <= other.begin && range.end >= other.end
|
88
|
+
end
|
89
|
+
alias === include?
|
90
|
+
end
|
91
|
+
end
|
@@ -18,7 +18,7 @@ module ActiveModel::Validations
|
|
18
18
|
next unless value && greater
|
19
19
|
unless value < greater
|
20
20
|
attr2 = attr_name.respond_to?(:call) ? 'it is' : record.class.human_attribute_name(attr_name)
|
21
|
-
record.errors.add(attribute, :before, options.except(:before).merge!(attribute2: attr2, value: value))
|
21
|
+
record.errors.add(attribute, :before, **options.except(:before).merge!(attribute2: attr2, value: value))
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -33,7 +33,7 @@ module ActiveModel::Validations
|
|
33
33
|
next unless value && lesser
|
34
34
|
unless value > lesser
|
35
35
|
attr2 = attr_name.respond_to?(:call) ? 'it is' : record.class.human_attribute_name(attr_name)
|
36
|
-
record.errors.add(attribute, :after, options.except(:after).merge!(attribute2: attr2, value: value))
|
36
|
+
record.errors.add(attribute, :after, **options.except(:after).merge!(attribute2: attr2, value: value))
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -22,7 +22,7 @@ module ActiveModel::Validations
|
|
22
22
|
u2 = u = URI.parse(value) rescue nil
|
23
23
|
end
|
24
24
|
if !u || !u2 || u.relative? || allowed_schemes.exclude?(u.scheme)
|
25
|
-
record.errors.add(attribute, :invalid_url, options.merge(value: value, scheme: allowed_schemes))
|
25
|
+
record.errors.add(attribute, :invalid_url, **options.merge(value: value, scheme: allowed_schemes))
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -12,15 +12,21 @@ module ActiveModel::Validations
|
|
12
12
|
# nil to be allowed. prevent this.
|
13
13
|
def validate(record)
|
14
14
|
attributes.each do |attribute|
|
15
|
-
|
16
|
-
validate_each(record, attribute, value)
|
15
|
+
validate_each(record, attribute, nil)
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
def validate_each(record, attribute,
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
def validate_each(record, attribute, _)
|
20
|
+
return unless record.persisted?
|
21
|
+
if !record.respond_to?("#{attribute}_changed?") && record.respond_to?("#{attribute}_id_changed?")
|
22
|
+
attr2 = "#{attribute}_id"
|
23
|
+
else
|
24
|
+
attr2 = attribute
|
25
|
+
end
|
26
|
+
if record.send("#{attr2}_changed?")
|
27
|
+
if options[:immutable_nil] || !record.send("#{attr2}_was").nil?
|
28
|
+
value = record.read_attribute_for_validation(attribute)
|
29
|
+
record.errors.add(attribute, :unchangeable, **options.except(:immutable_nil).merge!(value: value))
|
24
30
|
end
|
25
31
|
end
|
26
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: can_has_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thomas morgan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '5.0'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '6.
|
22
|
+
version: '6.2'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '5.0'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '6.
|
32
|
+
version: '6.2'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: sqlite3
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,7 +44,8 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '0'
|
47
|
-
description: Assorted Rails 5.x-6.x validators
|
47
|
+
description: 'Assorted Rails 5.x-6.x validators: Array, Email, Existence, Grandparent,
|
48
|
+
Hostname, IP address, Ordering, URL, Write Once'
|
48
49
|
email:
|
49
50
|
- tm@iprog.com
|
50
51
|
executables: []
|
@@ -56,11 +57,15 @@ files:
|
|
56
57
|
- Rakefile
|
57
58
|
- lib/can_has_validations.rb
|
58
59
|
- lib/can_has_validations/locale/en.yml
|
60
|
+
- lib/can_has_validations/locale/es.yml
|
59
61
|
- lib/can_has_validations/validators/array_validator.rb
|
60
62
|
- lib/can_has_validations/validators/email_validator.rb
|
61
63
|
- lib/can_has_validations/validators/existence_validator.rb
|
62
64
|
- lib/can_has_validations/validators/grandparent_validator.rb
|
65
|
+
- lib/can_has_validations/validators/hash_keys_validator.rb
|
66
|
+
- lib/can_has_validations/validators/hash_values_validator.rb
|
63
67
|
- lib/can_has_validations/validators/hostname_validator.rb
|
68
|
+
- lib/can_has_validations/validators/ipaddr_validator.rb
|
64
69
|
- lib/can_has_validations/validators/ordering_validator.rb
|
65
70
|
- lib/can_has_validations/validators/url_validator.rb
|
66
71
|
- lib/can_has_validations/validators/write_once_validator.rb
|
@@ -98,9 +103,10 @@ files:
|
|
98
103
|
- test/dummy/script/rails
|
99
104
|
- test/test_helper.rb
|
100
105
|
homepage: https://github.com/zarqman/can_has_validations
|
101
|
-
licenses:
|
106
|
+
licenses:
|
107
|
+
- MIT
|
102
108
|
metadata: {}
|
103
|
-
post_install_message:
|
109
|
+
post_install_message:
|
104
110
|
rdoc_options: []
|
105
111
|
require_paths:
|
106
112
|
- lib
|
@@ -115,8 +121,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
121
|
- !ruby/object:Gem::Version
|
116
122
|
version: '0'
|
117
123
|
requirements: []
|
118
|
-
rubygems_version: 3.0.
|
119
|
-
signing_key:
|
124
|
+
rubygems_version: 3.0.9
|
125
|
+
signing_key:
|
120
126
|
specification_version: 4
|
121
127
|
summary: Assorted Rails 5.x-6.x validators
|
122
128
|
test_files:
|