virtus2 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +18 -0
- data/Changelog.md +258 -0
- data/Gemfile +10 -0
- data/Guardfile +19 -0
- data/LICENSE +20 -0
- data/README.md +630 -0
- data/Rakefile +15 -0
- data/TODO.md +6 -0
- data/lib/virtus/attribute/accessor.rb +103 -0
- data/lib/virtus/attribute/boolean.rb +55 -0
- data/lib/virtus/attribute/builder.rb +182 -0
- data/lib/virtus/attribute/coercer.rb +45 -0
- data/lib/virtus/attribute/coercible.rb +20 -0
- data/lib/virtus/attribute/collection.rb +103 -0
- data/lib/virtus/attribute/default_value/from_callable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_clonable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
- data/lib/virtus/attribute/default_value.rb +51 -0
- data/lib/virtus/attribute/embedded_value.rb +67 -0
- data/lib/virtus/attribute/enum.rb +45 -0
- data/lib/virtus/attribute/hash.rb +130 -0
- data/lib/virtus/attribute/lazy_default.rb +18 -0
- data/lib/virtus/attribute/nullify_blank.rb +24 -0
- data/lib/virtus/attribute/strict.rb +26 -0
- data/lib/virtus/attribute.rb +245 -0
- data/lib/virtus/attribute_set.rb +240 -0
- data/lib/virtus/builder/hook_context.rb +51 -0
- data/lib/virtus/builder.rb +133 -0
- data/lib/virtus/class_inclusions.rb +48 -0
- data/lib/virtus/class_methods.rb +90 -0
- data/lib/virtus/coercer.rb +41 -0
- data/lib/virtus/configuration.rb +72 -0
- data/lib/virtus/const_missing_extensions.rb +18 -0
- data/lib/virtus/extensions.rb +105 -0
- data/lib/virtus/instance_methods.rb +218 -0
- data/lib/virtus/model.rb +68 -0
- data/lib/virtus/module_extensions.rb +88 -0
- data/lib/virtus/support/equalizer.rb +128 -0
- data/lib/virtus/support/options.rb +113 -0
- data/lib/virtus/support/type_lookup.rb +109 -0
- data/lib/virtus/value_object.rb +150 -0
- data/lib/virtus/version.rb +3 -0
- data/lib/virtus.rb +310 -0
- data/spec/integration/attributes_attribute_spec.rb +28 -0
- data/spec/integration/building_module_spec.rb +90 -0
- data/spec/integration/collection_member_coercion_spec.rb +96 -0
- data/spec/integration/custom_attributes_spec.rb +42 -0
- data/spec/integration/custom_collection_attributes_spec.rb +101 -0
- data/spec/integration/default_values_spec.rb +87 -0
- data/spec/integration/defining_attributes_spec.rb +86 -0
- data/spec/integration/embedded_value_spec.rb +50 -0
- data/spec/integration/extending_objects_spec.rb +35 -0
- data/spec/integration/hash_attributes_coercion_spec.rb +54 -0
- data/spec/integration/inheritance_spec.rb +42 -0
- data/spec/integration/injectible_coercers_spec.rb +48 -0
- data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
- data/spec/integration/overriding_virtus_spec.rb +46 -0
- data/spec/integration/required_attributes_spec.rb +25 -0
- data/spec/integration/struct_as_embedded_value_spec.rb +28 -0
- data/spec/integration/using_modules_spec.rb +55 -0
- data/spec/integration/value_object_with_custom_constructor_spec.rb +42 -0
- data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
- data/spec/integration/virtus/value_object_spec.rb +99 -0
- data/spec/shared/constants_helpers.rb +9 -0
- data/spec/shared/freeze_method_behavior.rb +40 -0
- data/spec/shared/idempotent_method_behaviour.rb +5 -0
- data/spec/shared/options_class_method.rb +19 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +43 -0
- data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +180 -0
- data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
- data/spec/unit/virtus/attribute/coerce_spec.rb +129 -0
- data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +105 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +74 -0
- data/spec/unit/virtus/attribute/collection/value_coerced_predicate_spec.rb +31 -0
- data/spec/unit/virtus/attribute/comparison_spec.rb +20 -0
- data/spec/unit/virtus/attribute/custom_collection_spec.rb +29 -0
- data/spec/unit/virtus/attribute/defined_spec.rb +20 -0
- data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +70 -0
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +91 -0
- data/spec/unit/virtus/attribute/get_spec.rb +32 -0
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +106 -0
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +92 -0
- data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/rename_spec.rb +16 -0
- data/spec/unit/virtus/attribute/required_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute/set_default_value_spec.rb +107 -0
- data/spec/unit/virtus/attribute/set_spec.rb +29 -0
- data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +47 -0
- data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/each_spec.rb +65 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +17 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +64 -0
- data/spec/unit/virtus/attribute_set/merge_spec.rb +34 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +71 -0
- data/spec/unit/virtus/attribute_spec.rb +229 -0
- data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
- data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
- data/spec/unit/virtus/class_methods/finalize_spec.rb +67 -0
- data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
- data/spec/unit/virtus/config_spec.rb +13 -0
- data/spec/unit/virtus/element_reader_spec.rb +21 -0
- data/spec/unit/virtus/element_writer_spec.rb +19 -0
- data/spec/unit/virtus/freeze_spec.rb +41 -0
- data/spec/unit/virtus/model_spec.rb +197 -0
- data/spec/unit/virtus/module_spec.rb +174 -0
- data/spec/unit/virtus/set_default_attributes_spec.rb +32 -0
- data/spec/unit/virtus/value_object_spec.rb +138 -0
- data/virtus2.gemspec +26 -0
- metadata +225 -0
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|