active_interaction 5.2.0 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
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