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,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#<<' do
|
4
|
+
subject { object << attribute }
|
5
|
+
|
6
|
+
let(:attributes) { [] }
|
7
|
+
let(:parent) { described_class.new }
|
8
|
+
let(:object) { described_class.new(parent, attributes) }
|
9
|
+
let(:name) { :name }
|
10
|
+
|
11
|
+
context 'with a new attribute' do
|
12
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
13
|
+
|
14
|
+
it { is_expected.to equal(object) }
|
15
|
+
|
16
|
+
it 'adds an attribute' do
|
17
|
+
expect { subject }.to change { object.to_a }.
|
18
|
+
from(attributes).
|
19
|
+
to([ attribute ])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'indexes the new attribute under its #name property' do
|
23
|
+
expect { subject }.to change { object[name] }.
|
24
|
+
from(nil).
|
25
|
+
to(attribute)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'indexes the new attribute under the string version of its #name property' do
|
29
|
+
expect { subject }.to change { object[name.to_s] }.
|
30
|
+
from(nil).
|
31
|
+
to(attribute)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with a duplicate attribute' do
|
36
|
+
let(:attributes) { [Virtus::Attribute.build(String, :name => name)] }
|
37
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
38
|
+
|
39
|
+
it { is_expected.to equal(object) }
|
40
|
+
|
41
|
+
it "replaces the original attribute object" do
|
42
|
+
expect { subject }.to change { object.to_a.map(&:__id__) }.
|
43
|
+
from(attributes.map(&:__id__)).
|
44
|
+
to([attribute.__id__])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#define_reader_method' do
|
4
|
+
subject(:attribute_set) { described_class.new }
|
5
|
+
|
6
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => method_name) }
|
7
|
+
let(:method_name) { :foo_bar }
|
8
|
+
|
9
|
+
before do
|
10
|
+
attribute_set.define_reader_method(attribute, method_name, visibility)
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with public visibility" do
|
14
|
+
let(:visibility) { :public }
|
15
|
+
|
16
|
+
it "defines public writer" do
|
17
|
+
expect(attribute_set.public_instance_methods).to include(method_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with private visibility" do
|
22
|
+
let(:visibility) { :private }
|
23
|
+
|
24
|
+
it "defines public writer" do
|
25
|
+
expect(attribute_set.private_instance_methods).to include(method_name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with protected visibility" do
|
30
|
+
let(:visibility) { :protected }
|
31
|
+
|
32
|
+
it "defines protected writer" do
|
33
|
+
expect(attribute_set.protected_instance_methods).to include(method_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#define_writer_method' do
|
4
|
+
subject(:attribute_set) { described_class.new }
|
5
|
+
|
6
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => method_name) }
|
7
|
+
let(:method_name) { :foo_bar }
|
8
|
+
|
9
|
+
before do
|
10
|
+
attribute_set.define_writer_method(attribute, method_name, visibility)
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with public visibility" do
|
14
|
+
let(:visibility) { :public }
|
15
|
+
|
16
|
+
it "defines public writer" do
|
17
|
+
expect(attribute_set.public_instance_methods).to include(method_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with private visibility" do
|
22
|
+
let(:visibility) { :private }
|
23
|
+
|
24
|
+
it "defines private writer" do
|
25
|
+
expect(attribute_set.private_instance_methods).to include(method_name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with protected visibility" do
|
30
|
+
let(:visibility) { :protected }
|
31
|
+
|
32
|
+
it "defines protected writer" do
|
33
|
+
expect(attribute_set.protected_instance_methods).to include(method_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#each' do
|
4
|
+
subject(:attribute_set) { described_class.new(parent, attributes) }
|
5
|
+
|
6
|
+
let(:name) { :name }
|
7
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => :name) }
|
8
|
+
let(:attributes) { [ attribute ] }
|
9
|
+
let(:parent) { described_class.new }
|
10
|
+
let(:yields) { Set[] }
|
11
|
+
|
12
|
+
context 'with no block' do
|
13
|
+
it 'returns an enumerator when block is not provided' do
|
14
|
+
expect(attribute_set.each).to be_kind_of(Enumerator)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'yields the expected attributes' do
|
18
|
+
result = []
|
19
|
+
attribute_set.each { |attribute| result << attribute }
|
20
|
+
expect(result).to eql(attributes)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a block' do
|
25
|
+
subject { attribute_set.each { |attribute| yields << attribute } }
|
26
|
+
|
27
|
+
context 'when the parent has no attributes' do
|
28
|
+
it { is_expected.to equal(attribute_set) }
|
29
|
+
|
30
|
+
it 'yields the expected attributes' do
|
31
|
+
expect { subject }.to change { yields.dup }.
|
32
|
+
from(Set[]).
|
33
|
+
to(attributes.to_set)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the parent has attributes that are not duplicates' do
|
38
|
+
let(:parent_attribute) { Virtus::Attribute.build(String, :name => :parent_name) }
|
39
|
+
let(:parent) { described_class.new([ parent_attribute ]) }
|
40
|
+
|
41
|
+
it { is_expected.to equal(attribute_set) }
|
42
|
+
|
43
|
+
it 'yields the expected attributes' do
|
44
|
+
result = []
|
45
|
+
|
46
|
+
attribute_set.each { |attribute| result << attribute }
|
47
|
+
|
48
|
+
expect(result).to eql([parent_attribute, attribute])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when the parent has attributes that are duplicates' do
|
53
|
+
let(:parent_attribute) { Virtus::Attribute.build(String, :name => name) }
|
54
|
+
let(:parent) { described_class.new([ parent_attribute ]) }
|
55
|
+
|
56
|
+
it { is_expected.to equal(attribute_set) }
|
57
|
+
|
58
|
+
it 'yields the expected attributes' do
|
59
|
+
expect { subject }.to change { yields.dup }.
|
60
|
+
from(Set[]).
|
61
|
+
to(Set[ attribute ])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#[]' do
|
4
|
+
subject { object[name] }
|
5
|
+
|
6
|
+
let(:name) { :name }
|
7
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => :name) }
|
8
|
+
let(:attributes) { [ attribute ] }
|
9
|
+
let(:parent) { described_class.new }
|
10
|
+
let(:object) { described_class.new(parent, attributes) }
|
11
|
+
|
12
|
+
it { is_expected.to equal(attribute) }
|
13
|
+
|
14
|
+
it 'allows indexed access to attributes by the string representation of their name' do
|
15
|
+
expect(object[name.to_s]).to equal(attribute)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#[]=' do
|
4
|
+
subject { object[name] = attribute }
|
5
|
+
|
6
|
+
let(:attributes) { [] }
|
7
|
+
let(:parent) { described_class.new }
|
8
|
+
let(:object) { described_class.new(parent, attributes) }
|
9
|
+
let(:name) { :name }
|
10
|
+
|
11
|
+
context 'with a new attribute' do
|
12
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
13
|
+
|
14
|
+
it { is_expected.to equal(attribute) }
|
15
|
+
|
16
|
+
it 'adds an attribute' do
|
17
|
+
expect { subject }.to change { object.to_a }.from(attributes).to([ attribute ])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allows #[] to access the attribute with a symbol' do
|
21
|
+
expect { subject }.to change { object['name'] }.from(nil).to(attribute)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'allows #[] to access the attribute with a string' do
|
25
|
+
expect { subject }.to change { object[:name] }.from(nil).to(attribute)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'allows #reset to track overridden attributes' do
|
29
|
+
expect { subject }.to change { object.reset.to_a }.from(attributes).to([ attribute ])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a duplicate attribute' do
|
34
|
+
let(:original) { Virtus::Attribute.build(String, :name => name) }
|
35
|
+
let(:attributes) { [ original ] }
|
36
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
37
|
+
|
38
|
+
it { is_expected.to equal(attribute) }
|
39
|
+
|
40
|
+
it "replaces the original attribute object" do
|
41
|
+
expect { subject }.to change { object.to_a.map(&:__id__) }.
|
42
|
+
from(attributes.map(&:__id__)).
|
43
|
+
to([attribute.__id__])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'allows #[] to access the attribute with a string' do
|
47
|
+
expect { subject }.to change { object['name'].__id__ }.
|
48
|
+
from(original.__id__).
|
49
|
+
to(attribute.__id__)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'allows #[] to access the attribute with a symbol' do
|
53
|
+
expect { subject }.to change { object[:name].__id__ }.
|
54
|
+
from(original.__id__).
|
55
|
+
to(attribute.__id__)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'allows #reset to track overridden attributes' do
|
59
|
+
expect { subject }.to change { object.reset.to_a.map(&:__id__) }.
|
60
|
+
from(attributes.map(&:__id__)).
|
61
|
+
to([attribute.__id__])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#merge' do
|
4
|
+
subject { object.merge(other) }
|
5
|
+
|
6
|
+
let(:parent) { described_class.new }
|
7
|
+
let(:object) { described_class.new(parent, attributes) }
|
8
|
+
let(:name) { :name }
|
9
|
+
let(:other) { [attribute] }
|
10
|
+
|
11
|
+
context 'with a new attribute' do
|
12
|
+
let(:attributes) { [] }
|
13
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
14
|
+
|
15
|
+
it { is_expected.to equal(object) }
|
16
|
+
|
17
|
+
it 'adds an attribute' do
|
18
|
+
expect { subject }.to change { object.to_a }.from(attributes).to([attribute])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with a duplicate attribute' do
|
23
|
+
let(:attributes) { [Virtus::Attribute.build(String, :name => name)] }
|
24
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => name) }
|
25
|
+
|
26
|
+
it { is_expected.to equal(object) }
|
27
|
+
|
28
|
+
it "replaces the original attribute object" do
|
29
|
+
expect { subject }.to change { object.to_a.map(&:__id__) }.
|
30
|
+
from(attributes.map(&:__id__)).
|
31
|
+
to([attribute.__id__])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributeSet, '#reset' do
|
4
|
+
subject { object.reset }
|
5
|
+
|
6
|
+
let(:name) { :name }
|
7
|
+
let(:attribute) { Virtus::Attribute.build(String, :name => :name) }
|
8
|
+
let(:attributes) { [ attribute ] }
|
9
|
+
let(:object) { described_class.new(parent, attributes) }
|
10
|
+
|
11
|
+
context 'when the parent has no attributes' do
|
12
|
+
let(:parent) { described_class.new }
|
13
|
+
|
14
|
+
it { is_expected.to equal(object) }
|
15
|
+
|
16
|
+
describe '#to_set' do
|
17
|
+
subject { super().to_set }
|
18
|
+
it { is_expected.to eq(Set[ attribute ]) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when the parent has attributes that are not duplicates' do
|
23
|
+
let(:parent_attribute) { Virtus::Attribute.build(String, :name => :parent_name) }
|
24
|
+
let(:parent) { described_class.new([ parent_attribute ]) }
|
25
|
+
|
26
|
+
it { is_expected.to equal(object) }
|
27
|
+
|
28
|
+
describe '#to_set' do
|
29
|
+
subject { super().to_set }
|
30
|
+
it { is_expected.to eq(Set[ attribute, parent_attribute ]) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when the parent has attributes that are duplicates' do
|
35
|
+
let(:parent_attribute) { Virtus::Attribute.build(String, :name => name) }
|
36
|
+
let(:parent) { described_class.new([ parent_attribute ]) }
|
37
|
+
|
38
|
+
it { is_expected.to equal(object) }
|
39
|
+
|
40
|
+
describe '#to_set' do
|
41
|
+
subject { super().to_set }
|
42
|
+
it { is_expected.to eq(Set[ attribute ]) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when the parent has changed' do
|
47
|
+
let(:parent_attribute) { Virtus::Attribute.build(String, :name => :parent_name) }
|
48
|
+
let(:parent) { described_class.new([ parent_attribute ]) }
|
49
|
+
let(:new_attribute) { Virtus::Attribute.build(String, :name => :parent_name) }
|
50
|
+
|
51
|
+
it { is_expected.to equal(object) }
|
52
|
+
|
53
|
+
it 'includes changes from the parent' do
|
54
|
+
expect(object.to_set).to eql(Set[attribute, parent_attribute])
|
55
|
+
|
56
|
+
parent << new_attribute
|
57
|
+
|
58
|
+
expect(subject.to_set).to eql(Set[attribute, new_attribute])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when the parent is nil' do
|
63
|
+
let(:parent) { nil }
|
64
|
+
|
65
|
+
it { is_expected.to equal(object) }
|
66
|
+
|
67
|
+
it 'includes changes from the parent' do
|
68
|
+
expect { subject }.to_not change { object.to_set }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus, '#attribute' do
|
4
|
+
let(:name) { :test }
|
5
|
+
let(:options) { {} }
|
6
|
+
|
7
|
+
shared_examples_for 'a class with boolean attribute' do
|
8
|
+
subject { Test }
|
9
|
+
|
10
|
+
let(:object) { subject.new }
|
11
|
+
|
12
|
+
it 'defines reader and writer' do
|
13
|
+
object.test = true
|
14
|
+
|
15
|
+
expect(object).to be_test
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'defines predicate method' do
|
19
|
+
object.test = false
|
20
|
+
|
21
|
+
expect(object).to_not be_test
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
shared_examples_for 'an object with string attribute' do
|
26
|
+
it { is_expected.to respond_to(:test) }
|
27
|
+
it { is_expected.to respond_to(:test=) }
|
28
|
+
|
29
|
+
it 'can write and read the attribute' do
|
30
|
+
subject.test = :foo
|
31
|
+
expect(subject.test).to eql('foo')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns self' do
|
36
|
+
klass = Class.new { include Virtus }
|
37
|
+
expect(klass.attribute(:test, String)).to be(klass)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'raises error when :name is a reserved name on a class' do
|
41
|
+
klass = Class.new { include Virtus }
|
42
|
+
expect { klass.attribute(:attributes, Set) }.to raise_error(
|
43
|
+
ArgumentError, ':attributes is not allowed as an attribute name'
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises error when :name is a reserved name on an instance' do
|
48
|
+
object = Class.new.new.extend(Virtus)
|
49
|
+
expect { object.attribute(:attributes, Set) }.to raise_error(
|
50
|
+
ArgumentError, ':attributes is not allowed as an attribute name'
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows :attributes as an attribute name when mass-assignment is not included' do
|
55
|
+
klass = Class.new { include Virtus::Model::Core }
|
56
|
+
klass.attribute(:attributes, Set)
|
57
|
+
expect(klass.attribute_set[:attributes]).to be_instance_of(Virtus::Attribute::Collection)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'allows specifying attribute without type' do
|
61
|
+
klass = Class.new { include Virtus::Model::Core }
|
62
|
+
klass.attribute(:name)
|
63
|
+
expect(klass.attribute_set[:name]).to be_instance_of(Virtus::Attribute)
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with a class' do
|
67
|
+
context 'when type is Boolean' do
|
68
|
+
before :all do
|
69
|
+
class Test
|
70
|
+
include Virtus
|
71
|
+
|
72
|
+
attribute :test, Boolean
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
after :all do
|
77
|
+
Object.send(:remove_const, :Test)
|
78
|
+
end
|
79
|
+
|
80
|
+
it_behaves_like 'a class with boolean attribute'
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when type is "Boolean"' do
|
84
|
+
before :all do
|
85
|
+
class Test
|
86
|
+
include Virtus
|
87
|
+
|
88
|
+
attribute :test, 'Boolean'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
after :all do
|
93
|
+
Object.send(:remove_const, :Test)
|
94
|
+
end
|
95
|
+
|
96
|
+
it_behaves_like 'a class with boolean attribute'
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when type is Axiom::Types::Boolean' do
|
100
|
+
before :all do
|
101
|
+
class Test
|
102
|
+
include Virtus
|
103
|
+
|
104
|
+
attribute :test, Axiom::Types::Boolean
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
after :all do
|
109
|
+
Object.send(:remove_const, :Test)
|
110
|
+
end
|
111
|
+
|
112
|
+
it_behaves_like 'a class with boolean attribute' do
|
113
|
+
before do
|
114
|
+
pending 'this will be fixed once Attribute::Boolean subclass is gone'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when type is :Boolean' do
|
120
|
+
before :all do
|
121
|
+
class Test
|
122
|
+
include Virtus
|
123
|
+
|
124
|
+
attribute :test, 'Boolean'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
after :all do
|
129
|
+
Object.send(:remove_const, :Test)
|
130
|
+
end
|
131
|
+
|
132
|
+
it_behaves_like 'a class with boolean attribute'
|
133
|
+
|
134
|
+
context 'with a subclass' do
|
135
|
+
it_behaves_like 'a class with boolean attribute' do
|
136
|
+
subject { Class.new(Test) }
|
137
|
+
|
138
|
+
it 'gets attributes from the parent class' do
|
139
|
+
Test.attribute :other, Integer
|
140
|
+
expect(subject.attribute_set[:other]).to eql(Test.attribute_set[:other])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'when type is Decimal' do
|
147
|
+
before :all do
|
148
|
+
class Test
|
149
|
+
include Virtus
|
150
|
+
|
151
|
+
attribute :test, Decimal
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
after :all do
|
156
|
+
Object.send(:remove_const, :Test)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'maps type to the corresponding axiom type' do
|
160
|
+
expect(Test.attribute_set[:test].type).to be(Axiom::Types::Decimal)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'with a module' do
|
166
|
+
let(:mod) {
|
167
|
+
Module.new {
|
168
|
+
include Virtus
|
169
|
+
|
170
|
+
attribute :test, String
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
let(:model) { Class.new }
|
175
|
+
|
176
|
+
context 'included in the class' do
|
177
|
+
before do
|
178
|
+
model.send(:include, mod)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'adds attributes from the module to a class that includes it' do
|
182
|
+
expect(model.attribute_set[:test]).to be_instance_of(Virtus::Attribute)
|
183
|
+
end
|
184
|
+
|
185
|
+
it_behaves_like 'an object with string attribute' do
|
186
|
+
subject { model.new }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'included in the class' do
|
191
|
+
it_behaves_like 'an object with string attribute' do
|
192
|
+
subject { model.new.extend(mod) }
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'with an instance' do
|
198
|
+
subject { model.new }
|
199
|
+
|
200
|
+
let(:model) { Class.new }
|
201
|
+
|
202
|
+
before do
|
203
|
+
subject.extend(Virtus)
|
204
|
+
subject.attribute(:test, String)
|
205
|
+
end
|
206
|
+
|
207
|
+
it_behaves_like 'an object with string attribute'
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'using custom module' do
|
211
|
+
subject { model.new }
|
212
|
+
|
213
|
+
let(:model) {
|
214
|
+
Class.new {
|
215
|
+
include Virtus.model { |config| config.coerce = false }
|
216
|
+
|
217
|
+
attribute :test, String
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
it { is_expected.to respond_to(:test) }
|
222
|
+
it { is_expected.to respond_to(:test=) }
|
223
|
+
|
224
|
+
it 'writes and reads attributes' do
|
225
|
+
subject.test = :foo
|
226
|
+
expect(subject.test).to be(:foo)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|