active_interaction 4.1.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/README.md +63 -28
  4. data/lib/active_interaction/array_input.rb +77 -0
  5. data/lib/active_interaction/base.rb +14 -98
  6. data/lib/active_interaction/concerns/active_recordable.rb +3 -3
  7. data/lib/active_interaction/concerns/missable.rb +2 -2
  8. data/lib/active_interaction/errors.rb +6 -88
  9. data/lib/active_interaction/exceptions.rb +47 -0
  10. data/lib/active_interaction/filter/column.rb +59 -0
  11. data/lib/active_interaction/filter/error.rb +40 -0
  12. data/lib/active_interaction/filter.rb +44 -53
  13. data/lib/active_interaction/filters/abstract_date_time_filter.rb +9 -6
  14. data/lib/active_interaction/filters/abstract_numeric_filter.rb +7 -3
  15. data/lib/active_interaction/filters/array_filter.rb +34 -6
  16. data/lib/active_interaction/filters/boolean_filter.rb +4 -3
  17. data/lib/active_interaction/filters/date_filter.rb +1 -1
  18. data/lib/active_interaction/filters/date_time_filter.rb +1 -1
  19. data/lib/active_interaction/filters/decimal_filter.rb +1 -1
  20. data/lib/active_interaction/filters/float_filter.rb +1 -1
  21. data/lib/active_interaction/filters/hash_filter.rb +23 -15
  22. data/lib/active_interaction/filters/integer_filter.rb +1 -1
  23. data/lib/active_interaction/filters/interface_filter.rb +12 -12
  24. data/lib/active_interaction/filters/object_filter.rb +9 -3
  25. data/lib/active_interaction/filters/record_filter.rb +21 -11
  26. data/lib/active_interaction/filters/string_filter.rb +1 -1
  27. data/lib/active_interaction/filters/symbol_filter.rb +1 -1
  28. data/lib/active_interaction/filters/time_filter.rb +4 -4
  29. data/lib/active_interaction/hash_input.rb +43 -0
  30. data/lib/active_interaction/input.rb +23 -0
  31. data/lib/active_interaction/inputs.rb +157 -46
  32. data/lib/active_interaction/locale/en.yml +0 -1
  33. data/lib/active_interaction/locale/fr.yml +0 -1
  34. data/lib/active_interaction/locale/it.yml +0 -1
  35. data/lib/active_interaction/locale/ja.yml +0 -1
  36. data/lib/active_interaction/locale/pt-BR.yml +0 -1
  37. data/lib/active_interaction/modules/validation.rb +6 -17
  38. data/lib/active_interaction/version.rb +1 -1
  39. data/lib/active_interaction.rb +43 -36
  40. data/spec/active_interaction/array_input_spec.rb +166 -0
  41. data/spec/active_interaction/base_spec.rb +15 -240
  42. data/spec/active_interaction/concerns/active_modelable_spec.rb +3 -3
  43. data/spec/active_interaction/concerns/active_recordable_spec.rb +7 -7
  44. data/spec/active_interaction/concerns/hashable_spec.rb +8 -8
  45. data/spec/active_interaction/concerns/missable_spec.rb +9 -9
  46. data/spec/active_interaction/concerns/runnable_spec.rb +34 -32
  47. data/spec/active_interaction/errors_spec.rb +60 -43
  48. data/spec/active_interaction/{filter_column_spec.rb → filter/column_spec.rb} +3 -10
  49. data/spec/active_interaction/filter_spec.rb +6 -6
  50. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +2 -2
  51. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +2 -2
  52. data/spec/active_interaction/filters/array_filter_spec.rb +99 -16
  53. data/spec/active_interaction/filters/boolean_filter_spec.rb +12 -11
  54. data/spec/active_interaction/filters/date_filter_spec.rb +32 -27
  55. data/spec/active_interaction/filters/date_time_filter_spec.rb +34 -29
  56. data/spec/active_interaction/filters/decimal_filter_spec.rb +20 -18
  57. data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
  58. data/spec/active_interaction/filters/float_filter_spec.rb +19 -17
  59. data/spec/active_interaction/filters/hash_filter_spec.rb +16 -18
  60. data/spec/active_interaction/filters/integer_filter_spec.rb +24 -22
  61. data/spec/active_interaction/filters/interface_filter_spec.rb +105 -82
  62. data/spec/active_interaction/filters/object_filter_spec.rb +52 -36
  63. data/spec/active_interaction/filters/record_filter_spec.rb +61 -39
  64. data/spec/active_interaction/filters/string_filter_spec.rb +7 -7
  65. data/spec/active_interaction/filters/symbol_filter_spec.rb +6 -6
  66. data/spec/active_interaction/filters/time_filter_spec.rb +57 -34
  67. data/spec/active_interaction/hash_input_spec.rb +58 -0
  68. data/spec/active_interaction/i18n_spec.rb +22 -17
  69. data/spec/active_interaction/inputs_spec.rb +167 -23
  70. data/spec/active_interaction/integration/array_interaction_spec.rb +3 -7
  71. data/spec/active_interaction/modules/validation_spec.rb +8 -31
  72. data/spec/spec_helper.rb +8 -0
  73. data/spec/support/concerns.rb +2 -2
  74. data/spec/support/filters.rb +27 -51
  75. data/spec/support/interactions.rb +4 -4
  76. metadata +40 -91
  77. data/lib/active_interaction/filter_column.rb +0 -57
@@ -15,19 +15,19 @@ BackupObjectThing = ObjectThing
15
15
 
16
16
  describe ActiveInteraction::ObjectFilter, :filter do
17
17
  include_context 'filters'
18
- it_behaves_like 'a filter'
19
-
20
18
  before do
21
19
  options[:class] = ObjectThing
22
20
  end
23
21
 
24
- describe '#cast' do
22
+ it_behaves_like 'a filter'
23
+
24
+ describe '#process' do
25
25
  let(:value) { ObjectThing.new }
26
- let(:result) { filter.send(:cast, value, nil) }
26
+ let(:result) { filter.process(value, nil) }
27
27
 
28
28
  context 'with an instance of the class' do
29
29
  it 'returns the instance' do
30
- expect(result).to eql value
30
+ expect(result.value).to eql value
31
31
  end
32
32
 
33
33
  context 'with an instance that is a subclass' do
@@ -35,18 +35,18 @@ describe ActiveInteraction::ObjectFilter, :filter do
35
35
  let(:value) { subclass.new }
36
36
 
37
37
  it 'returns the instance' do
38
- expect(result).to eql value
38
+ expect(result.value).to eql value
39
39
  end
40
40
  end
41
41
 
42
42
  it 'handles reconstantizing' do
43
- expect(result).to eql value
43
+ expect(result.value).to eql value
44
44
 
45
45
  Object.send(:remove_const, :ObjectThing)
46
46
  ObjectThing = BackupObjectThing # rubocop:disable Lint/ConstantDefinitionInBlock
47
47
  value = ObjectThing.new
48
48
 
49
- expect(filter.send(:cast, value, nil)).to eql value
49
+ expect(filter.process(value, nil).value).to eql value
50
50
  end
51
51
 
52
52
  it 'handles reconstantizing subclasses' do
@@ -57,11 +57,12 @@ describe ActiveInteraction::ObjectFilter, :filter do
57
57
  class SubObjectThing < ObjectThing; end # rubocop:disable Lint/ConstantDefinitionInBlock
58
58
  value = SubObjectThing.new
59
59
 
60
- expect(filter.send(:cast, value, nil)).to eql value
60
+ expect(filter.process(value, nil).value).to eql value
61
61
  end
62
62
 
63
63
  context 'without the class available' do
64
64
  before { Object.send(:remove_const, :ObjectThing) }
65
+
65
66
  after { ObjectThing = BackupObjectThing } # rubocop:disable Lint/ConstantDefinitionInBlock
66
67
 
67
68
  it 'does not raise an error on initialization' do
@@ -76,7 +77,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
76
77
  end
77
78
 
78
79
  it 'returns the instance' do
79
- expect(result).to eql value
80
+ expect(result.value).to eql value
80
81
  end
81
82
  end
82
83
 
@@ -86,7 +87,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
86
87
  before { options[:class] = ObjectThings }
87
88
 
88
89
  it 'returns the instance' do
89
- expect(result).to eql value
90
+ expect(result.value).to eql value
90
91
  end
91
92
  end
92
93
 
@@ -111,7 +112,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
111
112
  end
112
113
 
113
114
  it 'calls the class method' do
114
- expect(result).to eql ObjectThing.converter(value)
115
+ expect(result.value).to eql ObjectThing.converter(value)
115
116
  end
116
117
  end
117
118
 
@@ -121,7 +122,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
121
122
  end
122
123
 
123
124
  it 'gets called' do
124
- expect(result).to eql ObjectThing.converter(value)
125
+ expect(result.value).to eql ObjectThing.converter(value)
125
126
  end
126
127
  end
127
128
 
@@ -129,8 +130,13 @@ describe ActiveInteraction::ObjectFilter, :filter do
129
130
  let(:value) { ObjectThing.new }
130
131
 
131
132
  it 'does not call the converter' do
132
- expect(ObjectThing).to_not receive(:converter)
133
- expect(result).to eql value
133
+ allow(ObjectThing).to receive(:converter)
134
+ result.value
135
+ expect(ObjectThing).to_not have_received(:converter)
136
+ end
137
+
138
+ it 'returns the correct value' do
139
+ expect(result.value).to eql value
134
140
  end
135
141
  end
136
142
 
@@ -139,18 +145,29 @@ describe ActiveInteraction::ObjectFilter, :filter do
139
145
  let(:value) { subclass.new }
140
146
 
141
147
  it 'does not call the converter' do
142
- expect(subclass).to_not receive(:converter)
143
- expect(result).to eql value
148
+ allow(subclass).to receive(:converter)
149
+ result.value
150
+ expect(subclass).to_not have_received(:converter)
151
+ end
152
+
153
+ it 'returns the correct value' do
154
+ expect(result.value).to eql value
144
155
  end
145
156
  end
146
157
 
147
158
  context 'with a nil value' do
148
159
  let(:value) { nil }
160
+
149
161
  include_context 'optional'
150
162
 
151
163
  it 'returns nil' do
152
- expect(ObjectThing).to_not receive(:converter)
153
- expect(result).to eql value
164
+ allow(ObjectThing).to receive(:converter)
165
+ result.value
166
+ expect(ObjectThing).to_not have_received(:converter)
167
+ end
168
+
169
+ it 'returns the correct value' do
170
+ expect(result.value).to eql value
154
171
  end
155
172
  end
156
173
 
@@ -171,17 +188,14 @@ describe ActiveInteraction::ObjectFilter, :filter do
171
188
  options[:converter] = :converter_with_error
172
189
  end
173
190
 
174
- it 'raises an error' do
175
- expect do
176
- result
177
- end.to raise_error ActiveInteraction::InvalidValueError
191
+ it 'indicates an error' do
192
+ error = result.errors.first
193
+
194
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
195
+ expect(error.type).to be :invalid_type
178
196
  end
179
197
  end
180
- end
181
- end
182
198
 
183
- describe '#clean' do
184
- context 'with a converter' do
185
199
  context 'that returns a nil' do
186
200
  let(:value) { '' }
187
201
 
@@ -190,10 +204,11 @@ describe ActiveInteraction::ObjectFilter, :filter do
190
204
  options[:converter] = ->(_) {}
191
205
  end
192
206
 
193
- it 'raises an error' do
194
- expect do
195
- filter.clean(value, nil)
196
- end.to raise_error ActiveInteraction::InvalidValueError
207
+ it 'indicates an error' do
208
+ error = filter.process(value, nil).errors.first
209
+
210
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
211
+ expect(error.type).to be :invalid_type
197
212
  end
198
213
  end
199
214
 
@@ -204,10 +219,11 @@ describe ActiveInteraction::ObjectFilter, :filter do
204
219
  options[:converter] = ->(_) { 'invalid' }
205
220
  end
206
221
 
207
- it 'raises an error' do
208
- expect do
209
- filter.clean(value, nil)
210
- end.to raise_error ActiveInteraction::InvalidValueError
222
+ it 'indicates an error' do
223
+ error = filter.process(value, nil).errors.first
224
+
225
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
226
+ expect(error.type).to be :invalid_type
211
227
  end
212
228
  end
213
229
  end
@@ -215,7 +231,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
215
231
 
216
232
  describe '#database_column_type' do
217
233
  it 'returns :string' do
218
- expect(filter.database_column_type).to eql :string
234
+ expect(filter.database_column_type).to be :string
219
235
  end
220
236
  end
221
237
  end
@@ -14,11 +14,7 @@ class RecordThing
14
14
  end
15
15
 
16
16
  def self.finds_bad_value(_)
17
- ''
18
- end
19
-
20
- def self.passthrough(obj)
21
- obj
17
+ Object.new
22
18
  end
23
19
  end
24
20
 
@@ -27,23 +23,19 @@ BackupRecordThing = RecordThing
27
23
 
28
24
  describe ActiveInteraction::RecordFilter, :filter do
29
25
  include_context 'filters'
30
- it_behaves_like 'a filter'
31
-
32
26
  before do
33
27
  options[:class] = RecordThing
34
28
  end
35
29
 
36
- describe '#cast' do
37
- before do
38
- options[:finder] = :finder
39
- end
30
+ it_behaves_like 'a filter'
40
31
 
32
+ describe '#process' do
41
33
  let(:value) { RecordThing.new }
42
- let(:result) { filter.send(:cast, value, nil) }
34
+ let(:result) { filter.process(value, nil) }
43
35
 
44
36
  context 'with an instance of the class' do
45
37
  it 'returns the instance' do
46
- expect(result).to eql value
38
+ expect(result.value).to eql value
47
39
  end
48
40
 
49
41
  context 'with an instance that is a subclass' do
@@ -51,18 +43,18 @@ describe ActiveInteraction::RecordFilter, :filter do
51
43
  let(:value) { subclass.new }
52
44
 
53
45
  it 'returns the instance' do
54
- expect(result).to eql value
46
+ expect(result.value).to eql value
55
47
  end
56
48
  end
57
49
 
58
50
  it 'handles reconstantizing' do
59
- expect(result).to eql value
51
+ expect(result.value).to eql value
60
52
 
61
53
  Object.send(:remove_const, :RecordThing)
62
54
  RecordThing = BackupRecordThing # rubocop:disable Lint/ConstantDefinitionInBlock
63
55
  value = RecordThing.new
64
56
 
65
- expect(filter.send(:cast, value, nil)).to eql value
57
+ expect(filter.process(value, nil).value).to eql value
66
58
  end
67
59
 
68
60
  it 'handles reconstantizing subclasses' do
@@ -73,11 +65,12 @@ describe ActiveInteraction::RecordFilter, :filter do
73
65
  class SubRecordThing < RecordThing; end # rubocop:disable Lint/ConstantDefinitionInBlock
74
66
  value = SubRecordThing.new
75
67
 
76
- expect(filter.send(:cast, value, nil)).to eql value
68
+ expect(filter.process(value, nil).value).to eql value
77
69
  end
78
70
 
79
71
  context 'without the class available' do
80
72
  before { Object.send(:remove_const, :RecordThing) }
73
+
81
74
  after { RecordThing = BackupRecordThing } # rubocop:disable Lint/ConstantDefinitionInBlock
82
75
 
83
76
  it 'does not raise an error on initialization' do
@@ -92,7 +85,7 @@ describe ActiveInteraction::RecordFilter, :filter do
92
85
  end
93
86
 
94
87
  it 'returns the instance' do
95
- expect(result).to eql value
88
+ expect(result.value).to eql value
96
89
  end
97
90
  end
98
91
 
@@ -102,7 +95,7 @@ describe ActiveInteraction::RecordFilter, :filter do
102
95
  end
103
96
 
104
97
  it 'returns the instance' do
105
- expect(result).to eql value
98
+ expect(result.value).to eql value
106
99
  end
107
100
  end
108
101
 
@@ -112,7 +105,7 @@ describe ActiveInteraction::RecordFilter, :filter do
112
105
  before { options[:class] = RecordThings }
113
106
 
114
107
  it 'returns the instance' do
115
- expect(result).to eql value
108
+ expect(result.value).to eql value
116
109
  end
117
110
  end
118
111
 
@@ -129,48 +122,77 @@ describe ActiveInteraction::RecordFilter, :filter do
129
122
  end
130
123
 
131
124
  context 'with a value that does not match the class' do
132
- let(:value) { '' }
125
+ let(:value) { 1 }
133
126
 
134
- it 'calls the finder' do
135
- expect(result).to eql RecordThing.finder(value)
127
+ it 'calls the default finder' do
128
+ allow(RecordThing).to receive(:find)
129
+ result
130
+ expect(RecordThing).to have_received(:find).with(value)
136
131
  end
137
132
 
138
133
  context 'with a custom finder' do
134
+ before do
135
+ options[:finder] = :finder
136
+ end
137
+
139
138
  it 'calls the custom finder' do
140
- expect(result).to eql RecordThing.finder(value)
139
+ allow(RecordThing).to receive(:finder)
140
+ result
141
+ expect(RecordThing).to have_received(:finder).with(value)
141
142
  end
142
143
  end
143
- end
144
- end
145
144
 
146
- describe '#clean' do
147
- context 'with a value that does not match the class' do
148
145
  context 'that returns a nil' do
149
- let(:value) { '' }
146
+ let(:value) { 1 }
150
147
 
151
148
  before do
152
149
  options[:default] = RecordThing.new
153
150
  options[:finder] = :finds_nil
154
151
  end
155
152
 
156
- it 'raises an error' do
157
- expect do
158
- filter.clean(value, nil)
159
- end.to raise_error ActiveInteraction::InvalidValueError
153
+ it 'indicates an error' do
154
+ error = filter.process(value, nil).errors.first
155
+
156
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
157
+ expect(error.type).to be :invalid_type
160
158
  end
161
159
  end
162
160
 
163
161
  context 'that returns an invalid value' do
164
- let(:value) { '' }
162
+ let(:value) { 1 }
165
163
 
166
164
  before do
167
165
  options[:finder] = :finds_bad_value
168
166
  end
169
167
 
170
- it 'raises an error' do
171
- expect do
172
- filter.clean(value, nil)
173
- end.to raise_error ActiveInteraction::InvalidValueError
168
+ it 'indicates an error' do
169
+ error = filter.process(value, nil).errors.first
170
+
171
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
172
+ expect(error.type).to be :invalid_type
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'with a blank String' do
178
+ let(:value) { ' ' }
179
+
180
+ context 'optional' do
181
+ include_context 'optional'
182
+
183
+ it 'returns the default' do
184
+ expect(filter.process(value, nil).value).to eql options[:default]
185
+ end
186
+ end
187
+
188
+ context 'required' do
189
+ include_context 'required'
190
+
191
+ it 'indicates an error' do
192
+ error = filter.process(value, nil).errors.first
193
+
194
+ expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
195
+ expect(error.type).to be :missing
174
196
  end
175
197
  end
176
198
  end
@@ -178,7 +200,7 @@ describe ActiveInteraction::RecordFilter, :filter do
178
200
 
179
201
  describe '#database_column_type' do
180
202
  it 'returns :string' do
181
- expect(filter.database_column_type).to eql :string
203
+ expect(filter.database_column_type).to be :string
182
204
  end
183
205
  end
184
206
  end
@@ -10,14 +10,14 @@ describe ActiveInteraction::StringFilter, :filter do
10
10
  end
11
11
  end
12
12
 
13
- describe '#cast' do
14
- let(:result) { filter.send(:cast, value, nil) }
13
+ describe '#process' do
14
+ let(:result) { filter.process(value, nil) }
15
15
 
16
16
  context 'with a String' do
17
17
  let(:value) { SecureRandom.hex }
18
18
 
19
19
  it 'returns the String' do
20
- expect(result).to eql value
20
+ expect(result.value).to eql value
21
21
  end
22
22
  end
23
23
 
@@ -31,7 +31,7 @@ describe ActiveInteraction::StringFilter, :filter do
31
31
  end
32
32
 
33
33
  it 'returns the String' do
34
- expect(result).to eql value.to_str
34
+ expect(result.value).to eql value.to_str
35
35
  end
36
36
  end
37
37
 
@@ -39,14 +39,14 @@ describe ActiveInteraction::StringFilter, :filter do
39
39
  let(:value) { " #{SecureRandom.hex} " }
40
40
 
41
41
  it 'returns the stripped string' do
42
- expect(result).to eql value.strip
42
+ expect(result.value).to eql value.strip
43
43
  end
44
44
 
45
45
  context 'without strip' do
46
46
  include_context 'without strip'
47
47
 
48
48
  it 'returns the String' do
49
- expect(result).to eql value
49
+ expect(result.value).to eql value
50
50
  end
51
51
  end
52
52
  end
@@ -54,7 +54,7 @@ describe ActiveInteraction::StringFilter, :filter do
54
54
 
55
55
  describe '#database_column_type' do
56
56
  it 'returns :string' do
57
- expect(filter.database_column_type).to eql :string
57
+ expect(filter.database_column_type).to be :string
58
58
  end
59
59
  end
60
60
  end
@@ -4,14 +4,14 @@ describe ActiveInteraction::SymbolFilter, :filter do
4
4
  include_context 'filters'
5
5
  it_behaves_like 'a filter'
6
6
 
7
- describe '#cast' do
8
- let(:result) { filter.send(:cast, value, nil) }
7
+ describe '#process' do
8
+ let(:result) { filter.process(value, nil) }
9
9
 
10
10
  context 'with a Symbol' do
11
11
  let(:value) { SecureRandom.hex.to_sym }
12
12
 
13
13
  it 'returns the Symbol' do
14
- expect(result).to eql value
14
+ expect(result.value).to eql value
15
15
  end
16
16
  end
17
17
 
@@ -25,7 +25,7 @@ describe ActiveInteraction::SymbolFilter, :filter do
25
25
  end
26
26
 
27
27
  it 'returns a symbol' do
28
- expect(result).to eql value.to_sym
28
+ expect(result.value).to eql value.to_sym
29
29
  end
30
30
  end
31
31
 
@@ -33,14 +33,14 @@ describe ActiveInteraction::SymbolFilter, :filter do
33
33
  let(:value) { SecureRandom.hex }
34
34
 
35
35
  it 'returns a Symbol' do
36
- expect(result).to eql value.to_sym
36
+ expect(result.value).to eql value.to_sym
37
37
  end
38
38
  end
39
39
  end
40
40
 
41
41
  describe '#database_column_type' do
42
42
  it 'returns :string' do
43
- expect(filter.database_column_type).to eql :string
43
+ expect(filter.database_column_type).to be :string
44
44
  end
45
45
  end
46
46
  end