active_interaction 3.8.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  },