active_interaction 0.5.0 → 0.6.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 +27 -3
- data/README.md +8 -6
- data/lib/active_interaction.rb +5 -3
- data/lib/active_interaction/active_model.rb +29 -0
- data/lib/active_interaction/base.rb +82 -116
- data/lib/active_interaction/errors.rb +79 -5
- data/lib/active_interaction/filter.rb +195 -21
- data/lib/active_interaction/filters.rb +26 -0
- data/lib/active_interaction/filters/array_filter.rb +22 -25
- data/lib/active_interaction/filters/boolean_filter.rb +12 -12
- data/lib/active_interaction/filters/date_filter.rb +32 -5
- data/lib/active_interaction/filters/date_time_filter.rb +34 -7
- data/lib/active_interaction/filters/file_filter.rb +12 -9
- data/lib/active_interaction/filters/float_filter.rb +13 -11
- data/lib/active_interaction/filters/hash_filter.rb +36 -17
- data/lib/active_interaction/filters/integer_filter.rb +13 -11
- data/lib/active_interaction/filters/model_filter.rb +15 -15
- data/lib/active_interaction/filters/string_filter.rb +19 -8
- data/lib/active_interaction/filters/symbol_filter.rb +29 -0
- data/lib/active_interaction/filters/time_filter.rb +38 -16
- data/lib/active_interaction/method_missing.rb +18 -0
- data/lib/active_interaction/overload_hash.rb +1 -0
- data/lib/active_interaction/validation.rb +19 -0
- data/lib/active_interaction/version.rb +1 -1
- data/spec/active_interaction/active_model_spec.rb +33 -0
- data/spec/active_interaction/base_spec.rb +54 -48
- data/spec/active_interaction/errors_spec.rb +99 -0
- data/spec/active_interaction/filter_spec.rb +12 -20
- data/spec/active_interaction/filters/array_filter_spec.rb +50 -28
- data/spec/active_interaction/filters/boolean_filter_spec.rb +15 -15
- data/spec/active_interaction/filters/date_filter_spec.rb +30 -18
- data/spec/active_interaction/filters/date_time_filter_spec.rb +31 -19
- data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
- data/spec/active_interaction/filters/float_filter_spec.rb +13 -11
- data/spec/active_interaction/filters/hash_filter_spec.rb +38 -29
- data/spec/active_interaction/filters/integer_filter_spec.rb +18 -8
- data/spec/active_interaction/filters/model_filter_spec.rb +24 -20
- data/spec/active_interaction/filters/string_filter_spec.rb +14 -8
- data/spec/active_interaction/filters/symbol_filter_spec.rb +24 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +33 -69
- data/spec/active_interaction/filters_spec.rb +21 -0
- data/spec/active_interaction/i18n_spec.rb +0 -15
- data/spec/active_interaction/integration/array_interaction_spec.rb +2 -22
- data/spec/active_interaction/integration/hash_interaction_spec.rb +5 -25
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +5 -0
- data/spec/active_interaction/method_missing_spec.rb +69 -0
- data/spec/active_interaction/validation_spec.rb +55 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/filters.rb +168 -14
- data/spec/support/interactions.rb +11 -13
- metadata +31 -13
- data/lib/active_interaction/filter_method.rb +0 -13
- data/lib/active_interaction/filter_methods.rb +0 -26
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +0 -25
- data/spec/active_interaction/filter_method_spec.rb +0 -43
- data/spec/active_interaction/filter_methods_spec.rb +0 -30
@@ -37,7 +37,7 @@ describe ArrayInteraction do
|
|
37
37
|
Class.new(ActiveInteraction::Base) do
|
38
38
|
array :a, default: Object.new
|
39
39
|
end
|
40
|
-
}.to raise_error ActiveInteraction::
|
40
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -49,27 +49,7 @@ describe ArrayInteraction do
|
|
49
49
|
array
|
50
50
|
end
|
51
51
|
end
|
52
|
-
}.to raise_error ActiveInteraction::
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'with a validly nested default' do
|
57
|
-
let(:described_class) do
|
58
|
-
Class.new(ActiveInteraction::Base) do
|
59
|
-
array :a do
|
60
|
-
array default: [rand]
|
61
|
-
end
|
62
|
-
def execute; a end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
let(:options) { { a: [] } }
|
66
|
-
|
67
|
-
it 'does not raise an error' do
|
68
|
-
expect { described_class.run(options) }.to_not raise_error
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'ignores the nested default value' do
|
72
|
-
expect(described_class.run!(options)).to eq options[:a]
|
52
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
73
53
|
end
|
74
54
|
end
|
75
55
|
end
|
@@ -4,8 +4,8 @@ class HashInteraction < ActiveInteraction::Base
|
|
4
4
|
hash :a do
|
5
5
|
hash :x
|
6
6
|
end
|
7
|
-
hash :b, default: {
|
8
|
-
hash :x
|
7
|
+
hash :b, default: {} do
|
8
|
+
hash :x, default: {}
|
9
9
|
end
|
10
10
|
|
11
11
|
def execute
|
@@ -23,7 +23,7 @@ describe HashInteraction do
|
|
23
23
|
before { options.merge!(a: a) }
|
24
24
|
|
25
25
|
it 'returns the correct value for :a' do
|
26
|
-
expect(result[:a]).to eq a
|
26
|
+
expect(result[:a]).to eq a.symbolize_keys
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'returns the correct value for :b' do
|
@@ -37,7 +37,7 @@ describe HashInteraction do
|
|
37
37
|
Class.new(ActiveInteraction::Base) do
|
38
38
|
hash :a, default: Object.new
|
39
39
|
end
|
40
|
-
}.to raise_error ActiveInteraction::
|
40
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -49,27 +49,7 @@ describe HashInteraction do
|
|
49
49
|
hash :x
|
50
50
|
end
|
51
51
|
end
|
52
|
-
}.to raise_error ActiveInteraction::
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'with a validly nested default' do
|
57
|
-
let(:described_class) do
|
58
|
-
Class.new(ActiveInteraction::Base) do
|
59
|
-
hash :a do
|
60
|
-
hash :x, default: { y: rand }
|
61
|
-
end
|
62
|
-
def execute; a end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
let(:options) { { a: { x: {} } } }
|
66
|
-
|
67
|
-
it 'does not raise an error' do
|
68
|
-
expect { described_class.run(options) }.to_not raise_error
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'merges the nested default value' do
|
72
|
-
expect(described_class.run!(options)[:x]).to have_key(:y)
|
52
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
73
53
|
end
|
74
54
|
end
|
75
55
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::MethodMissing do
|
4
|
+
let(:model) do
|
5
|
+
Class.new do
|
6
|
+
include ActiveInteraction::MethodMissing
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject(:instance) { model.new }
|
11
|
+
|
12
|
+
describe '#method_missing' do
|
13
|
+
context 'with invalid slug' do
|
14
|
+
let(:slug) { :slug }
|
15
|
+
|
16
|
+
it 'calls super' do
|
17
|
+
expect {
|
18
|
+
instance.method_missing(slug)
|
19
|
+
}.to raise_error NoMethodError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with valid slug' do
|
24
|
+
let(:filter) { ActiveInteraction::Filter.factory(slug) }
|
25
|
+
let(:slug) { :boolean }
|
26
|
+
|
27
|
+
it 'returns self' do
|
28
|
+
expect(instance.method_missing(slug)).to eq instance
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'yields' do
|
32
|
+
expect { |b|
|
33
|
+
instance.method_missing(slug, &b)
|
34
|
+
}.to yield_with_args(filter, [], {})
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with names' do
|
38
|
+
let(:names) { [:a, :b, :c] }
|
39
|
+
|
40
|
+
it 'yields' do
|
41
|
+
expect { |b|
|
42
|
+
instance.method_missing(:boolean, *names, &b)
|
43
|
+
}.to yield_with_args(filter, names, {})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with options' do
|
48
|
+
let(:options) { { a: nil, b: false, c: true } }
|
49
|
+
|
50
|
+
it 'yields' do
|
51
|
+
expect { |b|
|
52
|
+
instance.method_missing(:boolean, options, &b)
|
53
|
+
}.to yield_with_args(filter, [], options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with names & options' do
|
58
|
+
let(:names) { [:a, :b, :c] }
|
59
|
+
let(:options) { { a: nil, b: false, c: true } }
|
60
|
+
|
61
|
+
it 'yields' do
|
62
|
+
expect { |b|
|
63
|
+
instance.method_missing(:boolean, *names, options, &b)
|
64
|
+
}.to yield_with_args(filter, names, options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::Validation do
|
4
|
+
describe '.validate(filters, inputs)' do
|
5
|
+
let(:inputs) { {} }
|
6
|
+
let(:filter) { ActiveInteraction::Filter.new(:name, {}) }
|
7
|
+
let(:filters) { ActiveInteraction::Filters.new.add(filter) }
|
8
|
+
let(:result) { described_class.validate(filters, inputs) }
|
9
|
+
|
10
|
+
context 'no filters are given' do
|
11
|
+
let(:filters) { ActiveInteraction::Filters.new }
|
12
|
+
|
13
|
+
it 'returns no errors' do
|
14
|
+
expect(result).to eq []
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'filter.cast returns a value' do
|
19
|
+
let(:inputs) { {name: 1} }
|
20
|
+
|
21
|
+
before do
|
22
|
+
filter.stub(:cast).and_return(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns no errors' do
|
26
|
+
expect(result).to eq []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'filter throws' do
|
31
|
+
before do
|
32
|
+
filter.stub(:cast).and_raise(exception)
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'InvalidValueError' do
|
36
|
+
let(:exception) { ActiveInteraction::InvalidValueError }
|
37
|
+
let(:filter) { ActiveInteraction::FloatFilter.new(:name, {}) }
|
38
|
+
|
39
|
+
it 'returns an :invalid_nested error' do
|
40
|
+
type = I18n.translate("#{ActiveInteraction::Base.i18n_scope}.types.#{filter.class.slug.to_s}")
|
41
|
+
|
42
|
+
expect(result).to eq [[filter.name, :invalid, nil, type: type]]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'MissingValueError' do
|
47
|
+
let(:exception) { ActiveInteraction::MissingValueError }
|
48
|
+
|
49
|
+
it 'returns an :invalid_nested error' do
|
50
|
+
expect(result).to eq [[filter.name, :missing]]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,3 +4,9 @@ Coveralls.wear!
|
|
4
4
|
require 'active_interaction'
|
5
5
|
|
6
6
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
10
|
+
config.run_all_when_everything_filtered = true
|
11
|
+
config.filter_run_including :focus
|
12
|
+
end
|
data/spec/support/filters.rb
CHANGED
@@ -1,37 +1,191 @@
|
|
1
1
|
shared_context 'filters' do
|
2
|
-
let(:
|
3
|
-
let(:
|
2
|
+
let(:block) { nil }
|
3
|
+
let(:name) { SecureRandom.hex.to_sym }
|
4
4
|
let(:options) { {} }
|
5
|
-
|
6
|
-
subject(:
|
5
|
+
|
6
|
+
subject(:filter) { described_class.new(name, options, &block) }
|
7
|
+
|
8
|
+
shared_context 'optional' do
|
9
|
+
before do
|
10
|
+
options.merge!(default: nil)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
shared_context 'required' do
|
15
|
+
before do
|
16
|
+
options.delete(:default)
|
17
|
+
end
|
18
|
+
end
|
7
19
|
end
|
8
20
|
|
9
21
|
shared_examples_for 'a filter' do
|
10
22
|
include_context 'filters'
|
11
23
|
|
12
|
-
|
13
|
-
context 'with
|
24
|
+
describe '.factory' do
|
25
|
+
context 'with an invalid slug' do
|
14
26
|
it 'raises an error' do
|
15
|
-
expect {
|
27
|
+
expect {
|
28
|
+
described_class.factory(:invalid)
|
29
|
+
}.to raise_error ActiveInteraction::MissingFilterError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a valid slug' do
|
34
|
+
it 'returns a Filter' do
|
35
|
+
expect(
|
36
|
+
described_class.factory(described_class.slug)
|
37
|
+
).to eq described_class
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.slug' do
|
43
|
+
it 'returns a symbol' do
|
44
|
+
expect(described_class.slug).to be_a Symbol
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#cast' do
|
49
|
+
let(:value) { nil }
|
50
|
+
|
51
|
+
context 'optional' do
|
52
|
+
include_context 'optional'
|
53
|
+
|
54
|
+
it 'returns nil' do
|
55
|
+
expect(filter.cast(value)).to be_nil
|
16
56
|
end
|
17
57
|
end
|
18
58
|
|
19
|
-
context '
|
20
|
-
|
59
|
+
context 'required' do
|
60
|
+
include_context 'required'
|
21
61
|
|
22
62
|
it 'raises an error' do
|
23
|
-
expect {
|
63
|
+
expect {
|
64
|
+
filter.cast(value)
|
65
|
+
}.to raise_error ActiveInteraction::MissingValueError
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with an invalid default' do
|
69
|
+
let(:value) { Object.new }
|
70
|
+
|
71
|
+
it 'raises an error' do
|
72
|
+
expect {
|
73
|
+
filter.cast(value)
|
74
|
+
}.to raise_error ActiveInteraction::InvalidValueError
|
75
|
+
end
|
24
76
|
end
|
25
77
|
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#clean' do
|
81
|
+
let(:value) { nil }
|
26
82
|
|
27
83
|
context 'optional' do
|
28
|
-
|
84
|
+
include_context 'optional'
|
85
|
+
|
86
|
+
it 'returns the default' do
|
87
|
+
expect(filter.clean(value)).to eq options[:default]
|
88
|
+
end
|
89
|
+
end
|
29
90
|
|
30
|
-
|
31
|
-
|
32
|
-
|
91
|
+
context 'required' do
|
92
|
+
include_context 'required'
|
93
|
+
|
94
|
+
it 'raises an error' do
|
95
|
+
expect {
|
96
|
+
filter.clean(value)
|
97
|
+
}.to raise_error ActiveInteraction::MissingValueError
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with an invalid value' do
|
101
|
+
let(:value) { Object.new }
|
102
|
+
|
103
|
+
it 'raises an error' do
|
104
|
+
expect {
|
105
|
+
filter.clean(value)
|
106
|
+
}.to raise_error ActiveInteraction::InvalidValueError
|
33
107
|
end
|
34
108
|
end
|
35
109
|
end
|
110
|
+
|
111
|
+
context 'with an invalid default' do
|
112
|
+
before do
|
113
|
+
options.merge!(default: Object.new)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'raises an error' do
|
117
|
+
expect {
|
118
|
+
filter.clean(value)
|
119
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#default' do
|
125
|
+
context 'optional' do
|
126
|
+
include_context 'optional'
|
127
|
+
|
128
|
+
it 'returns the default' do
|
129
|
+
expect(filter.default).to eq options[:default]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'required' do
|
134
|
+
include_context 'required'
|
135
|
+
|
136
|
+
it 'raises an error' do
|
137
|
+
expect {
|
138
|
+
filter.default
|
139
|
+
}.to raise_error ActiveInteraction::NoDefaultError
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with an invalid default' do
|
144
|
+
before do
|
145
|
+
options.merge!(default: Object.new)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'raises an error' do
|
149
|
+
expect {
|
150
|
+
filter.default
|
151
|
+
}.to raise_error ActiveInteraction::InvalidDefaultError
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '#filters' do
|
157
|
+
it 'returns Filters' do
|
158
|
+
expect(filter.filters).to be_an ActiveInteraction::Filters
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#has_default?' do
|
163
|
+
context 'optional' do
|
164
|
+
include_context 'optional'
|
165
|
+
|
166
|
+
it 'returns true' do
|
167
|
+
expect(filter).to have_default
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'required' do
|
172
|
+
include_context 'required'
|
173
|
+
|
174
|
+
it 'returns false' do
|
175
|
+
expect(filter).to_not have_default
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#name' do
|
181
|
+
it 'returns the name' do
|
182
|
+
expect(filter.name).to eq name
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#options' do
|
187
|
+
it 'returns the options' do
|
188
|
+
expect(filter.options).to eq options
|
189
|
+
end
|
36
190
|
end
|
37
191
|
end
|
@@ -1,3 +1,12 @@
|
|
1
|
+
class TestInteraction < ActiveInteraction::Base
|
2
|
+
def self.name
|
3
|
+
SecureRandom.hex
|
4
|
+
end
|
5
|
+
|
6
|
+
def execute
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
1
10
|
shared_context 'interactions' do
|
2
11
|
let(:options) { {} }
|
3
12
|
let(:outcome) { described_class.run(options) }
|
@@ -8,25 +17,18 @@ shared_examples_for 'an interaction' do |type, generator, filter_options = {}|
|
|
8
17
|
include_context 'interactions'
|
9
18
|
|
10
19
|
let(:described_class) do
|
11
|
-
Class.new(
|
20
|
+
Class.new(TestInteraction) do
|
12
21
|
send(type, :required, filter_options)
|
13
|
-
send(type, :optional, filter_options.merge(
|
22
|
+
send(type, :optional, filter_options.merge(default: nil))
|
14
23
|
send(type, :default, filter_options.merge(default: generator.call))
|
15
|
-
send(type, :nil_default,
|
16
|
-
filter_options.merge(allow_nil: true, default: nil))
|
17
24
|
send(type, :defaults_1, :defaults_2,
|
18
25
|
filter_options.merge(default: generator.call))
|
19
26
|
|
20
|
-
def self.name
|
21
|
-
SecureRandom.hex
|
22
|
-
end
|
23
|
-
|
24
27
|
def execute
|
25
28
|
{
|
26
29
|
required: required,
|
27
30
|
optional: optional,
|
28
31
|
default: default,
|
29
|
-
nil_default: nil_default,
|
30
32
|
defaults_1: defaults_1,
|
31
33
|
defaults_2: defaults_2
|
32
34
|
}
|
@@ -66,10 +68,6 @@ shared_examples_for 'an interaction' do |type, generator, filter_options = {}|
|
|
66
68
|
expect(result[:default]).to_not be_nil
|
67
69
|
end
|
68
70
|
|
69
|
-
it 'returns nil for :nil_default' do
|
70
|
-
expect(result[:nil_default]).to be_nil
|
71
|
-
end
|
72
|
-
|
73
71
|
it 'does not return nil for :defaults_1' do
|
74
72
|
expect(result[:defaults_1]).to_not be_nil
|
75
73
|
end
|