active_interaction 5.2.0 → 5.4.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 +26 -0
- data/README.md +11 -12
- data/lib/active_interaction/base.rb +2 -0
- data/lib/active_interaction/errors.rb +16 -16
- data/lib/active_interaction/filters/hash_filter.rb +5 -1
- data/lib/active_interaction/version.rb +1 -1
- metadata +26 -112
- data/spec/active_interaction/array_input_spec.rb +0 -166
- data/spec/active_interaction/base_spec.rb +0 -537
- data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -45
- data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -49
- data/spec/active_interaction/concerns/hashable_spec.rb +0 -46
- data/spec/active_interaction/concerns/missable_spec.rb +0 -99
- data/spec/active_interaction/concerns/runnable_spec.rb +0 -397
- data/spec/active_interaction/errors_spec.rb +0 -196
- data/spec/active_interaction/filter/column_spec.rb +0 -87
- data/spec/active_interaction/filter_spec.rb +0 -60
- data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +0 -13
- data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +0 -13
- data/spec/active_interaction/filters/array_filter_spec.rb +0 -250
- data/spec/active_interaction/filters/boolean_filter_spec.rb +0 -87
- data/spec/active_interaction/filters/date_filter_spec.rb +0 -178
- data/spec/active_interaction/filters/date_time_filter_spec.rb +0 -189
- data/spec/active_interaction/filters/decimal_filter_spec.rb +0 -126
- data/spec/active_interaction/filters/file_filter_spec.rb +0 -40
- data/spec/active_interaction/filters/float_filter_spec.rb +0 -110
- data/spec/active_interaction/filters/hash_filter_spec.rb +0 -129
- data/spec/active_interaction/filters/integer_filter_spec.rb +0 -104
- data/spec/active_interaction/filters/interface_filter_spec.rb +0 -461
- data/spec/active_interaction/filters/object_filter_spec.rb +0 -237
- data/spec/active_interaction/filters/record_filter_spec.rb +0 -206
- data/spec/active_interaction/filters/string_filter_spec.rb +0 -60
- data/spec/active_interaction/filters/symbol_filter_spec.rb +0 -46
- data/spec/active_interaction/filters/time_filter_spec.rb +0 -251
- data/spec/active_interaction/grouped_input_spec.rb +0 -17
- data/spec/active_interaction/hash_input_spec.rb +0 -58
- data/spec/active_interaction/i18n_spec.rb +0 -113
- data/spec/active_interaction/inputs_spec.rb +0 -266
- data/spec/active_interaction/integration/array_interaction_spec.rb +0 -88
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/date_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/file_interaction_spec.rb +0 -18
- data/spec/active_interaction/integration/float_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -76
- data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/interface_interaction_spec.rb +0 -19
- data/spec/active_interaction/integration/object_interaction_spec.rb +0 -14
- data/spec/active_interaction/integration/record_integration_spec.rb +0 -5
- data/spec/active_interaction/integration/string_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -5
- data/spec/active_interaction/integration/time_interaction_spec.rb +0 -88
- data/spec/active_interaction/modules/validation_spec.rb +0 -57
- data/spec/spec_helper.rb +0 -20
- data/spec/support/concerns.rb +0 -13
- data/spec/support/filters.rb +0 -227
- data/spec/support/interactions.rb +0 -124
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ActiveInteraction::Missable do
|
4
|
-
include_context 'concerns', described_class
|
5
|
-
|
6
|
-
describe '#respond_to?(slug, include_all = false)' do
|
7
|
-
context 'with invalid slug' do
|
8
|
-
let(:slug) { :slug }
|
9
|
-
|
10
|
-
it 'returns false' do
|
11
|
-
expect(instance).to_not respond_to(slug)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'with valid slug' do
|
16
|
-
let(:slug) { :boolean }
|
17
|
-
|
18
|
-
it 'returns true' do
|
19
|
-
expect(instance).to respond_to(slug)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#method(sym)' do
|
25
|
-
context 'with invalid slug' do
|
26
|
-
let(:slug) { :slug }
|
27
|
-
|
28
|
-
it 'returns false' do
|
29
|
-
expect { instance.method(slug) }.to raise_error NameError
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'with valid slug' do
|
34
|
-
let(:slug) { :boolean }
|
35
|
-
|
36
|
-
it 'returns true' do
|
37
|
-
expect(instance.method(slug)).to be_a Method
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '#method_missing' do
|
43
|
-
context 'with invalid slug' do
|
44
|
-
let(:slug) { :slug }
|
45
|
-
|
46
|
-
it 'calls super' do
|
47
|
-
expect do
|
48
|
-
instance.public_send(slug)
|
49
|
-
end.to raise_error NameError
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'with valid slug' do
|
54
|
-
let(:filter) { ActiveInteraction::Filter.factory(slug) }
|
55
|
-
let(:slug) { :boolean }
|
56
|
-
|
57
|
-
it 'returns self' do
|
58
|
-
expect(instance.public_send(slug)).to eql instance
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'yields' do
|
62
|
-
expect do |b|
|
63
|
-
instance.public_send(slug, &b)
|
64
|
-
end.to yield_with_args(filter, [], {})
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'with names' do
|
68
|
-
let(:names) { %i[a b c] }
|
69
|
-
|
70
|
-
it 'yields' do
|
71
|
-
expect do |b|
|
72
|
-
instance.public_send(:boolean, *names, &b)
|
73
|
-
end.to yield_with_args(filter, names, {})
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context 'with options' do
|
78
|
-
let(:options) { { a: nil, b: false, c: true } }
|
79
|
-
|
80
|
-
it 'yields' do
|
81
|
-
expect do |b|
|
82
|
-
instance.public_send(:boolean, options, &b)
|
83
|
-
end.to yield_with_args(filter, [], options)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
context 'with names & options' do
|
88
|
-
let(:names) { %i[a b c] }
|
89
|
-
let(:options) { { a: nil, b: false, c: true } }
|
90
|
-
|
91
|
-
it 'yields' do
|
92
|
-
expect do |b|
|
93
|
-
instance.public_send(:boolean, *names, options, &b)
|
94
|
-
end.to yield_with_args(filter, names, options)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,397 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ActiveInteraction::Runnable do
|
4
|
-
include_context 'concerns', described_class
|
5
|
-
|
6
|
-
class WrappableFailingInteraction # rubocop:disable Lint/ConstantDefinitionInBlock
|
7
|
-
include ActiveInteraction::Runnable
|
8
|
-
|
9
|
-
def execute
|
10
|
-
errors.add(:base)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
shared_context 'with an error' do
|
15
|
-
before { instance.errors.add(:base) }
|
16
|
-
end
|
17
|
-
|
18
|
-
shared_context 'with a validator' do
|
19
|
-
before { klass.validate { errors.add(:base) } }
|
20
|
-
end
|
21
|
-
|
22
|
-
shared_context 'with #execute defined' do
|
23
|
-
before { klass.send(:define_method, :execute) { rand } }
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'validations' do
|
27
|
-
describe '#runtime_errors' do
|
28
|
-
include_context 'with an error'
|
29
|
-
|
30
|
-
it 'is invalid' do
|
31
|
-
instance.result = nil
|
32
|
-
expect(instance).to_not be_valid
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'becomes valid if errors are cleared' do
|
36
|
-
instance.result = nil
|
37
|
-
instance.errors.clear
|
38
|
-
instance.result = nil
|
39
|
-
expect(instance).to be_valid
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'callbacks' do
|
45
|
-
describe '.set_callback' do
|
46
|
-
include_context 'with #execute defined'
|
47
|
-
|
48
|
-
shared_examples 'set_callback examples' do |name|
|
49
|
-
context name do
|
50
|
-
it 'does not raise an error' do
|
51
|
-
expect do
|
52
|
-
klass.set_callback name, :before, -> {}
|
53
|
-
end.to_not raise_error
|
54
|
-
end
|
55
|
-
|
56
|
-
%i[after around before].each do |type|
|
57
|
-
it type do
|
58
|
-
has_run = false
|
59
|
-
|
60
|
-
klass.set_callback name, type, -> { has_run = true }
|
61
|
-
|
62
|
-
klass.run
|
63
|
-
expect(has_run).to be_truthy
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
include_examples 'set_callback examples', :validate
|
70
|
-
include_examples 'set_callback examples', :execute
|
71
|
-
|
72
|
-
context 'execute with composed interaction' do
|
73
|
-
class WithFailingCompose # rubocop:disable Lint/ConstantDefinitionInBlock
|
74
|
-
include ActiveInteraction::Runnable
|
75
|
-
|
76
|
-
def execute
|
77
|
-
compose(WrappableFailingInteraction)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context 'around' do
|
82
|
-
it 'is yielded errors from composed interactions' do
|
83
|
-
block_result = nil
|
84
|
-
WithFailingCompose.set_callback :execute, :around do |_, block|
|
85
|
-
block_result = block.call
|
86
|
-
end
|
87
|
-
|
88
|
-
WithFailingCompose.run
|
89
|
-
expect(block_result).to be_an(ActiveInteraction::Errors)
|
90
|
-
expect(block_result).to include(:base)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
context 'after' do
|
95
|
-
it 'is yielded errors from composed interactions' do
|
96
|
-
has_run = false
|
97
|
-
WithFailingCompose.set_callback :execute, :after do
|
98
|
-
has_run = true
|
99
|
-
end
|
100
|
-
|
101
|
-
WithFailingCompose.run
|
102
|
-
expect(has_run).to be_truthy
|
103
|
-
end
|
104
|
-
|
105
|
-
context 'using if' do
|
106
|
-
it 'yields errors to the if' do
|
107
|
-
has_run = false
|
108
|
-
WithFailingCompose.set_callback :execute, :after, if: -> { errors.any? } do
|
109
|
-
has_run = true
|
110
|
-
end
|
111
|
-
|
112
|
-
WithFailingCompose.run
|
113
|
-
expect(has_run).to be_truthy
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
describe '#errors' do
|
122
|
-
it 'returns the errors' do
|
123
|
-
expect(instance.errors).to be_an ActiveInteraction::Errors
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe '#execute' do
|
128
|
-
it 'raises an error' do
|
129
|
-
expect { instance.execute }.to raise_error NotImplementedError
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe '#result' do
|
134
|
-
it 'returns the result' do
|
135
|
-
expect(instance.result).to be_nil
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
describe '#result=' do
|
140
|
-
let(:result) { double }
|
141
|
-
|
142
|
-
it 'returns the result' do
|
143
|
-
expect((instance.result = result)).to eql result
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'sets the result' do
|
147
|
-
instance.result = result
|
148
|
-
expect(instance.result).to eql result
|
149
|
-
end
|
150
|
-
|
151
|
-
context 'with an error' do
|
152
|
-
include_context 'with an error'
|
153
|
-
|
154
|
-
it 'sets the result' do
|
155
|
-
instance.result = result
|
156
|
-
expect(instance.result).to eql result
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
context 'with a validator' do
|
161
|
-
include_context 'with a validator'
|
162
|
-
|
163
|
-
it 'sets the result' do
|
164
|
-
instance.result = result
|
165
|
-
expect(instance.result).to eql result
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
describe '#valid?' do
|
171
|
-
let(:result) { double }
|
172
|
-
|
173
|
-
it 'returns true' do
|
174
|
-
expect(instance).to be_valid
|
175
|
-
end
|
176
|
-
|
177
|
-
context 'with an error' do
|
178
|
-
include_context 'with an error'
|
179
|
-
|
180
|
-
it 'returns true' do
|
181
|
-
expect(instance).to be_valid
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
context 'with a validator' do
|
186
|
-
include_context 'with a validator'
|
187
|
-
|
188
|
-
it 'returns false' do
|
189
|
-
expect(instance).to_not be_valid
|
190
|
-
end
|
191
|
-
|
192
|
-
it 'does not duplicate errors on subsequent calls' do
|
193
|
-
instance.valid?
|
194
|
-
count = instance.errors.count
|
195
|
-
instance.valid?
|
196
|
-
|
197
|
-
expect(instance.errors.count).to eql count
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
describe '.run' do
|
203
|
-
let(:outcome) { klass.run }
|
204
|
-
|
205
|
-
it 'raises an error' do
|
206
|
-
expect { outcome }.to raise_error NotImplementedError
|
207
|
-
end
|
208
|
-
|
209
|
-
context 'with #execute defined' do
|
210
|
-
include_context 'with #execute defined'
|
211
|
-
|
212
|
-
it 'returns an instance of Runnable' do
|
213
|
-
expect(outcome).to be_a klass
|
214
|
-
end
|
215
|
-
|
216
|
-
it 'sets the result' do
|
217
|
-
expect(outcome.result).to_not be_nil
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'with a validator' do
|
221
|
-
include_context 'with a validator'
|
222
|
-
|
223
|
-
it 'returns an instance of Runnable' do
|
224
|
-
expect(outcome).to be_a klass
|
225
|
-
end
|
226
|
-
|
227
|
-
it 'sets the result to nil' do
|
228
|
-
expect(outcome.result).to be_nil
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
context 'caches the result of the run' do
|
234
|
-
context 'when it is invalid' do
|
235
|
-
let(:klass) do
|
236
|
-
Class.new(ActiveInteraction::Base) do
|
237
|
-
invalid = [false, true].cycle
|
238
|
-
|
239
|
-
validate do |interaction|
|
240
|
-
interaction.errors.add(:base, 'failed') unless invalid.next
|
241
|
-
end
|
242
|
-
|
243
|
-
def execute
|
244
|
-
true
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
it 'fails' do
|
250
|
-
expect(outcome).to_not be_valid
|
251
|
-
expect(outcome.result).to be_nil
|
252
|
-
expect(outcome).to_not be_valid
|
253
|
-
expect(outcome.result).to be_nil
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
context 'when it is valid' do
|
258
|
-
let(:klass) do
|
259
|
-
Class.new(ActiveInteraction::Base) do
|
260
|
-
valid = [true, false].cycle
|
261
|
-
|
262
|
-
validate do |interaction|
|
263
|
-
interaction.errors.add(:base, 'failed') unless valid.next
|
264
|
-
end
|
265
|
-
|
266
|
-
def execute
|
267
|
-
true
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
it 'succeeds' do
|
273
|
-
expect(outcome).to be_valid
|
274
|
-
expect(outcome.result).to be true
|
275
|
-
expect(outcome).to be_valid
|
276
|
-
expect(outcome.result).to be true
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
context 'with valid post-execution state' do
|
282
|
-
before do
|
283
|
-
klass.class_exec do
|
284
|
-
attr_accessor :attribute
|
285
|
-
|
286
|
-
validate { errors.add(:attribute) unless attribute }
|
287
|
-
|
288
|
-
def execute
|
289
|
-
self.attribute = true
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
it 'is invalid' do
|
295
|
-
expect(outcome).to_not be_valid
|
296
|
-
end
|
297
|
-
|
298
|
-
it 'stays invalid' do
|
299
|
-
outcome.attribute = false
|
300
|
-
expect(outcome).to_not be_valid
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
context 'with invalid post-execution state' do
|
305
|
-
before do
|
306
|
-
klass.class_exec do
|
307
|
-
attr_accessor :attribute
|
308
|
-
|
309
|
-
validate { errors.add(:attribute) if attribute }
|
310
|
-
|
311
|
-
def execute
|
312
|
-
self.attribute = true
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
it 'is valid' do
|
318
|
-
expect(outcome).to be_valid
|
319
|
-
end
|
320
|
-
|
321
|
-
it 'stays valid' do
|
322
|
-
outcome.attribute = true
|
323
|
-
expect(outcome).to be_valid
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
context 'with failing composition' do
|
328
|
-
class CheckInnerForFailure # rubocop:disable Lint/ConstantDefinitionInBlock
|
329
|
-
include ActiveInteraction::Runnable
|
330
|
-
|
331
|
-
attr_reader :caught_error
|
332
|
-
|
333
|
-
def execute
|
334
|
-
compose(WrappableFailingInteraction)
|
335
|
-
rescue StandardError
|
336
|
-
@caught_error = true
|
337
|
-
raise
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
it 'throws an error from the inner interaction' do
|
342
|
-
outcome = CheckInnerForFailure.run
|
343
|
-
expect(outcome.caught_error).to be true
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
context 'with block not called and error in execute around callback' do
|
348
|
-
class CheckExecuteAroundCallbackForFailure # rubocop:disable Lint/ConstantDefinitionInBlock
|
349
|
-
include ActiveInteraction::ActiveModelable
|
350
|
-
include ActiveInteraction::Runnable
|
351
|
-
|
352
|
-
set_callback :execute, :around, -> { errors.add(:base, 'invalid') }
|
353
|
-
|
354
|
-
def execute
|
355
|
-
true
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
it 'is invalid' do
|
360
|
-
outcome = CheckExecuteAroundCallbackForFailure.run
|
361
|
-
expect(outcome).to_not be_valid
|
362
|
-
end
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
describe '.run!' do
|
367
|
-
let(:result) { klass.run! }
|
368
|
-
|
369
|
-
it 'raises an error' do
|
370
|
-
expect { result }.to raise_error NotImplementedError
|
371
|
-
end
|
372
|
-
|
373
|
-
context 'with #execute defined' do
|
374
|
-
include_context 'with #execute defined'
|
375
|
-
|
376
|
-
it 'returns the result' do
|
377
|
-
expect(result).to_not be_nil
|
378
|
-
end
|
379
|
-
|
380
|
-
context 'with a validator' do
|
381
|
-
include_context 'with a validator'
|
382
|
-
|
383
|
-
it 'raises an error' do
|
384
|
-
expect do
|
385
|
-
result
|
386
|
-
end.to raise_error ActiveInteraction::InvalidInteractionError
|
387
|
-
end
|
388
|
-
|
389
|
-
it 'adds interaction instance to this error' do
|
390
|
-
expect { result }.to raise_error do |error|
|
391
|
-
expect(error.interaction).to be_a klass
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
@@ -1,196 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'active_record'
|
3
|
-
require 'sqlite3'
|
4
|
-
|
5
|
-
ActiveRecord::Base.establish_connection(
|
6
|
-
adapter: 'sqlite3',
|
7
|
-
database: ':memory:'
|
8
|
-
)
|
9
|
-
|
10
|
-
describe ActiveInteraction::Errors do
|
11
|
-
subject(:errors) { described_class.new(klass.new) }
|
12
|
-
|
13
|
-
let(:klass) do
|
14
|
-
Class.new(ActiveInteraction::Base) do
|
15
|
-
string :attribute, defualt: nil
|
16
|
-
array :array, defualt: nil
|
17
|
-
|
18
|
-
def self.name
|
19
|
-
@name ||= SecureRandom.hex
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#merge!' do
|
25
|
-
let(:other) { described_class.new(klass.new) }
|
26
|
-
|
27
|
-
context 'with an error' do
|
28
|
-
before do
|
29
|
-
other.add(:attribute)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'adds the error' do
|
33
|
-
errors.merge!(other)
|
34
|
-
expect(errors.messages[:attribute]).to eql ['is invalid']
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'does not add duplicate errors' do
|
38
|
-
other.add(:attribute)
|
39
|
-
errors.merge!(other)
|
40
|
-
expect(errors.messages[:attribute]).to eql ['is invalid']
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'with a detailed error' do
|
45
|
-
context 'that is a symbol' do
|
46
|
-
before do
|
47
|
-
other.add(:attribute)
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'adds the error' do
|
51
|
-
errors.merge!(other)
|
52
|
-
expect(errors.details[:attribute]).to eql [{ error: :invalid }]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'that is a symbol on base' do
|
57
|
-
before do
|
58
|
-
other.add(:base)
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'adds the error' do
|
62
|
-
errors.merge!(other)
|
63
|
-
expect(errors.details[:base]).to eql [{ error: :invalid }]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'that is a string' do
|
68
|
-
let(:message) { SecureRandom.hex }
|
69
|
-
|
70
|
-
before do
|
71
|
-
other.add(:base, message)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'adds the error' do
|
75
|
-
errors.merge!(other)
|
76
|
-
expect(errors.details[:base]).to eql [{ error: message }]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'that uses the :message option' do
|
81
|
-
let(:message) { SecureRandom.hex }
|
82
|
-
let(:error_name) { :some_error }
|
83
|
-
|
84
|
-
before do
|
85
|
-
other.add(:base, error_name, message: message)
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'adds the error' do
|
89
|
-
errors.merge!(other)
|
90
|
-
expect(errors.details[:base]).to eql [{ error: error_name }]
|
91
|
-
expect(errors.messages[:base]).to eql [message]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context 'with an interpolated detailed error' do
|
97
|
-
before do
|
98
|
-
I18n.backend.store_translations('en',
|
99
|
-
activemodel: {
|
100
|
-
errors: {
|
101
|
-
models: {
|
102
|
-
klass.name => {
|
103
|
-
attributes: {
|
104
|
-
attribute: {
|
105
|
-
invalid_type: 'is not a valid %<type>s'
|
106
|
-
}
|
107
|
-
}
|
108
|
-
}
|
109
|
-
}
|
110
|
-
}
|
111
|
-
}
|
112
|
-
)
|
113
|
-
|
114
|
-
other.add(:attribute, :invalid_type, type: nil)
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'does not raise an error' do
|
118
|
-
expect { errors.merge!(other) }.to_not raise_error
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'with nested index errors' do
|
123
|
-
let(:other) { described_class.new(klass.new) }
|
124
|
-
|
125
|
-
before do
|
126
|
-
if ActiveRecord.respond_to?(:index_nested_attribute_errors)
|
127
|
-
allow(ActiveRecord).to receive(:index_nested_attribute_errors).and_return(true)
|
128
|
-
else
|
129
|
-
allow(ActiveRecord::Base).to receive(:index_nested_attribute_errors).and_return(true)
|
130
|
-
end
|
131
|
-
|
132
|
-
other.add(:'array[0]')
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'adds the error' do
|
136
|
-
errors.merge!(other)
|
137
|
-
expect(errors.messages[:'array[0]']).to eql ['is invalid']
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
context 'with ActiveModel errors' do
|
142
|
-
let(:other) { ActiveModel::Errors.new(klass.new) }
|
143
|
-
|
144
|
-
it 'does not raise an error' do
|
145
|
-
expect { errors.merge!(other) }.to_not raise_error
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'merges messages' do
|
149
|
-
message = SecureRandom.hex
|
150
|
-
other.add(:base, message)
|
151
|
-
errors.merge!(other)
|
152
|
-
expect(errors.messages[:base]).to include message
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
context 'with nested errors' do
|
157
|
-
let(:a_klass) do
|
158
|
-
Class.new(ActiveRecord::Base) do
|
159
|
-
has_one :b
|
160
|
-
accepts_nested_attributes_for :b
|
161
|
-
end
|
162
|
-
end
|
163
|
-
let(:a) { A.create(b_attributes: { name: nil }) }
|
164
|
-
let(:b_klass) do
|
165
|
-
Class.new(ActiveRecord::Base) do
|
166
|
-
belongs_to :a
|
167
|
-
|
168
|
-
validates :name, presence: true
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
before do
|
173
|
-
# suppress create_table output
|
174
|
-
allow($stdout).to receive(:puts)
|
175
|
-
ActiveRecord::Schema.define do
|
176
|
-
create_table(:as)
|
177
|
-
create_table(:bs) do |t|
|
178
|
-
t.column :a_id, :integer
|
179
|
-
t.column :name, :string
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
stub_const('A', a_klass)
|
184
|
-
stub_const('B', b_klass)
|
185
|
-
end
|
186
|
-
|
187
|
-
it 'merges the nested errors' do
|
188
|
-
a.valid?
|
189
|
-
expect(a.errors.messages).to eq('b.name': ["can't be blank"])
|
190
|
-
expect(a.errors.size).to be 1
|
191
|
-
expect { errors.merge!(a.errors) }.to_not raise_error
|
192
|
-
expect(errors.size).to be 1
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|