active_validation 4.0.10 → 4.0.11

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
  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