virtus 1.0.3 → 1.0.4
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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -5
- data/Changelog.md +7 -1
- data/Gemfile +14 -5
- data/README.md +40 -19
- data/Rakefile +13 -3
- data/lib/virtus/configuration.rb +10 -5
- data/lib/virtus/version.rb +1 -1
- data/spec/integration/collection_member_coercion_spec.rb +34 -13
- data/spec/integration/hash_attributes_coercion_spec.rb +5 -5
- data/spec/shared/freeze_method_behavior.rb +5 -2
- data/spec/shared/idempotent_method_behaviour.rb +1 -1
- data/spec/shared/options_class_method.rb +3 -3
- data/spec/spec_helper.rb +3 -18
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +3 -3
- data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +3 -3
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +48 -24
- data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +2 -2
- data/spec/unit/virtus/attribute/coerce_spec.rb +9 -9
- data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +2 -2
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +2 -2
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +5 -5
- data/spec/unit/virtus/attribute/custom_collection_spec.rb +8 -2
- data/spec/unit/virtus/attribute/defined_spec.rb +2 -2
- data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +30 -15
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +25 -11
- data/spec/unit/virtus/attribute/get_spec.rb +2 -2
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +7 -7
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +9 -9
- data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +2 -2
- data/spec/unit/virtus/attribute/rename_spec.rb +6 -3
- data/spec/unit/virtus/attribute/required_predicate_spec.rb +2 -2
- data/spec/unit/virtus/attribute/set_default_value_spec.rb +43 -10
- data/spec/unit/virtus/attribute/set_spec.rb +1 -1
- data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +2 -2
- data/spec/unit/virtus/attribute_set/append_spec.rb +2 -2
- data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +12 -11
- data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +13 -12
- data/spec/unit/virtus/attribute_set/each_spec.rb +3 -3
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +1 -1
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +2 -2
- data/spec/unit/virtus/attribute_set/merge_spec.rb +2 -2
- data/spec/unit/virtus/attribute_set/reset_spec.rb +17 -8
- data/spec/unit/virtus/attribute_spec.rb +4 -4
- data/spec/unit/virtus/element_reader_spec.rb +1 -1
- data/spec/unit/virtus/freeze_spec.rb +10 -3
- data/spec/unit/virtus/model_spec.rb +35 -4
- data/spec/unit/virtus/set_default_attributes_spec.rb +10 -3
- data/spec/unit/virtus/value_object_spec.rb +13 -3
- data/virtus.gemspec +6 -4
- metadata +16 -8
- data/Gemfile.devtools +0 -71
- data/config/flay.yml +0 -3
- data/config/flog.yml +0 -2
- data/config/mutant.yml +0 -15
- data/config/reek.yml +0 -146
- data/config/yardstick.yml +0 -2
@@ -8,18 +8,18 @@ describe Virtus::Attribute, '.build' do
|
|
8
8
|
let(:options) { {} }
|
9
9
|
|
10
10
|
shared_examples_for 'a valid attribute instance' do
|
11
|
-
it {
|
11
|
+
it { is_expected.to be_instance_of(Virtus::Attribute) }
|
12
12
|
|
13
|
-
it {
|
13
|
+
it { is_expected.to be_frozen }
|
14
14
|
end
|
15
15
|
|
16
16
|
context 'without options' do
|
17
17
|
it_behaves_like 'a valid attribute instance'
|
18
18
|
|
19
|
-
it {
|
20
|
-
it {
|
21
|
-
it {
|
22
|
-
it {
|
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
23
|
|
24
24
|
it 'sets up a coercer' do
|
25
25
|
expect(subject.options[:coerce]).to be(true)
|
@@ -30,7 +30,10 @@ describe Virtus::Attribute, '.build' do
|
|
30
30
|
context 'when name is passed as a string' do
|
31
31
|
let(:name) { 'something' }
|
32
32
|
|
33
|
-
|
33
|
+
describe '#name' do
|
34
|
+
subject { super().name }
|
35
|
+
it { is_expected.to be(:something) }
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
context 'when coercion is turned off in options' do
|
@@ -38,7 +41,7 @@ describe Virtus::Attribute, '.build' do
|
|
38
41
|
|
39
42
|
it_behaves_like 'a valid attribute instance'
|
40
43
|
|
41
|
-
it {
|
44
|
+
it { is_expected.not_to be_coercible }
|
42
45
|
end
|
43
46
|
|
44
47
|
context 'when options specify reader visibility' do
|
@@ -46,8 +49,8 @@ describe Virtus::Attribute, '.build' do
|
|
46
49
|
|
47
50
|
it_behaves_like 'a valid attribute instance'
|
48
51
|
|
49
|
-
it {
|
50
|
-
it {
|
52
|
+
it { is_expected.not_to be_public_reader }
|
53
|
+
it { is_expected.to be_public_writer }
|
51
54
|
end
|
52
55
|
|
53
56
|
context 'when options specify writer visibility' do
|
@@ -55,8 +58,8 @@ describe Virtus::Attribute, '.build' do
|
|
55
58
|
|
56
59
|
it_behaves_like 'a valid attribute instance'
|
57
60
|
|
58
|
-
it {
|
59
|
-
it {
|
61
|
+
it { is_expected.to be_public_reader }
|
62
|
+
it { is_expected.not_to be_public_writer }
|
60
63
|
end
|
61
64
|
|
62
65
|
context 'when options specify lazy accessor' do
|
@@ -64,7 +67,7 @@ describe Virtus::Attribute, '.build' do
|
|
64
67
|
|
65
68
|
it_behaves_like 'a valid attribute instance'
|
66
69
|
|
67
|
-
it {
|
70
|
+
it { is_expected.to be_lazy }
|
68
71
|
end
|
69
72
|
|
70
73
|
context 'when options specify strict mode' do
|
@@ -72,7 +75,7 @@ describe Virtus::Attribute, '.build' do
|
|
72
75
|
|
73
76
|
it_behaves_like 'a valid attribute instance'
|
74
77
|
|
75
|
-
it {
|
78
|
+
it { is_expected.to be_strict }
|
76
79
|
end
|
77
80
|
|
78
81
|
context 'when type is a string' do
|
@@ -80,7 +83,10 @@ describe Virtus::Attribute, '.build' do
|
|
80
83
|
|
81
84
|
it_behaves_like 'a valid attribute instance'
|
82
85
|
|
83
|
-
|
86
|
+
describe '#type' do
|
87
|
+
subject { super().type }
|
88
|
+
it { is_expected.to be(Axiom::Types::Integer) }
|
89
|
+
end
|
84
90
|
end
|
85
91
|
|
86
92
|
context 'when type is a range' do
|
@@ -88,7 +94,10 @@ describe Virtus::Attribute, '.build' do
|
|
88
94
|
|
89
95
|
it_behaves_like 'a valid attribute instance'
|
90
96
|
|
91
|
-
|
97
|
+
describe '#type' do
|
98
|
+
subject { super().type }
|
99
|
+
it { is_expected.to be(Axiom::Types.infer(Range)) }
|
100
|
+
end
|
92
101
|
end
|
93
102
|
|
94
103
|
context 'when type is a symbol of an existing class constant' do
|
@@ -96,7 +105,10 @@ describe Virtus::Attribute, '.build' do
|
|
96
105
|
|
97
106
|
it_behaves_like 'a valid attribute instance'
|
98
107
|
|
99
|
-
|
108
|
+
describe '#type' do
|
109
|
+
subject { super().type }
|
110
|
+
it { is_expected.to be(Axiom::Types::String) }
|
111
|
+
end
|
100
112
|
end
|
101
113
|
|
102
114
|
context 'when type is an axiom type' do
|
@@ -104,7 +116,10 @@ describe Virtus::Attribute, '.build' do
|
|
104
116
|
|
105
117
|
it_behaves_like 'a valid attribute instance'
|
106
118
|
|
107
|
-
|
119
|
+
describe '#type' do
|
120
|
+
subject { super().type }
|
121
|
+
it { is_expected.to be(type) }
|
122
|
+
end
|
108
123
|
end
|
109
124
|
|
110
125
|
context 'when custom attribute class exists for a given primitive' do
|
@@ -115,9 +130,12 @@ describe Virtus::Attribute, '.build' do
|
|
115
130
|
attribute.primitive(type)
|
116
131
|
end
|
117
132
|
|
118
|
-
it {
|
133
|
+
it { is_expected.to be_instance_of(attribute) }
|
119
134
|
|
120
|
-
|
135
|
+
describe '#type' do
|
136
|
+
subject { super().type }
|
137
|
+
it { is_expected.to be(Axiom::Types::Object) }
|
138
|
+
end
|
121
139
|
end
|
122
140
|
|
123
141
|
context 'when custom attribute class exists for a given array with member coercion defined' do
|
@@ -128,9 +146,12 @@ describe Virtus::Attribute, '.build' do
|
|
128
146
|
attribute.primitive(type.class)
|
129
147
|
end
|
130
148
|
|
131
|
-
it {
|
149
|
+
it { is_expected.to be_instance_of(attribute) }
|
132
150
|
|
133
|
-
|
151
|
+
describe '#type' do
|
152
|
+
subject { super().type }
|
153
|
+
it { is_expected.to be < Axiom::Types::Collection }
|
154
|
+
end
|
134
155
|
end
|
135
156
|
|
136
157
|
context 'when custom collection-like attribute class exists for a given enumerable primitive' do
|
@@ -141,8 +162,11 @@ describe Virtus::Attribute, '.build' do
|
|
141
162
|
attribute.primitive(type)
|
142
163
|
end
|
143
164
|
|
144
|
-
it {
|
165
|
+
it { is_expected.to be_instance_of(attribute) }
|
145
166
|
|
146
|
-
|
167
|
+
describe '#type' do
|
168
|
+
subject { super().type }
|
169
|
+
it { is_expected.to be < Axiom::Types::Collection }
|
170
|
+
end
|
147
171
|
end
|
148
172
|
end
|
@@ -19,7 +19,7 @@ describe Virtus::Attribute, '.coerce' do
|
|
19
19
|
described_class.coerce(true)
|
20
20
|
end
|
21
21
|
|
22
|
-
it {
|
22
|
+
it { is_expected.to be(true) }
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'when it is set to false' do
|
@@ -27,6 +27,6 @@ describe Virtus::Attribute, '.coerce' do
|
|
27
27
|
described_class.coerce(false)
|
28
28
|
end
|
29
29
|
|
30
|
-
it {
|
30
|
+
it { is_expected.to be(false) }
|
31
31
|
end
|
32
32
|
end
|
@@ -18,7 +18,7 @@ describe Virtus::Attribute, '#coerce' do
|
|
18
18
|
let(:strict) { false }
|
19
19
|
|
20
20
|
it 'uses coercer to coerce the input value' do
|
21
|
-
|
21
|
+
mock(coercer).call(input) { output }
|
22
22
|
|
23
23
|
expect(subject).to be(output)
|
24
24
|
|
@@ -30,8 +30,8 @@ describe Virtus::Attribute, '#coerce' do
|
|
30
30
|
let(:strict) { true }
|
31
31
|
|
32
32
|
it 'uses coercer to coerce the input value' do
|
33
|
-
|
34
|
-
|
33
|
+
mock(coercer).call(input) { output }
|
34
|
+
mock(coercer).success?(String, output) { true }
|
35
35
|
|
36
36
|
expect(subject).to be(output)
|
37
37
|
|
@@ -44,8 +44,8 @@ describe Virtus::Attribute, '#coerce' do
|
|
44
44
|
let(:input) { nil }
|
45
45
|
|
46
46
|
it 'returns nil' do
|
47
|
-
|
48
|
-
|
47
|
+
mock(coercer).call(input) { input }
|
48
|
+
mock(coercer).success?(String, input) { false }
|
49
49
|
|
50
50
|
expect(subject).to be(nil)
|
51
51
|
|
@@ -58,8 +58,8 @@ describe Virtus::Attribute, '#coerce' do
|
|
58
58
|
let(:input) { nil }
|
59
59
|
|
60
60
|
it 'returns raises error' do
|
61
|
-
|
62
|
-
|
61
|
+
mock(coercer).call(input) { input }
|
62
|
+
mock(coercer).success?(String, input) { false }
|
63
63
|
|
64
64
|
expect { subject }.to raise_error(Virtus::CoercionError)
|
65
65
|
|
@@ -69,8 +69,8 @@ describe Virtus::Attribute, '#coerce' do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'raises error when input was not coerced' do
|
72
|
-
|
73
|
-
|
72
|
+
mock(coercer).call(input) { input }
|
73
|
+
mock(coercer).success?(String, input) { false }
|
74
74
|
|
75
75
|
expect { subject }.to raise_error(Virtus::CoercionError)
|
76
76
|
|
@@ -9,12 +9,12 @@ describe Virtus::Attribute, '#coercible?' do
|
|
9
9
|
context 'when :coerce is set to true' do
|
10
10
|
let(:coerce) { true }
|
11
11
|
|
12
|
-
it {
|
12
|
+
it { is_expected.to be(true) }
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'when :coerce is set to false' do
|
16
16
|
let(:coerce) { false }
|
17
17
|
|
18
|
-
it {
|
18
|
+
it { is_expected.to be(false) }
|
19
19
|
end
|
20
20
|
end
|
@@ -6,9 +6,9 @@ describe Virtus::Attribute, '.build' do
|
|
6
6
|
let(:options) { {} }
|
7
7
|
|
8
8
|
shared_examples_for 'a valid collection attribute instance' do
|
9
|
-
it {
|
9
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::Collection) }
|
10
10
|
|
11
|
-
it {
|
11
|
+
it { is_expected.to be_frozen }
|
12
12
|
end
|
13
13
|
|
14
14
|
context 'when type is Array' do
|
@@ -16,10 +16,10 @@ describe Virtus::Attribute::Collection, '#coerce' do
|
|
16
16
|
}
|
17
17
|
|
18
18
|
it 'uses coercer to coerce members' do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
mock(coercer).call(input) { input }
|
20
|
+
mock(member_type).finalize { member_type }
|
21
|
+
mock(member_type).coerce('1') { 1 }
|
22
|
+
mock(member_type).coerce('2') { 2 }
|
23
23
|
|
24
24
|
expect(subject).to eq([1, 2])
|
25
25
|
|
@@ -66,7 +66,7 @@ describe Virtus::Attribute::Collection, '#coerce' do
|
|
66
66
|
}
|
67
67
|
|
68
68
|
it 'returns nil' do
|
69
|
-
|
69
|
+
mock(coercer).call(input) { input }
|
70
70
|
|
71
71
|
expect(subject).to be(input)
|
72
72
|
end
|
@@ -12,12 +12,18 @@ describe Virtus::Attribute::Collection, 'custom subclass' do
|
|
12
12
|
context 'when primitive is set on the attribute subclass' do
|
13
13
|
let(:attribute_class) { Class.new(described_class).primitive(primitive) }
|
14
14
|
|
15
|
-
|
15
|
+
describe '#primitive' do
|
16
|
+
subject { super().primitive }
|
17
|
+
it { is_expected.to be(primitive) }
|
18
|
+
end
|
16
19
|
end
|
17
20
|
|
18
21
|
context 'when primitive is not set on the attribute subclass' do
|
19
22
|
let(:attribute_class) { Class.new(described_class) }
|
20
23
|
|
21
|
-
|
24
|
+
describe '#primitive' do
|
25
|
+
subject { super().primitive }
|
26
|
+
it { is_expected.to be(primitive) }
|
27
|
+
end
|
22
28
|
end
|
23
29
|
end
|
@@ -10,11 +10,11 @@ describe Virtus::Attribute, '#defined?' do
|
|
10
10
|
let(:instance) { model.new }
|
11
11
|
|
12
12
|
context 'when the attribute value has not been defined' do
|
13
|
-
it {
|
13
|
+
it { is_expected.to be(false) }
|
14
14
|
end
|
15
15
|
|
16
16
|
context 'when the attribute value has been defined' do
|
17
17
|
before { instance.test = nil }
|
18
|
-
it {
|
18
|
+
it { is_expected.to be(true) }
|
19
19
|
end
|
20
20
|
end
|
@@ -6,50 +6,65 @@ describe Virtus::Attribute::EmbeddedValue, '.build' do
|
|
6
6
|
context 'when type is a Virtus.model' do
|
7
7
|
let(:type) { Class.new { include Virtus.model } }
|
8
8
|
|
9
|
-
it {
|
9
|
+
it { is_expected.to be_frozen }
|
10
10
|
|
11
|
-
it {
|
11
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::EmbeddedValue) }
|
12
12
|
|
13
|
-
|
13
|
+
describe '#coercer' do
|
14
|
+
subject { super().coercer }
|
15
|
+
it { is_expected.to be_instance_of(described_class::FromOpenStruct) }
|
16
|
+
end
|
14
17
|
end
|
15
18
|
|
16
19
|
context 'when type includes Virtus' do
|
17
20
|
let(:type) { Class.new { include Virtus } }
|
18
21
|
|
19
|
-
it {
|
22
|
+
it { is_expected.to be_frozen }
|
20
23
|
|
21
|
-
it {
|
24
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::EmbeddedValue) }
|
22
25
|
|
23
|
-
|
26
|
+
describe '#coercer' do
|
27
|
+
subject { super().coercer }
|
28
|
+
it { is_expected.to be_instance_of(described_class::FromOpenStruct) }
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
context 'when type is an OpenStruct subclass' do
|
27
33
|
let(:type) { Class.new(OpenStruct) }
|
28
34
|
|
29
|
-
it {
|
35
|
+
it { is_expected.to be_frozen }
|
30
36
|
|
31
|
-
it {
|
37
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::EmbeddedValue) }
|
32
38
|
|
33
|
-
|
39
|
+
describe '#coercer' do
|
40
|
+
subject { super().coercer }
|
41
|
+
it { is_expected.to be_instance_of(described_class::FromOpenStruct) }
|
42
|
+
end
|
34
43
|
end
|
35
44
|
|
36
45
|
context 'when type is OpenStruct' do
|
37
46
|
let(:type) { OpenStruct }
|
38
47
|
|
39
|
-
it {
|
48
|
+
it { is_expected.to be_frozen }
|
40
49
|
|
41
|
-
it {
|
50
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::EmbeddedValue) }
|
42
51
|
|
43
|
-
|
52
|
+
describe '#coercer' do
|
53
|
+
subject { super().coercer }
|
54
|
+
it { is_expected.to be_instance_of(described_class::FromOpenStruct) }
|
55
|
+
end
|
44
56
|
end
|
45
57
|
|
46
58
|
context 'when type is Struct' do
|
47
59
|
let(:type) { Struct.new(:test) }
|
48
60
|
|
49
|
-
it {
|
61
|
+
it { is_expected.to be_frozen }
|
50
62
|
|
51
|
-
it {
|
63
|
+
it { is_expected.to be_instance_of(Virtus::Attribute::EmbeddedValue) }
|
52
64
|
|
53
|
-
|
65
|
+
describe '#coercer' do
|
66
|
+
subject { super().coercer }
|
67
|
+
it { is_expected.to be_instance_of(described_class::FromStruct) }
|
68
|
+
end
|
54
69
|
end
|
55
70
|
end
|
@@ -12,22 +12,29 @@ describe Virtus::Attribute::EmbeddedValue, '#coerce' do
|
|
12
12
|
context 'when input is an attribute hash' do
|
13
13
|
let(:input) { Hash[name: 'Piotr', age: 30] }
|
14
14
|
|
15
|
-
it {
|
15
|
+
it { is_expected.to be_instance_of(model) }
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
describe '#name' do
|
18
|
+
subject { super().name }
|
19
|
+
it { is_expected.to eql('Piotr') }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#age' do
|
23
|
+
subject { super().age }
|
24
|
+
it { is_expected.to eql(30) }
|
25
|
+
end
|
19
26
|
end
|
20
27
|
|
21
28
|
context 'when input is nil' do
|
22
29
|
let(:input) { nil }
|
23
30
|
|
24
|
-
it {
|
31
|
+
it { is_expected.to be(nil) }
|
25
32
|
end
|
26
33
|
|
27
34
|
context 'when input is a model instance' do
|
28
35
|
let(:input) { OpenStruct.new }
|
29
36
|
|
30
|
-
it {
|
37
|
+
it { is_expected.to be(input) }
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
@@ -37,22 +44,29 @@ describe Virtus::Attribute::EmbeddedValue, '#coerce' do
|
|
37
44
|
context 'when input is an attribute hash' do
|
38
45
|
let(:input) { ['Piotr', 30] }
|
39
46
|
|
40
|
-
it {
|
47
|
+
it { is_expected.to be_instance_of(model) }
|
41
48
|
|
42
|
-
|
43
|
-
|
49
|
+
describe '#name' do
|
50
|
+
subject { super().name }
|
51
|
+
it { is_expected.to eql('Piotr') }
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#age' do
|
55
|
+
subject { super().age }
|
56
|
+
it { is_expected.to eql(30) }
|
57
|
+
end
|
44
58
|
end
|
45
59
|
|
46
60
|
context 'when input is nil' do
|
47
61
|
let(:input) { nil }
|
48
62
|
|
49
|
-
it {
|
63
|
+
it { is_expected.to be(nil) }
|
50
64
|
end
|
51
65
|
|
52
66
|
context 'when input is a model instance' do
|
53
67
|
let(:input) { model.new('Piotr', 30) }
|
54
68
|
|
55
|
-
it {
|
69
|
+
it { is_expected.to be(input) }
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
@@ -63,7 +77,7 @@ describe Virtus::Attribute::EmbeddedValue, '#coerce' do
|
|
63
77
|
context 'when input is coercible' do
|
64
78
|
let(:input) { ['Piotr'] }
|
65
79
|
|
66
|
-
it {
|
80
|
+
it { is_expected.to eql(model.new('Piotr')) }
|
67
81
|
end
|
68
82
|
|
69
83
|
context 'when input is not coercible' do
|