virtus2 2.0.1

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 (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +39 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +1 -0
  5. data/CONTRIBUTING.md +18 -0
  6. data/Changelog.md +258 -0
  7. data/Gemfile +10 -0
  8. data/Guardfile +19 -0
  9. data/LICENSE +20 -0
  10. data/README.md +630 -0
  11. data/Rakefile +15 -0
  12. data/TODO.md +6 -0
  13. data/lib/virtus/attribute/accessor.rb +103 -0
  14. data/lib/virtus/attribute/boolean.rb +55 -0
  15. data/lib/virtus/attribute/builder.rb +182 -0
  16. data/lib/virtus/attribute/coercer.rb +45 -0
  17. data/lib/virtus/attribute/coercible.rb +20 -0
  18. data/lib/virtus/attribute/collection.rb +103 -0
  19. data/lib/virtus/attribute/default_value/from_callable.rb +35 -0
  20. data/lib/virtus/attribute/default_value/from_clonable.rb +35 -0
  21. data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
  22. data/lib/virtus/attribute/default_value.rb +51 -0
  23. data/lib/virtus/attribute/embedded_value.rb +67 -0
  24. data/lib/virtus/attribute/enum.rb +45 -0
  25. data/lib/virtus/attribute/hash.rb +130 -0
  26. data/lib/virtus/attribute/lazy_default.rb +18 -0
  27. data/lib/virtus/attribute/nullify_blank.rb +24 -0
  28. data/lib/virtus/attribute/strict.rb +26 -0
  29. data/lib/virtus/attribute.rb +245 -0
  30. data/lib/virtus/attribute_set.rb +240 -0
  31. data/lib/virtus/builder/hook_context.rb +51 -0
  32. data/lib/virtus/builder.rb +133 -0
  33. data/lib/virtus/class_inclusions.rb +48 -0
  34. data/lib/virtus/class_methods.rb +90 -0
  35. data/lib/virtus/coercer.rb +41 -0
  36. data/lib/virtus/configuration.rb +72 -0
  37. data/lib/virtus/const_missing_extensions.rb +18 -0
  38. data/lib/virtus/extensions.rb +105 -0
  39. data/lib/virtus/instance_methods.rb +218 -0
  40. data/lib/virtus/model.rb +68 -0
  41. data/lib/virtus/module_extensions.rb +88 -0
  42. data/lib/virtus/support/equalizer.rb +128 -0
  43. data/lib/virtus/support/options.rb +113 -0
  44. data/lib/virtus/support/type_lookup.rb +109 -0
  45. data/lib/virtus/value_object.rb +150 -0
  46. data/lib/virtus/version.rb +3 -0
  47. data/lib/virtus.rb +310 -0
  48. data/spec/integration/attributes_attribute_spec.rb +28 -0
  49. data/spec/integration/building_module_spec.rb +90 -0
  50. data/spec/integration/collection_member_coercion_spec.rb +96 -0
  51. data/spec/integration/custom_attributes_spec.rb +42 -0
  52. data/spec/integration/custom_collection_attributes_spec.rb +101 -0
  53. data/spec/integration/default_values_spec.rb +87 -0
  54. data/spec/integration/defining_attributes_spec.rb +86 -0
  55. data/spec/integration/embedded_value_spec.rb +50 -0
  56. data/spec/integration/extending_objects_spec.rb +35 -0
  57. data/spec/integration/hash_attributes_coercion_spec.rb +54 -0
  58. data/spec/integration/inheritance_spec.rb +42 -0
  59. data/spec/integration/injectible_coercers_spec.rb +48 -0
  60. data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
  61. data/spec/integration/overriding_virtus_spec.rb +46 -0
  62. data/spec/integration/required_attributes_spec.rb +25 -0
  63. data/spec/integration/struct_as_embedded_value_spec.rb +28 -0
  64. data/spec/integration/using_modules_spec.rb +55 -0
  65. data/spec/integration/value_object_with_custom_constructor_spec.rb +42 -0
  66. data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
  67. data/spec/integration/virtus/value_object_spec.rb +99 -0
  68. data/spec/shared/constants_helpers.rb +9 -0
  69. data/spec/shared/freeze_method_behavior.rb +40 -0
  70. data/spec/shared/idempotent_method_behaviour.rb +5 -0
  71. data/spec/shared/options_class_method.rb +19 -0
  72. data/spec/spec_helper.rb +41 -0
  73. data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +43 -0
  74. data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
  75. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +180 -0
  76. data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
  77. data/spec/unit/virtus/attribute/coerce_spec.rb +129 -0
  78. data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
  79. data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +105 -0
  80. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +74 -0
  81. data/spec/unit/virtus/attribute/collection/value_coerced_predicate_spec.rb +31 -0
  82. data/spec/unit/virtus/attribute/comparison_spec.rb +20 -0
  83. data/spec/unit/virtus/attribute/custom_collection_spec.rb +29 -0
  84. data/spec/unit/virtus/attribute/defined_spec.rb +20 -0
  85. data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +70 -0
  86. data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +91 -0
  87. data/spec/unit/virtus/attribute/get_spec.rb +32 -0
  88. data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +106 -0
  89. data/spec/unit/virtus/attribute/hash/coerce_spec.rb +92 -0
  90. data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
  91. data/spec/unit/virtus/attribute/rename_spec.rb +16 -0
  92. data/spec/unit/virtus/attribute/required_predicate_spec.rb +19 -0
  93. data/spec/unit/virtus/attribute/set_default_value_spec.rb +107 -0
  94. data/spec/unit/virtus/attribute/set_spec.rb +29 -0
  95. data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
  96. data/spec/unit/virtus/attribute_set/append_spec.rb +47 -0
  97. data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +36 -0
  98. data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +36 -0
  99. data/spec/unit/virtus/attribute_set/each_spec.rb +65 -0
  100. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +17 -0
  101. data/spec/unit/virtus/attribute_set/element_set_spec.rb +64 -0
  102. data/spec/unit/virtus/attribute_set/merge_spec.rb +34 -0
  103. data/spec/unit/virtus/attribute_set/reset_spec.rb +71 -0
  104. data/spec/unit/virtus/attribute_spec.rb +229 -0
  105. data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
  106. data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
  107. data/spec/unit/virtus/class_methods/finalize_spec.rb +67 -0
  108. data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
  109. data/spec/unit/virtus/config_spec.rb +13 -0
  110. data/spec/unit/virtus/element_reader_spec.rb +21 -0
  111. data/spec/unit/virtus/element_writer_spec.rb +19 -0
  112. data/spec/unit/virtus/freeze_spec.rb +41 -0
  113. data/spec/unit/virtus/model_spec.rb +197 -0
  114. data/spec/unit/virtus/module_spec.rb +174 -0
  115. data/spec/unit/virtus/set_default_attributes_spec.rb +32 -0
  116. data/spec/unit/virtus/value_object_spec.rb +138 -0
  117. data/virtus2.gemspec +26 -0
  118. metadata +225 -0
@@ -0,0 +1,9 @@
1
+ module ConstantsHelpers
2
+
3
+ extend self
4
+
5
+ # helper to remove constants after test-runs.
6
+ def undef_constant(mod, constant_name)
7
+ mod.send(:remove_const, constant_name)
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ shared_examples_for 'a #freeze method' do
2
+ let(:sample_exception) do
3
+ begin
4
+ object.dup.freeze.instance_variable_set(:@foo, :bar)
5
+ rescue => exception
6
+ exception
7
+ end
8
+ end
9
+
10
+ let(:expected_exception_class) do
11
+ # Ruby 1.8 blows up with TypeError Ruby 1.9 with RuntimeError
12
+ sample_exception.class
13
+ end
14
+
15
+ let(:expected_exception_message) do
16
+ # Ruby 1.8 blows up with a different message than Ruby 1.9
17
+ sample_exception.message
18
+ end
19
+
20
+ it_should_behave_like 'an idempotent method'
21
+
22
+ it 'returns object' do
23
+ is_expected.to be(object)
24
+ end
25
+
26
+ it 'prevents future modifications' do
27
+ subject
28
+ expectation = raise_error(expected_exception_class,expected_exception_message)
29
+ expect { object.instance_variable_set(:@foo, :bar) }.to(expectation)
30
+ end
31
+
32
+ describe '#frozen?' do
33
+ subject { super().frozen? }
34
+ it { is_expected.to be(true) }
35
+ end
36
+
37
+ it 'allows to access attribute' do
38
+ expect(subject.name).to eql('John')
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ shared_examples_for 'an idempotent method' do
2
+ it 'is idempotent' do
3
+ is_expected.to equal(subject)
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ shared_examples_for 'an options class method' do
2
+ context 'with no argument' do
3
+ subject { object.send(method) }
4
+
5
+ it { is_expected.to be(default) }
6
+ end
7
+
8
+ context 'with a default value' do
9
+ subject { object.send(method, value) }
10
+
11
+ let(:value) { mock('value') }
12
+
13
+ it { is_expected.to equal(object) }
14
+
15
+ it 'sets the default value for the class method' do
16
+ expect { subject }.to change { object.send(method) }.from(default).to(value)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ if RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '3.0'
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ end
5
+
6
+ require 'rspec'
7
+ require 'bogus/rspec'
8
+ require 'virtus'
9
+
10
+ module Virtus
11
+ def self.warn(*)
12
+ # shut up in tests
13
+ end
14
+ end
15
+
16
+ ENV['TZ'] = 'UTC'
17
+
18
+ # require spec support files and shared behavior
19
+ Dir[File.expand_path('../shared/**/*.rb', __FILE__)].each { |file| require file }
20
+
21
+ RSpec.configure do |config|
22
+ # Remove anonymous- and example- Attribute classes from Attribute descendants
23
+ config.after :all do
24
+ stack = [ Virtus::Attribute ]
25
+ while klass = stack.pop
26
+ klass.descendants.delete_if do |descendant|
27
+ descendant.name.nil? || descendant.name.empty? || descendant.name.start_with?('Examples::')
28
+ end
29
+ stack.concat(klass.descendants)
30
+ end
31
+ end
32
+
33
+ # Remove constants in the Example-Module
34
+ config.after :each do
35
+ if defined?(Examples)
36
+ Examples.constants.each do |const_name|
37
+ ConstantsHelpers.undef_constant(Examples, const_name)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Boolean, '#coerce' do
4
+ subject { object.coerce(input) }
5
+
6
+ let(:object) { described_class.build('Boolean', options) }
7
+ let(:options) { {} }
8
+
9
+ context 'when strict is turned off' do
10
+ context 'with a truthy value' do
11
+ let(:input) { 1 }
12
+
13
+ it { is_expected.to be(true) }
14
+ end
15
+
16
+ context 'with a falsy value' do
17
+ let(:input) { 0 }
18
+
19
+ it { is_expected.to be(false) }
20
+ end
21
+ end
22
+
23
+ context 'when strict is turned on' do
24
+ let(:options) { { :strict => true } }
25
+
26
+ context 'with a coercible input' do
27
+ let(:input) { 1 }
28
+
29
+ it { is_expected.to be(true) }
30
+ end
31
+
32
+ context 'with a non-coercible input' do
33
+ let(:input) { 'no idea if true or false' }
34
+
35
+ it 'raises coercion error' do
36
+ expect { subject }.to raise_error(
37
+ Virtus::CoercionError,
38
+ /Failed to coerce "no idea if true or false"/
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Boolean, '#value_coerced?' do
4
+ subject { object.value_coerced?(input) }
5
+
6
+ let(:object) { described_class.build('Boolean') }
7
+
8
+ context 'when input is true' do
9
+ let(:input) { true }
10
+
11
+ it { is_expected.to be(true) }
12
+ end
13
+
14
+ context 'when input is false' do
15
+ let(:input) { false }
16
+
17
+ it { is_expected.to be(true) }
18
+ end
19
+
20
+ context 'when input is not coerced' do
21
+ let(:input) { 1 }
22
+
23
+ it { is_expected.to be(false) }
24
+ end
25
+ end
@@ -0,0 +1,180 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute, '.build' do
4
+ subject { described_class.build(type, options.merge(:name => name)) }
5
+
6
+ let(:name) { :test }
7
+ let(:type) { String }
8
+ let(:options) { {} }
9
+
10
+ shared_examples_for 'a valid attribute instance' do
11
+ it { is_expected.to be_instance_of(Virtus::Attribute) }
12
+
13
+ it { is_expected.to be_frozen }
14
+ end
15
+
16
+ context 'without options' do
17
+ it_behaves_like 'a valid attribute instance'
18
+
19
+ it { is_expected.to be_coercible }
20
+ it { is_expected.to be_public_reader }
21
+ it { is_expected.to be_public_writer }
22
+ it { is_expected.not_to be_lazy }
23
+
24
+ it 'sets up a coercer' do
25
+ expect(subject.options[:coerce]).to be(true)
26
+ expect(subject.coercer).to be_instance_of(Virtus::Attribute::Coercer)
27
+ end
28
+ end
29
+
30
+ context 'when name is passed as a string' do
31
+ let(:name) { 'something' }
32
+
33
+ describe '#name' do
34
+ subject { super().name }
35
+ it { is_expected.to be(:something) }
36
+ end
37
+ end
38
+
39
+ context 'when coercion is turned off in options' do
40
+ let(:options) { { :coerce => false } }
41
+
42
+ it_behaves_like 'a valid attribute instance'
43
+
44
+ it { is_expected.not_to be_coercible }
45
+ end
46
+
47
+ context 'when options specify reader visibility' do
48
+ let(:options) { { :reader => :private } }
49
+
50
+ it_behaves_like 'a valid attribute instance'
51
+
52
+ it { is_expected.not_to be_public_reader }
53
+ it { is_expected.to be_public_writer }
54
+ end
55
+
56
+ context 'when options specify writer visibility' do
57
+ let(:options) { { :writer => :private } }
58
+
59
+ it_behaves_like 'a valid attribute instance'
60
+
61
+ it { is_expected.to be_public_reader }
62
+ it { is_expected.not_to be_public_writer }
63
+ end
64
+
65
+ context 'when options specify lazy accessor' do
66
+ let(:options) { { :lazy => true } }
67
+
68
+ it_behaves_like 'a valid attribute instance'
69
+
70
+ it { is_expected.to be_lazy }
71
+ end
72
+
73
+ context 'when options specify strict mode' do
74
+ let(:options) { { :strict => true } }
75
+
76
+ it_behaves_like 'a valid attribute instance'
77
+
78
+ it { is_expected.to be_strict }
79
+ end
80
+
81
+ context 'when options specify nullify blank mode' do
82
+ let(:options) { { :nullify_blank => true } }
83
+
84
+ it_behaves_like 'a valid attribute instance'
85
+
86
+ it { is_expected.to be_nullify_blank }
87
+ end
88
+
89
+ context 'when type is a string' do
90
+ let(:type) { 'Integer' }
91
+
92
+ it_behaves_like 'a valid attribute instance'
93
+
94
+ describe '#type' do
95
+ subject { super().type }
96
+ it { is_expected.to be(Axiom::Types::Integer) }
97
+ end
98
+ end
99
+
100
+ context 'when type is a range' do
101
+ let(:type) { 0..10 }
102
+
103
+ it_behaves_like 'a valid attribute instance'
104
+
105
+ describe '#type' do
106
+ subject { super().type }
107
+ it { is_expected.to be(Axiom::Types.infer(Range)) }
108
+ end
109
+ end
110
+
111
+ context 'when type is a symbol of an existing class constant' do
112
+ let(:type) { :String }
113
+
114
+ it_behaves_like 'a valid attribute instance'
115
+
116
+ describe '#type' do
117
+ subject { super().type }
118
+ it { is_expected.to be(Axiom::Types::String) }
119
+ end
120
+ end
121
+
122
+ context 'when type is an axiom type' do
123
+ let(:type) { Axiom::Types::Integer }
124
+
125
+ it_behaves_like 'a valid attribute instance'
126
+
127
+ describe '#type' do
128
+ subject { super().type }
129
+ it { is_expected.to be(type) }
130
+ end
131
+ end
132
+
133
+ context 'when custom attribute class exists for a given primitive' do
134
+ let(:type) { Class.new }
135
+ let(:attribute) { Class.new(Virtus::Attribute) }
136
+
137
+ before do
138
+ attribute.primitive(type)
139
+ end
140
+
141
+ it { is_expected.to be_instance_of(attribute) }
142
+
143
+ describe '#type' do
144
+ subject { super().type }
145
+ it { is_expected.to be(Axiom::Types::Object) }
146
+ end
147
+ end
148
+
149
+ context 'when custom attribute class exists for a given array with member coercion defined' do
150
+ let(:type) { Class.new(Array)[String] }
151
+ let(:attribute) { Class.new(Virtus::Attribute) }
152
+
153
+ before do
154
+ attribute.primitive(type.class)
155
+ end
156
+
157
+ it { is_expected.to be_instance_of(attribute) }
158
+
159
+ describe '#type' do
160
+ subject { super().type }
161
+ it { is_expected.to be < Axiom::Types::Collection }
162
+ end
163
+ end
164
+
165
+ context 'when custom collection-like attribute class exists for a given enumerable primitive' do
166
+ let(:type) { Class.new { include Enumerable } }
167
+ let(:attribute) { Class.new(Virtus::Attribute::Collection) }
168
+
169
+ before do
170
+ attribute.primitive(type)
171
+ end
172
+
173
+ it { is_expected.to be_instance_of(attribute) }
174
+
175
+ describe '#type' do
176
+ subject { super().type }
177
+ it { is_expected.to be < Axiom::Types::Collection }
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute, '.coerce' do
4
+ subject { described_class.coerce }
5
+
6
+ after :all do
7
+ described_class.coerce(true)
8
+ end
9
+
10
+ context 'with a value' do
11
+ it 'sets the value and return self' do
12
+ expect(described_class.coerce(false)).to be(described_class)
13
+ expect(subject).to be(false)
14
+ end
15
+ end
16
+
17
+ context 'when it is set to true' do
18
+ before do
19
+ described_class.coerce(true)
20
+ end
21
+
22
+ it { is_expected.to be(true) }
23
+ end
24
+
25
+ context 'when it is set to false' do
26
+ before do
27
+ described_class.coerce(false)
28
+ end
29
+
30
+ it { is_expected.to be(false) }
31
+ end
32
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute, '#coerce' do
4
+ subject { object.coerce(input) }
5
+
6
+ fake(:coercer) { Virtus::Attribute::Coercer }
7
+
8
+ let(:object) {
9
+ described_class.build(String,
10
+ :coercer => coercer, :strict => strict, :required => required, :nullify_blank => nullify_blank)
11
+ }
12
+
13
+ let(:required) { true }
14
+ let(:nullify_blank) { false }
15
+ let(:input) { 1 }
16
+ let(:output) { '1' }
17
+
18
+ context 'when strict mode is turned off' do
19
+ let(:strict) { false }
20
+
21
+ it 'uses coercer to coerce the input value' do
22
+ mock(coercer).call(input) { output }
23
+
24
+ expect(subject).to be(output)
25
+
26
+ expect(coercer).to have_received.call(input)
27
+ end
28
+ end
29
+
30
+ context 'when strict mode is turned on' do
31
+ let(:strict) { true }
32
+
33
+ it 'uses coercer to coerce the input value' do
34
+ mock(coercer).call(input) { output }
35
+ mock(coercer).success?(String, output) { true }
36
+
37
+ expect(subject).to be(output)
38
+
39
+ expect(coercer).to have_received.call(input)
40
+ expect(coercer).to have_received.success?(String, output)
41
+ end
42
+
43
+ context 'when attribute is not required and input is nil' do
44
+ let(:required) { false }
45
+ let(:input) { nil }
46
+
47
+ it 'returns nil' do
48
+ mock(coercer).call(input) { input }
49
+ mock(coercer).success?(String, input) { false }
50
+
51
+ expect(subject).to be(nil)
52
+
53
+ expect(coercer).to have_received.call(input)
54
+ expect(coercer).to have_received.success?(String, input)
55
+ end
56
+ end
57
+
58
+ context 'when attribute is required and input is nil' do
59
+ let(:input) { nil }
60
+
61
+ it 'returns raises error' do
62
+ mock(coercer).call(input) { input }
63
+ mock(coercer).success?(String, input) { false }
64
+
65
+ expect { subject }.to raise_error(Virtus::CoercionError)
66
+
67
+ expect(coercer).to have_received.call(input)
68
+ expect(coercer).to have_received.success?(String, input)
69
+ end
70
+ end
71
+
72
+ it 'raises error when input was not coerced' do
73
+ mock(coercer).call(input) { input }
74
+ mock(coercer).success?(String, input) { false }
75
+
76
+ expect { subject }.to raise_error(Virtus::CoercionError)
77
+
78
+ expect(coercer).to have_received.call(input)
79
+ expect(coercer).to have_received.success?(String, input)
80
+ end
81
+ end
82
+
83
+ context 'when the input is an empty String' do
84
+ let(:input) { '' }
85
+ let(:output) { '' }
86
+
87
+ context 'when nullify_blank is turned on' do
88
+ let(:nullify_blank) { true }
89
+ let(:strict) { false }
90
+ let(:require) { false }
91
+
92
+ it 'returns nil' do
93
+ mock(coercer).call(input) { input }
94
+ mock(coercer).success?(String, input) { false }
95
+
96
+ expect(subject).to be_nil
97
+
98
+ expect(coercer).to have_received.call(input)
99
+ expect(coercer).to have_received.success?(String, input)
100
+ end
101
+
102
+ it 'returns the ouput if it was coerced' do
103
+ mock(coercer).call(input) { output }
104
+ mock(coercer).success?(String, output) { true }
105
+
106
+ expect(subject).to be(output)
107
+
108
+ expect(coercer).to have_received.call(input)
109
+ expect(coercer).to have_received.success?(String, output)
110
+ end
111
+ end
112
+
113
+ context 'when both nullify_blank and strict are turned on' do
114
+ let(:nullify_blank) { true }
115
+ let(:strict) { true }
116
+
117
+ it 'does not raises an coercion error' do
118
+ mock(coercer).call(input) { input }
119
+ mock(coercer).success?(String, input) { false }
120
+
121
+ expect { subject }.not_to raise_error
122
+ expect(subject).to be_nil
123
+
124
+ expect(coercer).to have_received.call(input)
125
+ expect(coercer).to have_received.success?(String, input)
126
+ end
127
+ end
128
+ end
129
+ end