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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b18aa1039ebc20678d9781ff0e90d495253b6d40fa39c3ffec9ae69c03b6d298
4
- data.tar.gz: 6657e468172b9f6cdbd7f69f7d68a105c82661b521e5473d50fda8ca37387558
3
+ metadata.gz: 8ebceb28bd42a889a2d7b4d8d589dcdc25bf722d2a1f028de00dabeb92beb826
4
+ data.tar.gz: 6acf11da982dddf4702db8c386af8d0780d940f9b8c4273e881e18fb0a4bc12b
5
5
  SHA512:
6
- metadata.gz: 129fe04836706f5aa89ba7d1116b857375bd3088f1b3953247c045c8c75c385adc981d2d1e0189810aae40009689c4a18da2f714ac52b79869ba01ea3cd9f823
7
- data.tar.gz: 3454442e03e74108c632ac8f7dfed962850e36a5ce16afd595eb21b1855653edf8c4cefa4181f4eb7f65b308fc85a7d2341afcbab0a7d31ad268f4e0da000e8e
6
+ metadata.gz: 89327c90cb07c1663fbe9cfa245eb62d964499e3b05220de5bc5ed5443f153ea5613249f9e1afec65e5b29c345a43484dd7cafd1988d76a633c97c5ac62819b3
7
+ data.tar.gz: 177ce2aedb07dd299209d8099b52c3344cfa7f157682a3edc67ea27adce8853d51779138f191bb12171de5ab72976c5bd512c80eaa42b1f6575919c580b636a7
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012-2020 thomas morgan
1
+ Copyright 2012-2021 thomas morgan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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
- Ensure an attribute is generally formatted as a URL. If `addressable/uri` is
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
- Ensure that once a value is written, it becomes readonly. There are two uses
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}"
@@ -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 array validation" if validations.empty?
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.validate_each(record, attribute, value)
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
- # more efficient than calling record.errors.size
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.send(options[:parent]) == cousin.send(options[:parent])
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 = /\A([a-zA-Z0-9_]([a-zA-Z0-9_-]+)?)?[a-zA-Z0-9]\z/
34
- FINAL_LABEL_REGEXP = /\A(xn--[a-zA-Z0-9]{2,}|[a-zA-Z]{2,})\z/
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
- value = record.read_attribute_for_validation(attribute)
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, value)
21
- if record.persisted? && record.send("#{attribute}_changed?")
22
- if options[:immutable_nil] || !record.send("#{attribute}_was").nil?
23
- record.errors.add(attribute, :unchangeable, options.except(:immutable_nil).merge!(value: value))
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
@@ -1,3 +1,3 @@
1
1
  module CanHasValidations
2
- VERSION = '1.0.2'
2
+ VERSION = '1.3.1'
3
3
  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.0.2
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: 2020-03-06 00:00:00.000000000 Z
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.1'
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.1'
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.3
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: