can_has_validations 0.6.3 → 1.2.0

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: d565ec38823188e137ba900db6247e643f3c436a6a8f0be7d210a8af0f2606e6
4
- data.tar.gz: 47fc9fc83d7224d9831d66a98e967b161b6f3fa66de7186b8b51c85ffc8287d1
3
+ metadata.gz: 92f14adff8817a7d4d4d6d5b9a1b00a0860f9279ca86dafaad567e67e4970ee6
4
+ data.tar.gz: ea792545cf1ed844ec5ad498068dd9990712b240c31f3e214d6c3518f429f9b4
5
5
  SHA512:
6
- metadata.gz: 1cfa321ceee3431a7b2a99027794a655a5cd2b0a570eb715ccbaf06be183956689bfb7e369b0927d31f6a80f781591fae5422803df836fe2b41a9e52babe3bbd
7
- data.tar.gz: 34d93b15d141061ded5dc58c2cdb62f2abe646e60c4e49e6b4ee8856d76356a3e83be0c080db3f71a5499ae095955299a3cfc978ee90d86fc50c4b465e289662
6
+ metadata.gz: c2b1cb68de2eeb27e3d1a7b0c8b86d9d64f76f62a7f51feddfc5e62c9f009f14caadf7fd6eafac768c47e77fb31ed27cc8edab196b5d565d626582e0025403db
7
+ data.tar.gz: a7a1fa2e08fe737f88b9e428047d01b398a852a82528439483abfb1251027d0e5542429b82c981f8dc3cf5ece39aca0e3404e009bc5d8b75a15850bf33585dd7
@@ -1,4 +1,4 @@
1
- Copyright 2012 YOURNAME
1
+ Copyright 2012-2020 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
@@ -10,6 +10,7 @@ Validations provided:
10
10
  * Existence
11
11
  * Grandparent
12
12
  * Hostname
13
+ * IP address
13
14
  * Ordering
14
15
  * URL
15
16
  * Write Once
@@ -147,6 +148,9 @@ TLD, so as to not fail as ICANN continues to add TLDs.
147
148
  # allows '_abc.example.com'
148
149
  validates :domain, hostname: {allow_underscore: true}
149
150
 
151
+ # allows '4.0/25.3.2.1.example.com'
152
+ validates :domain, hostname: {allow_slash: true}
153
+
150
154
  # allows 'a.example.com', but not 'example.com'
151
155
  validates :domain, hostname: {segments: 3..100}
152
156
 
@@ -158,6 +162,34 @@ TLD, so as to not fail as ICANN continues to add TLDs.
158
162
  # use 4 or 6 for ipv4 or ipv6 only
159
163
 
160
164
 
165
+ ## IP address validator ##
166
+
167
+ Ensures an attribute is generally formatted as a IP or IP block.
168
+
169
+ # allows '1.2.3.4' or '::1'
170
+ validates :ip, ipaddr: true
171
+
172
+ # allows '1.2.3.0/24' or '2001:db8::/64'
173
+ validates :cidr, ipaddr: {allow_block: true}
174
+
175
+ # if an ip block, the attribute must be fully contained within an allowed block.
176
+ # allows '10.0.0.1' and '10.0.0.0/24', but not '10.0.0.0/15'
177
+ validates :private_ip, ipaddr: {
178
+ allow_block: true,
179
+ within: [IPAddr.new('10.0.0.0/16'), '127.0.0.1']
180
+ # allowed IPs and blocks may be IPAddrs or Strings
181
+ }
182
+
183
+ # the inverse of :within
184
+ validates :public_ip6, ipaddr: {without: ['fc00::/7']]}
185
+
186
+ # :within and :without may also be procs or method names
187
+ validates :ip, ipaddr: {
188
+ within: :some_method,
189
+ without: ->(record){ ... }
190
+ }
191
+
192
+
161
193
  ## Ordering validators ##
162
194
 
163
195
  Ensures two attribute values maintain a relative order to one another. This is
@@ -183,13 +215,17 @@ Always skips over nil values; use `:presence` to validate those.
183
215
 
184
216
  ## URL validator ##
185
217
 
186
- Ensure an attribute is generally formatted as a URL. If `addressable/uri` is
218
+ Ensures an attribute is generally formatted as a URL. If `addressable/uri` is
187
219
  already loaded, it will be used to parse IDN's. Additionally, allowed schemes
188
220
  can be specified; they default to ['http','https'].
189
221
 
190
222
  validates :website, url: true
191
223
  validates :secure_url, url: {scheme: 'https'}
192
224
 
225
+ # Dynamic list of schemes. *Must* return an array.
226
+ validates :git, url: {scheme: :some_method}
227
+ validates :old_school, url: {scheme: ->(record){ %w(ftp gopher) }}
228
+
193
229
  # With IDN parsing:
194
230
  require 'addressable/uri'
195
231
  validates :website, url: true
@@ -201,8 +237,8 @@ can be specified; they default to ['http','https'].
201
237
 
202
238
  ## Write Once validator ##
203
239
 
204
- Ensure that once a value is written, it becomes readonly. There are two uses
205
- for this.
240
+ Ensures that once a value is written, it becomes readonly. There are a few
241
+ uses for this.
206
242
 
207
243
  The first is as an equivalent to `attr_readonly :user_id` except that it also
208
244
  produces a validation error instead of silently ignoring the change as
@@ -217,6 +253,10 @@ sorts.
217
253
 
218
254
  validates :user_id, allow_nil: true, write_once: true
219
255
 
256
+ The third use is to allow a nil value, and treat the nil also as write-once.
257
+
258
+ validates :source, write_once: {immutable_nil: true}
259
+
220
260
 
221
261
  ## Error messages
222
262
 
@@ -228,6 +268,9 @@ Default messages are as follows:
228
268
  messages:
229
269
  invalid_email: "is an invalid email"
230
270
  invalid_hostname: "is an invalid hostname"
271
+ invalid_ip: "is an invalid IP"
272
+ ip_not_allowed: "is not an allowed IP"
273
+ single_ip_required: "must be a single IP"
231
274
  invalid_url: "is an invalid URL"
232
275
  unchangeable: "cannot be changed"
233
276
  before: "must be before %{attribute2}"
@@ -236,4 +279,4 @@ Default messages are as follows:
236
279
 
237
280
  ## Compatibility ##
238
281
 
239
- Tested with Ruby 2.3-2.5 and ActiveSupport and ActiveModel 4.2-5.2.
282
+ The current version is tested with Ruby 2.5-2.6 and ActiveModel 5.2-6.0.
@@ -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 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
-
@@ -1,15 +1,55 @@
1
1
  # Ensure an attribute is generally formatted as an email.
2
2
  # eg: validates :user_email, email: true
3
3
 
4
+ require_relative 'hostname_validator'
5
+
4
6
  module ActiveModel::Validations
5
7
  class EmailValidator < ActiveModel::EachValidator
6
8
 
7
- EMAIL_REGEXP = /\A([a-z0-9._+-]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
9
+ EMAIL_REGEXP = /\A([a-z0-9._+-]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
10
+ SEGMENT_REGEXP = /\A[a-z0-9+_-]+\z/i
11
+ LABEL_REGEXP = HostnameValidator::LABEL_REGEXP
12
+ FINAL_LABEL_REGEXP = HostnameValidator::FINAL_LABEL_REGEXP
8
13
 
9
14
  def validate_each(record, attribute, value)
10
- unless value =~ EMAIL_REGEXP
15
+ unless email_valid?(value)
11
16
  record.errors.add(attribute, :invalid_email, options.merge(value: value))
12
17
  end
13
18
  end
19
+
20
+ def email_valid?(value)
21
+ return unless value
22
+ recipient, domain = value.split('@', 2)
23
+ is_valid = true
24
+
25
+ recipient ||= ''
26
+ is_valid &&= recipient.length <= 255
27
+ is_valid &&= recipient !~ /\.\./
28
+ is_valid &&= !recipient.starts_with?('.')
29
+ is_valid &&= !recipient.ends_with?('.')
30
+ recipient.split('.').each do |segment|
31
+ is_valid &&= segment =~ SEGMENT_REGEXP
32
+ end
33
+
34
+ domain ||= ''
35
+ if defined?(Addressable::IDNA)
36
+ domain &&= Addressable::IDNA.to_ascii(domain)
37
+ end
38
+ labels = domain.split('.')
39
+ is_valid &&= domain.length <= 255
40
+ is_valid &&= domain !~ /\.\./
41
+ is_valid &&= labels.size.in? 2..100
42
+ labels.each_with_index do |label, idx|
43
+ is_valid &&= label.length <= 63
44
+ if idx+1==labels.size
45
+ is_valid &&= label =~ FINAL_LABEL_REGEXP
46
+ else
47
+ is_valid &&= label =~ LABEL_REGEXP
48
+ end
49
+ end
50
+
51
+ is_valid
52
+ end
53
+
14
54
  end
15
55
  end
@@ -16,7 +16,8 @@ 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
@@ -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
@@ -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
@@ -7,7 +7,13 @@
7
7
  module ActiveModel::Validations
8
8
  class UrlValidator < ActiveModel::EachValidator
9
9
  def validate_each(record, attribute, value)
10
- allowed_schemes = Array.wrap(options[:scheme] || %w(http https))
10
+ allowed_schemes = if options[:scheme].respond_to?(:call)
11
+ options[:scheme].call(record)
12
+ elsif options[:scheme].is_a?(Symbol)
13
+ record.send(options[:scheme])
14
+ else
15
+ Array.wrap(options[:scheme] || %w(http https))
16
+ end
11
17
 
12
18
  if defined?(Addressable::URI)
13
19
  u = Addressable::URI.parse(value) rescue nil
@@ -16,7 +22,7 @@ module ActiveModel::Validations
16
22
  u2 = u = URI.parse(value) rescue nil
17
23
  end
18
24
  if !u || !u2 || u.relative? || allowed_schemes.exclude?(u.scheme)
19
- record.errors.add(attribute, :invalid_url, options.merge(value: value))
25
+ record.errors.add(attribute, :invalid_url, options.merge(value: value, scheme: allowed_schemes))
20
26
  end
21
27
  end
22
28
  end
@@ -12,14 +12,20 @@ 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?
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)
23
29
  record.errors.add(attribute, :unchangeable, options.except(:immutable_nil).merge!(value: value))
24
30
  end
25
31
  end
@@ -1,3 +1,3 @@
1
1
  module CanHasValidations
2
- VERSION = '0.6.3'
2
+ VERSION = '1.2.0'
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: 0.6.3
4
+ version: 1.2.0
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: 2018-08-07 00:00:00.000000000 Z
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
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
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '3.0'
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,7 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
- description: Assorted Rails 4.x-5.x validators.
47
+ description: Assorted Rails 5.x-6.x validators.
48
48
  email:
49
49
  - tm@iprog.com
50
50
  executables: []
@@ -61,6 +61,7 @@ files:
61
61
  - lib/can_has_validations/validators/existence_validator.rb
62
62
  - lib/can_has_validations/validators/grandparent_validator.rb
63
63
  - lib/can_has_validations/validators/hostname_validator.rb
64
+ - lib/can_has_validations/validators/ipaddr_validator.rb
64
65
  - lib/can_has_validations/validators/ordering_validator.rb
65
66
  - lib/can_has_validations/validators/url_validator.rb
66
67
  - lib/can_has_validations/validators/write_once_validator.rb
@@ -98,9 +99,10 @@ files:
98
99
  - test/dummy/script/rails
99
100
  - test/test_helper.rb
100
101
  homepage: https://github.com/zarqman/can_has_validations
101
- licenses: []
102
+ licenses:
103
+ - MIT
102
104
  metadata: {}
103
- post_install_message:
105
+ post_install_message:
104
106
  rdoc_options: []
105
107
  require_paths:
106
108
  - lib
@@ -115,40 +117,39 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
117
  - !ruby/object:Gem::Version
116
118
  version: '0'
117
119
  requirements: []
118
- rubyforge_project:
119
- rubygems_version: 2.7.6
120
- signing_key:
120
+ rubygems_version: 3.0.9
121
+ signing_key:
121
122
  specification_version: 4
122
- summary: Assorted Rails 4.x-5.x validators
123
+ summary: Assorted Rails 5.x-6.x validators
123
124
  test_files:
124
- - test/can_has_validations_test.rb
125
+ - test/dummy/app/controllers/application_controller.rb
126
+ - test/dummy/app/views/layouts/application.html.erb
125
127
  - test/dummy/app/assets/javascripts/application.js
126
128
  - test/dummy/app/assets/stylesheets/application.css
127
- - test/dummy/app/controllers/application_controller.rb
128
129
  - test/dummy/app/helpers/application_helper.rb
129
- - test/dummy/app/views/layouts/application.html.erb
130
- - test/dummy/config/application.rb
131
- - test/dummy/config/boot.rb
132
- - test/dummy/config/database.yml
133
- - test/dummy/config/environment.rb
134
- - test/dummy/config/environments/development.rb
130
+ - test/dummy/config/routes.rb
131
+ - test/dummy/config/locales/en.yml
135
132
  - test/dummy/config/environments/production.rb
133
+ - test/dummy/config/environments/development.rb
136
134
  - test/dummy/config/environments/test.rb
135
+ - test/dummy/config/environment.rb
136
+ - test/dummy/config/application.rb
137
+ - test/dummy/config/database.yml
138
+ - test/dummy/config/boot.rb
137
139
  - test/dummy/config/initializers/backtrace_silencers.rb
138
- - test/dummy/config/initializers/inflections.rb
139
140
  - test/dummy/config/initializers/mime_types.rb
140
- - test/dummy/config/initializers/secret_token.rb
141
141
  - test/dummy/config/initializers/session_store.rb
142
142
  - test/dummy/config/initializers/wrap_parameters.rb
143
- - test/dummy/config/locales/en.yml
144
- - test/dummy/config/routes.rb
143
+ - test/dummy/config/initializers/secret_token.rb
144
+ - test/dummy/config/initializers/inflections.rb
145
145
  - test/dummy/config.ru
146
- - test/dummy/log/test.log
147
- - test/dummy/public/404.html
146
+ - test/dummy/script/rails
147
+ - test/dummy/Rakefile
148
+ - test/dummy/public/favicon.ico
148
149
  - test/dummy/public/422.html
149
150
  - test/dummy/public/500.html
150
- - test/dummy/public/favicon.ico
151
- - test/dummy/Rakefile
151
+ - test/dummy/public/404.html
152
+ - test/dummy/log/test.log
152
153
  - test/dummy/README.rdoc
153
- - test/dummy/script/rails
154
+ - test/can_has_validations_test.rb
154
155
  - test/test_helper.rb