active_interaction 3.8.3 → 4.0.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/README.md +93 -107
  4. data/lib/active_interaction.rb +1 -7
  5. data/lib/active_interaction/base.rb +23 -26
  6. data/lib/active_interaction/concerns/active_modelable.rb +1 -3
  7. data/lib/active_interaction/concerns/active_recordable.rb +1 -6
  8. data/lib/active_interaction/concerns/hashable.rb +0 -1
  9. data/lib/active_interaction/concerns/missable.rb +0 -1
  10. data/lib/active_interaction/concerns/runnable.rb +6 -12
  11. data/lib/active_interaction/errors.rb +3 -6
  12. data/lib/active_interaction/filter.rb +51 -37
  13. data/lib/active_interaction/filter_column.rb +0 -3
  14. data/lib/active_interaction/filters/abstract_date_time_filter.rb +34 -36
  15. data/lib/active_interaction/filters/abstract_numeric_filter.rb +27 -17
  16. data/lib/active_interaction/filters/array_filter.rb +57 -36
  17. data/lib/active_interaction/filters/boolean_filter.rb +26 -12
  18. data/lib/active_interaction/filters/date_filter.rb +1 -2
  19. data/lib/active_interaction/filters/date_time_filter.rb +1 -2
  20. data/lib/active_interaction/filters/decimal_filter.rb +10 -28
  21. data/lib/active_interaction/filters/file_filter.rb +6 -5
  22. data/lib/active_interaction/filters/float_filter.rb +1 -2
  23. data/lib/active_interaction/filters/hash_filter.rb +37 -27
  24. data/lib/active_interaction/filters/integer_filter.rb +7 -8
  25. data/lib/active_interaction/filters/interface_filter.rb +48 -14
  26. data/lib/active_interaction/filters/object_filter.rb +23 -50
  27. data/lib/active_interaction/filters/record_filter.rb +10 -35
  28. data/lib/active_interaction/filters/string_filter.rb +21 -12
  29. data/lib/active_interaction/filters/symbol_filter.rb +13 -7
  30. data/lib/active_interaction/filters/time_filter.rb +19 -22
  31. data/lib/active_interaction/grouped_input.rb +0 -3
  32. data/lib/active_interaction/inputs.rb +89 -0
  33. data/lib/active_interaction/modules/input_processor.rb +1 -4
  34. data/lib/active_interaction/modules/validation.rb +9 -12
  35. data/lib/active_interaction/version.rb +1 -3
  36. data/spec/active_interaction/base_spec.rb +13 -41
  37. data/spec/active_interaction/concerns/active_modelable_spec.rb +0 -2
  38. data/spec/active_interaction/concerns/active_recordable_spec.rb +0 -2
  39. data/spec/active_interaction/concerns/hashable_spec.rb +1 -3
  40. data/spec/active_interaction/concerns/missable_spec.rb +0 -2
  41. data/spec/active_interaction/concerns/runnable_spec.rb +9 -13
  42. data/spec/active_interaction/errors_spec.rb +4 -25
  43. data/spec/active_interaction/filter_column_spec.rb +0 -2
  44. data/spec/active_interaction/filter_spec.rb +0 -2
  45. data/spec/active_interaction/filters/abstract_date_time_filter_spec.rb +1 -3
  46. data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -3
  47. data/spec/active_interaction/filters/array_filter_spec.rb +41 -15
  48. data/spec/active_interaction/filters/boolean_filter_spec.rb +58 -4
  49. data/spec/active_interaction/filters/date_filter_spec.rb +43 -3
  50. data/spec/active_interaction/filters/date_time_filter_spec.rb +43 -3
  51. data/spec/active_interaction/filters/decimal_filter_spec.rb +57 -3
  52. data/spec/active_interaction/filters/file_filter_spec.rb +1 -3
  53. data/spec/active_interaction/filters/float_filter_spec.rb +60 -4
  54. data/spec/active_interaction/filters/hash_filter_spec.rb +19 -9
  55. data/spec/active_interaction/filters/integer_filter_spec.rb +49 -7
  56. data/spec/active_interaction/filters/interface_filter_spec.rb +397 -24
  57. data/spec/active_interaction/filters/object_filter_spec.rb +23 -59
  58. data/spec/active_interaction/filters/record_filter_spec.rb +23 -49
  59. data/spec/active_interaction/filters/string_filter_spec.rb +15 -3
  60. data/spec/active_interaction/filters/symbol_filter_spec.rb +15 -3
  61. data/spec/active_interaction/filters/time_filter_spec.rb +65 -3
  62. data/spec/active_interaction/grouped_input_spec.rb +0 -2
  63. data/spec/active_interaction/i18n_spec.rb +3 -7
  64. data/spec/active_interaction/{modules/input_processor_spec.rb → inputs_spec.rb} +3 -5
  65. data/spec/active_interaction/integration/array_interaction_spec.rb +18 -22
  66. data/spec/active_interaction/integration/boolean_interaction_spec.rb +0 -2
  67. data/spec/active_interaction/integration/date_interaction_spec.rb +0 -2
  68. data/spec/active_interaction/integration/date_time_interaction_spec.rb +0 -2
  69. data/spec/active_interaction/integration/file_interaction_spec.rb +0 -2
  70. data/spec/active_interaction/integration/float_interaction_spec.rb +0 -2
  71. data/spec/active_interaction/integration/hash_interaction_spec.rb +0 -2
  72. data/spec/active_interaction/integration/integer_interaction_spec.rb +0 -2
  73. data/spec/active_interaction/integration/interface_interaction_spec.rb +1 -3
  74. data/spec/active_interaction/integration/object_interaction_spec.rb +0 -2
  75. data/spec/active_interaction/integration/string_interaction_spec.rb +0 -2
  76. data/spec/active_interaction/integration/symbol_interaction_spec.rb +0 -2
  77. data/spec/active_interaction/integration/time_interaction_spec.rb +14 -18
  78. data/spec/active_interaction/modules/validation_spec.rb +1 -3
  79. data/spec/spec_helper.rb +2 -6
  80. data/spec/support/concerns.rb +0 -2
  81. data/spec/support/filters.rb +13 -9
  82. data/spec/support/interactions.rb +22 -14
  83. metadata +77 -52
  84. data/lib/active_interaction/backports.rb +0 -59
  85. data/lib/active_interaction/filters/abstract_filter.rb +0 -19
  86. data/spec/active_interaction/filters/abstract_filter_spec.rb +0 -8
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  class ObjectThing
@@ -11,7 +9,8 @@ class ObjectThing
11
9
  raise 'error'
12
10
  end
13
11
  end
14
- class ObjectThings; end
12
+
13
+ class ObjectThings; end # rubocop:disable Lint/EmptyClass
15
14
  BackupObjectThing = ObjectThing
16
15
 
17
16
  describe ActiveInteraction::ObjectFilter, :filter do
@@ -24,86 +23,51 @@ describe ActiveInteraction::ObjectFilter, :filter do
24
23
 
25
24
  describe '#cast' do
26
25
  let(:value) { ObjectThing.new }
27
- let(:result) { filter.cast(value, nil) }
26
+ let(:result) { filter.send(:cast, value, nil) }
28
27
 
29
- context 'with class as a Class' do
28
+ context 'with an instance of the class' do
30
29
  it 'returns the instance' do
31
30
  expect(result).to eql value
32
31
  end
33
32
 
33
+ context 'with an instance that is a subclass' do
34
+ let(:subclass) { Class.new(ObjectThing) }
35
+ let(:value) { subclass.new }
36
+
37
+ it 'returns the instance' do
38
+ expect(result).to eql value
39
+ end
40
+ end
41
+
34
42
  it 'handles reconstantizing' do
35
43
  expect(result).to eql value
36
44
 
37
45
  Object.send(:remove_const, :ObjectThing)
38
- ObjectThing = BackupObjectThing
46
+ ObjectThing = BackupObjectThing # rubocop:disable Lint/ConstantDefinitionInBlock
39
47
  value = ObjectThing.new
40
48
 
41
- expect(filter.cast(value, nil)).to eql value
49
+ expect(filter.send(:cast, value, nil)).to eql value
42
50
  end
43
51
 
44
52
  it 'handles reconstantizing subclasses' do
45
53
  filter
46
54
 
47
55
  Object.send(:remove_const, :ObjectThing)
48
- ObjectThing = BackupObjectThing
49
- class SubObjectThing < ObjectThing; end
56
+ ObjectThing = BackupObjectThing # rubocop:disable Lint/ConstantDefinitionInBlock
57
+ class SubObjectThing < ObjectThing; end # rubocop:disable Lint/ConstantDefinitionInBlock
50
58
  value = SubObjectThing.new
51
59
 
52
- expect(filter.cast(value, nil)).to eql value
60
+ expect(filter.send(:cast, value, nil)).to eql value
53
61
  end
54
62
 
55
63
  context 'without the class available' do
56
64
  before { Object.send(:remove_const, :ObjectThing) }
57
- after { ObjectThing = BackupObjectThing }
65
+ after { ObjectThing = BackupObjectThing } # rubocop:disable Lint/ConstantDefinitionInBlock
58
66
 
59
67
  it 'does not raise an error on initialization' do
60
68
  expect { filter }.to_not raise_error
61
69
  end
62
70
  end
63
-
64
- context 'with bidirectional class comparisons' do
65
- let(:case_equality) { false }
66
- let(:class_equality) { false }
67
-
68
- before do
69
- allow(ObjectThing).to receive(:===).and_return(case_equality)
70
- allow(value).to receive(:is_a?).and_return(class_equality)
71
- end
72
-
73
- context 'without case or class equality' do
74
- it 'raises an error' do
75
- expect do
76
- result
77
- end.to raise_error ActiveInteraction::InvalidValueError
78
- end
79
- end
80
-
81
- context 'with case equality' do
82
- let(:case_equality) { true }
83
-
84
- it 'returns the instance' do
85
- expect(result).to eql value
86
- end
87
- end
88
-
89
- context 'with class equality' do
90
- let(:class_equality) { true }
91
-
92
- it 'returns the instance' do
93
- expect(result).to eql value
94
- end
95
- end
96
- end
97
- end
98
-
99
- context 'with class as a superclass' do
100
- before do
101
- options[:class] = ObjectThing.superclass
102
- end
103
-
104
- it 'returns the instance' do
105
- expect(result).to eql value
106
- end
107
71
  end
108
72
 
109
73
  context 'with class as a String' do
@@ -134,7 +98,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
134
98
  it 'raises an error' do
135
99
  expect do
136
100
  result
137
- end.to raise_error ActiveInteraction::InvalidClassError
101
+ end.to raise_error ActiveInteraction::InvalidNameError
138
102
  end
139
103
  end
140
104
 
@@ -161,7 +125,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
161
125
  end
162
126
  end
163
127
 
164
- context 'with a object of the correct class' do
128
+ context 'with an object of the correct class' do
165
129
  let(:value) { ObjectThing.new }
166
130
 
167
131
  it 'does not call the converter' do
@@ -170,7 +134,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
170
134
  end
171
135
  end
172
136
 
173
- context 'with a object is a subclass' do
137
+ context 'with an object that is a subclass' do
174
138
  let(:subclass) { Class.new(ObjectThing) }
175
139
  let(:value) { subclass.new }
176
140
 
@@ -223,7 +187,7 @@ describe ActiveInteraction::ObjectFilter, :filter do
223
187
 
224
188
  before do
225
189
  options[:default] = ObjectThing.new
226
- options[:converter] = ->(_) { nil }
190
+ options[:converter] = ->(_) {}
227
191
  end
228
192
 
229
193
  it 'raises an error' do
@@ -6,7 +6,7 @@ class RecordThing
6
6
  end
7
7
 
8
8
  def self.finder(_)
9
- @record2 ||= new
9
+ @finder ||= new
10
10
  end
11
11
 
12
12
  def self.finds_nil(_)
@@ -17,11 +17,12 @@ class RecordThing
17
17
  ''
18
18
  end
19
19
 
20
- def self.passthrough(x)
21
- x
20
+ def self.passthrough(obj)
21
+ obj
22
22
  end
23
23
  end
24
- class RecordThings; end
24
+
25
+ class RecordThings; end # rubocop:disable Lint/EmptyClass
25
26
  BackupRecordThing = RecordThing
26
27
 
27
28
  describe ActiveInteraction::RecordFilter, :filter do
@@ -38,78 +39,51 @@ describe ActiveInteraction::RecordFilter, :filter do
38
39
  end
39
40
 
40
41
  let(:value) { RecordThing.new }
41
- let(:result) { filter.cast(value, nil) }
42
+ let(:result) { filter.send(:cast, value, nil) }
42
43
 
43
- context 'with class as a Class' do
44
+ context 'with an instance of the class' do
44
45
  it 'returns the instance' do
45
46
  expect(result).to eql value
46
47
  end
47
48
 
49
+ context 'with an instance that is a subclass' do
50
+ let(:subclass) { Class.new(RecordThing) }
51
+ let(:value) { subclass.new }
52
+
53
+ it 'returns the instance' do
54
+ expect(result).to eql value
55
+ end
56
+ end
57
+
48
58
  it 'handles reconstantizing' do
49
59
  expect(result).to eql value
50
60
 
51
61
  Object.send(:remove_const, :RecordThing)
52
- RecordThing = BackupRecordThing
62
+ RecordThing = BackupRecordThing # rubocop:disable Lint/ConstantDefinitionInBlock
53
63
  value = RecordThing.new
54
64
 
55
- expect(filter.cast(value, nil)).to eql value
65
+ expect(filter.send(:cast, value, nil)).to eql value
56
66
  end
57
67
 
58
68
  it 'handles reconstantizing subclasses' do
59
69
  filter
60
70
 
61
71
  Object.send(:remove_const, :RecordThing)
62
- RecordThing = BackupRecordThing
63
- class SubRecordThing < RecordThing; end
72
+ RecordThing = BackupRecordThing # rubocop:disable Lint/ConstantDefinitionInBlock
73
+ class SubRecordThing < RecordThing; end # rubocop:disable Lint/ConstantDefinitionInBlock
64
74
  value = SubRecordThing.new
65
75
 
66
- expect(filter.cast(value, nil)).to eql value
76
+ expect(filter.send(:cast, value, nil)).to eql value
67
77
  end
68
78
 
69
79
  context 'without the class available' do
70
80
  before { Object.send(:remove_const, :RecordThing) }
71
- after { RecordThing = BackupRecordThing }
81
+ after { RecordThing = BackupRecordThing } # rubocop:disable Lint/ConstantDefinitionInBlock
72
82
 
73
83
  it 'does not raise an error on initialization' do
74
84
  expect { filter }.to_not raise_error
75
85
  end
76
86
  end
77
-
78
- context 'with bidirectional class comparisons' do
79
- let(:case_equality) { false }
80
- let(:class_equality) { false }
81
-
82
- before do
83
- options[:finder] = :passthrough
84
-
85
- allow(RecordThing).to receive(:===).and_return(case_equality)
86
- allow(value).to receive(:is_a?).and_return(class_equality)
87
- end
88
-
89
- context 'without case or class equality' do
90
- it 'raises an error' do
91
- expect do
92
- result
93
- end.to raise_error ActiveInteraction::InvalidValueError
94
- end
95
- end
96
-
97
- context 'with case equality' do
98
- let(:case_equality) { true }
99
-
100
- it 'returns the instance' do
101
- expect(result).to eql value
102
- end
103
- end
104
-
105
- context 'with class equality' do
106
- let(:class_equality) { true }
107
-
108
- it 'returns the instance' do
109
- expect(result).to eql value
110
- end
111
- end
112
- end
113
87
  end
114
88
 
115
89
  context 'with class as a superclass' do
@@ -150,7 +124,7 @@ describe ActiveInteraction::RecordFilter, :filter do
150
124
  it 'raises an error' do
151
125
  expect do
152
126
  result
153
- end.to raise_error ActiveInteraction::InvalidClassError
127
+ end.to raise_error ActiveInteraction::InvalidNameError
154
128
  end
155
129
  end
156
130
 
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ActiveInteraction::StringFilter, :filter do
@@ -13,7 +11,7 @@ describe ActiveInteraction::StringFilter, :filter do
13
11
  end
14
12
 
15
13
  describe '#cast' do
16
- let(:result) { filter.cast(value, nil) }
14
+ let(:result) { filter.send(:cast, value, nil) }
17
15
 
18
16
  context 'with a String' do
19
17
  let(:value) { SecureRandom.hex }
@@ -23,6 +21,20 @@ describe ActiveInteraction::StringFilter, :filter do
23
21
  end
24
22
  end
25
23
 
24
+ context 'with an implicit String' do
25
+ let(:value) do
26
+ Class.new do
27
+ def to_str
28
+ @to_str ||= SecureRandom.hex
29
+ end
30
+ end.new
31
+ end
32
+
33
+ it 'returns the String' do
34
+ expect(result).to eql value.to_str
35
+ end
36
+ end
37
+
26
38
  context 'with a strippable String' do
27
39
  let(:value) { " #{SecureRandom.hex} " }
28
40
 
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ActiveInteraction::SymbolFilter, :filter do
@@ -7,7 +5,7 @@ describe ActiveInteraction::SymbolFilter, :filter do
7
5
  it_behaves_like 'a filter'
8
6
 
9
7
  describe '#cast' do
10
- let(:result) { filter.cast(value, nil) }
8
+ let(:result) { filter.send(:cast, value, nil) }
11
9
 
12
10
  context 'with a Symbol' do
13
11
  let(:value) { SecureRandom.hex.to_sym }
@@ -17,6 +15,20 @@ describe ActiveInteraction::SymbolFilter, :filter do
17
15
  end
18
16
  end
19
17
 
18
+ context 'with an implicit Symbol' do
19
+ let(:value) do
20
+ Class.new do
21
+ def to_sym
22
+ :symbol
23
+ end
24
+ end.new
25
+ end
26
+
27
+ it 'returns a symbol' do
28
+ expect(result).to eql value.to_sym
29
+ end
30
+ end
31
+
20
32
  context 'with a String' do
21
33
  let(:value) { SecureRandom.hex }
22
34
 
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ActiveInteraction::TimeFilter, :filter do
@@ -37,7 +35,7 @@ describe ActiveInteraction::TimeFilter, :filter do
37
35
  end
38
36
 
39
37
  describe '#cast' do
40
- let(:result) { filter.cast(value, nil) }
38
+ let(:result) { filter.send(:cast, value, nil) }
41
39
 
42
40
  context 'with a Time' do
43
41
  let(:value) { Time.new }
@@ -85,6 +83,70 @@ describe ActiveInteraction::TimeFilter, :filter do
85
83
  end
86
84
  end
87
85
 
86
+ context 'with an implicit String' do
87
+ let(:value) do
88
+ Class.new do
89
+ def to_str
90
+ '2011-12-13 14:15:16 +1718'
91
+ end
92
+ end.new
93
+ end
94
+
95
+ it 'returns a Time' do
96
+ expect(result).to eql Time.parse(value)
97
+ end
98
+ end
99
+
100
+ context 'with a blank String' do
101
+ let(:value) do
102
+ Class.new do
103
+ def to_str
104
+ ' '
105
+ end
106
+ end.new
107
+ end
108
+
109
+ context 'optional' do
110
+ include_context 'optional'
111
+
112
+ it 'returns the default' do
113
+ expect(result).to eql options[:default]
114
+ end
115
+ end
116
+
117
+ context 'required' do
118
+ include_context 'required'
119
+
120
+ it 'raises an error' do
121
+ expect do
122
+ result
123
+ end.to raise_error ActiveInteraction::MissingValueError
124
+ end
125
+ end
126
+ end
127
+
128
+ context 'with an Integer' do
129
+ let(:value) { rand(1 << 16) }
130
+
131
+ it 'returns the Time' do
132
+ expect(result).to eql Time.at(value)
133
+ end
134
+ end
135
+
136
+ context 'with an implicit Integer' do
137
+ let(:value) do
138
+ Class.new do
139
+ def to_int
140
+ @to_int ||= rand(1 << 16)
141
+ end
142
+ end.new
143
+ end
144
+
145
+ it 'returns the Time' do
146
+ expect(result).to eql Time.at(value)
147
+ end
148
+ end
149
+
88
150
  context 'with a GroupedInput' do
89
151
  let(:year) { 2012 }
90
152
  let(:month) { 1 }
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ActiveInteraction::GroupedInput do
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ActiveInteraction do
@@ -20,7 +18,7 @@ end
20
18
  describe I18nInteraction do
21
19
  include_context 'interactions'
22
20
 
23
- TYPES = ActiveInteraction::Filter
21
+ TYPES = ActiveInteraction::Filter # rubocop:disable Lint/ConstantDefinitionInBlock
24
22
  .const_get(:CLASSES)
25
23
  .map { |slug, _| slug.to_s }
26
24
 
@@ -87,9 +85,7 @@ describe I18nInteraction do
87
85
  # This must appear before including the translation examples so that the
88
86
  # locale is available before it is assigned.
89
87
  locale = :hsilgne
90
- unless I18n.locale_available?(locale)
91
- I18n.config.available_locales = I18n.config.available_locales + [locale]
92
- end
88
+ I18n.config.available_locales = I18n.config.available_locales + [locale] unless I18n.locale_available?(locale)
93
89
  end
94
90
 
95
91
  include_examples 'translation', :hsilgne
@@ -100,7 +96,7 @@ describe I18nInteraction do
100
96
  errors: {
101
97
  messages: {
102
98
  invalid: 'is invalid'.reverse,
103
- invalid_type: "%{type} #{'is not a valid'.reverse}",
99
+ invalid_type: "%<type>s} #{'is not a valid'.reverse}",
104
100
  missing: 'missing'.reverse
105
101
  }
106
102
  },