active_interaction 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|