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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +190 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +96 -90
- data/lib/active_interaction.rb +1 -7
- data/lib/active_interaction/base.rb +48 -33
- data/lib/active_interaction/concerns/active_modelable.rb +1 -3
- data/lib/active_interaction/concerns/active_recordable.rb +1 -6
- data/lib/active_interaction/concerns/hashable.rb +0 -1
- data/lib/active_interaction/concerns/missable.rb +0 -1
- data/lib/active_interaction/concerns/runnable.rb +16 -28
- data/lib/active_interaction/errors.rb +8 -7
- data/lib/active_interaction/filter.rb +51 -37
- data/lib/active_interaction/filter_column.rb +0 -3
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +34 -36
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
- data/lib/active_interaction/filters/array_filter.rb +57 -36
- data/lib/active_interaction/filters/boolean_filter.rb +26 -12
- data/lib/active_interaction/filters/date_filter.rb +1 -2
- data/lib/active_interaction/filters/date_time_filter.rb +1 -2
- data/lib/active_interaction/filters/decimal_filter.rb +10 -28
- data/lib/active_interaction/filters/file_filter.rb +6 -5
- data/lib/active_interaction/filters/float_filter.rb +1 -2
- data/lib/active_interaction/filters/hash_filter.rb +37 -27
- data/lib/active_interaction/filters/integer_filter.rb +7 -8
- data/lib/active_interaction/filters/interface_filter.rb +48 -14
- data/lib/active_interaction/filters/object_filter.rb +23 -50
- data/lib/active_interaction/filters/record_filter.rb +10 -35
- data/lib/active_interaction/filters/string_filter.rb +21 -12
- data/lib/active_interaction/filters/symbol_filter.rb +13 -7
- data/lib/active_interaction/filters/time_filter.rb +19 -22
- data/lib/active_interaction/grouped_input.rb +0 -3
- data/lib/active_interaction/inputs.rb +89 -0
- data/lib/active_interaction/locale/ja.yml +24 -0
- data/lib/active_interaction/modules/input_processor.rb +9 -6
- data/lib/active_interaction/modules/validation.rb +9 -12
- data/lib/active_interaction/version.rb +1 -3
- data/spec/active_interaction/base_spec.rb +95 -35
- data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
- data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
- data/spec/active_interaction/concerns/hashable_spec.rb +1 -3
- data/spec/active_interaction/concerns/missable_spec.rb +0 -2
- data/spec/active_interaction/concerns/runnable_spec.rb +32 -12
- data/spec/active_interaction/errors_spec.rb +49 -22
- data/spec/active_interaction/filter_column_spec.rb +0 -2
- data/spec/active_interaction/filter_spec.rb +0 -2
- data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/array_filter_spec.rb +41 -15
- data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
- data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
- data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
- data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
- data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
- data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
- data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
- data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
- data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
- data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
- data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
- data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
- data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
- data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
- data/spec/active_interaction/grouped_input_spec.rb +0 -2
- data/spec/active_interaction/i18n_spec.rb +3 -7
- data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +5 -5
- data/spec/active_interaction/integration/array_interaction_spec.rb +23 -12
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
- data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
- data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
- data/spec/active_interaction/modules/validation_spec.rb +1 -3
- data/spec/spec_helper.rb +2 -6
- data/spec/support/concerns.rb +0 -2
- data/spec/support/filters.rb +13 -9
- data/spec/support/interactions.rb +22 -14
- metadata +106 -52
- data/lib/active_interaction/backports.rb +0 -59
- data/lib/active_interaction/filters/abstract_filter.rb +0 -19
- data/spec/active_interaction/filters/abstract_filter_spec.rb +0 -8
data/lib/active_interaction.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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].
|
|
240
|
-
|
|
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
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
+
next if ActiveInteraction::Inputs.reserved?(key)
|
|
295
|
+
|
|
296
|
+
populate_reader(key, value)
|
|
280
297
|
end
|
|
281
298
|
|
|
282
|
-
populate_filters(
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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 |
|
|
302
|
-
errors.add(
|
|
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
|
|
@@ -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
|
|
75
|
-
self.result =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
101
|
-
raise InvalidInteractionError, errors.full_messages.join(', ')
|
|
102
|
-
end
|
|
88
|
+
return result if valid?
|
|
103
89
|
|
|
104
|
-
|
|
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
|
|
9
|
+
# Raised if a constant name is invalid.
|
|
12
10
|
#
|
|
13
11
|
# @return [Class]
|
|
14
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =>
|
|
135
|
-
raise InvalidDefaultError, "#{name}: #{value.inspect} (#{
|
|
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
|
-
#
|
|
206
|
-
|
|
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)
|