active_interaction 5.2.0 → 5.5.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +11 -12
  4. data/lib/active_interaction/base.rb +2 -0
  5. data/lib/active_interaction/errors.rb +16 -16
  6. data/lib/active_interaction/filters/hash_filter.rb +5 -1
  7. data/lib/active_interaction/grouped_input.rb +23 -3
  8. data/lib/active_interaction/version.rb +1 -1
  9. metadata +12 -115
  10. data/spec/active_interaction/array_input_spec.rb +0 -166
  11. data/spec/active_interaction/base_spec.rb +0 -537
  12. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -45
  13. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -49
  14. data/spec/active_interaction/concerns/hashable_spec.rb +0 -46
  15. data/spec/active_interaction/concerns/missable_spec.rb +0 -99
  16. data/spec/active_interaction/concerns/runnable_spec.rb +0 -397
  17. data/spec/active_interaction/errors_spec.rb +0 -196
  18. data/spec/active_interaction/filter/column_spec.rb +0 -87
  19. data/spec/active_interaction/filter_spec.rb +0 -60
  20. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +0 -13
  21. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +0 -13
  22. data/spec/active_interaction/filters/array_filter_spec.rb +0 -250
  23. data/spec/active_interaction/filters/boolean_filter_spec.rb +0 -87
  24. data/spec/active_interaction/filters/date_filter_spec.rb +0 -178
  25. data/spec/active_interaction/filters/date_time_filter_spec.rb +0 -189
  26. data/spec/active_interaction/filters/decimal_filter_spec.rb +0 -126
  27. data/spec/active_interaction/filters/file_filter_spec.rb +0 -40
  28. data/spec/active_interaction/filters/float_filter_spec.rb +0 -110
  29. data/spec/active_interaction/filters/hash_filter_spec.rb +0 -129
  30. data/spec/active_interaction/filters/integer_filter_spec.rb +0 -104
  31. data/spec/active_interaction/filters/interface_filter_spec.rb +0 -461
  32. data/spec/active_interaction/filters/object_filter_spec.rb +0 -237
  33. data/spec/active_interaction/filters/record_filter_spec.rb +0 -206
  34. data/spec/active_interaction/filters/string_filter_spec.rb +0 -60
  35. data/spec/active_interaction/filters/symbol_filter_spec.rb +0 -46
  36. data/spec/active_interaction/filters/time_filter_spec.rb +0 -251
  37. data/spec/active_interaction/grouped_input_spec.rb +0 -17
  38. data/spec/active_interaction/hash_input_spec.rb +0 -58
  39. data/spec/active_interaction/i18n_spec.rb +0 -113
  40. data/spec/active_interaction/inputs_spec.rb +0 -266
  41. data/spec/active_interaction/integration/array_interaction_spec.rb +0 -88
  42. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -5
  43. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -5
  44. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -5
  45. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -18
  46. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -5
  47. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -76
  48. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -5
  49. data/spec/active_interaction/integration/interface_interaction_spec.rb +0 -19
  50. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -14
  51. data/spec/active_interaction/integration/record_integration_spec.rb +0 -5
  52. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -5
  53. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -5
  54. data/spec/active_interaction/integration/time_interaction_spec.rb +0 -88
  55. data/spec/active_interaction/modules/validation_spec.rb +0 -57
  56. data/spec/spec_helper.rb +0 -20
  57. data/spec/support/concerns.rb +0 -13
  58. data/spec/support/filters.rb +0 -227
  59. data/spec/support/interactions.rb +0 -124
@@ -1,88 +0,0 @@
1
- require 'spec_helper'
2
-
3
- TimeWithZone = Class.new do
4
- attr_reader :time
5
-
6
- def initialize(time)
7
- @time = time
8
- end
9
-
10
- def ==(other)
11
- !other.nil? && time == other.time
12
- end
13
-
14
- def at(*args)
15
- TimeWithZone.new(Time.at(*args) + 1)
16
- end
17
-
18
- def parse(*args)
19
- TimeWithZone.new(Time.parse(*args) + 1)
20
- rescue ArgumentError
21
- nil
22
- end
23
- end
24
-
25
- TimeInteraction = Class.new(TestInteraction) do
26
- time :a
27
- end
28
-
29
- describe TimeInteraction do
30
- include_context 'interactions'
31
- it_behaves_like 'an interaction', :time, -> { Time.now }
32
-
33
- context 'with a time zone' do
34
- let(:a) { nil }
35
-
36
- before do
37
- inputs[:a] = a
38
-
39
- allow(Time).to receive(:zone).and_return(TimeWithZone.new(0))
40
- end
41
-
42
- context 'with an integer' do
43
- let(:a) { rand(1 << 16) }
44
-
45
- it 'returns the correct value' do
46
- expect(result[:a]).to eq TimeWithZone.new(0).at(a)
47
- end
48
- end
49
-
50
- context 'with a string' do
51
- let(:a) { '2011-12-13T14:15:16Z' }
52
-
53
- it 'returns the correct value' do
54
- expect(result[:a]).to eq TimeWithZone.new(0).parse(a)
55
- end
56
- end
57
-
58
- context 'with an invalid String' do
59
- let(:a) { 'invalid' }
60
-
61
- it 'is invalid' do
62
- expect(outcome).to be_invalid
63
- end
64
- end
65
-
66
- context 'with a Time' do
67
- let(:a) { Time.now }
68
-
69
- it 'returns the correct value' do
70
- expect(result[:a]).to eql a
71
- end
72
- end
73
-
74
- context 'with a TimeZone' do
75
- let(:a) { TimeWithZone.new(Time.now) }
76
-
77
- it 'returns the correct value' do
78
- expect(result[:a]).to eql a
79
- end
80
-
81
- it 'handles time zone changes' do
82
- outcome
83
- allow(Time).to receive(:zone).and_return(nil)
84
- expect(described_class.run(inputs)).to be_invalid
85
- end
86
- end
87
- end
88
- end
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::Validation do
4
- describe '.validate(context, filters, inputs)' do
5
- let(:inputs) { {} }
6
- let(:filter) { ActiveInteraction::Filter.new(:name, {}) }
7
- let(:interaction) do
8
- name = filter.name
9
- klass = Class.new(ActiveInteraction::Base) { attr_writer(name) }
10
- klass.filters[name] = filter
11
- klass.new
12
- end
13
- let(:result) do
14
- described_class.validate(interaction, interaction.class.filters, inputs)
15
- end
16
-
17
- context 'no filters are given' do
18
- let(:interaction) { Class.new(ActiveInteraction::Base).new }
19
-
20
- it 'returns no errors' do
21
- expect(result).to eql []
22
- end
23
- end
24
-
25
- context 'filter returns no errors' do
26
- let(:inputs) { { name: 1 } }
27
-
28
- before do
29
- allow(filter).to receive(:process).and_return(ActiveInteraction::Input.new(filter, value: 1))
30
- end
31
-
32
- it 'returns no errors' do
33
- expect(result).to eql []
34
- end
35
- end
36
-
37
- context 'filter returns with errors' do
38
- before do
39
- allow(filter).to receive(:process).and_return(ActiveInteraction::Input.new(filter, error: exception))
40
- end
41
-
42
- context 'Filter::Error' do
43
- let(:filter) { ActiveInteraction::ArrayFilter.new(:name, [1.0, 'a']) { float } }
44
-
45
- let(:exception) { ActiveInteraction::Filter::Error.new(filter, :invalid_type) }
46
-
47
- it 'returns an :invalid_type error' do
48
- type = I18n.translate(
49
- "#{ActiveInteraction::Base.i18n_scope}.types.#{filter.class.slug}"
50
- )
51
-
52
- expect(result).to eql [[filter.name, :invalid_type, { type: type }]]
53
- end
54
- end
55
- end
56
- end
57
- end
data/spec/spec_helper.rb DELETED
@@ -1,20 +0,0 @@
1
- require 'i18n'
2
- I18n.config.enforce_available_locales = true if I18n.config.respond_to?(:enforce_available_locales)
3
-
4
- require 'active_interaction'
5
- require 'active_record'
6
-
7
- Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
8
-
9
- RSpec.configure do |config|
10
- config.run_all_when_everything_filtered = true
11
- config.filter_run_including :focus
12
-
13
- config.before(:suite) do
14
- if ActiveRecord.respond_to?(:index_nested_attribute_errors)
15
- ActiveRecord.index_nested_attribute_errors = false
16
- else
17
- ActiveRecord::Base.index_nested_attribute_errors = false
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- shared_context 'concerns' do |concern|
2
- subject(:instance) { klass.new }
3
-
4
- let(:klass) do
5
- Class.new do
6
- include concern
7
-
8
- def self.name
9
- SecureRandom.hex
10
- end
11
- end
12
- end
13
- end
@@ -1,227 +0,0 @@
1
- shared_context 'filters' do
2
- subject(:filter) { described_class.new(name, options, &block) }
3
-
4
- let(:block) { nil }
5
- let(:name) { SecureRandom.hex.to_sym }
6
- let(:options) { {} }
7
-
8
- shared_context 'optional' do
9
- before do
10
- options[:default] = nil
11
- end
12
- end
13
-
14
- shared_context 'required' do
15
- before do
16
- options.delete(:default)
17
- end
18
- end
19
- end
20
-
21
- shared_examples_for 'a filter' do
22
- include_context 'filters'
23
-
24
- describe '.factory' do
25
- context 'with an invalid slug' do
26
- it 'raises an error' do
27
- expect do
28
- described_class.factory(:invalid)
29
- end.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 eql 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 '#process' do
49
- let(:value) { nil }
50
-
51
- context 'optional' do
52
- include_context 'optional'
53
-
54
- it 'returns the default' do
55
- expect(filter.process(value, nil).value).to eql options[:default]
56
- end
57
- end
58
-
59
- context 'required' do
60
- include_context 'required'
61
-
62
- it 'indicates an error' do
63
- error = filter.process(value, nil).errors.first
64
-
65
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
66
- expect(error.type).to be :missing
67
- end
68
-
69
- context 'with an invalid value' do
70
- let(:value) { Object.new }
71
-
72
- it 'indicates an error' do
73
- error = filter.process(value, nil).errors.first
74
-
75
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
76
- expect(error.type).to be :invalid_type
77
- end
78
- end
79
- end
80
-
81
- context 'with an invalid default' do
82
- before do
83
- options[:default] = Object.new
84
- end
85
-
86
- it 'raises an error' do
87
- expect do
88
- filter.process(value, nil)
89
- end.to raise_error ActiveInteraction::InvalidDefaultError
90
- end
91
- end
92
-
93
- # BasicObject is missing a lot of methods
94
- context 'with a BasicObject' do
95
- let(:value) { BasicObject.new }
96
-
97
- it 'indicates an error' do
98
- error = filter.process(value, nil).errors.first
99
-
100
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
101
- expect(error.type).to be :invalid_type
102
- end
103
- end
104
- end
105
-
106
- describe '#default' do
107
- context 'optional' do
108
- include_context 'optional'
109
-
110
- it 'returns the default' do
111
- expect(filter.default(nil)).to eql options[:default]
112
- end
113
- end
114
-
115
- context 'required' do
116
- include_context 'required'
117
-
118
- it 'raises an error' do
119
- expect do
120
- filter.default(nil)
121
- end.to raise_error ActiveInteraction::NoDefaultError
122
- end
123
- end
124
-
125
- context 'with an invalid default' do
126
- before do
127
- options[:default] = Object.new
128
- end
129
-
130
- it 'raises an error' do
131
- expect do
132
- filter.default(nil)
133
- end.to raise_error ActiveInteraction::InvalidDefaultError
134
- end
135
- end
136
-
137
- context 'with a callable default' do
138
- include_context 'optional'
139
-
140
- before do
141
- default = options[:default]
142
- options[:default] = -> { default }
143
- end
144
-
145
- it 'returns the default' do
146
- expect(filter.default(nil)).to eql options[:default].call
147
- end
148
- end
149
-
150
- context 'with a callable default that takes an argument' do
151
- include_context 'optional'
152
-
153
- it 'returns the default' do
154
- default = options[:default]
155
-
156
- spec = self
157
- filter # Necessary to bring into scope for lambda.
158
- options[:default] = lambda do |this|
159
- spec.expect(this).to be filter
160
- default
161
- end
162
-
163
- expect(filter.default(nil)).to be default
164
- end
165
- end
166
- end
167
-
168
- describe '#desc' do
169
- it 'returns nil' do
170
- expect(filter.desc).to be_nil
171
- end
172
-
173
- context 'with a description' do
174
- let(:desc) { SecureRandom.hex }
175
-
176
- before do
177
- options[:desc] = desc
178
- end
179
-
180
- it 'returns the description' do
181
- expect(filter.desc).to eql desc
182
- end
183
- end
184
- end
185
-
186
- describe '#filters' do
187
- it 'returns Hash' do
188
- expect(filter.filters).to be_a Hash
189
- end
190
- end
191
-
192
- describe '#default?' do
193
- context 'optional' do
194
- include_context 'optional'
195
-
196
- it 'returns true' do
197
- expect(filter).to be_default
198
- end
199
- end
200
-
201
- context 'required' do
202
- include_context 'required'
203
-
204
- it 'returns false' do
205
- expect(filter).to_not be_default
206
- end
207
- end
208
- end
209
-
210
- describe '#name' do
211
- it 'returns the name' do
212
- expect(filter.name).to eql name
213
- end
214
- end
215
-
216
- describe '#options' do
217
- it 'returns the options' do
218
- expect(filter.options).to eql options
219
- end
220
- end
221
-
222
- describe '#database_column_type' do
223
- it 'returns a symbol' do
224
- expect(filter.database_column_type).to be_a Symbol
225
- end
226
- end
227
- end
@@ -1,124 +0,0 @@
1
- TestInteraction = Class.new(ActiveInteraction::Base) do
2
- def self.name
3
- SecureRandom.hex
4
- end
5
-
6
- def execute
7
- inputs
8
- end
9
- end
10
-
11
- shared_context 'interactions' do
12
- let(:inputs) { {} }
13
- let(:outcome) { described_class.run(inputs) }
14
- let(:result) { outcome.result }
15
- end
16
-
17
- shared_examples_for 'an interaction' do |type, generator, adjust_output = nil, **filter_options|
18
- include_context 'interactions'
19
-
20
- let(:described_class) do
21
- Class.new(TestInteraction) do
22
- public_send(type, :required, filter_options)
23
- public_send(type, :optional,
24
- filter_options.merge(
25
- default: nil
26
- )
27
- )
28
- public_send(type, :default,
29
- filter_options.merge(
30
- default: generator.call
31
- )
32
- )
33
- public_send(type, :defaults1, :defaults2,
34
- filter_options.merge(
35
- default: generator.call
36
- )
37
- )
38
- public_send(type, :defaults3,
39
- filter_options.merge(
40
- default: -> { required }
41
- )
42
- )
43
- end
44
- end
45
-
46
- context 'with an invalid lazy default' do
47
- let(:described_class) do
48
- Class.new(TestInteraction) do
49
- public_send(type, :default,
50
- filter_options.merge(default: -> { Object.new })
51
- )
52
- end
53
- end
54
-
55
- it 'raises an error' do
56
- expect { outcome }.to raise_error ActiveInteraction::InvalidDefaultError
57
- end
58
- end
59
-
60
- context 'without required inputs' do
61
- it 'is invalid' do
62
- expect(outcome).to be_invalid
63
- end
64
- end
65
-
66
- context 'with inputs[:required]' do
67
- let(:required) { generator.call }
68
-
69
- before { inputs[:required] = required }
70
-
71
- it 'is valid' do
72
- expect(outcome).to be_valid
73
- end
74
-
75
- it 'returns the correct value for :required' do
76
- expect(result[:required]).to eql(adjust_output ? adjust_output.call(required) : required)
77
- end
78
-
79
- it 'returns nil for :optional' do
80
- expect(result[:optional]).to be_nil
81
- end
82
-
83
- it 'does not return nil for :default' do
84
- expect(result[:default]).to_not be_nil
85
- end
86
-
87
- it 'does not return nil for :default when given nil' do
88
- inputs[:default] = nil
89
- expect(result[:default]).to_not be_nil
90
- end
91
-
92
- it 'does not return nil for :defaults1' do
93
- expect(result[:defaults1]).to_not be_nil
94
- end
95
-
96
- it 'does not return nil for :defaults2' do
97
- expect(result[:defaults2]).to_not be_nil
98
- end
99
-
100
- it 'evaluates :defaults3 in the interaction binding' do
101
- expect(result[:defaults3]).to eql result[:required]
102
- end
103
-
104
- context 'with inputs[:optional]' do
105
- let(:optional) { generator.call }
106
-
107
- before { inputs[:optional] = optional }
108
-
109
- it 'returns the correct value for :optional' do
110
- expect(result[:optional]).to eql(adjust_output ? adjust_output.call(optional) : optional)
111
- end
112
- end
113
-
114
- context 'with inputs[:default]' do
115
- let(:default) { generator.call }
116
-
117
- before { inputs[:default] = default }
118
-
119
- it 'returns the correct value for :default' do
120
- expect(result[:default]).to eql(adjust_output ? adjust_output.call(default) : default)
121
- end
122
- end
123
- end
124
- end