active_interaction 5.3.0 → 5.4.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +2 -2
  4. data/lib/active_interaction/errors.rb +1 -1
  5. data/lib/active_interaction/version.rb +1 -1
  6. metadata +26 -112
  7. data/spec/active_interaction/array_input_spec.rb +0 -164
  8. data/spec/active_interaction/base_spec.rb +0 -536
  9. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -43
  10. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -47
  11. data/spec/active_interaction/concerns/hashable_spec.rb +0 -44
  12. data/spec/active_interaction/concerns/missable_spec.rb +0 -97
  13. data/spec/active_interaction/concerns/runnable_spec.rb +0 -395
  14. data/spec/active_interaction/errors_spec.rb +0 -223
  15. data/spec/active_interaction/filter/column_spec.rb +0 -85
  16. data/spec/active_interaction/filter_spec.rb +0 -58
  17. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +0 -11
  18. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +0 -11
  19. data/spec/active_interaction/filters/array_filter_spec.rb +0 -248
  20. data/spec/active_interaction/filters/boolean_filter_spec.rb +0 -85
  21. data/spec/active_interaction/filters/date_filter_spec.rb +0 -176
  22. data/spec/active_interaction/filters/date_time_filter_spec.rb +0 -187
  23. data/spec/active_interaction/filters/decimal_filter_spec.rb +0 -124
  24. data/spec/active_interaction/filters/file_filter_spec.rb +0 -38
  25. data/spec/active_interaction/filters/float_filter_spec.rb +0 -109
  26. data/spec/active_interaction/filters/hash_filter_spec.rb +0 -127
  27. data/spec/active_interaction/filters/integer_filter_spec.rb +0 -102
  28. data/spec/active_interaction/filters/interface_filter_spec.rb +0 -459
  29. data/spec/active_interaction/filters/object_filter_spec.rb +0 -235
  30. data/spec/active_interaction/filters/record_filter_spec.rb +0 -204
  31. data/spec/active_interaction/filters/string_filter_spec.rb +0 -58
  32. data/spec/active_interaction/filters/symbol_filter_spec.rb +0 -44
  33. data/spec/active_interaction/filters/time_filter_spec.rb +0 -249
  34. data/spec/active_interaction/grouped_input_spec.rb +0 -15
  35. data/spec/active_interaction/hash_input_spec.rb +0 -56
  36. data/spec/active_interaction/i18n_spec.rb +0 -111
  37. data/spec/active_interaction/inputs_spec.rb +0 -264
  38. data/spec/active_interaction/integration/array_interaction_spec.rb +0 -87
  39. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -11
  40. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -3
  41. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -3
  42. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -17
  43. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -3
  44. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -106
  45. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -3
  46. data/spec/active_interaction/integration/interface_interaction_spec.rb +0 -18
  47. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -12
  48. data/spec/active_interaction/integration/record_integration_spec.rb +0 -3
  49. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -3
  50. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -3
  51. data/spec/active_interaction/integration/time_interaction_spec.rb +0 -86
  52. data/spec/active_interaction/modules/validation_spec.rb +0 -55
  53. data/spec/spec_helper.rb +0 -22
  54. data/spec/support/concerns.rb +0 -13
  55. data/spec/support/filters.rb +0 -227
  56. data/spec/support/interactions.rb +0 -124
@@ -1,111 +0,0 @@
1
- RSpec.describe ActiveInteraction do
2
- context 'I18n.load_path' do
3
- it 'contains localization file paths' do
4
- expect(I18n.load_path)
5
- .to include a_string_ending_with('active_interaction/locale/en.yml')
6
- end
7
- end
8
- end
9
-
10
- I18nInteraction = Class.new(TestInteraction) do
11
- hash :a do
12
- hash :x
13
- end
14
- end
15
-
16
- TYPES = ActiveInteraction::Filter
17
- .const_get(:CLASSES)
18
- .keys
19
- .map(&:to_s)
20
-
21
- RSpec.describe I18nInteraction do
22
- include_context 'interactions'
23
-
24
- shared_examples 'translation' do |locale|
25
- around do |example|
26
- old_locale = I18n.locale
27
- I18n.locale = locale
28
-
29
- example.run
30
-
31
- I18n.locale = old_locale
32
- end
33
-
34
- context 'types' do
35
- TYPES.each do |type|
36
- it "has a translation for #{type}" do
37
- key = "#{described_class.i18n_scope}.types.#{type}"
38
- expect { I18n.translate(key, raise: true) }.to_not raise_error
39
- end
40
- end
41
- end
42
-
43
- context 'error messages' do
44
- let(:translation) { I18n.translate(key, type: type, raise: true) }
45
- let(:type) { I18n.translate("#{described_class.i18n_scope}.types.hash") }
46
-
47
- shared_examples 'translations' do |key, value|
48
- context key.inspect do
49
- let(:key) { "#{described_class.i18n_scope}.errors.messages.#{key}" }
50
-
51
- before { inputs[:a] = value }
52
-
53
- it 'has a translation' do
54
- expect { translation }.to_not raise_error
55
- end
56
-
57
- it 'returns the translation' do
58
- expect(outcome.errors[:a]).to include translation
59
- end
60
- end
61
- end
62
-
63
- include_examples 'translations', :invalid_type, Object.new
64
- include_examples 'translations', :missing, nil
65
- end
66
- end
67
-
68
- context 'english' do
69
- include_examples 'translation', :en
70
- end
71
-
72
- context 'brazilian portuguese' do
73
- include_examples 'translation', :'pt-BR'
74
- end
75
-
76
- context 'french' do
77
- include_examples 'translation', :fr
78
- end
79
-
80
- context 'italian' do
81
- include_examples 'translation', :it
82
- end
83
-
84
- context 'hsilgne' do
85
- # This must appear before including the translation examples so that the
86
- # locale is available before it is assigned.
87
- around do |example|
88
- old_locals = I18n.config.available_locales
89
- I18n.config.available_locales += [:hsilgne]
90
-
91
- I18n.backend.store_translations('hsilgne',
92
- active_interaction: {
93
- errors: {
94
- messages: {
95
- invalid: 'is invalid'.reverse,
96
- invalid_type: "%<type>s} #{'is not a valid'.reverse}",
97
- missing: 'missing'.reverse
98
- }
99
- },
100
- types: TYPES.each_with_object({}) { |e, a| a[e] = e.reverse }
101
- }
102
- )
103
-
104
- example.run
105
-
106
- I18n.config.available_locales = old_locals
107
- end
108
-
109
- include_examples 'translation', :hsilgne
110
- end
111
- end
@@ -1,264 +0,0 @@
1
- RSpec.describe ActiveInteraction::Inputs do
2
- subject(:inputs) { described_class.new(args, base_class.new) }
3
-
4
- let(:args) { {} }
5
- let(:base_class) { ActiveInteraction::Base }
6
-
7
- describe '.reserved?(name)' do
8
- it 'returns true for anything starting with "_interaction_"' do
9
- expect(described_class).to be_reserved('_interaction_')
10
- end
11
-
12
- it 'returns true for existing instance methods' do
13
- (
14
- (ActiveInteraction::Base.instance_methods - Object.instance_methods) +
15
- (ActiveInteraction::Base.private_instance_methods - Object.private_instance_methods)
16
- ).each do |method|
17
- expect(described_class).to be_reserved(method)
18
- end
19
- end
20
-
21
- it 'returns false for anything else' do
22
- expect(described_class).to_not be_reserved(SecureRandom.hex)
23
- end
24
- end
25
-
26
- describe '#normalized' do
27
- let(:result) { inputs.normalized }
28
-
29
- context 'with invalid inputs' do
30
- let(:args) { nil }
31
-
32
- it 'raises an error' do
33
- expect { result }.to raise_error ArgumentError
34
- end
35
- end
36
-
37
- context 'with non-hash inputs' do
38
- let(:args) { [%i[k v]] }
39
-
40
- it 'raises an error' do
41
- expect { result }.to raise_error ArgumentError
42
- end
43
- end
44
-
45
- context 'with ActiveInteraction::Inputs inputs' do
46
- let(:args) { described_class.new({ key: :value }, base_class.new) }
47
-
48
- it 'does not raise an error' do
49
- expect { result }.to_not raise_error
50
- end
51
- end
52
-
53
- context 'with ActionController::Parameters inputs' do
54
- let(:args) { ::ActionController::Parameters.new }
55
-
56
- it 'does not raise an error' do
57
- expect { result }.to_not raise_error
58
- end
59
- end
60
-
61
- context 'with simple inputs' do
62
- before { args[:key] = :value }
63
-
64
- it 'sends them straight through' do
65
- expect(result).to eql args
66
- end
67
- end
68
-
69
- context 'with groupable inputs' do
70
- context 'without a matching simple input' do
71
- before do
72
- args.merge!(
73
- 'key(1i)' => :value1,
74
- 'key(2i)' => :value2
75
- )
76
- end
77
-
78
- it 'groups the inputs into a GroupedInput' do
79
- expect(result).to eq(
80
- key: ActiveInteraction::GroupedInput.new(
81
- '1' => :value1,
82
- '2' => :value2
83
- )
84
- )
85
- end
86
- end
87
-
88
- context 'with a matching simple input' do
89
- before do
90
- args.merge!(
91
- 'key(1i)' => :value1,
92
- key: :value2
93
- )
94
- end
95
-
96
- it 'groups the inputs into a GroupedInput' do
97
- expect(result).to eq(
98
- key: ActiveInteraction::GroupedInput.new(
99
- '1' => :value1
100
- )
101
- )
102
- end
103
- end
104
- end
105
-
106
- context 'with a reserved name' do
107
- before { args[:_interaction_key] = :value }
108
-
109
- it 'skips the input' do
110
- expect(result).to_not have_key(:_interaction_key)
111
- end
112
- end
113
- end
114
-
115
- describe '#given?' do
116
- let(:base_class) do
117
- Class.new(ActiveInteraction::Base) do
118
- float :x,
119
- default: nil
120
-
121
- def execute; end
122
- end
123
- end
124
-
125
- it 'is false when the input is not given' do
126
- expect(inputs.given?(:x)).to be false
127
- end
128
-
129
- it 'is true when the input is nil' do
130
- args[:x] = nil
131
- expect(inputs.given?(:x)).to be true
132
- end
133
-
134
- it 'is true when the input is given' do
135
- args[:x] = rand
136
- expect(inputs.given?(:x)).to be true
137
- end
138
-
139
- it 'symbolizes its argument' do
140
- args[:x] = rand
141
- expect(inputs.given?('x')).to be true
142
- end
143
-
144
- it 'only tracks inputs with filters' do
145
- args[:y] = rand
146
- expect(inputs.given?(:y)).to be false
147
- end
148
-
149
- context 'nested hash values' do
150
- let(:base_class) do
151
- Class.new(ActiveInteraction::Base) do
152
- hash :x, default: {} do
153
- boolean :y,
154
- default: true
155
- end
156
-
157
- def execute; end
158
- end
159
- end
160
-
161
- it 'is true when the nested inputs symbols are given' do
162
- described_class.class_exec do
163
- def execute
164
- given?(:x, :y)
165
- end
166
- end
167
-
168
- args[:x] = { y: false }
169
- expect(inputs.given?(:x, :y)).to be true
170
- end
171
-
172
- it 'is true when the nested inputs strings are given' do
173
- args['x'] = { 'y' => false }
174
- expect(inputs.given?(:x, :y)).to be true
175
- end
176
-
177
- it 'is false when the nested input is not given' do
178
- args[:x] = {}
179
- expect(inputs.given?(:x, :y)).to be false
180
- end
181
-
182
- it 'is false when the first input is not given' do
183
- expect(inputs.given?(:x, :y)).to be false
184
- end
185
-
186
- it 'is false when the first input is nil' do
187
- args[:x] = nil
188
- expect(inputs.given?(:x, :y)).to be false
189
- end
190
-
191
- it 'returns false if you go too far' do
192
- args[:x] = { y: true }
193
- expect(inputs.given?(:x, :y, :z)).to be false
194
- end
195
- end
196
-
197
- context 'nested array values' do
198
- let(:base_class) do
199
- Class.new(ActiveInteraction::Base) do
200
- array :x do
201
- hash do
202
- boolean :y, default: true
203
- end
204
- end
205
-
206
- def execute; end
207
- end
208
- end
209
-
210
- context 'has a positive index' do
211
- it 'returns true if found' do
212
- args[:x] = [{ y: true }]
213
- expect(inputs.given?(:x, 0, :y)).to be true
214
- end
215
-
216
- it 'returns false if not found' do
217
- args[:x] = []
218
- expect(inputs.given?(:x, 0, :y)).to be false
219
- end
220
- end
221
-
222
- context 'has a negative index' do
223
- it 'returns true if found' do
224
- args[:x] = [{ y: true }]
225
- expect(inputs.given?(:x, -1, :y)).to be true
226
- end
227
-
228
- it 'returns false if not found' do
229
- args[:x] = []
230
- expect(inputs.given?(:x, -1, :y)).to be false
231
- end
232
- end
233
-
234
- it 'returns false if you go too far' do
235
- args[:x] = [{}]
236
- expect(inputs.given?(:x, 10, :y)).to be false
237
- end
238
- end
239
-
240
- context 'multi-part date values' do
241
- let(:base_class) do
242
- Class.new(ActiveInteraction::Base) do
243
- date :thing,
244
- default: nil
245
-
246
- def execute; end
247
- end
248
- end
249
-
250
- it 'returns true when the input is given' do
251
- args.merge!(
252
- 'thing(1i)' => '2020',
253
- 'thing(2i)' => '12',
254
- 'thing(3i)' => '31'
255
- )
256
- expect(inputs.given?(:thing)).to be true
257
- end
258
-
259
- it 'returns false if not found' do
260
- expect(inputs.given?(:thing)).to be false
261
- end
262
- end
263
- end
264
- end
@@ -1,87 +0,0 @@
1
- require 'active_record'
2
- require 'sqlite3'
3
-
4
- ActiveRecord::Base.establish_connection(
5
- adapter: 'sqlite3',
6
- database: ':memory:'
7
- )
8
-
9
- ActiveRecord::Schema.define do
10
- create_table(:lists)
11
- create_table(:elements) { |t| t.column(:list_id, :integer) }
12
- end
13
-
14
- class List < ActiveRecord::Base
15
- has_many :elements
16
- end
17
-
18
- class Element < ActiveRecord::Base
19
- belongs_to :list
20
- end
21
-
22
- ArrayInteraction = Class.new(TestInteraction) do
23
- array :a do
24
- array
25
- end
26
- array :b, default: [[]] do
27
- array
28
- end
29
- end
30
-
31
- RSpec.describe ArrayInteraction do
32
- include_context 'interactions'
33
- it_behaves_like 'an interaction', :array, -> { [] }
34
- it_behaves_like 'an interaction', :array, -> { Element.where('1 = 1') }, ->(result) { result.to_a }
35
- it_behaves_like 'an interaction', :array, -> { List.create!.elements }, ->(result) { result.to_a }
36
-
37
- context 'with inputs[:a]' do
38
- let(:a) { [[]] }
39
-
40
- before { inputs[:a] = a }
41
-
42
- it 'returns the correct value for :a' do
43
- expect(result[:a]).to eql a
44
- end
45
-
46
- it 'returns the correct value for :b' do
47
- expect(result[:b]).to eql [[]]
48
- end
49
-
50
- it 'does not raise an error with an invalid nested value' do
51
- inputs[:a] = [false]
52
- expect { outcome }.to_not raise_error
53
- end
54
- end
55
-
56
- context 'with an invalid default' do
57
- it 'raises an error' do
58
- expect do
59
- Class.new(ActiveInteraction::Base) do
60
- array :a, default: Object.new
61
- end
62
- end.to raise_error ActiveInteraction::InvalidDefaultError
63
- end
64
- end
65
-
66
- context 'with an invalid default as a proc' do
67
- it 'does not raise an error' do
68
- expect do
69
- Class.new(ActiveInteraction::Base) do
70
- array :a, default: -> { Object.new }
71
- end
72
- end.to_not raise_error
73
- end
74
- end
75
-
76
- context 'with an invalid nested default' do
77
- it 'raises an error' do
78
- expect do
79
- Class.new(ActiveInteraction::Base) do
80
- array :a, default: [Object.new] do
81
- array
82
- end
83
- end
84
- end.to raise_error ActiveInteraction::InvalidDefaultError
85
- end
86
- end
87
- end
@@ -1,11 +0,0 @@
1
- BooleanInteraction = Class.new(TestInteraction) do
2
- boolean :x
3
- end
4
-
5
- RSpec.describe BooleanInteraction do
6
- it_behaves_like 'an interaction', :boolean, -> { [false, true].sample }
7
-
8
- it 'responds to #x?' do
9
- expect(described_class.new).to respond_to(:x?)
10
- end
11
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'DateInteraction' do
2
- it_behaves_like 'an interaction', :date, -> { Date.today }
3
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'DateTimeInteraction' do
2
- it_behaves_like 'an interaction', :date_time, -> { DateTime.now }
3
- end
@@ -1,17 +0,0 @@
1
- require 'action_dispatch'
2
-
3
- FileInteraction = Class.new(TestInteraction) do
4
- file :a
5
- end
6
-
7
- RSpec.describe FileInteraction do
8
- include_context 'interactions'
9
- it_behaves_like 'an interaction', :file, -> { File.open(__FILE__) }
10
-
11
- it 'works with an uploaded file' do
12
- file = File.open(__FILE__)
13
- uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: file)
14
- inputs[:a] = uploaded_file
15
- expect(outcome).to be_valid
16
- end
17
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'FloatInteraction' do
2
- it_behaves_like 'an interaction', :float, -> { rand }
3
- end
@@ -1,106 +0,0 @@
1
- HashInteraction = Class.new(TestInteraction) do
2
- hash :a do
3
- hash :x
4
- end
5
- hash :b, default: {} do
6
- hash :x, default: {}
7
- end
8
- end
9
-
10
- RSpec.describe HashInteraction do
11
- include_context 'interactions'
12
- it_behaves_like 'an interaction', :hash, -> { {} }
13
-
14
- context 'with inputs[:a]' do
15
- let(:a) { { x: {} } }
16
-
17
- before { inputs[:a] = a }
18
-
19
- it 'returns the correct value for :a' do
20
- expect(result[:a]).to eql ActiveSupport::HashWithIndifferentAccess.new(a)
21
- end
22
-
23
- it 'returns the correct value for :b' do
24
- expect(result[:b]).to eql('x' => {})
25
- end
26
-
27
- it 'does not raise an error with an invalid nested value' do
28
- inputs[:a] = { x: false }
29
- expect { outcome }.to_not raise_error
30
- end
31
- end
32
-
33
- context 'with an invalid default' do
34
- it 'raises an error' do
35
- expect do
36
- Class.new(ActiveInteraction::Base) do
37
- hash :a, default: Object.new
38
- end
39
- end.to raise_error ActiveInteraction::InvalidDefaultError
40
- end
41
- end
42
-
43
- context 'with an invalid default as a proc' do
44
- it 'does not raise an error' do
45
- expect do
46
- Class.new(ActiveInteraction::Base) do
47
- array :a, default: -> { Object.new }
48
- end
49
- end.to_not raise_error
50
- end
51
- end
52
-
53
- context 'with an invalid nested default' do
54
- it 'raises an error with a non-empty hash' do
55
- expect do
56
- Class.new(ActiveInteraction::Base) do
57
- hash :a, default: { x: Object.new } do
58
- hash :x
59
- end
60
- end
61
- end.to raise_error ActiveInteraction::InvalidDefaultError
62
- end
63
-
64
- it 'raises an error' do
65
- expect do
66
- Class.new(ActiveInteraction::Base) do
67
- hash :a, default: {} do
68
- hash :x
69
- end
70
- end
71
- end.to raise_error ActiveInteraction::InvalidDefaultError
72
- end
73
- end
74
-
75
- context 'with a default' do
76
- context 'with a lazy nested default' do
77
- it 'raises an error' do
78
- expect do
79
- Class.new(ActiveInteraction::Base) do
80
- hash :b, default: {} do
81
- hash :x, default: -> { {} }
82
- end
83
- end
84
- end.to raise_error ActiveInteraction::InvalidDefaultError
85
- end
86
- end
87
- end
88
-
89
- context 'with a lazy default' do
90
- context 'with a lazy nested default' do
91
- it 'returns the correct value' do
92
- klass = Class.new(ActiveInteraction::Base) do
93
- hash :b, default: -> { {} } do
94
- hash :x, default: -> { {} }
95
- end
96
-
97
- def execute
98
- b
99
- end
100
- end
101
-
102
- expect(klass.run!).to eql('x' => {})
103
- end
104
- end
105
- end
106
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'IntegerInteraction' do
2
- it_behaves_like 'an interaction', :integer, -> { rand(1 << 16) }
3
- end
@@ -1,18 +0,0 @@
1
- require 'json'
2
- require 'yaml'
3
-
4
- InterfaceInteraction = Class.new(TestInteraction) do
5
- interface :anything, methods: []
6
- end
7
-
8
- RSpec.describe InterfaceInteraction do
9
- include_context 'interactions'
10
- it_behaves_like 'an interaction',
11
- :interface,
12
- -> { [JSON, YAML].sample },
13
- methods: %i[dump load]
14
-
15
- it 'succeeds when given nil' do
16
- expect { result }.to_not raise_error
17
- end
18
- end
@@ -1,12 +0,0 @@
1
- ObjectInteraction = Class.new(TestInteraction) do
2
- object :object
3
- end
4
-
5
- RSpec.describe ObjectInteraction do
6
- include_context 'interactions'
7
- it_behaves_like 'an interaction', :object, -> { // }, class: Regexp
8
-
9
- it 'succeeds when given nil' do
10
- expect { result }.to_not raise_error
11
- end
12
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'RecordIntegration' do
2
- it_behaves_like 'an interaction', :record, -> { Encoding::US_ASCII }, class: Encoding
3
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'StringInteraction' do
2
- it_behaves_like 'an interaction', :string, -> { SecureRandom.hex }
3
- end
@@ -1,3 +0,0 @@
1
- RSpec.describe 'SymbolInteraction' do
2
- it_behaves_like 'an interaction', :symbol, -> { SecureRandom.hex.to_sym }
3
- end