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,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
@@ -1,88 +0,0 @@
1
- require 'spec_helper'
2
-
3
- TimeWithZone = Class.new do
4
- attr_reader :time
5
-
6
- def initialize(time)
7
- @time = time
8
- end
9
-
10
- def ==(other)
11
- !other.nil? && time == other.time
12
- end
13
-
14
- def at(*args)
15
- TimeWithZone.new(Time.at(*args) + 1)
16
- end
17
-
18
- def parse(*args)
19
- TimeWithZone.new(Time.parse(*args) + 1)
20
- rescue ArgumentError
21
- nil
22
- end
23
- end
24
-
25
- TimeInteraction = Class.new(TestInteraction) do
26
- time :a
27
- end
28
-
29
- describe TimeInteraction do
30
- include_context 'interactions'
31
- it_behaves_like 'an interaction', :time, -> { Time.now }
32
-
33
- context 'with a time zone' do
34
- let(:a) { nil }
35
-
36
- before do
37
- inputs[:a] = a
38
-
39
- allow(Time).to receive(:zone).and_return(TimeWithZone.new(0))
40
- end
41
-
42
- context 'with an integer' do
43
- let(:a) { rand(1 << 16) }
44
-
45
- it 'returns the correct value' do
46
- expect(result[:a]).to eq TimeWithZone.new(0).at(a)
47
- end
48
- end
49
-
50
- context 'with a string' do
51
- let(:a) { '2011-12-13T14:15:16Z' }
52
-
53
- it 'returns the correct value' do
54
- expect(result[:a]).to eq TimeWithZone.new(0).parse(a)
55
- end
56
- end
57
-
58
- context 'with an invalid String' do
59
- let(:a) { 'invalid' }
60
-
61
- it 'is invalid' do
62
- expect(outcome).to be_invalid
63
- end
64
- end
65
-
66
- context 'with a Time' do
67
- let(:a) { Time.now }
68
-
69
- it 'returns the correct value' do
70
- expect(result[:a]).to eql a
71
- end
72
- end
73
-
74
- context 'with a TimeZone' do
75
- let(:a) { TimeWithZone.new(Time.now) }
76
-
77
- it 'returns the correct value' do
78
- expect(result[:a]).to eql a
79
- end
80
-
81
- it 'handles time zone changes' do
82
- outcome
83
- allow(Time).to receive(:zone).and_return(nil)
84
- expect(described_class.run(inputs)).to be_invalid
85
- end
86
- end
87
- end
88
- end
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ActiveInteraction::Validation do
4
- describe '.validate(context, filters, inputs)' do
5
- let(:inputs) { {} }
6
- let(:filter) { ActiveInteraction::Filter.new(:name, {}) }
7
- let(:interaction) do
8
- name = filter.name
9
- klass = Class.new(ActiveInteraction::Base) { attr_writer(name) }
10
- klass.filters[name] = filter
11
- klass.new
12
- end
13
- let(:result) do
14
- described_class.validate(interaction, interaction.class.filters, inputs)
15
- end
16
-
17
- context 'no filters are given' do
18
- let(:interaction) { Class.new(ActiveInteraction::Base).new }
19
-
20
- it 'returns no errors' do
21
- expect(result).to eql []
22
- end
23
- end
24
-
25
- context 'filter returns no errors' do
26
- let(:inputs) { { name: 1 } }
27
-
28
- before do
29
- allow(filter).to receive(:process).and_return(ActiveInteraction::Input.new(filter, value: 1))
30
- end
31
-
32
- it 'returns no errors' do
33
- expect(result).to eql []
34
- end
35
- end
36
-
37
- context 'filter returns with errors' do
38
- before do
39
- allow(filter).to receive(:process).and_return(ActiveInteraction::Input.new(filter, error: exception))
40
- end
41
-
42
- context 'Filter::Error' do
43
- let(:filter) { ActiveInteraction::ArrayFilter.new(:name, [1.0, 'a']) { float } }
44
-
45
- let(:exception) { ActiveInteraction::Filter::Error.new(filter, :invalid_type) }
46
-
47
- it 'returns an :invalid_type error' do
48
- type = I18n.translate(
49
- "#{ActiveInteraction::Base.i18n_scope}.types.#{filter.class.slug}"
50
- )
51
-
52
- expect(result).to eql [[filter.name, :invalid_type, { type: type }]]
53
- end
54
- end
55
- end
56
- end
57
- end
data/spec/spec_helper.rb DELETED
@@ -1,20 +0,0 @@
1
- require 'i18n'
2
- I18n.config.enforce_available_locales = true if I18n.config.respond_to?(:enforce_available_locales)
3
-
4
- require 'active_interaction'
5
- require 'active_record'
6
-
7
- Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
8
-
9
- RSpec.configure do |config|
10
- config.run_all_when_everything_filtered = true
11
- config.filter_run_including :focus
12
-
13
- config.before(:suite) do
14
- if ActiveRecord.respond_to?(:index_nested_attribute_errors)
15
- ActiveRecord.index_nested_attribute_errors = false
16
- else
17
- ActiveRecord::Base.index_nested_attribute_errors = false
18
- end
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- shared_context 'concerns' do |concern|
2
- subject(:instance) { klass.new }
3
-
4
- let(:klass) do
5
- Class.new do
6
- include concern
7
-
8
- def self.name
9
- SecureRandom.hex
10
- end
11
- end
12
- end
13
- end
@@ -1,227 +0,0 @@
1
- shared_context 'filters' do
2
- subject(:filter) { described_class.new(name, options, &block) }
3
-
4
- let(:block) { nil }
5
- let(:name) { SecureRandom.hex.to_sym }
6
- let(:options) { {} }
7
-
8
- shared_context 'optional' do
9
- before do
10
- options[:default] = nil
11
- end
12
- end
13
-
14
- shared_context 'required' do
15
- before do
16
- options.delete(:default)
17
- end
18
- end
19
- end
20
-
21
- shared_examples_for 'a filter' do
22
- include_context 'filters'
23
-
24
- describe '.factory' do
25
- context 'with an invalid slug' do
26
- it 'raises an error' do
27
- expect do
28
- described_class.factory(:invalid)
29
- end.to raise_error ActiveInteraction::MissingFilterError
30
- end
31
- end
32
-
33
- context 'with a valid slug' do
34
- it 'returns a Filter' do
35
- expect(
36
- described_class.factory(described_class.slug)
37
- ).to eql described_class
38
- end
39
- end
40
- end
41
-
42
- describe '.slug' do
43
- it 'returns a symbol' do
44
- expect(described_class.slug).to be_a Symbol
45
- end
46
- end
47
-
48
- describe '#process' do
49
- let(:value) { nil }
50
-
51
- context 'optional' do
52
- include_context 'optional'
53
-
54
- it 'returns the default' do
55
- expect(filter.process(value, nil).value).to eql options[:default]
56
- end
57
- end
58
-
59
- context 'required' do
60
- include_context 'required'
61
-
62
- it 'indicates an error' do
63
- error = filter.process(value, nil).errors.first
64
-
65
- expect(error).to be_an_instance_of ActiveInteraction::Filter::Error
66
- expect(error.type).to be :missing
67
- end
68
-
69
- context 'with an invalid value' do
70
- let(:value) { Object.new }
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 :invalid_type
77
- end
78
- end
79
- end
80
-
81
- context 'with an invalid default' do
82
- before do
83
- options[:default] = Object.new
84
- end
85
-
86
- it 'raises an error' do
87
- expect do
88
- filter.process(value, nil)
89
- end.to raise_error ActiveInteraction::InvalidDefaultError
90
- end
91
- end
92
-
93
- # BasicObject is missing a lot of methods
94
- context 'with a BasicObject' do
95
- let(:value) { BasicObject.new }
96
-
97
- it 'indicates an error' do
98
- error = filter.process(value, nil).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
- describe '#default' do
107
- context 'optional' do
108
- include_context 'optional'
109
-
110
- it 'returns the default' do
111
- expect(filter.default(nil)).to eql options[:default]
112
- end
113
- end
114
-
115
- context 'required' do
116
- include_context 'required'
117
-
118
- it 'raises an error' do
119
- expect do
120
- filter.default(nil)
121
- end.to raise_error ActiveInteraction::NoDefaultError
122
- end
123
- end
124
-
125
- context 'with an invalid default' do
126
- before do
127
- options[:default] = Object.new
128
- end
129
-
130
- it 'raises an error' do
131
- expect do
132
- filter.default(nil)
133
- end.to raise_error ActiveInteraction::InvalidDefaultError
134
- end
135
- end
136
-
137
- context 'with a callable default' do
138
- include_context 'optional'
139
-
140
- before do
141
- default = options[:default]
142
- options[:default] = -> { default }
143
- end
144
-
145
- it 'returns the default' do
146
- expect(filter.default(nil)).to eql options[:default].call
147
- end
148
- end
149
-
150
- context 'with a callable default that takes an argument' do
151
- include_context 'optional'
152
-
153
- it 'returns the default' do
154
- default = options[:default]
155
-
156
- spec = self
157
- filter # Necessary to bring into scope for lambda.
158
- options[:default] = lambda do |this|
159
- spec.expect(this).to be filter
160
- default
161
- end
162
-
163
- expect(filter.default(nil)).to be default
164
- end
165
- end
166
- end
167
-
168
- describe '#desc' do
169
- it 'returns nil' do
170
- expect(filter.desc).to be_nil
171
- end
172
-
173
- context 'with a description' do
174
- let(:desc) { SecureRandom.hex }
175
-
176
- before do
177
- options[:desc] = desc
178
- end
179
-
180
- it 'returns the description' do
181
- expect(filter.desc).to eql desc
182
- end
183
- end
184
- end
185
-
186
- describe '#filters' do
187
- it 'returns Hash' do
188
- expect(filter.filters).to be_a Hash
189
- end
190
- end
191
-
192
- describe '#default?' do
193
- context 'optional' do
194
- include_context 'optional'
195
-
196
- it 'returns true' do
197
- expect(filter).to be_default
198
- end
199
- end
200
-
201
- context 'required' do
202
- include_context 'required'
203
-
204
- it 'returns false' do
205
- expect(filter).to_not be_default
206
- end
207
- end
208
- end
209
-
210
- describe '#name' do
211
- it 'returns the name' do
212
- expect(filter.name).to eql name
213
- end
214
- end
215
-
216
- describe '#options' do
217
- it 'returns the options' do
218
- expect(filter.options).to eql options
219
- end
220
- end
221
-
222
- describe '#database_column_type' do
223
- it 'returns a symbol' do
224
- expect(filter.database_column_type).to be_a Symbol
225
- end
226
- end
227
- end