active_interaction 3.7.1 → 4.0.0

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +190 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +96 -90
  5. data/lib/active_interaction.rb +1 -7
  6. data/lib/active_interaction/base.rb +48 -33
  7. data/lib/active_interaction/concerns/active_modelable.rb +1 -3
  8. data/lib/active_interaction/concerns/active_recordable.rb +1 -6
  9. data/lib/active_interaction/concerns/hashable.rb +0 -1
  10. data/lib/active_interaction/concerns/missable.rb +0 -1
  11. data/lib/active_interaction/concerns/runnable.rb +16 -28
  12. data/lib/active_interaction/errors.rb +8 -7
  13. data/lib/active_interaction/filter.rb +51 -37
  14. data/lib/active_interaction/filter_column.rb +0 -3
  15. data/lib/active_interaction/filters/abstract_date_time_filter.rb +34 -36
  16. data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
  17. data/lib/active_interaction/filters/array_filter.rb +57 -36
  18. data/lib/active_interaction/filters/boolean_filter.rb +26 -12
  19. data/lib/active_interaction/filters/date_filter.rb +1 -2
  20. data/lib/active_interaction/filters/date_time_filter.rb +1 -2
  21. data/lib/active_interaction/filters/decimal_filter.rb +10 -28
  22. data/lib/active_interaction/filters/file_filter.rb +6 -5
  23. data/lib/active_interaction/filters/float_filter.rb +1 -2
  24. data/lib/active_interaction/filters/hash_filter.rb +37 -27
  25. data/lib/active_interaction/filters/integer_filter.rb +7 -8
  26. data/lib/active_interaction/filters/interface_filter.rb +48 -14
  27. data/lib/active_interaction/filters/object_filter.rb +23 -50
  28. data/lib/active_interaction/filters/record_filter.rb +10 -35
  29. data/lib/active_interaction/filters/string_filter.rb +21 -12
  30. data/lib/active_interaction/filters/symbol_filter.rb +13 -7
  31. data/lib/active_interaction/filters/time_filter.rb +19 -22
  32. data/lib/active_interaction/grouped_input.rb +0 -3
  33. data/lib/active_interaction/inputs.rb +89 -0
  34. data/lib/active_interaction/locale/ja.yml +24 -0
  35. data/lib/active_interaction/modules/input_processor.rb +9 -6
  36. data/lib/active_interaction/modules/validation.rb +9 -12
  37. data/lib/active_interaction/version.rb +1 -3
  38. data/spec/active_interaction/base_spec.rb +95 -35
  39. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
  40. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
  41. data/spec/active_interaction/concerns/hashable_spec.rb +1 -3
  42. data/spec/active_interaction/concerns/missable_spec.rb +0 -2
  43. data/spec/active_interaction/concerns/runnable_spec.rb +32 -12
  44. data/spec/active_interaction/errors_spec.rb +49 -22
  45. data/spec/active_interaction/filter_column_spec.rb +0 -2
  46. data/spec/active_interaction/filter_spec.rb +0 -2
  47. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
  48. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
  49. data/spec/active_interaction/filters/array_filter_spec.rb +41 -15
  50. data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
  51. data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
  52. data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
  53. data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
  54. data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
  55. data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
  56. data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
  57. data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
  58. data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
  59. data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
  60. data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
  61. data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
  62. data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
  63. data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
  64. data/spec/active_interaction/grouped_input_spec.rb +0 -2
  65. data/spec/active_interaction/i18n_spec.rb +3 -7
  66. data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +5 -5
  67. data/spec/active_interaction/integration/array_interaction_spec.rb +23 -12
  68. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
  69. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
  70. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
  71. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
  72. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
  73. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -2
  74. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
  75. data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
  76. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
  77. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
  78. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
  79. data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
  80. data/spec/active_interaction/modules/validation_spec.rb +1 -3
  81. data/spec/spec_helper.rb +2 -6
  82. data/spec/support/concerns.rb +0 -2
  83. data/spec/support/filters.rb +13 -9
  84. data/spec/support/interactions.rb +22 -14
  85. metadata +106 -52
  86. data/lib/active_interaction/backports.rb +0 -59
  87. data/lib/active_interaction/filters/abstract_filter.rb +0 -19
  88. data/spec/active_interaction/filters/abstract_filter_spec.rb +0 -8
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'active_model'
@@ -7,8 +6,6 @@ require 'active_model'
7
6
  #
8
7
  # @author Aaron Lasseigne <aaron.lasseigne@gmail.com>
9
8
  # @author Taylor Fausak <taylor@fausak.me>
10
- #
11
- # @since 1.0.0
12
9
  module ActiveInteraction
13
10
  end
14
11
 
@@ -22,13 +19,12 @@ require 'active_interaction/concerns/missable'
22
19
  require 'active_interaction/concerns/runnable'
23
20
 
24
21
  require 'active_interaction/grouped_input'
22
+ require 'active_interaction/inputs'
25
23
 
26
- require 'active_interaction/modules/input_processor'
27
24
  require 'active_interaction/modules/validation'
28
25
 
29
26
  require 'active_interaction/filter_column'
30
27
  require 'active_interaction/filter'
31
- require 'active_interaction/filters/abstract_filter'
32
28
  require 'active_interaction/filters/interface_filter'
33
29
  require 'active_interaction/filters/abstract_date_time_filter'
34
30
  require 'active_interaction/filters/abstract_numeric_filter'
@@ -49,8 +45,6 @@ require 'active_interaction/filters/time_filter'
49
45
 
50
46
  require 'active_interaction/base'
51
47
 
52
- require 'active_interaction/backports'
53
-
54
48
  I18n.load_path.unshift(
55
49
  *Dir.glob(
56
50
  File.expand_path(
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'active_support/core_ext/hash/indifferent_access'
@@ -74,9 +73,7 @@ module ActiveInteraction
74
73
  # @return [String, nil] The description.
75
74
  def desc(desc = nil)
76
75
  if desc.nil?
77
- unless instance_variable_defined?(:@_interaction_desc)
78
- @_interaction_desc = nil
79
- end
76
+ @_interaction_desc = nil unless instance_variable_defined?(:@_interaction_desc)
80
77
  else
81
78
  @_interaction_desc = desc
82
79
  end
@@ -88,17 +85,21 @@ module ActiveInteraction
88
85
  #
89
86
  # @return [Hash{Symbol => Filter}]
90
87
  def filters
88
+ # rubocop:disable Naming/MemoizedInstanceVariableName
91
89
  @_interaction_filters ||= {}
90
+ # rubocop:enable Naming/MemoizedInstanceVariableName
92
91
  end
93
92
 
94
93
  # @private
95
- def method_missing(*args, &block) # rubocop:disable Style/MethodMissing
94
+ # rubocop:disable Style/MissingRespondToMissing
95
+ def method_missing(*args, &block)
96
96
  super do |klass, names, options|
97
97
  raise InvalidFilterError, 'missing attribute name' if names.empty?
98
98
 
99
99
  names.each { |name| add_filter(klass, name, options, &block) }
100
100
  end
101
101
  end
102
+ # rubocop:enable Style/MissingRespondToMissing
102
103
 
103
104
  private
104
105
 
@@ -106,9 +107,7 @@ module ActiveInteraction
106
107
  # @param name [Symbol]
107
108
  # @param options [Hash]
108
109
  def add_filter(klass, name, options, &block)
109
- if InputProcessor.reserved?(name)
110
- raise InvalidFilterError, %("#{name}" is a reserved name)
111
- end
110
+ raise InvalidFilterError, %("#{name}" is a reserved name) if ActiveInteraction::Inputs.reserved?(name)
112
111
 
113
112
  initialize_filter(klass.new(name, options, &block))
114
113
  end
@@ -133,7 +132,7 @@ module ActiveInteraction
133
132
  other_filters.select! { |k, _| [*only].include?(k) } if only
134
133
  other_filters.reject! { |k, _| [*except].include?(k) } if except
135
134
 
136
- other_filters.values.each { |filter| initialize_filter(filter) }
135
+ other_filters.each_value { |filter| initialize_filter(filter) }
137
136
  end
138
137
 
139
138
  # @param klass [Class]
@@ -146,13 +145,10 @@ module ActiveInteraction
146
145
  # @param filter [Filter]
147
146
  def initialize_filter(filter)
148
147
  attribute = filter.name
149
- if filters.key?(attribute)
150
- warn "WARNING: Redefining #{name}##{attribute} filter"
151
- end
148
+ warn "WARNING: Redefining #{name}##{attribute} filter" if filters.key?(attribute)
152
149
  filters[attribute] = filter
153
150
 
154
151
  attr_accessor attribute
155
- define_method("#{attribute}?") { !public_send(attribute).nil? }
156
152
 
157
153
  eagerly_evaluate_default(filter)
158
154
  end
@@ -195,15 +191,17 @@ module ActiveInteraction
195
191
  #
196
192
  # @return [Hash{Symbol => Object}] All inputs passed to {.run} or {.run!}.
197
193
  def inputs
198
- self.class.filters.keys.each_with_object({}) do |name, h|
199
- h[name] = public_send(name)
200
- end
194
+ @inputs ||= self.class.filters
195
+ .each_key.with_object(ActiveInteraction::Inputs.new) do |name, h|
196
+ h[name] = public_send(name)
197
+ end.freeze
201
198
  end
202
199
 
203
200
  # Returns `true` if the given key was in the hash passed to {.run}.
204
201
  # Otherwise returns `false`. Use this to figure out if an input was given,
205
202
  # even if it was `nil`. Keys within nested hash filter can also be checked
206
- # by passing them in series.
203
+ # by passing them in series. Arrays can be checked in the same manor as
204
+ # hashes by passing an index.
207
205
  #
208
206
  # @example
209
207
  # class Example < ActiveInteraction::Base
@@ -219,32 +217,49 @@ module ActiveInteraction
219
217
  # hash :x, default: {} do
220
218
  # integer :y, default: nil
221
219
  # end
222
- # def execute; given?(:x, :y) end
220
+ # array :a, default: [] do
221
+ # integer
222
+ # end
223
+ # def execute; given?(:x, :y) || given?(:a, 2) end
223
224
  # end
224
225
  # Example.run!() # => false
225
226
  # Example.run!(x: nil) # => false
226
227
  # Example.run!(x: {}) # => false
227
228
  # Example.run!(x: { y: nil }) # => true
228
229
  # Example.run!(x: { y: rand }) # => true
230
+ # Example.run!(a: [1, 2]) # => false
231
+ # Example.run!(a: [1, 2, 3]) # => true
229
232
  #
230
233
  # @param input [#to_sym]
231
234
  #
232
235
  # @return [Boolean]
233
236
  #
234
237
  # @since 2.1.0
235
- def given?(input, *rest) # rubocop:disable Metrics/CyclomaticComplexity
238
+ # rubocop:disable all
239
+ def given?(input, *rest)
236
240
  filter_level = self.class
237
241
  input_level = @_interaction_inputs
238
242
 
239
- [input, *rest].map(&:to_sym).each do |key|
240
- filter_level = filter_level.filters[key]
243
+ [input, *rest].each do |key_or_index|
244
+ if key_or_index.is_a?(Symbol) || key_or_index.is_a?(String)
245
+ key_or_index = key_or_index.to_sym
246
+ filter_level = filter_level.filters[key_or_index]
247
+
248
+ break false if filter_level.nil? || input_level.nil?
249
+ break false unless input_level.key?(key_or_index) || input_level.key?(key_or_index.to_s)
241
250
 
242
- break false if filter_level.nil? || input_level.nil?
243
- break false unless input_level.key?(key) || input_level.key?(key.to_s)
251
+ input_level = input_level[key_or_index] || input_level[key_or_index.to_s]
252
+ else
253
+ filter_level = filter_level.filters.first.last
254
+
255
+ break false if filter_level.nil? || input_level.nil?
256
+ break false unless key_or_index.between?(-input_level.size, input_level.size - 1)
244
257
 
245
- input_level = input_level[key] || input_level[key.to_s]
258
+ input_level = input_level[key_or_index]
259
+ end
246
260
  end && true
247
261
  end
262
+ # rubocop:enable all
248
263
 
249
264
  protected
250
265
 
@@ -276,10 +291,12 @@ module ActiveInteraction
276
291
  @_interaction_inputs = inputs
277
292
 
278
293
  inputs.each do |key, value|
279
- populate_reader(key, value) unless InputProcessor.reserved?(key)
294
+ next if ActiveInteraction::Inputs.reserved?(key)
295
+
296
+ populate_reader(key, value)
280
297
  end
281
298
 
282
- populate_filters(InputProcessor.process(inputs))
299
+ populate_filters(ActiveInteraction::Inputs.process(inputs))
283
300
  end
284
301
 
285
302
  def populate_reader(key, value)
@@ -288,18 +305,16 @@ module ActiveInteraction
288
305
 
289
306
  def populate_filters(inputs)
290
307
  self.class.filters.each do |name, filter|
291
- begin
292
- public_send("#{name}=", filter.clean(inputs[name], self))
293
- rescue InvalidValueError, MissingValueError, NoDefaultError
294
- nil # #type_check will add errors if appropriate.
295
- end
308
+ public_send("#{name}=", filter.clean(inputs[name], self))
309
+ rescue InvalidValueError, MissingValueError, NoDefaultError
310
+ nil # #type_check will add errors if appropriate.
296
311
  end
297
312
  end
298
313
 
299
314
  def type_check
300
315
  run_callbacks(:type_check) do
301
- Validation.validate(self, self.class.filters, inputs).each do |error|
302
- errors.add(*error)
316
+ Validation.validate(self, self.class.filters, inputs).each do |attr, type, kwargs = {}|
317
+ errors.add(attr, type, **kwargs)
303
318
  end
304
319
  end
305
320
  end
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module ActiveInteraction
@@ -34,8 +33,7 @@ module ActiveInteraction
34
33
  false
35
34
  end
36
35
 
37
- #
38
- module ClassMethods
36
+ module ClassMethods # rubocop:disable Style/Documentation
39
37
  # @return [Symbol]
40
38
  #
41
39
  # @see ActiveModel::Translation#i18n_scope
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module ActiveInteraction
@@ -24,8 +23,6 @@ module ActiveInteraction
24
23
  # # => nil
25
24
  #
26
25
  # @return [FilterColumn, nil]
27
- #
28
- # @since 1.2.0
29
26
  def column_for_attribute(name)
30
27
  filter = self.class.filters[name]
31
28
  FilterColumn.intern(filter.database_column_type) if filter
@@ -49,9 +46,7 @@ module ActiveInteraction
49
46
  # # => false
50
47
  #
51
48
  # @return [Boolean]
52
- #
53
- # @since 1.5.0
54
- def has_attribute?(name) # rubocop:disable Style/PredicateName
49
+ def has_attribute?(name) # rubocop:disable Naming/PredicateName
55
50
  self.class.filters.key?(name.to_sym)
56
51
  end
57
52
  end
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module ActiveInteraction
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module ActiveInteraction
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module ActiveInteraction
@@ -46,9 +45,7 @@ module ActiveInteraction
46
45
 
47
46
  # @return [Boolean]
48
47
  def valid?(*)
49
- if instance_variable_defined?(:@_interaction_valid)
50
- return @_interaction_valid
51
- end
48
+ return @_interaction_valid if instance_variable_defined?(:@_interaction_valid)
52
49
 
53
50
  super
54
51
  end
@@ -71,24 +68,15 @@ module ActiveInteraction
71
68
 
72
69
  # @return (see #result=)
73
70
  # @return [nil]
74
- def run # rubocop:disable MethodLength
75
- self.result =
76
- if valid?
77
- run_callbacks(:execute) do
78
- result_or_errors =
79
- begin
80
- execute
81
- rescue Interrupt => interrupt
82
- interrupt.errors
83
- end
84
-
85
- if result_or_errors.is_a?(ActiveInteraction::Errors)
86
- errors.merge!(result_or_errors)
87
- else
88
- result_or_errors
89
- end
90
- end
91
- end
71
+ def run
72
+ return self.result = nil unless valid?
73
+
74
+ self.result = run_callbacks(:execute) do
75
+ execute
76
+ rescue Interrupt => e
77
+ errors.backtrace = e.errors.backtrace || e.backtrace
78
+ errors.merge!(e.errors)
79
+ end
92
80
  end
93
81
 
94
82
  # @return [Object]
@@ -97,15 +85,15 @@ module ActiveInteraction
97
85
  def run!
98
86
  run
99
87
 
100
- unless valid?
101
- raise InvalidInteractionError, errors.full_messages.join(', ')
102
- end
88
+ return result if valid?
103
89
 
104
- result
90
+ e = InvalidInteractionError.new(errors.full_messages.join(', '))
91
+ e.interaction = self
92
+ e.set_backtrace(errors.backtrace) if errors.backtrace
93
+ raise e
105
94
  end
106
95
 
107
- #
108
- module ClassMethods
96
+ module ClassMethods # rubocop:disable Style/Documentation
109
97
  def new(*)
110
98
  super.tap do |instance|
111
99
  {
@@ -1,17 +1,15 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- #
5
3
  module ActiveInteraction
6
4
  # Top-level error class. All other errors subclass this.
7
5
  #
8
6
  # @return [Class]
9
7
  Error = Class.new(StandardError)
10
8
 
11
- # Raised if a class name is invalid.
9
+ # Raised if a constant name is invalid.
12
10
  #
13
11
  # @return [Class]
14
- InvalidClassError = Class.new(Error)
12
+ InvalidNameError = Class.new(Error)
15
13
 
16
14
  # Raised if a converter is invalid.
17
15
  #
@@ -31,7 +29,9 @@ module ActiveInteraction
31
29
  # Raised if an interaction is invalid.
32
30
  #
33
31
  # @return [Class]
34
- InvalidInteractionError = Class.new(Error)
32
+ class InvalidInteractionError < Error
33
+ attr_accessor :interaction
34
+ end
35
35
 
36
36
  # Raised if a user-supplied value is invalid.
37
37
  #
@@ -90,6 +90,8 @@ module ActiveInteraction
90
90
 
91
91
  # An extension that provides the ability to merge other errors into itself.
92
92
  class Errors < ActiveModel::Errors
93
+ attr_accessor :backtrace
94
+
93
95
  # Merge other errors into this one.
94
96
  #
95
97
  # @param other [Errors]
@@ -147,9 +149,8 @@ module ActiveInteraction
147
149
  if attribute?(attribute) || attribute == :base
148
150
  options = detail.dup
149
151
  error = options.delete(:error)
150
- options[:message] = message
151
152
 
152
- add(attribute, error, options) unless added?(attribute, error, options)
153
+ add(attribute, error, **options.merge(message: message)) unless added?(attribute, error, **options)
153
154
  else
154
155
  merge_message!(attribute, message)
155
156
  end
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'active_support/inflector'
@@ -78,16 +77,16 @@ module ActiveInteraction
78
77
  # to the default value.
79
78
  #
80
79
  # @example
81
- # ActiveInteraction::Filter.new(:example).clean(nil)
80
+ # ActiveInteraction::Filter.new(:example).clean(nil, nil)
82
81
  # # => ActiveInteraction::MissingValueError: example
83
82
  # @example
84
- # ActiveInteraction::Filter.new(:example).clean(0)
83
+ # ActiveInteraction::Filter.new(:example).clean(0, nil)
85
84
  # # => ActiveInteraction::InvalidValueError: example: 0
86
85
  # @example
87
- # ActiveInteraction::Filter.new(:example, default: nil).clean(nil)
86
+ # ActiveInteraction::Filter.new(:example, default: nil).clean(nil, nil)
88
87
  # # => nil
89
88
  # @example
90
- # ActiveInteraction::Filter.new(:example, default: 0).clean(nil)
89
+ # ActiveInteraction::Filter.new(:example, default: 0).clean(nil, nil)
91
90
  # # => ActiveInteraction::InvalidDefaultError: example: 0
92
91
  #
93
92
  # @param value [Object]
@@ -95,7 +94,9 @@ module ActiveInteraction
95
94
  #
96
95
  # @return [Object]
97
96
  #
98
- # @raise (see #cast)
97
+ # @raise [MissingValueError] If the value is missing and there is no
98
+ # default.
99
+ # @raise [InvalidValueError] If the value is invalid.
99
100
  # @raise (see #default)
100
101
  def clean(value, context)
101
102
  value = cast(value, context)
@@ -120,7 +121,7 @@ module ActiveInteraction
120
121
  #
121
122
  # @param context [Base, nil]
122
123
  #
123
- # @return (see #raw_default)
124
+ # @return [Object]
124
125
  #
125
126
  # @raise [NoDefaultError] If the default is missing.
126
127
  # @raise [InvalidDefaultError] If the default is invalid.
@@ -131,8 +132,8 @@ module ActiveInteraction
131
132
  raise InvalidValueError if value.is_a?(GroupedInput)
132
133
 
133
134
  cast(value, context)
134
- rescue InvalidNestedValueError => error
135
- raise InvalidDefaultError, "#{name}: #{value.inspect} (#{error})"
135
+ rescue InvalidNestedValueError => e
136
+ raise InvalidDefaultError, "#{name}: #{value.inspect} (#{e})"
136
137
  rescue InvalidValueError, MissingValueError
137
138
  raise InvalidDefaultError, "#{name}: #{value.inspect}"
138
139
  end
@@ -162,27 +163,6 @@ module ActiveInteraction
162
163
  options.key?(:default)
163
164
  end
164
165
 
165
- # @param value [Object]
166
- # @param _interaction [Base, nil]
167
- #
168
- # @return [Object]
169
- #
170
- # @raise [MissingValueError] If the value is missing and there is no
171
- # default.
172
- # @raise [InvalidValueError] If the value is invalid.
173
- #
174
- # @private
175
- def cast(value, _interaction)
176
- case value
177
- when NilClass
178
- raise MissingValueError, name unless default?
179
-
180
- nil
181
- else
182
- raise InvalidValueError, "#{name}: #{describe(value)}"
183
- end
184
- end
185
-
186
166
  # Gets the type of database column that would represent the filter data.
187
167
  #
188
168
  # @example
@@ -194,25 +174,59 @@ module ActiveInteraction
194
174
  #
195
175
  # @return [Symbol] A database column type. If no sensible mapping exists,
196
176
  # returns `:string`.
197
- #
198
- # @since 1.2.0
199
177
  def database_column_type
200
178
  :string
201
179
  end
202
180
 
203
181
  private
204
182
 
205
- # @param value [Object]
206
- # @return [String]
183
+ # rubocop:disable Metrics/MethodLength
184
+ def cast(value, context, convert: true, reconstantize: true)
185
+ if matches?(value)
186
+ adjust_output(value, context)
187
+ # we can't use `nil?` because BasicObject doesn't have it
188
+ elsif value == nil # rubocop:disable Style/NilComparison
189
+ raise MissingValueError, name unless default?
190
+
191
+ nil
192
+ elsif reconstantize
193
+ send(__method__, value, context,
194
+ convert: convert,
195
+ reconstantize: false
196
+ )
197
+ elsif convert
198
+ send(__method__, convert(value), context,
199
+ convert: false,
200
+ reconstantize: reconstantize
201
+ )
202
+ else
203
+ raise InvalidValueError, "#{name}: #{describe(value)}"
204
+ end
205
+ end
206
+ # rubocop:enable Metrics/MethodLength
207
+
208
+ def matches?(_value)
209
+ false
210
+ end
211
+
212
+ def adjust_output(value, _context)
213
+ value
214
+ end
215
+
216
+ def convert(value)
217
+ value
218
+ end
219
+
220
+ def klass
221
+ @klass ||= Object.const_get(self.class.slug.to_s.camelize, false)
222
+ end
223
+
207
224
  def describe(value)
208
225
  value.inspect
209
226
  rescue NoMethodError
210
227
  "(Object doesn't support #inspect)"
211
228
  end
212
229
 
213
- # @param context [Base, nil]
214
- #
215
- # @return [Object]
216
230
  def raw_default(context)
217
231
  value = options.fetch(:default)
218
232
  return value unless value.is_a?(Proc)