active_interaction 0.9.0 → 0.9.1
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 +10 -1
- data/README.md +4 -4
- data/lib/active_interaction.rb +2 -0
- data/lib/active_interaction/base.rb +26 -14
- data/lib/active_interaction/errors.rb +7 -10
- data/lib/active_interaction/filter.rb +10 -10
- data/lib/active_interaction/filters.rb +2 -0
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +4 -2
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +3 -1
- data/lib/active_interaction/filters/array_filter.rb +5 -3
- data/lib/active_interaction/filters/boolean_filter.rb +2 -0
- data/lib/active_interaction/filters/date_filter.rb +2 -0
- data/lib/active_interaction/filters/date_time_filter.rb +2 -0
- data/lib/active_interaction/filters/file_filter.rb +2 -0
- data/lib/active_interaction/filters/float_filter.rb +2 -0
- data/lib/active_interaction/filters/hash_filter.rb +7 -6
- data/lib/active_interaction/filters/integer_filter.rb +2 -0
- data/lib/active_interaction/filters/model_filter.rb +2 -0
- data/lib/active_interaction/filters/string_filter.rb +2 -0
- data/lib/active_interaction/filters/symbol_filter.rb +2 -0
- data/lib/active_interaction/filters/time_filter.rb +2 -0
- data/lib/active_interaction/modules/active_model.rb +2 -0
- data/lib/active_interaction/modules/core.rb +4 -1
- data/lib/active_interaction/modules/method_missing.rb +2 -0
- data/lib/active_interaction/modules/overload_hash.rb +2 -0
- data/lib/active_interaction/modules/validation.rb +10 -5
- data/lib/active_interaction/version.rb +4 -1
- data/spec/active_interaction/base_spec.rb +60 -35
- data/spec/active_interaction/errors_spec.rb +48 -14
- data/spec/active_interaction/filter_spec.rb +4 -2
- data/spec/active_interaction/filters/array_filter_spec.rb +13 -6
- data/spec/active_interaction/filters/boolean_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/date_filter_spec.rb +6 -4
- data/spec/active_interaction/filters/date_time_filter_spec.rb +6 -4
- data/spec/active_interaction/filters/file_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/float_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/hash_filter_spec.rb +16 -4
- data/spec/active_interaction/filters/integer_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/model_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/string_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/symbol_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +6 -4
- data/spec/active_interaction/filters_spec.rb +2 -0
- data/spec/active_interaction/i18n_spec.rb +9 -9
- data/spec/active_interaction/integration/array_interaction_spec.rb +10 -8
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/date_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/file_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/float_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/hash_interaction_spec.rb +10 -8
- data/spec/active_interaction/integration/integer_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/model_interaction_spec.rb +3 -1
- data/spec/active_interaction/integration/string_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/time_interaction_spec.rb +9 -7
- data/spec/active_interaction/modules/active_model_spec.rb +2 -0
- data/spec/active_interaction/modules/core_spec.rb +12 -7
- data/spec/active_interaction/modules/method_missing_spec.rb +12 -10
- data/spec/active_interaction/modules/overload_hash_spec.rb +7 -5
- data/spec/active_interaction/modules/validation_spec.rb +5 -2
- data/spec/spec_helper.rb +4 -0
- data/spec/support/filters.rb +18 -16
- data/spec/support/interactions.rb +13 -11
- metadata +20 -6
@@ -1,5 +1,8 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'active_record'
|
5
|
+
# rubocop:disable HandleExceptions
|
3
6
|
rescue LoadError
|
4
7
|
# ActiveRecord is an optional dependency.
|
5
8
|
end
|
@@ -49,7 +52,7 @@ module ActiveInteraction
|
|
49
52
|
if outcome.valid?
|
50
53
|
outcome.result
|
51
54
|
else
|
52
|
-
|
55
|
+
fail InvalidInteractionError, outcome.errors.full_messages.join(', ')
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
@@ -1,19 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
module ActiveInteraction
|
2
4
|
# @private
|
3
5
|
module Validation
|
4
6
|
def self.validate(filters, inputs)
|
5
|
-
filters.
|
7
|
+
filters.each_with_object([]) do |filter, errors|
|
6
8
|
begin
|
7
9
|
filter.cast(inputs[filter.name])
|
8
|
-
|
9
|
-
errors
|
10
10
|
rescue InvalidValueError
|
11
|
-
|
12
|
-
errors << [filter.name, :invalid, nil, type: type]
|
11
|
+
errors << [filter.name, :invalid, nil, type: type(filter)]
|
13
12
|
rescue MissingValueError
|
14
13
|
errors << [filter.name, :missing]
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.type(filter)
|
21
|
+
I18n.translate("#{Base.i18n_scope}.types.#{filter.class.slug}")
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
@@ -1,33 +1,43 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
InteractionWithFilter = Class.new(TestInteraction) do
|
6
|
+
float :thing
|
5
7
|
|
6
|
-
|
8
|
+
def execute
|
9
|
+
thing
|
10
|
+
end
|
11
|
+
end
|
7
12
|
|
8
|
-
|
9
|
-
|
13
|
+
describe ActiveInteraction::Base do
|
14
|
+
include_context 'interactions'
|
10
15
|
|
11
|
-
|
12
|
-
thing
|
13
|
-
end
|
14
|
-
end
|
16
|
+
subject(:interaction) { described_class.new(inputs) }
|
15
17
|
|
16
|
-
describe '.new(
|
18
|
+
describe '.new(inputs = {})' do
|
17
19
|
it 'does not allow :_interaction_* as an option' do
|
18
20
|
key = :"_interaction_#{SecureRandom.hex}"
|
19
|
-
|
20
|
-
expect
|
21
|
+
inputs.merge!(key => nil)
|
22
|
+
expect do
|
21
23
|
interaction
|
22
|
-
|
24
|
+
end.to raise_error ActiveInteraction::InvalidValueError
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'does not allow "_interaction_*" as an option' do
|
26
28
|
key = "_interaction_#{SecureRandom.hex}"
|
27
|
-
|
28
|
-
expect
|
29
|
+
inputs.merge!(key => nil)
|
30
|
+
expect do
|
29
31
|
interaction
|
30
|
-
|
32
|
+
end.to raise_error ActiveInteraction::InvalidValueError
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with invalid inputs' do
|
36
|
+
let(:inputs) { nil }
|
37
|
+
|
38
|
+
it 'raises an error' do
|
39
|
+
expect { interaction }.to raise_error ArgumentError
|
40
|
+
end
|
31
41
|
end
|
32
42
|
|
33
43
|
context 'with an attribute' do
|
@@ -45,7 +55,7 @@ describe ActiveInteraction::Base do
|
|
45
55
|
let(:thing) { SecureRandom.hex }
|
46
56
|
|
47
57
|
context 'failing validations' do
|
48
|
-
before {
|
58
|
+
before { inputs.merge!(thing: nil) }
|
49
59
|
|
50
60
|
it 'returns an invalid outcome' do
|
51
61
|
expect(interaction).to be_invalid
|
@@ -53,7 +63,7 @@ describe ActiveInteraction::Base do
|
|
53
63
|
end
|
54
64
|
|
55
65
|
context 'passing validations' do
|
56
|
-
before {
|
66
|
+
before { inputs.merge!(thing: thing) }
|
57
67
|
|
58
68
|
it 'returns a valid outcome' do
|
59
69
|
expect(interaction).to be_valid
|
@@ -69,7 +79,7 @@ describe ActiveInteraction::Base do
|
|
69
79
|
let(:described_class) { InteractionWithFilter }
|
70
80
|
|
71
81
|
context 'failing validations' do
|
72
|
-
before {
|
82
|
+
before { inputs.merge!(thing: thing) }
|
73
83
|
|
74
84
|
context 'with an invalid value' do
|
75
85
|
let(:thing) { 'a' }
|
@@ -89,7 +99,7 @@ describe ActiveInteraction::Base do
|
|
89
99
|
end
|
90
100
|
|
91
101
|
context 'passing validations' do
|
92
|
-
before {
|
102
|
+
before { inputs.merge!(thing: 1) }
|
93
103
|
|
94
104
|
it 'sets the attribute to the filtered value' do
|
95
105
|
expect(interaction.thing).to eql 1.0
|
@@ -100,11 +110,11 @@ describe ActiveInteraction::Base do
|
|
100
110
|
|
101
111
|
describe '.method_missing(filter_type, *args, &block)' do
|
102
112
|
it 'raises an error for an invalid filter type' do
|
103
|
-
expect
|
113
|
+
expect do
|
104
114
|
Class.new(described_class) do
|
105
115
|
not_a_valid_filter_type :thing
|
106
116
|
end
|
107
|
-
|
117
|
+
end.to raise_error NoMethodError
|
108
118
|
end
|
109
119
|
|
110
120
|
it do
|
@@ -152,7 +162,7 @@ describe ActiveInteraction::Base do
|
|
152
162
|
let(:described_class) { InteractionWithFilter }
|
153
163
|
let(:thing) { rand }
|
154
164
|
|
155
|
-
describe '.run(
|
165
|
+
describe '.run(inputs = {})' do
|
156
166
|
it "returns an instance of #{described_class}" do
|
157
167
|
expect(outcome).to be_a described_class
|
158
168
|
end
|
@@ -186,7 +196,7 @@ describe ActiveInteraction::Base do
|
|
186
196
|
end
|
187
197
|
|
188
198
|
context 'passing validations' do
|
189
|
-
before {
|
199
|
+
before { inputs.merge!(thing: thing) }
|
190
200
|
|
191
201
|
context 'failing runtime validations' do
|
192
202
|
before do
|
@@ -231,25 +241,25 @@ describe ActiveInteraction::Base do
|
|
231
241
|
it 'calls transaction' do
|
232
242
|
allow(described_class).to receive(:transaction)
|
233
243
|
outcome
|
234
|
-
expect(described_class).to have_received(:transaction).once
|
235
|
-
with(no_args)
|
244
|
+
expect(described_class).to have_received(:transaction).once
|
245
|
+
.with(no_args)
|
236
246
|
end
|
237
247
|
end
|
238
248
|
end
|
239
249
|
|
240
|
-
describe '.run!(
|
241
|
-
subject(:result) { described_class.run!(
|
250
|
+
describe '.run!(inputs = {})' do
|
251
|
+
subject(:result) { described_class.run!(inputs) }
|
242
252
|
|
243
253
|
context 'failing validations' do
|
244
254
|
it 'raises an error' do
|
245
|
-
expect
|
255
|
+
expect do
|
246
256
|
result
|
247
|
-
|
257
|
+
end.to raise_error ActiveInteraction::InvalidInteractionError
|
248
258
|
end
|
249
259
|
end
|
250
260
|
|
251
261
|
context 'passing validations' do
|
252
|
-
before {
|
262
|
+
before { inputs.merge!(thing: thing) }
|
253
263
|
|
254
264
|
it 'returns the result' do
|
255
265
|
expect(result).to eq thing
|
@@ -261,7 +271,7 @@ describe ActiveInteraction::Base do
|
|
261
271
|
describe '#inputs' do
|
262
272
|
let(:described_class) { InteractionWithFilter }
|
263
273
|
let(:other_val) { SecureRandom.hex }
|
264
|
-
let(:
|
274
|
+
let(:inputs) { { thing: 1, other: other_val } }
|
265
275
|
|
266
276
|
it 'casts filtered inputs' do
|
267
277
|
expect(interaction.inputs[:thing]).to eql 1.0
|
@@ -297,7 +307,7 @@ describe ActiveInteraction::Base do
|
|
297
307
|
|
298
308
|
context 'with valid composition' do
|
299
309
|
before do
|
300
|
-
|
310
|
+
inputs.merge!(x: x, y: y)
|
301
311
|
end
|
302
312
|
|
303
313
|
it 'is valid' do
|
@@ -315,8 +325,8 @@ describe ActiveInteraction::Base do
|
|
315
325
|
end
|
316
326
|
|
317
327
|
it 'has the correct errors' do
|
318
|
-
expect(outcome.errors[:base])
|
319
|
-
to match_array ['X is required', 'Y is required']
|
328
|
+
expect(outcome.errors[:base])
|
329
|
+
.to match_array ['X is required', 'Y is required']
|
320
330
|
end
|
321
331
|
end
|
322
332
|
end
|
@@ -326,4 +336,19 @@ describe ActiveInteraction::Base do
|
|
326
336
|
expect { interaction.execute }.to raise_error NotImplementedError
|
327
337
|
end
|
328
338
|
end
|
339
|
+
|
340
|
+
context 'inheritance' do
|
341
|
+
it 'keeps the filters of the parent class' do
|
342
|
+
ParentInteraction = Class.new(ActiveInteraction::Base) do
|
343
|
+
boolean :x,
|
344
|
+
default: nil
|
345
|
+
|
346
|
+
def execute
|
347
|
+
inputs
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
expect(Class.new(ParentInteraction).run!).to eql(x: nil)
|
352
|
+
end
|
353
|
+
end
|
329
354
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe ActiveInteraction::Errors do
|
@@ -18,22 +20,22 @@ describe ActiveInteraction::Errors do
|
|
18
20
|
describe '#add_sym' do
|
19
21
|
it 'defaults to :invalid' do
|
20
22
|
errors.add_sym(:attribute)
|
21
|
-
expect(errors.symbolic).to eq
|
23
|
+
expect(errors.symbolic[:attribute]).to eq [:invalid]
|
22
24
|
end
|
23
25
|
|
24
26
|
it 'adds a symbol' do
|
25
27
|
errors.add_sym(:attribute, :symbol)
|
26
|
-
expect(errors.symbolic).to eq
|
28
|
+
expect(errors.symbolic[:attribute]).to eq [:symbol]
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'accepts a message' do
|
30
32
|
errors.add_sym(:attribute, :symbol, 'message')
|
31
|
-
expect(errors.symbolic).to eq
|
33
|
+
expect(errors.symbolic[:attribute]).to eq [:symbol]
|
32
34
|
end
|
33
35
|
|
34
36
|
it 'accepts a message and options' do
|
35
|
-
errors.add_sym(:attribute, :symbol, 'message',
|
36
|
-
expect(errors.symbolic).to eq
|
37
|
+
errors.add_sym(:attribute, :symbol, 'message', key: :value)
|
38
|
+
expect(errors.symbolic[:attribute]).to eq [:symbol]
|
37
39
|
end
|
38
40
|
|
39
41
|
context 'calling #add' do
|
@@ -43,26 +45,26 @@ describe ActiveInteraction::Errors do
|
|
43
45
|
|
44
46
|
it 'with the default' do
|
45
47
|
errors.add_sym(:attribute)
|
46
|
-
expect(errors).to have_received(:add).once
|
47
|
-
with(:attribute, :invalid, {})
|
48
|
+
expect(errors).to have_received(:add).once
|
49
|
+
.with(:attribute, :invalid, {})
|
48
50
|
end
|
49
51
|
|
50
52
|
it 'with a symbol' do
|
51
53
|
errors.add_sym(:attribute, :symbol)
|
52
|
-
expect(errors).to have_received(:add).once
|
53
|
-
with(:attribute, :symbol, {})
|
54
|
+
expect(errors).to have_received(:add).once
|
55
|
+
.with(:attribute, :symbol, {})
|
54
56
|
end
|
55
57
|
|
56
58
|
it 'with a symbol and message' do
|
57
59
|
errors.add_sym(:attribute, :symbol, 'message')
|
58
|
-
expect(errors).to have_received(:add).once
|
59
|
-
with(:attribute, 'message', {})
|
60
|
+
expect(errors).to have_received(:add).once
|
61
|
+
.with(:attribute, 'message', {})
|
60
62
|
end
|
61
63
|
|
62
64
|
it 'with a symbol, message and options' do
|
63
|
-
errors.add_sym(:attribute, :symbol, 'message',
|
64
|
-
expect(errors).to have_received(:add).once
|
65
|
-
with(:attribute, 'message',
|
65
|
+
errors.add_sym(:attribute, :symbol, 'message', key: :value)
|
66
|
+
expect(errors).to have_received(:add).once
|
67
|
+
.with(:attribute, 'message', key: :value)
|
66
68
|
end
|
67
69
|
end
|
68
70
|
end
|
@@ -96,4 +98,36 @@ describe ActiveInteraction::Errors do
|
|
96
98
|
expect(errors.symbolic).to be_empty
|
97
99
|
end
|
98
100
|
end
|
101
|
+
|
102
|
+
describe '#merge!' do
|
103
|
+
let(:other) { described_class.new(klass.new) }
|
104
|
+
|
105
|
+
context 'with an error' do
|
106
|
+
before do
|
107
|
+
other.add(:attribute)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'adds the error' do
|
111
|
+
errors.merge!(other)
|
112
|
+
expect(errors.messages[:attribute]).to eq ['is invalid']
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'does not add duplicate errors' do
|
116
|
+
other.add(:attribute)
|
117
|
+
errors.merge!(other)
|
118
|
+
expect(errors.messages[:attribute]).to eq ['is invalid']
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with a symbolic error' do
|
123
|
+
before do
|
124
|
+
other.add_sym(:attribute)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'adds the error' do
|
128
|
+
errors.merge!(other)
|
129
|
+
expect(errors.symbolic[:attribute]).to eq [:invalid]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
99
133
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
class ActiveInteraction::TestFilter < ActiveInteraction::Filter; end
|
@@ -7,9 +9,9 @@ describe ActiveInteraction::Filter, :filter do
|
|
7
9
|
|
8
10
|
describe '.slug' do
|
9
11
|
it 'raises an error' do
|
10
|
-
expect
|
12
|
+
expect do
|
11
13
|
described_class.slug
|
12
|
-
|
14
|
+
end.to raise_error ActiveInteraction::InvalidClassError
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe ActiveInteraction::ArrayFilter, :filter do
|
@@ -5,7 +7,12 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
5
7
|
it_behaves_like 'a filter'
|
6
8
|
|
7
9
|
context 'with multiple nested filters' do
|
8
|
-
let(:block)
|
10
|
+
let(:block) do
|
11
|
+
proc do
|
12
|
+
array
|
13
|
+
array
|
14
|
+
end
|
15
|
+
end
|
9
16
|
|
10
17
|
it 'raises an error' do
|
11
18
|
expect { filter }.to raise_error ActiveInteraction::InvalidFilterError
|
@@ -13,7 +20,7 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
13
20
|
end
|
14
21
|
|
15
22
|
context 'with a nested name' do
|
16
|
-
let(:block) {
|
23
|
+
let(:block) { proc { array :a } }
|
17
24
|
|
18
25
|
it 'raises an error' do
|
19
26
|
expect { filter }.to raise_error ActiveInteraction::InvalidFilterError
|
@@ -21,7 +28,7 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
21
28
|
end
|
22
29
|
|
23
30
|
context 'with a nested default' do
|
24
|
-
let(:block) {
|
31
|
+
let(:block) { proc { array default: nil } }
|
25
32
|
|
26
33
|
it 'raises an error' do
|
27
34
|
expect { filter }.to raise_error ActiveInteraction::InvalidDefaultError
|
@@ -46,7 +53,7 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
46
53
|
end
|
47
54
|
|
48
55
|
context 'with a nested filter' do
|
49
|
-
let(:block) {
|
56
|
+
let(:block) { proc { array } }
|
50
57
|
|
51
58
|
context 'with an Array' do
|
52
59
|
let(:value) { [] }
|
@@ -68,9 +75,9 @@ describe ActiveInteraction::ArrayFilter, :filter do
|
|
68
75
|
let(:value) { [[], false, 0.0, {}, 0, '', :''] }
|
69
76
|
|
70
77
|
it 'raises an error' do
|
71
|
-
expect
|
78
|
+
expect do
|
72
79
|
filter.cast(value)
|
73
|
-
|
80
|
+
end.to raise_error ActiveInteraction::InvalidValueError
|
74
81
|
end
|
75
82
|
end
|
76
83
|
end
|