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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e68e565089ff76403013cd80554802c6a8c3bcfd
4
- data.tar.gz: d320d62d5461c22839062827c0d55c5e99197df1
3
+ metadata.gz: 13401eb32dc1430b9fdf5bfba438388b30cfe377
4
+ data.tar.gz: ec4688cf3c3dafb36fa78c9cb3e3d3581d3ffc57
5
5
  SHA512:
6
- metadata.gz: 9eab1bed6675c4b0e9061300c95a8d5b2823f77c47e127611280dd3a53cacce3bedbcc08cdec403c09573f120e491c4bfb44ac6bf63e8f5781354d7112695d87
7
- data.tar.gz: feaa1e2be8a7ec98715f9be51ee673434fc25dd1fd69ffe0a12f991dcc48e11f907f294cac69d820130ca808eaf847c5eaeb1e8967adc5205489a54d24564a34
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: .co, .org, .museum
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
@@ -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'
@@ -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}'
@@ -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 hex imei
8
- ip isbn isin mac_address name password phone sedol slug ssn time_zone tracking_number type url
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|
@@ -1,4 +1,5 @@
1
1
  class BooleanValidator < ActiveModel::EachValidator
2
+
2
3
  FALSE_VALUES ||= [false, 0, '0', 'f', 'F', 'false', 'FALSE'].freeze
3
4
  TRUE_VALUES ||= [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze
4
5
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ActiveValidation
2
- VERSION = '4.0.10'.freeze
2
+ VERSION = '4.0.11'.freeze
3
3
  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.10
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-26 00:00:00.000000000 Z
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