active_validation 4.0.10 → 4.0.11
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.
- checksums.yaml +4 -4
- data/README.md +70 -2
- data/active_validation.gemspec +2 -0
- data/config/locales/en.yml +26 -0
- data/lib/active_validation.rb +5 -3
- data/lib/active_validation/validators/boolean_validator.rb +1 -0
- data/lib/active_validation/validators/csv_validator.rb +94 -0
- data/lib/active_validation/validators/file_size_validator.rb +85 -0
- data/lib/active_validation/version.rb +1 -1
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13401eb32dc1430b9fdf5bfba438388b30cfe377
|
4
|
+
data.tar.gz: ec4688cf3c3dafb36fa78c9cb3e3d3581d3ffc57
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb066efd1bb2bb378090cde1da22eb361fdf1a47640870d608c79cfe7807b04dd3eb66013b586c11f0438bd43c81b1a5f32a6e4f1f591333db0b4ad23c2685a8
|
7
|
+
data.tar.gz: 1542bd9f3a210d2c5cdb50b10d35aaed128e05ba83fd43777012111183fecc5216819635edfa439c19a20241780e869550a787a625cc9b2725ef1ed69ec2c048
|
data/README.md
CHANGED
@@ -34,10 +34,12 @@ Or install it yourself as:
|
|
34
34
|
* [Boolean](#booleanvalidator)
|
35
35
|
* [Coordinates](#coordinatesvalidator)
|
36
36
|
* [Credit Card](#creditcardvalidator)
|
37
|
+
* [Csv](#csvvalidator)
|
37
38
|
* [Currency](#currencyvalidator)
|
38
39
|
* [CUSIP](#cusipvalidator)
|
39
40
|
* [Email](#emailvalidator)
|
40
41
|
* [Equality](#equalityvalidator)
|
42
|
+
* [FileSize](#filesizevalidator)
|
41
43
|
* [Hex](#hexvalidator)
|
42
44
|
* [IMEI](#imeivalidator)
|
43
45
|
* [IP](#ipvalidator)
|
@@ -309,6 +311,40 @@ describe Invoice do
|
|
309
311
|
end
|
310
312
|
```
|
311
313
|
|
314
|
+
## CsvValidator
|
315
|
+
|
316
|
+
Options: :columns :columns_in, :columns_less_than, :columns_less_than_or_equal_to,
|
317
|
+
:columns_greater_than, :columns_greater_than_or_equal_to, :rows, :rows_in, :rows_less_than,
|
318
|
+
:rows_less_than_or_equal_to, :rows_greater_than, :rows_greater_than_or_equal_to
|
319
|
+
|
320
|
+
With an ActiveRecord model:
|
321
|
+
|
322
|
+
```ruby
|
323
|
+
class Product < ActiveRecord::Base
|
324
|
+
attr_accessor :csv, :name
|
325
|
+
validates :csv, csv: { columns: 6, rows_less_than: 20 }
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
Or any ruby class:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
class Product
|
333
|
+
include ActiveModel::Validations
|
334
|
+
attr_accessor :csv, :name
|
335
|
+
validates :csv, csv: { columns_less_than: 6, rows: 20 }
|
336
|
+
end
|
337
|
+
```
|
338
|
+
|
339
|
+
RSpec matcher is also available for your convenience:
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
describe Product do
|
343
|
+
it { should ensure_valid_csv_format_of(:csv) }
|
344
|
+
it { should_not ensure_valid_csv_format_of(:name) }
|
345
|
+
end
|
346
|
+
```
|
347
|
+
|
312
348
|
## CurrencyValidator
|
313
349
|
|
314
350
|
**Ex:** 123.00 or .1
|
@@ -394,10 +430,10 @@ end
|
|
394
430
|
**Ex:** user@example.com or user+123@example-site.com
|
395
431
|
|
396
432
|
**Rules:**
|
397
|
-
* Characters in username: a-z 0-9
|
433
|
+
* Characters in username: a-z 0-9 -.+_
|
398
434
|
* Must include: @
|
399
435
|
* Characters in domain: a-z 0-9 -
|
400
|
-
* Must include extension: .
|
436
|
+
* Must include extension: .com, .org, .museum
|
401
437
|
|
402
438
|
With an ActiveRecord model:
|
403
439
|
|
@@ -477,6 +513,38 @@ describe Auction do
|
|
477
513
|
end
|
478
514
|
```
|
479
515
|
|
516
|
+
## FileSizeValidator
|
517
|
+
|
518
|
+
Options: :in, :less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to
|
519
|
+
|
520
|
+
With an ActiveRecord model:
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
class Product < ActiveRecord::Base
|
524
|
+
attr_accessor :file, :name
|
525
|
+
validates :file, file_size: { in: 5.megabytes..10.megabytes }
|
526
|
+
end
|
527
|
+
```
|
528
|
+
|
529
|
+
Or any ruby class:
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
class Product
|
533
|
+
include ActiveModel::Validations
|
534
|
+
attr_accessor :file, :name
|
535
|
+
validates :file, file_size: { in: 5.megabytes..10.megabytes }
|
536
|
+
end
|
537
|
+
```
|
538
|
+
|
539
|
+
RSpec matcher is also available for your convenience:
|
540
|
+
|
541
|
+
```ruby
|
542
|
+
describe Product do
|
543
|
+
it { should ensure_valid_csv_format_of(:file) }
|
544
|
+
it { should_not ensure_valid_csv_format_of(:name) }
|
545
|
+
end
|
546
|
+
```
|
547
|
+
|
480
548
|
## HexValidator
|
481
549
|
|
482
550
|
**Ex:** #a9a9a9 or #999 or aaaaaa or AAA
|
data/active_validation.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = %w[lib]
|
22
22
|
|
23
|
+
spec.add_runtime_dependency 'actionpack'
|
23
24
|
spec.add_runtime_dependency 'activemodel'
|
24
25
|
spec.add_runtime_dependency 'activesupport'
|
25
26
|
|
@@ -27,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
27
28
|
spec.add_development_dependency 'coveralls'
|
28
29
|
spec.add_development_dependency 'rake'
|
29
30
|
spec.add_development_dependency 'rspec'
|
31
|
+
spec.add_development_dependency 'rspec-rails'
|
30
32
|
spec.add_development_dependency 'shoulda'
|
31
33
|
spec.add_development_dependency 'fasterer'
|
32
34
|
spec.add_development_dependency 'reek'
|
data/config/locales/en.yml
CHANGED
@@ -11,10 +11,30 @@ en:
|
|
11
11
|
latitude: 'is not a valid latitude'
|
12
12
|
longitude: 'is not a valid longitude'
|
13
13
|
credit_card: 'is not a valid credit card format'
|
14
|
+
csv:
|
15
|
+
columns: 'is not %{count}'
|
16
|
+
columns_in: 'is not between %{min} and %{max}'
|
17
|
+
columns_less_than: 'is not less than %{count}'
|
18
|
+
columns_less_than_or_equal_to: 'is not less than or equal to %{count}'
|
19
|
+
columns_greater_than: 'is not greater than %{count}'
|
20
|
+
columns_greater_than_or_equal_to: 'is not greater than or equal to %{count}'
|
21
|
+
not_valid: 'is not a valid csv'
|
22
|
+
rows: 'is not %{count}'
|
23
|
+
rows_in: 'is not between %{min} and %{max}'
|
24
|
+
rows_less_than: 'is not less than %{count}'
|
25
|
+
rows_less_than_or_equal_to: 'is not less than or equal to %{count}'
|
26
|
+
rows_greater_than: 'is not greater than %{count}'
|
27
|
+
rows_greater_than_or_equal_to: 'is not greater than or equal to %{count}'
|
14
28
|
currency: 'is not in valid currency format'
|
15
29
|
cusip: 'is not in valid CUSIP format'
|
16
30
|
email: 'is not in valid email format'
|
17
31
|
equality: 'is not %{operator} %{attr}'
|
32
|
+
file_size:
|
33
|
+
in: 'is not between %{min} and %{max}'
|
34
|
+
less_than: 'is not less than %{count}'
|
35
|
+
less_than_or_equal_to: 'is not less than or equal to %{count}'
|
36
|
+
greater_than: 'is not greater than %{count}'
|
37
|
+
greater_than_or_equal_to: 'is not greater than or equal to %{count}'
|
18
38
|
hex: 'is not in valid hex color format'
|
19
39
|
imei: 'is not in valid IMEI format'
|
20
40
|
ip: 'is not in valid IP format'
|
@@ -52,6 +72,9 @@ en:
|
|
52
72
|
ensure_valid_credit_card_format_of:
|
53
73
|
failure_message_for_should: '%{model} should ensure valid credit card format of attribute %{attr}'
|
54
74
|
failure_message_for_should_not: '%{model} should not ensure valid credit card format of attribute %{attr}'
|
75
|
+
ensure_valid_csv_format_of:
|
76
|
+
failure_message_for_should: '%{model} should ensure valid csv format of attribute %{attr}'
|
77
|
+
failure_message_for_should_not: '%{model} should not ensure valid csv format of attribute %{attr}'
|
55
78
|
ensure_valid_currency_format_of:
|
56
79
|
failure_message_for_should: '%{model} should ensure valid currency format of attribute %{attr}'
|
57
80
|
failure_message_for_should_not: '%{model} should not ensure valid currency format of attribute %{attr}'
|
@@ -64,6 +87,9 @@ en:
|
|
64
87
|
ensure_valid_equality_format_of:
|
65
88
|
failure_message_for_should: '%{model} should ensure equality of %{operator} on attribute %{attr}'
|
66
89
|
failure_message_for_should_not: '%{model} should not ensure equality of %{operator} on attribute %{attr}'
|
90
|
+
ensure_valid_file_size_format_of:
|
91
|
+
failure_message_for_should: '%{model} should ensure valid file size format of attribute %{attr}'
|
92
|
+
failure_message_for_should_not: '%{model} should not ensure valid file size format of attribute %{attr}'
|
67
93
|
ensure_valid_hex_format_of:
|
68
94
|
failure_message_for_should: '%{model} should ensure valid hex format of attribute %{attr}'
|
69
95
|
failure_message_for_should_not: '%{model} should not ensure valid hex format of attribute %{attr}'
|
data/lib/active_validation.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'action_dispatch'
|
1
2
|
require 'active_model'
|
2
3
|
require 'active_support'
|
3
4
|
require 'active_support/core_ext/time/zones'
|
4
5
|
require 'active_validation/version'
|
6
|
+
require 'csv'
|
5
7
|
|
6
8
|
ACTIVE_VALIDATION_VALIDATORS ||= %w[
|
7
|
-
alpha alpha_numeric base64 boolean coordinate credit_card currency cusip email equality
|
8
|
-
ip isbn isin mac_address name password phone sedol slug ssn time_zone
|
9
|
-
username uuid
|
9
|
+
alpha alpha_numeric base64 boolean coordinate credit_card csv currency cusip email equality
|
10
|
+
file_size hex imei ip isbn isin mac_address name password phone sedol slug ssn time_zone
|
11
|
+
tracking_number type url username uuid
|
10
12
|
].freeze
|
11
13
|
|
12
14
|
ACTIVE_VALIDATION_VALIDATORS.each do |file_name|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class CsvValidator < ActiveModel::EachValidator
|
2
|
+
|
3
|
+
CHECKS ||= {
|
4
|
+
columns: :==,
|
5
|
+
columns_in: :===,
|
6
|
+
columns_less_than: :<,
|
7
|
+
columns_less_than_or_equal_to: :<=,
|
8
|
+
columns_greater_than: :>,
|
9
|
+
columns_greater_than_or_equal_to: :>=,
|
10
|
+
rows: :==,
|
11
|
+
rows_in: :===,
|
12
|
+
rows_less_than: :<,
|
13
|
+
rows_less_than_or_equal_to: :<=,
|
14
|
+
rows_greater_than: :>,
|
15
|
+
rows_greater_than_or_equal_to: :>=
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def validate_each(record, attribute, value)
|
19
|
+
assert_valid_options!
|
20
|
+
values = parse_values(record, attribute, value)
|
21
|
+
|
22
|
+
if values.nil?
|
23
|
+
record.errors.add(attribute, 'not a valid csv')
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
options.slice(*CHECKS.keys).each do |option, option_value|
|
28
|
+
option_value = option_value.call(record) if option_value.is_a?(Proc)
|
29
|
+
|
30
|
+
next unless values.any? { |val| !valid_size?(val, option, option_value) }
|
31
|
+
error_text = filtered_options(values).merge!(detect_error_options(option_value))
|
32
|
+
error_text = options[:message] ||
|
33
|
+
I18n.t("active_validation.errors.messages.csv.#{option}", error_text)
|
34
|
+
record.errors[attribute] << (options[:message] || error_text)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def assert_valid_options!
|
41
|
+
unless (CHECKS.keys & options.keys).present?
|
42
|
+
raise ArgumentError,
|
43
|
+
"You must at least pass in one of these options - #{CHECKS.map(&:inspect).join(', ')}"
|
44
|
+
end
|
45
|
+
|
46
|
+
check_options(Numeric, options.slice(*(CHECKS.keys - %i[columns_in rows_in])))
|
47
|
+
check_options(Range, options.slice(:columns_in, :rows_in))
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_extension?(record, attribute, value)
|
51
|
+
value.path.end_with?('.csv')
|
52
|
+
rescue
|
53
|
+
record.errors[attribute] <<
|
54
|
+
(options[:message] || I18n.t('active_validation.errors.messages.csv.not_valid'))
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_values(record, attribute, value)
|
59
|
+
return nil unless valid_extension?(record, attribute, value)
|
60
|
+
[CSV.read(value.path)]
|
61
|
+
rescue CSV::MalformedCSVError
|
62
|
+
record.errors[attribute] <<
|
63
|
+
(options[:message] || I18n.t('active_validation.errors.messages.csv.not_valid'))
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_options(klass, options)
|
68
|
+
options.each do |option, value|
|
69
|
+
next if value.is_a?(klass) || value.is_a?(Proc)
|
70
|
+
raise ArgumentError,
|
71
|
+
":#{option} must be a #{klass.name.to_s.downcase} or a proc"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_size?(value, option, option_value)
|
76
|
+
size = /columns/.match?(option) ? value.first.length : value.length
|
77
|
+
|
78
|
+
return false if size.zero?
|
79
|
+
return option_value.send(CHECKS[option], size) if option_value.is_a?(Range)
|
80
|
+
size.send(CHECKS[option], option_value)
|
81
|
+
end
|
82
|
+
|
83
|
+
def filtered_options(value)
|
84
|
+
filtered = options.except(*CHECKS.keys)
|
85
|
+
filtered[:value] = value
|
86
|
+
filtered
|
87
|
+
end
|
88
|
+
|
89
|
+
def detect_error_options(option_value)
|
90
|
+
return { count: option_value } unless option_value.is_a?(Range)
|
91
|
+
{ min: option_value.min, max: option_value.max }
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class FileSizeValidator < ActiveModel::EachValidator
|
2
|
+
|
3
|
+
CHECKS ||= {
|
4
|
+
in: :===,
|
5
|
+
less_than: :<,
|
6
|
+
less_than_or_equal_to: :<=,
|
7
|
+
greater_than: :>,
|
8
|
+
greater_than_or_equal_to: :>=
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
def validate_each(record, attribute, value)
|
12
|
+
assert_valid_options!
|
13
|
+
values = parse_values(value)
|
14
|
+
return if values.empty?
|
15
|
+
|
16
|
+
options.slice(*CHECKS.keys).each do |option, option_value|
|
17
|
+
option_value = option_value.call(record) if option_value.is_a?(Proc)
|
18
|
+
|
19
|
+
next unless values.any? { |val| !valid_size?(val.size, option, option_value) }
|
20
|
+
error_text = filtered_options(values).merge!(detect_error_options(option_value))
|
21
|
+
error_text = options[:message] ||
|
22
|
+
I18n.t("active_validation.errors.messages.file_size.#{option}", error_text)
|
23
|
+
record.errors[attribute] << (options[:message] || error_text)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def assert_valid_options!
|
30
|
+
unless (CHECKS.keys & options.keys).present?
|
31
|
+
raise ArgumentError,
|
32
|
+
"You must at least pass in one of these options - #{CHECKS.map(&:inspect).join(', ')}"
|
33
|
+
end
|
34
|
+
|
35
|
+
check_options(Numeric, options.slice(*(CHECKS.keys - %i[in])))
|
36
|
+
check_options(Range, options.slice(:in))
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_values(value)
|
40
|
+
return [] unless value.present?
|
41
|
+
|
42
|
+
value = JSON.parse(value) if value.is_a?(String)
|
43
|
+
return [] unless value.present?
|
44
|
+
|
45
|
+
value = OpenStruct.new(value) if value.is_a?(Hash)
|
46
|
+
[value].reject(&:blank?)
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_options(klass, options)
|
50
|
+
options.each do |option, value|
|
51
|
+
next if value.is_a?(klass) || value.is_a?(Proc)
|
52
|
+
raise ArgumentError,
|
53
|
+
":#{option} must be a #{klass.name.to_s.downcase} or a proc"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def valid_size?(size, option, option_value)
|
58
|
+
return false if size.nil?
|
59
|
+
return option_value.send(CHECKS[option], size) if option_value.is_a?(Range)
|
60
|
+
size.send(CHECKS[option], option_value)
|
61
|
+
end
|
62
|
+
|
63
|
+
def filtered_options(value)
|
64
|
+
filtered = options.except(*CHECKS.keys)
|
65
|
+
filtered[:value] = value
|
66
|
+
filtered
|
67
|
+
end
|
68
|
+
|
69
|
+
def detect_error_options(option_value)
|
70
|
+
return { count: human_size(option_value) } unless option_value.is_a?(Range)
|
71
|
+
{ min: human_size(option_value.min), max: human_size(option_value.max) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def human_size(size)
|
75
|
+
if defined?(ActiveSupport::NumberHelper)
|
76
|
+
ActiveSupport::NumberHelper.number_to_human_size(size)
|
77
|
+
else
|
78
|
+
opts = { locale: options[:locale], raise: true }
|
79
|
+
format = I18n.t(:'number.human.storage_units.format', opts)
|
80
|
+
unit = I18n.t(:'number.human.storage_units.units.byte', opts.merge(count: size.to_i))
|
81
|
+
format.gsub(/%n/, size.to_i.to_s).gsub(/%u/, unit).html_safe
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_validation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-05-
|
11
|
+
date: 2017-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: activemodel
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +108,20 @@ dependencies:
|
|
94
108
|
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec-rails
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: shoulda
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,10 +212,12 @@ files:
|
|
184
212
|
- lib/active_validation/validators/boolean_validator.rb
|
185
213
|
- lib/active_validation/validators/coordinate_validator.rb
|
186
214
|
- lib/active_validation/validators/credit_card_validator.rb
|
215
|
+
- lib/active_validation/validators/csv_validator.rb
|
187
216
|
- lib/active_validation/validators/currency_validator.rb
|
188
217
|
- lib/active_validation/validators/cusip_validator.rb
|
189
218
|
- lib/active_validation/validators/email_validator.rb
|
190
219
|
- lib/active_validation/validators/equality_validator.rb
|
220
|
+
- lib/active_validation/validators/file_size_validator.rb
|
191
221
|
- lib/active_validation/validators/hex_validator.rb
|
192
222
|
- lib/active_validation/validators/imei_validator.rb
|
193
223
|
- lib/active_validation/validators/ip_validator.rb
|