active_interaction 5.3.0 → 5.4.0

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