active_storage_validations 0.9.7 → 1.0.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: 0f77bdb678731cdb6bb3aca1bddf3a3b1d1d4f34355a778c504931d067f6d0e2
4
- data.tar.gz: cb723cb17a4cbfcfec2e7a9b8432ac0d2641afbef327a92e38baa376d15ba2fd
3
+ metadata.gz: c3fa76d4dc0cf3d5bffc62dcc604d6f38edf165931c94d9314288606b6205a56
4
+ data.tar.gz: 9adb8defb7d44c65a4ea67cf632087d423a1c2c44b918822653638132f8beef4
5
5
  SHA512:
6
- metadata.gz: d44aed46b1f790d3df1913009fc1c84ea5831be3c8809063c94a0c50de3d8b9f40613eab920c49fb8f81dd0808d9c489aebb1705e022defe1e745d1205a48203
7
- data.tar.gz: 3d770860efb253f13ea1f18086c5b8a51253caf69875ba1d9d7921c17b9c5ce43f8001f4d8eeb1e095f8f37d1f0924dbddcdb35ca24b721d6fd5cf2a002d9a90
6
+ metadata.gz: 9a2d4ce5113ed8a018d71b4d40b01c11946892eea438d2b7481b7007511022695998271d06215e6ddf07929d4e057fc8411a2b0babcb508f70af6140fb4d65c8
7
+ data.tar.gz: 3d31b7848f3174a8df330e5410b5c1aec018a243bfdefa2abdfc8eee9d9f652e34645853da38dee2b1627ef42efa679fabdac11e896a6a37ce9cd0ed1c5569f7
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
2
+ />](https://www.railsjazz.com/?utm_source=github&utm_medium=top&utm_campaign=active_storage_validations)
3
+
1
4
  # Active Storage Validations
2
5
 
3
6
  [![MiniTest](https://github.com/igorkasyanchuk/active_storage_validations/workflows/MiniTest/badge.svg)](https://github.com/igorkasyanchuk/active_storage_validations/actions)
@@ -17,6 +20,7 @@ This gems doing it for you. Just use `attached: true` or `content_type: 'image/p
17
20
  * validates number of uploaded files (min/max required)
18
21
  * validates aspect ratio (if square, portrait, landscape, is_16_9, ...)
19
22
  * custom error messages
23
+ * allow procs for dynamic determination of values
20
24
 
21
25
  ## Usage
22
26
 
@@ -121,6 +125,18 @@ class User < ApplicationRecord
121
125
  end
122
126
  ```
123
127
 
128
+ - Proc Usage:
129
+
130
+ Procs can be used instead of values in all the above examples. They will be called on every validation.
131
+ ```ruby
132
+ class User < ApplicationRecord
133
+ has_many_attached :proc_files
134
+
135
+ validates :proc_files, limit: { max: -> (record) { record.admin? ? 100 : 10 } }
136
+ end
137
+
138
+ ```
139
+
124
140
  ## Internationalization (I18n)
125
141
 
126
142
  Active Storage Validations uses I18n for error messages. For this, add these keys in your translation file:
@@ -303,12 +319,10 @@ To run tests in root folder of gem:
303
319
  Snippet to run in console:
304
320
 
305
321
  ```
306
- BUNDLE_GEMFILE=gemfiles/rails_5_2.gemfile bundle
307
322
  BUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle
308
323
  BUNDLE_GEMFILE=gemfiles/rails_6_1.gemfile bundle
309
324
  BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle
310
325
  BUNDLE_GEMFILE=gemfiles/rails_next.gemfile bundle
311
- BUNDLE_GEMFILE=gemfiles/rails_5_2.gemfile bundle exec rake test
312
326
  BUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake test
313
327
  BUNDLE_GEMFILE=gemfiles/rails_6_1.gemfile bundle exec rake test
314
328
  BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test
@@ -375,10 +389,16 @@ You are welcome to contribute.
375
389
  - https://github.com/luiseugenio
376
390
  - https://github.com/equivalent
377
391
  - https://github.com/NARKOZ
392
+ - https://github.com/stephensolis
393
+ - https://github.com/kwent
394
+ = https://github.com/Animesh-Ghosh
395
+ = https://github.com/gr8bit
396
+ = https://github.com/codegeek319
397
+ = https://github.com/clwy-cn
378
398
 
379
399
  ## License
380
400
 
381
401
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
382
402
 
383
403
  [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
384
- />](https://www.railsjazz.com/)
404
+ />](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=active_storage_validations)
@@ -0,0 +1,22 @@
1
+ zh-CN:
2
+ errors:
3
+ messages:
4
+ content_type_invalid: "文件类型错误"
5
+ file_size_out_of_range: "文件大小 %{file_size} 超出限定范围"
6
+ limit_out_of_range: "文件数超出限定范围"
7
+ image_metadata_missing: "不是有效的图像"
8
+ dimension_min_inclusion: "必须大于或等于 %{width} x %{height} 像素"
9
+ dimension_max_inclusion: "必须小于或等于 %{width} x %{height} 像素"
10
+ dimension_width_inclusion: "宽度不在 %{min} 和 %{max} 像素之间"
11
+ dimension_height_inclusion: "高度不在 %{min} 和 %{max} 像素之间"
12
+ dimension_width_greater_than_or_equal_to: "宽度必须大于或等于 %{length} 像素"
13
+ dimension_height_greater_than_or_equal_to: "高度必须大于或等于 %{length} 像素"
14
+ dimension_width_less_than_or_equal_to: "宽度必须小于或等于 %{length} 像素"
15
+ dimension_height_less_than_or_equal_to: "高度必须小于或等于 %{length} 像素"
16
+ dimension_width_equal_to: "宽度必须等于 %{length} 像素"
17
+ dimension_height_equal_to: "高度必须等于 %{length} 像素"
18
+ aspect_ratio_not_square: "必须是方形图片"
19
+ aspect_ratio_not_portrait: "必须是竖屏图片"
20
+ aspect_ratio_not_landscape: "必须是横屏图片"
21
+ aspect_ratio_is_not: "纵横比必须是 %{aspect_ratio}"
22
+ aspect_ratio_unknown: "未知的纵横比"
@@ -4,17 +4,14 @@ require_relative 'metadata.rb'
4
4
 
5
5
  module ActiveStorageValidations
6
6
  class AspectRatioValidator < ActiveModel::EachValidator # :nodoc
7
+ include OptionProcUnfolding
8
+
7
9
  AVAILABLE_CHECKS = %i[with].freeze
8
10
  PRECISION = 3
9
11
 
10
- def initialize(options)
11
- super(options)
12
- end
13
-
14
-
15
12
  def check_validity!
16
13
  return true if AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
17
- raise ArgumentError, 'You must pass "aspect_ratio: :OPTION" option to the validator'
14
+ raise ArgumentError, 'You must pass :with to the validator'
18
15
  end
19
16
 
20
17
  if Rails.gem_version >= Gem::Version.new('6.0.0')
@@ -55,26 +52,27 @@ module ActiveStorageValidations
55
52
 
56
53
 
57
54
  def is_valid?(record, attribute, metadata)
55
+ flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
58
56
  if metadata[:width].to_i <= 0 || metadata[:height].to_i <= 0
59
- add_error(record, attribute, options[:message].presence || :image_metadata_missing)
57
+ add_error(record, attribute, :image_metadata_missing, flat_options[:with])
60
58
  return false
61
59
  end
62
60
 
63
- case options[:with]
61
+ case flat_options[:with]
64
62
  when :square
65
63
  return true if metadata[:width] == metadata[:height]
66
- add_error(record, attribute, :aspect_ratio_not_square)
64
+ add_error(record, attribute, :aspect_ratio_not_square, flat_options[:with])
67
65
 
68
66
  when :portrait
69
67
  return true if metadata[:height] > metadata[:width]
70
- add_error(record, attribute, :aspect_ratio_not_portrait)
68
+ add_error(record, attribute, :aspect_ratio_not_portrait, flat_options[:with])
71
69
 
72
70
  when :landscape
73
71
  return true if metadata[:width] > metadata[:height]
74
- add_error(record, attribute, :aspect_ratio_not_landscape)
72
+ add_error(record, attribute, :aspect_ratio_not_landscape, flat_options[:with])
75
73
 
76
74
  else
77
- if options[:with] =~ /is\_(\d*)\_(\d*)/
75
+ if flat_options[:with] =~ /is_(\d*)_(\d*)/
78
76
  x = $1.to_i
79
77
  y = $2.to_i
80
78
 
@@ -82,17 +80,17 @@ module ActiveStorageValidations
82
80
 
83
81
  add_error(record, attribute, :aspect_ratio_is_not, "#{x}x#{y}")
84
82
  else
85
- add_error(record, attribute, :aspect_ratio_unknown)
83
+ add_error(record, attribute, :aspect_ratio_unknown, flat_options[:with])
86
84
  end
87
85
  end
88
86
  false
89
87
  end
90
88
 
91
89
 
92
- def add_error(record, attribute, type, interpolate = options[:with])
93
- key = options[:message].presence || type
94
- return if record.errors.added?(attribute, key)
95
- record.errors.add(attribute, key, aspect_ratio: interpolate)
90
+ def add_error(record, attribute, default_message, interpolate)
91
+ message = options[:message].presence || default_message
92
+ return if record.errors.added?(attribute, message)
93
+ record.errors.add(attribute, message, aspect_ratio: interpolate)
96
94
  end
97
95
 
98
96
  end
@@ -2,16 +2,23 @@
2
2
 
3
3
  module ActiveStorageValidations
4
4
  class ContentTypeValidator < ActiveModel::EachValidator # :nodoc:
5
+ include OptionProcUnfolding
6
+
7
+ AVAILABLE_CHECKS = %i[with in].freeze
8
+
5
9
  def validate_each(record, attribute, _value)
6
- return true if !record.send(attribute).attached? || types.empty?
10
+ return true unless record.send(attribute).attached?
7
11
 
12
+ types = authorized_types(record)
13
+ return true if types.empty?
14
+
8
15
  files = Array.wrap(record.send(attribute))
9
16
 
10
- errors_options = { authorized_types: types_to_human_format }
17
+ errors_options = { authorized_types: types_to_human_format(types) }
11
18
  errors_options[:message] = options[:message] if options[:message].present?
12
19
 
13
20
  files.each do |file|
14
- next if is_valid?(file)
21
+ next if is_valid?(file, types)
15
22
 
16
23
  errors_options[:content_type] = content_type(file)
17
24
  record.errors.add(attribute, :content_type_invalid, **errors_options)
@@ -19,10 +26,9 @@ module ActiveStorageValidations
19
26
  end
20
27
  end
21
28
 
22
- def types
23
- return @types if defined? @types
24
-
25
- @types = (Array.wrap(options[:with]) + Array.wrap(options[:in])).compact.map do |type|
29
+ def authorized_types(record)
30
+ flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
31
+ (Array.wrap(flat_options[:with]) + Array.wrap(flat_options[:in])).compact.map do |type|
26
32
  if type.is_a?(Regexp)
27
33
  type
28
34
  else
@@ -31,7 +37,7 @@ module ActiveStorageValidations
31
37
  end
32
38
  end
33
39
 
34
- def types_to_human_format
40
+ def types_to_human_format(types)
35
41
  types
36
42
  .map { |type| type.to_s.split('/').last.upcase }
37
43
  .join(', ')
@@ -41,7 +47,7 @@ module ActiveStorageValidations
41
47
  file.blob.present? && file.blob.content_type
42
48
  end
43
49
 
44
- def is_valid?(file)
50
+ def is_valid?(file, types)
45
51
  file_type = content_type(file)
46
52
  types.any? do |type|
47
53
  type == file_type || (type.is_a?(Regexp) && type.match?(file_type.to_s))
@@ -4,25 +4,30 @@ require_relative 'metadata.rb'
4
4
 
5
5
  module ActiveStorageValidations
6
6
  class DimensionValidator < ActiveModel::EachValidator # :nodoc
7
+ include OptionProcUnfolding
8
+
7
9
  AVAILABLE_CHECKS = %i[width height min max].freeze
8
10
 
9
- def initialize(options)
11
+ def process_options(record)
12
+ flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
13
+
10
14
  [:width, :height].each do |length|
11
- if options[length] and options[length].is_a?(Hash)
12
- if range = options[length][:in]
15
+ if flat_options[length] and flat_options[length].is_a?(Hash)
16
+ if (range = flat_options[length][:in])
13
17
  raise ArgumentError, ":in must be a Range" unless range.is_a?(Range)
14
- options[length][:min], options[length][:max] = range.min, range.max
18
+ flat_options[length][:min], flat_options[length][:max] = range.min, range.max
15
19
  end
16
20
  end
17
21
  end
18
22
  [:min, :max].each do |dim|
19
- if range = options[dim]
23
+ if (range = flat_options[dim])
20
24
  raise ArgumentError, ":#{dim} must be a Range (width..height)" unless range.is_a?(Range)
21
- options[:width] = { dim => range.first }
22
- options[:height] = { dim => range.last }
25
+ flat_options[:width] = { dim => range.first }
26
+ flat_options[:height] = { dim => range.last }
23
27
  end
24
28
  end
25
- super
29
+
30
+ flat_options
26
31
  end
27
32
 
28
33
 
@@ -64,63 +69,67 @@ module ActiveStorageValidations
64
69
 
65
70
 
66
71
  def is_valid?(record, attribute, file_metadata)
72
+ flat_options = process_options(record)
67
73
  # Validation fails unless file metadata contains valid width and height.
68
74
  if file_metadata[:width].to_i <= 0 || file_metadata[:height].to_i <= 0
69
- add_error(record, attribute, options[:message].presence || :image_metadata_missing)
75
+ add_error(record, attribute, :image_metadata_missing)
70
76
  return false
71
77
  end
72
78
 
73
79
  # Validation based on checks :min and :max (:min, :max has higher priority to :width, :height).
74
- if options[:min] || options[:max]
75
- if options[:min] && (
76
- (options[:width][:min] && file_metadata[:width] < options[:width][:min]) ||
77
- (options[:height][:min] && file_metadata[:height] < options[:height][:min])
80
+ if flat_options[:min] || flat_options[:max]
81
+ if flat_options[:min] && (
82
+ (flat_options[:width][:min] && file_metadata[:width] < flat_options[:width][:min]) ||
83
+ (flat_options[:height][:min] && file_metadata[:height] < flat_options[:height][:min])
78
84
  )
79
- add_error(record, attribute, options[:message].presence || :"dimension_min_inclusion", width: options[:width][:min], height: options[:height][:min])
85
+ add_error(record, attribute, :dimension_min_inclusion, width: flat_options[:width][:min], height: flat_options[:height][:min])
80
86
  return false
81
87
  end
82
- if options[:max] && (
83
- (options[:width][:max] && file_metadata[:width] > options[:width][:max]) ||
84
- (options[:height][:max] && file_metadata[:height] > options[:height][:max])
88
+ if flat_options[:max] && (
89
+ (flat_options[:width][:max] && file_metadata[:width] > flat_options[:width][:max]) ||
90
+ (flat_options[:height][:max] && file_metadata[:height] > flat_options[:height][:max])
85
91
  )
86
- add_error(record, attribute, options[:message].presence || :"dimension_max_inclusion", width: options[:width][:max], height: options[:height][:max])
92
+ add_error(record, attribute, :dimension_max_inclusion, width: flat_options[:width][:max], height: flat_options[:height][:max])
87
93
  return false
88
94
  end
89
95
 
90
96
  # Validation based on checks :width and :height.
91
97
  else
98
+ width_or_height_invalid = false
92
99
  [:width, :height].each do |length|
93
- next unless options[length]
94
- if options[length].is_a?(Hash)
95
- if options[length][:in] && (file_metadata[length] < options[length][:min] || file_metadata[length] > options[length][:max])
96
- add_error(record, attribute, options[:message].presence || :"dimension_#{length}_inclusion", min: options[length][:min], max: options[length][:max])
97
- return false
100
+ next unless flat_options[length]
101
+ if flat_options[length].is_a?(Hash)
102
+ if flat_options[length][:in] && (file_metadata[length] < flat_options[length][:min] || file_metadata[length] > flat_options[length][:max])
103
+ add_error(record, attribute, :"dimension_#{length}_inclusion", min: flat_options[length][:min], max: flat_options[length][:max])
104
+ width_or_height_invalid = true
98
105
  else
99
- if options[length][:min] && file_metadata[length] < options[length][:min]
100
- add_error(record, attribute, options[:message].presence || :"dimension_#{length}_greater_than_or_equal_to", length: options[length][:min])
101
- return false
106
+ if flat_options[length][:min] && file_metadata[length] < flat_options[length][:min]
107
+ add_error(record, attribute, :"dimension_#{length}_greater_than_or_equal_to", length: flat_options[length][:min])
108
+ width_or_height_invalid = true
102
109
  end
103
- if options[length][:max] && file_metadata[length] > options[length][:max]
104
- add_error(record, attribute, options[:message].presence || :"dimension_#{length}_less_than_or_equal_to", length: options[length][:max])
105
- return false
110
+ if flat_options[length][:max] && file_metadata[length] > flat_options[length][:max]
111
+ add_error(record, attribute, :"dimension_#{length}_less_than_or_equal_to", length: flat_options[length][:max])
112
+ width_or_height_invalid = true
106
113
  end
107
114
  end
108
115
  else
109
- if file_metadata[length] != options[length]
110
- add_error(record, attribute, options[:message].presence || :"dimension_#{length}_equal_to", length: options[length])
111
- return false
116
+ if file_metadata[length] != flat_options[length]
117
+ add_error(record, attribute, :"dimension_#{length}_equal_to", length: flat_options[length])
118
+ width_or_height_invalid = true
112
119
  end
113
120
  end
114
121
  end
122
+
123
+ return false if width_or_height_invalid
115
124
  end
116
125
 
117
126
  true # valid file
118
127
  end
119
128
 
120
- def add_error(record, attribute, type, **attrs)
121
- key = options[:message].presence || type
122
- return if record.errors.added?(attribute, key)
123
- record.errors.add(attribute, key, **attrs)
129
+ def add_error(record, attribute, default_message, **attrs)
130
+ message = options[:message].presence || default_message
131
+ return if record.errors.added?(attribute, message)
132
+ record.errors.add(attribute, message, **attrs)
124
133
  end
125
134
 
126
135
  end
@@ -2,11 +2,12 @@
2
2
 
3
3
  module ActiveStorageValidations
4
4
  class LimitValidator < ActiveModel::EachValidator # :nodoc:
5
+ include OptionProcUnfolding
6
+
5
7
  AVAILABLE_CHECKS = %i[max min].freeze
6
8
 
7
9
  def check_validity!
8
10
  return true if AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
9
-
10
11
  raise ArgumentError, 'You must pass either :max or :min to the validator'
11
12
  end
12
13
 
@@ -14,19 +15,20 @@ module ActiveStorageValidations
14
15
  return true unless record.send(attribute).attached?
15
16
 
16
17
  files = Array.wrap(record.send(attribute)).compact.uniq
17
- errors_options = { min: options[:min], max: options[:max] }
18
+ flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
19
+ errors_options = { min: flat_options[:min], max: flat_options[:max] }
18
20
 
19
- return true if files_count_valid?(files.count)
21
+ return true if files_count_valid?(files.count, flat_options)
20
22
  record.errors.add(attribute, options[:message].presence || :limit_out_of_range, **errors_options)
21
23
  end
22
24
 
23
- def files_count_valid?(count)
24
- if options[:max].present? && options[:min].present?
25
- count >= options[:min] && count <= options[:max]
26
- elsif options[:max].present?
27
- count <= options[:max]
28
- elsif options[:min].present?
29
- count >= options[:min]
25
+ def files_count_valid?(count, flat_options)
26
+ if flat_options[:max].present? && flat_options[:min].present?
27
+ count >= flat_options[:min] && count <= flat_options[:max]
28
+ elsif flat_options[:max].present?
29
+ count <= flat_options[:max]
30
+ elsif flat_options[:min].present?
31
+ count >= flat_options[:min]
30
32
  end
31
33
  end
32
34
  end
@@ -10,6 +10,14 @@ module ActiveStorageValidations
10
10
  def image_processor
11
11
  Rails.application.config.active_storage.variant_processor
12
12
  end
13
+
14
+ def exception_class
15
+ if image_processor == :vips && defined?(Vips)
16
+ Vips::Error
17
+ elsif defined?(MiniMagick)
18
+ MiniMagick::Error
19
+ end
20
+ end
13
21
 
14
22
  def require_image_processor
15
23
  if image_processor == :vips
@@ -55,15 +63,15 @@ module ActiveStorageValidations
55
63
  tempfile.flush
56
64
  tempfile.rewind
57
65
 
58
- image = if image_processor == :vips && Vips::get_suffixes.include?(File.extname(tempfile.path))
66
+ image = if image_processor == :vips && defined?(Vips) && Vips::get_suffixes.include?(File.extname(tempfile.path).downcase)
59
67
  Vips::Image.new_from_file(tempfile.path)
60
- else
68
+ elsif defined?(MiniMagick)
61
69
  MiniMagick::Image.new(tempfile.path)
62
70
  end
63
71
  else
64
- image = if image_processor == :vips && Vips::get_suffixes.include?(File.extname(read_file_path))
72
+ image = if image_processor == :vips && defined?(Vips) && Vips::get_suffixes.include?(File.extname(read_file_path).downcase)
65
73
  Vips::Image.new_from_file(read_file_path)
66
- else
74
+ elsif defined?(MiniMagick)
67
75
  MiniMagick::Image.new(read_file_path)
68
76
  end
69
77
  end
@@ -77,30 +85,27 @@ module ActiveStorageValidations
77
85
  rescue LoadError, NameError
78
86
  logger.info "Skipping image analysis because the mini_magick or ruby-vips gem isn't installed"
79
87
  {}
80
- rescue MiniMagick::Error => error
81
- logger.error "Skipping image analysis due to an ImageMagick error: #{error.message}"
82
- {}
83
- rescue Vips::Error => error
84
- logger.error "Skipping image analysis due to a Vips error: #{error.message}"
88
+ rescue exception_class => error
89
+ logger.error "Skipping image analysis due to an #{exception_class.name.split('::').map(&:downcase).join(' ').capitalize} error: #{error.message}"
85
90
  {}
86
91
  ensure
87
92
  image = nil
88
93
  end
89
94
 
90
95
  def valid_image?(image)
91
- image_processor == :vips ? image.avg : image.valid?
92
- rescue Vips::Error
96
+ image_processor == :vips && image.is_a?(Vips::Image) ? image.avg : image.valid?
97
+ rescue exception_class
93
98
  false
94
99
  end
95
100
 
96
101
  def rotated_image?(image)
97
- if image_processor == :vips
102
+ if image_processor == :vips && image.is_a?(Vips::Image)
98
103
  image.get('exif-ifd0-Orientation').include?('Right-top') ||
99
104
  image.get('exif-ifd0-Orientation').include?('Left-bottom')
100
105
  else
101
106
  %w[ RightTop LeftBottom ].include?(image["%[orientation]"])
102
107
  end
103
- rescue Vips::Error # field "exif-ifd0-Orientation" not found
108
+ rescue exception_class # field "exif-ifd0-Orientation" not found
104
109
  false
105
110
  end
106
111
 
@@ -0,0 +1,16 @@
1
+ module ActiveStorageValidations
2
+ module OptionProcUnfolding
3
+
4
+ def unfold_procs(record, object, only_keys = nil)
5
+ case object
6
+ when Hash
7
+ object.merge(object) { |key, value| only_keys&.exclude?(key) ? unfold_procs(record, value, []) : unfold_procs(record, value) }
8
+ when Array
9
+ object.map { |o| unfold_procs(record, o, only_keys) }
10
+ else
11
+ object.is_a?(Proc) ? object.call(record) : object
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -2,14 +2,15 @@
2
2
 
3
3
  module ActiveStorageValidations
4
4
  class SizeValidator < ActiveModel::EachValidator # :nodoc:
5
+ include OptionProcUnfolding
6
+
5
7
  delegate :number_to_human_size, to: ActiveSupport::NumberHelper
6
8
 
7
9
  AVAILABLE_CHECKS = %i[less_than less_than_or_equal_to greater_than greater_than_or_equal_to between].freeze
8
10
 
9
11
  def check_validity!
10
12
  return true if AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
11
-
12
- raise ArgumentError, 'You must pass either :less_than, :greater_than, or :between to the validator'
13
+ raise ArgumentError, 'You must pass either :less_than(_or_equal_to), :greater_than(_or_equal_to), or :between to the validator'
13
14
  end
14
15
 
15
16
  def validate_each(record, attribute, _value)
@@ -20,39 +21,40 @@ module ActiveStorageValidations
20
21
 
21
22
  errors_options = {}
22
23
  errors_options[:message] = options[:message] if options[:message].present?
24
+ flat_options = unfold_procs(record, self.options, AVAILABLE_CHECKS)
23
25
 
24
26
  files.each do |file|
25
- next if content_size_valid?(file.blob.byte_size)
27
+ next if content_size_valid?(file.blob.byte_size, flat_options)
26
28
 
27
29
  errors_options[:file_size] = number_to_human_size(file.blob.byte_size)
28
- errors_options[:min_size] = number_to_human_size(min_size)
29
- errors_options[:max_size] = number_to_human_size(max_size)
30
+ errors_options[:min_size] = number_to_human_size(min_size(flat_options))
31
+ errors_options[:max_size] = number_to_human_size(max_size(flat_options))
30
32
 
31
33
  record.errors.add(attribute, :file_size_out_of_range, **errors_options)
32
34
  break
33
35
  end
34
36
  end
35
37
 
36
- def content_size_valid?(file_size)
37
- if options[:between].present?
38
- options[:between].include?(file_size)
39
- elsif options[:less_than].present?
40
- file_size < options[:less_than]
41
- elsif options[:less_than_or_equal_to].present?
42
- file_size <= options[:less_than_or_equal_to]
43
- elsif options[:greater_than].present?
44
- file_size > options[:greater_than]
45
- elsif options[:greater_than_or_equal_to].present?
46
- file_size >= options[:greater_than_or_equal_to]
38
+ def content_size_valid?(file_size, flat_options)
39
+ if flat_options[:between].present?
40
+ flat_options[:between].include?(file_size)
41
+ elsif flat_options[:less_than].present?
42
+ file_size < flat_options[:less_than]
43
+ elsif flat_options[:less_than_or_equal_to].present?
44
+ file_size <= flat_options[:less_than_or_equal_to]
45
+ elsif flat_options[:greater_than].present?
46
+ file_size > flat_options[:greater_than]
47
+ elsif flat_options[:greater_than_or_equal_to].present?
48
+ file_size >= flat_options[:greater_than_or_equal_to]
47
49
  end
48
50
  end
49
51
 
50
- def min_size
51
- options[:between]&.min || options[:greater_than] || options[:greater_than_or_equal_to]
52
+ def min_size(flat_options)
53
+ flat_options[:between]&.min || flat_options[:greater_than] || flat_options[:greater_than_or_equal_to]
52
54
  end
53
55
 
54
- def max_size
55
- options[:between]&.max || options[:less_than] || options[:less_than_or_equal_to]
56
+ def max_size(flat_options)
57
+ flat_options[:between]&.max || flat_options[:less_than] || flat_options[:less_than_or_equal_to]
56
58
  end
57
59
  end
58
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageValidations
4
- VERSION = '0.9.7'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'active_storage_validations/railtie'
4
4
  require 'active_storage_validations/engine'
5
+ require 'active_storage_validations/option_proc_unfolding'
5
6
  require 'active_storage_validations/attached_validator'
6
7
  require 'active_storage_validations/content_type_validator'
7
8
  require 'active_storage_validations/size_validator'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_storage_validations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Kasyanchuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-05 00:00:00.000000000 Z
11
+ date: 2022-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: globalid
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  description: Validations for Active Storage (presence)
168
182
  email:
169
183
  - igorkasyanchuk@gmail.com
@@ -187,6 +201,7 @@ files:
187
201
  - config/locales/tr.yml
188
202
  - config/locales/uk.yml
189
203
  - config/locales/vi.yml
204
+ - config/locales/zh-CN.yml
190
205
  - lib/active_storage_validations.rb
191
206
  - lib/active_storage_validations/aspect_ratio_validator.rb
192
207
  - lib/active_storage_validations/attached_validator.rb
@@ -200,6 +215,7 @@ files:
200
215
  - lib/active_storage_validations/matchers/dimension_validator_matcher.rb
201
216
  - lib/active_storage_validations/matchers/size_validator_matcher.rb
202
217
  - lib/active_storage_validations/metadata.rb
218
+ - lib/active_storage_validations/option_proc_unfolding.rb
203
219
  - lib/active_storage_validations/railtie.rb
204
220
  - lib/active_storage_validations/size_validator.rb
205
221
  - lib/active_storage_validations/version.rb
@@ -223,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
239
  - !ruby/object:Gem::Version
224
240
  version: '0'
225
241
  requirements: []
226
- rubygems_version: 3.2.3
242
+ rubygems_version: 3.3.7
227
243
  signing_key:
228
244
  specification_version: 4
229
245
  summary: Validations for Active Storage