active_interaction 5.1.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -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/filter.rb +9 -12
  7. data/lib/active_interaction/filters/array_filter.rb +1 -1
  8. data/lib/active_interaction/filters/hash_filter.rb +5 -1
  9. data/lib/active_interaction/grouped_input.rb +23 -3
  10. data/lib/active_interaction/locale/es.yml +23 -0
  11. data/lib/active_interaction/version.rb +1 -1
  12. metadata +13 -115
  13. data/spec/active_interaction/array_input_spec.rb +0 -166
  14. data/spec/active_interaction/base_spec.rb +0 -537
  15. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -45
  16. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -49
  17. data/spec/active_interaction/concerns/hashable_spec.rb +0 -46
  18. data/spec/active_interaction/concerns/missable_spec.rb +0 -99
  19. data/spec/active_interaction/concerns/runnable_spec.rb +0 -397
  20. data/spec/active_interaction/errors_spec.rb +0 -196
  21. data/spec/active_interaction/filter/column_spec.rb +0 -87
  22. data/spec/active_interaction/filter_spec.rb +0 -39
  23. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +0 -13
  24. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +0 -13
  25. data/spec/active_interaction/filters/array_filter_spec.rb +0 -245
  26. data/spec/active_interaction/filters/boolean_filter_spec.rb +0 -87
  27. data/spec/active_interaction/filters/date_filter_spec.rb +0 -178
  28. data/spec/active_interaction/filters/date_time_filter_spec.rb +0 -189
  29. data/spec/active_interaction/filters/decimal_filter_spec.rb +0 -126
  30. data/spec/active_interaction/filters/file_filter_spec.rb +0 -40
  31. data/spec/active_interaction/filters/float_filter_spec.rb +0 -110
  32. data/spec/active_interaction/filters/hash_filter_spec.rb +0 -129
  33. data/spec/active_interaction/filters/integer_filter_spec.rb +0 -104
  34. data/spec/active_interaction/filters/interface_filter_spec.rb +0 -461
  35. data/spec/active_interaction/filters/object_filter_spec.rb +0 -237
  36. data/spec/active_interaction/filters/record_filter_spec.rb +0 -206
  37. data/spec/active_interaction/filters/string_filter_spec.rb +0 -60
  38. data/spec/active_interaction/filters/symbol_filter_spec.rb +0 -46
  39. data/spec/active_interaction/filters/time_filter_spec.rb +0 -251
  40. data/spec/active_interaction/grouped_input_spec.rb +0 -17
  41. data/spec/active_interaction/hash_input_spec.rb +0 -58
  42. data/spec/active_interaction/i18n_spec.rb +0 -113
  43. data/spec/active_interaction/inputs_spec.rb +0 -266
  44. data/spec/active_interaction/integration/array_interaction_spec.rb +0 -88
  45. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -5
  46. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -5
  47. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -5
  48. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -18
  49. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -5
  50. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -76
  51. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -5
  52. data/spec/active_interaction/integration/interface_interaction_spec.rb +0 -19
  53. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -14
  54. data/spec/active_interaction/integration/record_integration_spec.rb +0 -5
  55. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -5
  56. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -5
  57. data/spec/active_interaction/integration/time_interaction_spec.rb +0 -88
  58. data/spec/active_interaction/modules/validation_spec.rb +0 -57
  59. data/spec/spec_helper.rb +0 -20
  60. data/spec/support/concerns.rb +0 -13
  61. data/spec/support/filters.rb +0 -227
  62. data/spec/support/interactions.rb +0 -124
@@ -1,251 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::TimeFilter, :filter do
4
- include_context 'filters'
5
- it_behaves_like 'a filter'
6
-
7
- shared_context 'with format' do
8
- let(:format) { '%d/%m/%Y %H:%M:%S %z' }
9
-
10
- before do
11
- options[:format] = format
12
- end
13
- end
14
-
15
- describe '#initialize' do
16
- context 'with a format' do
17
- before { options[:format] = '%T' }
18
-
19
- context 'with a time zone' do
20
- before do
21
- time_with_zone = double
22
-
23
- time_zone = double
24
- allow(time_zone).to receive(:at).and_return(time_with_zone)
25
-
26
- allow(Time).to receive(:zone).and_return(time_zone)
27
- end
28
-
29
- it 'raises an error' do
30
- expect do
31
- filter
32
- end.to raise_error(ActiveInteraction::InvalidFilterError)
33
- end
34
- end
35
- end
36
- end
37
-
38
- describe '#process' do
39
- let(:result) { filter.process(value, nil) }
40
-
41
- context 'with a Time' do
42
- let(:value) { Time.new }
43
-
44
- it 'returns the Time' do
45
- expect(result.value).to eql value
46
- end
47
- end
48
-
49
- context 'with a String' do
50
- let(:value) { '2011-12-13 14:15:16 +1718' }
51
-
52
- it 'returns a Time' do
53
- expect(result.value).to eql Time.parse(value)
54
- end
55
-
56
- context 'with a time zone' do
57
- before do
58
- klass = double
59
- allow(klass).to receive(:parse).with(value).and_return(nil)
60
-
61
- allow(filter).to receive(:matches?).and_return(false)
62
- allow(filter).to receive(:klass).and_return(klass)
63
- end
64
-
65
- it 'indicates an error the string is not parsable' do
66
- error = result.errors.first
67
-
68
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
69
- expect(error.type).to be :invalid_type
70
- end
71
- end
72
-
73
- context 'with format' do
74
- include_context 'with format'
75
-
76
- let(:value) { '13/12/2011 14:15:16 +1718' }
77
-
78
- it 'returns a Time' do
79
- expect(result.value).to eql Time.strptime(value, format)
80
- end
81
- end
82
- end
83
-
84
- context 'with an invalid String' do
85
- let(:value) { 'invalid' }
86
-
87
- it 'indicates an error' do
88
- error = result.errors.first
89
-
90
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
91
- expect(error.type).to be :invalid_type
92
- end
93
-
94
- context 'with format' do
95
- include_context 'with format'
96
-
97
- it 'indicates an error' do
98
- error = result.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
- context 'with an implicit String' do
107
- let(:value) do
108
- Class.new do
109
- def to_str
110
- '2011-12-13 14:15:16 +1718'
111
- end
112
- end.new
113
- end
114
-
115
- it 'returns a Time' do
116
- expect(result.value).to eql Time.parse(value)
117
- end
118
- end
119
-
120
- context 'with a blank String' do
121
- let(:value) do
122
- Class.new do
123
- def to_str
124
- ' '
125
- end
126
- end.new
127
- end
128
-
129
- context 'optional' do
130
- include_context 'optional'
131
-
132
- it 'returns the default' do
133
- expect(result.value).to eql options[:default]
134
- end
135
- end
136
-
137
- context 'required' do
138
- include_context 'required'
139
-
140
- it 'indicates an error' do
141
- error = result.errors.first
142
-
143
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
144
- expect(error.type).to be :missing
145
- end
146
- end
147
- end
148
-
149
- context 'with an Integer' do
150
- let(:value) { rand(1 << 16) }
151
-
152
- it 'returns the Time' do
153
- expect(result.value).to eql Time.at(value)
154
- end
155
- end
156
-
157
- context 'with an implicit Integer' do
158
- let(:value) do
159
- Class.new do
160
- def to_int
161
- @to_int ||= rand(1 << 16)
162
- end
163
- end.new
164
- end
165
-
166
- it 'returns the Time' do
167
- expect(result.value).to eql Time.at(value)
168
- end
169
- end
170
-
171
- context 'with a GroupedInput' do
172
- let(:year) { 2012 }
173
- let(:month) { 1 }
174
- let(:day) { 2 }
175
- let(:hour) { 3 }
176
- let(:min) { 4 }
177
- let(:sec) { 5 }
178
- let(:value) do
179
- ActiveInteraction::GroupedInput.new(
180
- '1' => year.to_s,
181
- '2' => month.to_s,
182
- '3' => day.to_s,
183
- '4' => hour.to_s,
184
- '5' => min.to_s,
185
- '6' => sec.to_s
186
- )
187
- end
188
-
189
- it 'returns a Time' do
190
- expect(
191
- result.value
192
- ).to eql Time.new(year, month, day, hour, min, sec)
193
- end
194
- end
195
-
196
- context 'with an invalid GroupedInput' do
197
- context 'empty' do
198
- let(:value) { ActiveInteraction::GroupedInput.new }
199
-
200
- it 'indicates an error' do
201
- error = result.errors.first
202
-
203
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
204
- expect(error.type).to be :invalid_type
205
- end
206
- end
207
-
208
- context 'partial inputs' do
209
- let(:value) do
210
- ActiveInteraction::GroupedInput.new(
211
- '2' => '1'
212
- )
213
- end
214
-
215
- it 'indicates an error' do
216
- error = result.errors.first
217
-
218
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
219
- expect(error.type).to be :invalid_type
220
- end
221
- end
222
- end
223
- end
224
-
225
- describe '#database_column_type' do
226
- it 'returns :datetime' do
227
- expect(filter.database_column_type).to be :datetime
228
- end
229
- end
230
-
231
- describe '#default' do
232
- context 'with a GroupedInput' do
233
- before do
234
- options[:default] = ActiveInteraction::GroupedInput.new(
235
- '1' => '2012',
236
- '2' => '1',
237
- '3' => '2',
238
- '4' => '3',
239
- '5' => '4',
240
- '6' => '5'
241
- )
242
- end
243
-
244
- it 'raises an error' do
245
- expect do
246
- filter.default(nil)
247
- end.to raise_error ActiveInteraction::InvalidDefaultError
248
- end
249
- end
250
- end
251
- end
@@ -1,17 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::GroupedInput do
4
- subject(:grouped_input) { described_class.new }
5
-
6
- it 'subclasses OpenStruct' do
7
- expect(grouped_input).to be_an OpenStruct
8
- end
9
-
10
- it 'responds to #[]' do
11
- expect { grouped_input[:key] }.to_not raise_error
12
- end
13
-
14
- it 'responds to #[]=' do
15
- expect { grouped_input[:key] = :value }.to_not raise_error
16
- end
17
- end
@@ -1,58 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::HashInput do
4
- subject(:input) do
5
- described_class.new(filter,
6
- value: value,
7
- error: error,
8
- children: children
9
- )
10
- end
11
-
12
- let(:filter) do
13
- ActiveInteraction::HashFilter.new(:h, &block)
14
- end
15
- let(:block) { proc { integer :i } }
16
- let(:value) { nil }
17
- let(:error) { nil }
18
- let(:children) { {} }
19
-
20
- describe '#errors' do
21
- context 'with no errors' do
22
- it 'returns an empty array' do
23
- expect(input.errors).to be_empty
24
- end
25
- end
26
-
27
- context 'with an error on the hash' do
28
- let(:error) { ActiveInteraction::Filter::Error.new(filter, :invalid_type) }
29
-
30
- it 'returns one error in the array' do
31
- expect(input.errors.size).to be 1
32
-
33
- error = input.errors.first
34
- expect(error.name).to be filter.name
35
- expect(error.type).to be :invalid_type
36
- end
37
- end
38
-
39
- context 'with children with errors' do
40
- let(:child_i) do
41
- filter = ActiveInteraction::IntegerFilter.new(:i)
42
- ActiveInteraction::Input.new(filter,
43
- value: nil,
44
- error: ActiveInteraction::Filter::Error.new(filter, :missing)
45
- )
46
- end
47
- let(:children) { { i: child_i } }
48
-
49
- it 'returns the error' do
50
- expect(input.errors.size).to be 1
51
-
52
- error = input.errors.first
53
- expect(error.name).to be :"#{filter.name}.i"
54
- expect(error.type).to be :missing
55
- end
56
- end
57
- end
58
- end
@@ -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