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.
- 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
|