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,196 +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
- describe ActiveInteraction::Errors do
11
- subject(:errors) { described_class.new(klass.new) }
12
-
13
- let(:klass) do
14
- Class.new(ActiveInteraction::Base) do
15
- string :attribute, defualt: nil
16
- array :array, defualt: nil
17
-
18
- def self.name
19
- @name ||= SecureRandom.hex
20
- end
21
- end
22
- end
23
-
24
- describe '#merge!' do
25
- let(:other) { described_class.new(klass.new) }
26
-
27
- context 'with an error' do
28
- before do
29
- other.add(:attribute)
30
- end
31
-
32
- it 'adds the error' do
33
- errors.merge!(other)
34
- expect(errors.messages[:attribute]).to eql ['is invalid']
35
- end
36
-
37
- it 'does not add duplicate errors' do
38
- other.add(:attribute)
39
- errors.merge!(other)
40
- expect(errors.messages[:attribute]).to eql ['is invalid']
41
- end
42
- end
43
-
44
- context 'with a detailed error' do
45
- context 'that is a symbol' do
46
- before do
47
- other.add(:attribute)
48
- end
49
-
50
- it 'adds the error' do
51
- errors.merge!(other)
52
- expect(errors.details[:attribute]).to eql [{ error: :invalid }]
53
- end
54
- end
55
-
56
- context 'that is a symbol on base' do
57
- before do
58
- other.add(:base)
59
- end
60
-
61
- it 'adds the error' do
62
- errors.merge!(other)
63
- expect(errors.details[:base]).to eql [{ error: :invalid }]
64
- end
65
- end
66
-
67
- context 'that is a string' do
68
- let(:message) { SecureRandom.hex }
69
-
70
- before do
71
- other.add(:base, message)
72
- end
73
-
74
- it 'adds the error' do
75
- errors.merge!(other)
76
- expect(errors.details[:base]).to eql [{ error: message }]
77
- end
78
- end
79
-
80
- context 'that uses the :message option' do
81
- let(:message) { SecureRandom.hex }
82
- let(:error_name) { :some_error }
83
-
84
- before do
85
- other.add(:base, error_name, message: message)
86
- end
87
-
88
- it 'adds the error' do
89
- errors.merge!(other)
90
- expect(errors.details[:base]).to eql [{ error: error_name }]
91
- expect(errors.messages[:base]).to eql [message]
92
- end
93
- end
94
- end
95
-
96
- context 'with an interpolated detailed error' do
97
- before do
98
- I18n.backend.store_translations('en',
99
- activemodel: {
100
- errors: {
101
- models: {
102
- klass.name => {
103
- attributes: {
104
- attribute: {
105
- invalid_type: 'is not a valid %<type>s'
106
- }
107
- }
108
- }
109
- }
110
- }
111
- }
112
- )
113
-
114
- other.add(:attribute, :invalid_type, type: nil)
115
- end
116
-
117
- it 'does not raise an error' do
118
- expect { errors.merge!(other) }.to_not raise_error
119
- end
120
- end
121
-
122
- context 'with nested index errors' do
123
- let(:other) { described_class.new(klass.new) }
124
-
125
- before do
126
- if ActiveRecord.respond_to?(:index_nested_attribute_errors)
127
- allow(ActiveRecord).to receive(:index_nested_attribute_errors).and_return(true)
128
- else
129
- allow(ActiveRecord::Base).to receive(:index_nested_attribute_errors).and_return(true)
130
- end
131
-
132
- other.add(:'array[0]')
133
- end
134
-
135
- it 'adds the error' do
136
- errors.merge!(other)
137
- expect(errors.messages[:'array[0]']).to eql ['is invalid']
138
- end
139
- end
140
-
141
- context 'with ActiveModel errors' do
142
- let(:other) { ActiveModel::Errors.new(klass.new) }
143
-
144
- it 'does not raise an error' do
145
- expect { errors.merge!(other) }.to_not raise_error
146
- end
147
-
148
- it 'merges messages' do
149
- message = SecureRandom.hex
150
- other.add(:base, message)
151
- errors.merge!(other)
152
- expect(errors.messages[:base]).to include message
153
- end
154
- end
155
-
156
- context 'with nested errors' do
157
- let(:a_klass) do
158
- Class.new(ActiveRecord::Base) do
159
- has_one :b
160
- accepts_nested_attributes_for :b
161
- end
162
- end
163
- let(:a) { A.create(b_attributes: { name: nil }) }
164
- let(:b_klass) do
165
- Class.new(ActiveRecord::Base) do
166
- belongs_to :a
167
-
168
- validates :name, presence: true
169
- end
170
- end
171
-
172
- before do
173
- # suppress create_table output
174
- allow($stdout).to receive(:puts)
175
- ActiveRecord::Schema.define do
176
- create_table(:as)
177
- create_table(:bs) do |t|
178
- t.column :a_id, :integer
179
- t.column :name, :string
180
- end
181
- end
182
-
183
- stub_const('A', a_klass)
184
- stub_const('B', b_klass)
185
- end
186
-
187
- it 'merges the nested errors' do
188
- a.valid?
189
- expect(a.errors.messages).to eq('b.name': ["can't be blank"])
190
- expect(a.errors.size).to be 1
191
- expect { errors.merge!(a.errors) }.to_not raise_error
192
- expect(errors.size).to be 1
193
- end
194
- end
195
- end
196
- end
@@ -1,87 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::Filter::Column do
4
- subject(:column) { described_class.intern(type) }
5
-
6
- let(:type) { :float }
7
-
8
- describe '.intern(type)' do
9
- it 'returns the same object for each type' do
10
- expect(described_class.intern(type)).to equal column
11
- end
12
-
13
- it 'returns different objects for different types' do
14
- expect(described_class.intern(:integer)).to_not equal column
15
- end
16
- end
17
-
18
- describe '.new(type)' do
19
- it 'is private' do
20
- expect { described_class.new(type) }.to raise_error NoMethodError
21
- end
22
- end
23
-
24
- describe '#limit' do
25
- it 'returns nil' do
26
- expect(column.limit).to be_nil
27
- end
28
- end
29
-
30
- describe '#type' do
31
- it 'returns the type' do
32
- expect(column.type).to eql type
33
- end
34
- end
35
-
36
- describe '#number?' do
37
- let(:number?) { column.number? }
38
-
39
- context 'type is' do
40
- context ':integer' do
41
- let(:type) { :integer }
42
-
43
- it 'returns true' do
44
- expect(number?).to be_truthy
45
- end
46
- end
47
-
48
- context ':float' do
49
- let(:type) { :float }
50
-
51
- it 'returns true' do
52
- expect(number?).to be_truthy
53
- end
54
- end
55
-
56
- context 'anything else' do
57
- let(:type) { :string }
58
-
59
- it 'returns false' do
60
- expect(number?).to be_falsey
61
- end
62
- end
63
- end
64
- end
65
-
66
- describe '#text?' do
67
- let(:text?) { column.text? }
68
-
69
- context 'type is' do
70
- context ':string' do
71
- let(:type) { :string }
72
-
73
- it 'returns true' do
74
- expect(text?).to be_truthy
75
- end
76
- end
77
-
78
- context 'anything else' do
79
- let(:type) { :float }
80
-
81
- it 'returns false' do
82
- expect(text?).to be_falsey
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,39 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::Filter, :filter do
4
- include_context 'filters'
5
-
6
- describe '#database_column_type' do
7
- it 'returns `:string`' do
8
- expect(filter.database_column_type).to be :string
9
- end
10
- end
11
-
12
- context 'with an unregistered subclass' do
13
- let(:klass) { Class.new(described_class) }
14
-
15
- describe '.slug' do
16
- it 'is nil' do
17
- expect(klass.slug).to be_nil
18
- end
19
- end
20
- end
21
-
22
- context 'with a registered subclass' do
23
- let(:slug) { SecureRandom.hex.to_sym }
24
- let(:described_class) do
25
- s = slug
26
- Class.new(ActiveInteraction::Filter) do
27
- register s
28
- end
29
- end
30
-
31
- it_behaves_like 'a filter'
32
-
33
- describe '.slug' do
34
- it 'returns the registered slug' do
35
- expect(described_class.slug).to eql slug
36
- end
37
- end
38
- end
39
- end
@@ -1,13 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::AbstractDateTimeFilter, :filter do
4
- include_context 'filters'
5
-
6
- describe '#process' do
7
- let(:value) { nil }
8
-
9
- it 'raises an error' do
10
- expect { filter.process(value, nil) }.to raise_error NameError
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::AbstractNumericFilter, :filter do
4
- include_context 'filters'
5
-
6
- describe '#process' do
7
- let(:value) { nil }
8
-
9
- it 'raises an error' do
10
- expect { filter.process(value, nil) }.to raise_error NameError
11
- end
12
- end
13
- end
@@ -1,245 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::ArrayFilter, :filter do
4
- include_context 'filters'
5
- it_behaves_like 'a filter'
6
-
7
- context 'with multiple nested filters' do
8
- let(:block) do
9
- proc do
10
- array
11
- array
12
- end
13
- end
14
-
15
- it 'raises an error' do
16
- expect { filter }.to raise_error ActiveInteraction::InvalidFilterError
17
- end
18
- end
19
-
20
- context 'with a nested name' do
21
- let(:block) { proc { array :a } }
22
-
23
- it 'raises an error' do
24
- expect { filter }.to raise_error ActiveInteraction::InvalidFilterError
25
- end
26
- end
27
-
28
- describe '#process' do
29
- let(:result) { filter.process(value, nil) }
30
-
31
- context 'with an Array' do
32
- let(:value) { [] }
33
-
34
- it 'returns an ArrayInput' do
35
- expect(result).to be_an_instance_of ActiveInteraction::ArrayInput
36
- end
37
-
38
- it 'returns the Array' do
39
- expect(result.value).to eql value
40
- end
41
-
42
- it 'has no children' do
43
- expect(result.children).to eql []
44
- end
45
- end
46
-
47
- context 'with an implicit Array' do
48
- let(:value) do
49
- Class.new do
50
- def to_ary
51
- [1, 2, 3]
52
- end
53
- end.new
54
- end
55
-
56
- it 'returns the Array' do
57
- expect(result.value).to eql value.to_ary
58
- end
59
- end
60
-
61
- context 'with a heterogenous Array' do
62
- let(:value) { [[], false, 0.0, {}, 0, '', :''] }
63
-
64
- it 'returns the Array' do
65
- expect(result.value).to eql value
66
- end
67
- end
68
-
69
- context 'with a nested filter where the value transforms' do
70
- let(:block) { proc { symbol } }
71
- let(:name) { :test }
72
- let(:value) { ['test'] }
73
-
74
- it 'returns the transformed value' do
75
- expect(result.value).to eql [:test]
76
- end
77
- end
78
-
79
- context 'with a nested filter' do
80
- let(:block) { proc { array } }
81
-
82
- context 'with an Array' do
83
- let(:child_value) { [] }
84
- let(:value) { [child_value, child_value] }
85
-
86
- it 'returns the Array' do
87
- expect(result.value).to eql value
88
- end
89
-
90
- it 'has children' do
91
- expect(result.children.size).to be 2
92
- result.children.each do |child|
93
- expect(child).to be_an_instance_of ActiveInteraction::ArrayInput
94
- expect(child.value).to eql child_value
95
- end
96
- end
97
-
98
- context 'given a nil value' do
99
- let(:value) { nil }
100
-
101
- it 'returns an error' do
102
- error = result.errors.first
103
-
104
- expect(result.value).to eql value
105
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
106
- expect(error.type).to be :missing
107
- end
108
- end
109
- end
110
-
111
- context 'with an Array of Arrays' do
112
- let(:value) { [[]] }
113
-
114
- it 'returns the Array' do
115
- expect(result.value).to eql value
116
- end
117
- end
118
-
119
- context 'with a heterogenous Array' do
120
- let(:value) { [[], false, 0.0] }
121
-
122
- it 'indicates an error' do
123
- error = result.errors.first
124
-
125
- expect(result.errors.size).to be 1
126
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
127
- expect(error.name).to be filter.name
128
- expect(error.type).to be :invalid_type
129
- end
130
-
131
- context 'when :index_errors is true' do
132
- before do
133
- options[:index_errors] = true
134
- end
135
-
136
- it 'shows the index of where the error occurred' do
137
- expect(result.errors.size).to be 2
138
-
139
- result.errors.each.with_index(1) do |error, i|
140
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
141
- expect(error.name).to be :"#{filter.name}[#{i}]"
142
- expect(error.type).to be :invalid_type
143
- end
144
- end
145
- end
146
-
147
- context 'when ActiveRecord.index_nested_attribute_errors is true' do
148
- before do
149
- if ActiveRecord.respond_to?(:index_nested_attribute_errors)
150
- allow(ActiveRecord).to receive(:index_nested_attribute_errors).and_return(true)
151
- else
152
- allow(ActiveRecord::Base).to receive(:index_nested_attribute_errors).and_return(true)
153
- end
154
- end
155
-
156
- it 'shows the index of where the error occurred' do
157
- expect(result.errors.size).to be 2
158
-
159
- result.errors.each.with_index(1) do |error, i|
160
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
161
- expect(error.name).to be :"#{filter.name}[#{i}]"
162
- expect(error.type).to be :invalid_type
163
- end
164
- end
165
-
166
- context 'when :index_errors is false' do
167
- before do
168
- options[:index_errors] = false
169
- end
170
-
171
- it 'does not attach the index' do
172
- error = result.errors.first
173
-
174
- expect(result.errors.size).to be 1
175
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
176
- expect(error.name).to be filter.name
177
- expect(error.type).to be :invalid_type
178
- end
179
- end
180
- end
181
- end
182
- end
183
-
184
- [
185
- %i[object class],
186
- %i[record class],
187
- %i[interface from]
188
- ].each do |(type, option)|
189
- context "with a nested #{type} filter" do
190
- let(:block) { proc { public_send(type) } }
191
- let(:name) { :objects }
192
- let(:value) { [''] }
193
-
194
- it 'does not raise an error' do
195
- expect { result }.to_not raise_error
196
- end
197
-
198
- it 'has a filter with the right key' do
199
- expect(filter.filters).to have_key(:'0')
200
- end
201
-
202
- it 'has a filter with the right option' do
203
- expect(filter.filters[:'0'].options).to have_key(option)
204
- end
205
-
206
- context 'with a class set' do
207
- let(:block) { proc { public_send(type, "#{option}": String) } }
208
-
209
- it "does not override the #{option}" do
210
- expect(filter.filters[:'0'].options[option]).to eql String
211
- end
212
- end
213
- end
214
- end
215
-
216
- context 'with a nested interface type' do
217
- context 'with the methods option set' do
218
- let(:block) { proc { public_send(:interface, methods: %i[to_s]) } }
219
-
220
- it 'has a filter with the right option' do
221
- expect(filter.filters[:'0'].options).to have_key(:methods)
222
- expect(filter.filters[:'0'].options[:methods]).to eql %i[to_s]
223
- end
224
- end
225
-
226
- context 'with another option set' do
227
- let(:block) { proc { public_send(:object, converter: :new) } }
228
- let(:name) { :objects }
229
-
230
- it 'has a filter with the right options' do
231
- expect(filter.filters[:'0'].options).to have_key(:class)
232
- expect(filter.filters[:'0'].options[:class]).to be :Object
233
- expect(filter.filters[:'0'].options).to have_key(:converter)
234
- expect(filter.filters[:'0'].options[:converter]).to be :new
235
- end
236
- end
237
- end
238
- end
239
-
240
- describe '#database_column_type' do
241
- it 'returns :string' do
242
- expect(filter.database_column_type).to be :string
243
- end
244
- end
245
- end
@@ -1,87 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::BooleanFilter, :filter do
4
- include_context 'filters'
5
- it_behaves_like 'a filter'
6
-
7
- describe '#process' do
8
- context 'falsey' do
9
- [false, '0', 'false', 'FALSE', 'off', 'OFF'].each do |value|
10
- it "returns false for #{value.inspect}" do
11
- expect(filter.process(value, nil).value).to be_falsey
12
- end
13
- end
14
-
15
- context 'with an implicit string' do
16
- let(:value) do
17
- Class.new do
18
- def to_str
19
- 'false'
20
- end
21
- end.new
22
- end
23
-
24
- it 'returns false' do
25
- expect(filter.process(value, nil).value).to be_falsey
26
- end
27
- end
28
- end
29
-
30
- context 'truthy' do
31
- [true, '1', 'true', 'TRUE', 'on', 'ON'].each do |value|
32
- it "returns true for #{value.inspect}" do
33
- expect(filter.process(value, nil).value).to be_truthy
34
- end
35
- end
36
-
37
- context 'with an implicit string' do
38
- let(:value) do
39
- Class.new do
40
- def to_str
41
- 'true'
42
- end
43
- end.new
44
- end
45
-
46
- it 'returns true' do
47
- expect(filter.process(value, nil).value).to be_truthy
48
- end
49
- end
50
- end
51
-
52
- context 'with a blank String' do
53
- let(:value) do
54
- Class.new do
55
- def to_str
56
- ' '
57
- end
58
- end.new
59
- end
60
-
61
- context 'optional' do
62
- include_context 'optional'
63
-
64
- it 'returns the default' do
65
- expect(filter.process(value, nil).value).to eql options[:default]
66
- end
67
- end
68
-
69
- context 'required' do
70
- include_context 'required'
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 :missing
77
- end
78
- end
79
- end
80
- end
81
-
82
- describe '#database_column_type' do
83
- it 'returns :boolean' do
84
- expect(filter.database_column_type).to be :boolean
85
- end
86
- end
87
- end