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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -3
  3. data/README.md +8 -6
  4. data/lib/active_interaction.rb +5 -3
  5. data/lib/active_interaction/active_model.rb +29 -0
  6. data/lib/active_interaction/base.rb +82 -116
  7. data/lib/active_interaction/errors.rb +79 -5
  8. data/lib/active_interaction/filter.rb +195 -21
  9. data/lib/active_interaction/filters.rb +26 -0
  10. data/lib/active_interaction/filters/array_filter.rb +22 -25
  11. data/lib/active_interaction/filters/boolean_filter.rb +12 -12
  12. data/lib/active_interaction/filters/date_filter.rb +32 -5
  13. data/lib/active_interaction/filters/date_time_filter.rb +34 -7
  14. data/lib/active_interaction/filters/file_filter.rb +12 -9
  15. data/lib/active_interaction/filters/float_filter.rb +13 -11
  16. data/lib/active_interaction/filters/hash_filter.rb +36 -17
  17. data/lib/active_interaction/filters/integer_filter.rb +13 -11
  18. data/lib/active_interaction/filters/model_filter.rb +15 -15
  19. data/lib/active_interaction/filters/string_filter.rb +19 -8
  20. data/lib/active_interaction/filters/symbol_filter.rb +29 -0
  21. data/lib/active_interaction/filters/time_filter.rb +38 -16
  22. data/lib/active_interaction/method_missing.rb +18 -0
  23. data/lib/active_interaction/overload_hash.rb +1 -0
  24. data/lib/active_interaction/validation.rb +19 -0
  25. data/lib/active_interaction/version.rb +1 -1
  26. data/spec/active_interaction/active_model_spec.rb +33 -0
  27. data/spec/active_interaction/base_spec.rb +54 -48
  28. data/spec/active_interaction/errors_spec.rb +99 -0
  29. data/spec/active_interaction/filter_spec.rb +12 -20
  30. data/spec/active_interaction/filters/array_filter_spec.rb +50 -28
  31. data/spec/active_interaction/filters/boolean_filter_spec.rb +15 -15
  32. data/spec/active_interaction/filters/date_filter_spec.rb +30 -18
  33. data/spec/active_interaction/filters/date_time_filter_spec.rb +31 -19
  34. data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
  35. data/spec/active_interaction/filters/float_filter_spec.rb +13 -11
  36. data/spec/active_interaction/filters/hash_filter_spec.rb +38 -29
  37. data/spec/active_interaction/filters/integer_filter_spec.rb +18 -8
  38. data/spec/active_interaction/filters/model_filter_spec.rb +24 -20
  39. data/spec/active_interaction/filters/string_filter_spec.rb +14 -8
  40. data/spec/active_interaction/filters/symbol_filter_spec.rb +24 -0
  41. data/spec/active_interaction/filters/time_filter_spec.rb +33 -69
  42. data/spec/active_interaction/filters_spec.rb +21 -0
  43. data/spec/active_interaction/i18n_spec.rb +0 -15
  44. data/spec/active_interaction/integration/array_interaction_spec.rb +2 -22
  45. data/spec/active_interaction/integration/hash_interaction_spec.rb +5 -25
  46. data/spec/active_interaction/integration/symbol_interaction_spec.rb +5 -0
  47. data/spec/active_interaction/method_missing_spec.rb +69 -0
  48. data/spec/active_interaction/validation_spec.rb +55 -0
  49. data/spec/spec_helper.rb +6 -0
  50. data/spec/support/filters.rb +168 -14
  51. data/spec/support/interactions.rb +11 -13
  52. metadata +31 -13
  53. data/lib/active_interaction/filter_method.rb +0 -13
  54. data/lib/active_interaction/filter_methods.rb +0 -26
  55. data/lib/active_interaction/filters/abstract_date_time_filter.rb +0 -25
  56. data/spec/active_interaction/filter_method_spec.rb +0 -43
  57. 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::InvalidDefaultValue
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::InvalidDefaultValue
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: { x: {} } do
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::InvalidDefaultValue
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::InvalidDefaultValue
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,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'SymbolInteraction' do
4
+ it_behaves_like 'an interaction', :symbol, -> { SecureRandom.hex.to_sym }
5
+ 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
@@ -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
@@ -1,37 +1,191 @@
1
1
  shared_context 'filters' do
2
- let(:key) { SecureRandom.hex }
3
- let(:value) { nil }
2
+ let(:block) { nil }
3
+ let(:name) { SecureRandom.hex.to_sym }
4
4
  let(:options) { {} }
5
- let(:block) { Proc.new {} }
6
- subject(:result) { described_class.prepare(key, value, options, &block) }
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
- context '.prepare(key, value, options = {}, &block)' do
13
- context 'with nil' do
24
+ describe '.factory' do
25
+ context 'with an invalid slug' do
14
26
  it 'raises an error' do
15
- expect { result }.to raise_error ActiveInteraction::MissingValue
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 'with anything else' do
20
- let(:value) { Object.new }
59
+ context 'required' do
60
+ include_context 'required'
21
61
 
22
62
  it 'raises an error' do
23
- expect { result }.to raise_error ActiveInteraction::InvalidValue
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
- before { options.merge!(allow_nil: true) }
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
- context 'with nil' do
31
- it 'returns nil' do
32
- expect(result).to be_nil
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(ActiveInteraction::Base) do
20
+ Class.new(TestInteraction) do
12
21
  send(type, :required, filter_options)
13
- send(type, :optional, filter_options.merge(allow_nil: true))
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