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