virtus 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +10 -2
- data/Guardfile +9 -0
- data/README.md +12 -8
- data/TODO +0 -35
- data/config/flay.yml +1 -1
- data/config/site.reek +3 -1
- data/lib/virtus.rb +1 -0
- data/lib/virtus/attribute.rb +1 -1
- data/lib/virtus/attribute/collection.rb +2 -2
- data/lib/virtus/attribute/default_value/from_symbol.rb +2 -2
- data/lib/virtus/attribute_set.rb +4 -17
- data/lib/virtus/attributes_accessor.rb +22 -20
- data/lib/virtus/class_methods.rb +1 -1
- data/lib/virtus/coercion/hash.rb +1 -1
- data/lib/virtus/coercion/object.rb +1 -1
- data/lib/virtus/coercion/string.rb +20 -7
- data/lib/virtus/coercion/time_coercions.rb +5 -1
- data/lib/virtus/instance_methods.rb +8 -8
- data/lib/virtus/support/type_lookup.rb +24 -8
- data/lib/virtus/value_object/equalizer.rb +3 -2
- data/lib/virtus/version.rb +1 -1
- data/spec/shared/options_class_method.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +1 -7
- data/spec/unit/virtus/attribute/coerce_spec.rb +2 -2
- data/spec/unit/virtus/attribute/collection/coerce_and_append_member_spec.rb +16 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +20 -7
- data/spec/unit/virtus/attribute/collection/member_coercion/coerce_and_append_member_spec.rb +18 -0
- data/spec/unit/virtus/attribute/collection/member_type_spec.rb +17 -0
- data/spec/unit/virtus/attribute/collection/new_collection_spec.rb +9 -0
- data/spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb +25 -0
- data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +0 -43
- data/spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb +20 -0
- data/spec/unit/virtus/attribute/default_value/from_callable/evaluate_spec.rb +21 -0
- data/spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb +20 -0
- data/spec/unit/virtus/attribute/default_value/from_clonable/evaluate_spec.rb +22 -0
- data/spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb +20 -0
- data/spec/unit/virtus/attribute/default_value/from_symbol/evaluate_spec.rb +18 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +5 -4
- data/spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb +36 -0
- data/spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb +36 -0
- data/spec/unit/virtus/coercion/array/{to_set_spec.rb → class_methods/to_set_spec.rb} +0 -0
- data/spec/unit/virtus/coercion/{class_name_reference_spec.rb → class_methods/element_reference_spec.rb} +0 -0
- data/spec/unit/virtus/coercion/class_methods/primitive_spec.rb +13 -0
- data/spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb +10 -0
- data/spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb +10 -0
- data/spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb +10 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb +0 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb +0 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb +0 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb +0 -0
- data/spec/unit/virtus/coercion/object/class_methods/to_string_spec.rb +1 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb +10 -0
- data/spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb +11 -0
- data/spec/unit/virtus/instance_methods/attributes_spec.rb +25 -0
- data/spec/unit/virtus/instance_methods/initialize_spec.rb +42 -0
- data/spec/unit/virtus/type_lookup/class_methods/extended_spec.rb +10 -0
- data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +16 -0
- data/spec/unit/virtus/value_object/equalizer/append_spec.rb +61 -0
- data/virtus.gemspec +4 -3
- metadata +80 -75
- data/spec/unit/virtus/attribute_set/parent_spec.rb +0 -11
@@ -24,6 +24,8 @@ module Virtus
|
|
24
24
|
compile
|
25
25
|
end
|
26
26
|
|
27
|
+
private
|
28
|
+
|
27
29
|
# Compile the equalizer methods based on #keys
|
28
30
|
#
|
29
31
|
# @return [self]
|
@@ -37,8 +39,6 @@ module Virtus
|
|
37
39
|
self
|
38
40
|
end
|
39
41
|
|
40
|
-
private
|
41
|
-
|
42
42
|
# Define an inspect method that reports the values of the instance's keys
|
43
43
|
#
|
44
44
|
# @return [undefined]
|
@@ -122,3 +122,4 @@ module Virtus
|
|
122
122
|
end # class Equalizer
|
123
123
|
end # module ValueObject
|
124
124
|
end # module Virtus
|
125
|
+
|
data/lib/virtus/version.rb
CHANGED
@@ -13,7 +13,7 @@ shared_examples_for 'an options class method' do
|
|
13
13
|
it { should equal(object) }
|
14
14
|
|
15
15
|
it 'sets the default value for the class method' do
|
16
|
-
expect { subject }.to change { object.send(method) }.from(
|
16
|
+
expect { subject }.to change { object.send(method) }.from(default).to(value)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,7 +13,7 @@ require 'virtus'
|
|
13
13
|
ENV['TZ'] = 'UTC'
|
14
14
|
|
15
15
|
# require spec support files and shared behavior
|
16
|
-
Dir[File.expand_path('
|
16
|
+
Dir[File.expand_path('../shared/**/*.rb', __FILE__)].each { |file| require file }
|
17
17
|
|
18
18
|
RSpec.configure do |config|
|
19
19
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Virtus::Attribute, '.determine_type' do
|
4
4
|
let(:object) { described_class }
|
5
5
|
|
6
|
-
described_class.descendants.each do |attribute_class|
|
6
|
+
(described_class.descendants - [ Virtus::Attribute::Collection ]).each do |attribute_class|
|
7
7
|
context "with class #{attribute_class.inspect}" do
|
8
8
|
subject { object.determine_type(attribute_class) }
|
9
9
|
|
@@ -16,12 +16,6 @@ describe Virtus::Attribute, '.determine_type' do
|
|
16
16
|
context "with #{attribute_class.inspect} and primitive #{primitive.inspect}" do
|
17
17
|
subject { object.determine_type(primitive) }
|
18
18
|
|
19
|
-
before do
|
20
|
-
if [Virtus::Attribute::Collection].include? attribute_class
|
21
|
-
pending
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
19
|
it 'returns the corresponding attribute class' do
|
26
20
|
should be(attribute_class)
|
27
21
|
end
|
@@ -9,7 +9,7 @@ describe Virtus::Attribute, '#coerce' do
|
|
9
9
|
let(:coercion_method) { stub('coercion_method') }
|
10
10
|
let(:value) { mock('value', :class => value_class) }
|
11
11
|
let(:value_class) { stub('value_class') }
|
12
|
-
let(:coercer) { mock('coercer', :
|
12
|
+
let(:coercer) { mock('coercer', :public_send => coerced) }
|
13
13
|
let(:coerced) { stub('coerced') }
|
14
14
|
|
15
15
|
before do
|
@@ -24,7 +24,7 @@ describe Virtus::Attribute, '#coerce' do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'asks the coercer to coerce the value' do
|
27
|
-
coercer.should_receive(:
|
27
|
+
coercer.should_receive(:public_send).with(coercion_method, value)
|
28
28
|
subject
|
29
29
|
end
|
30
30
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::Collection, '#coerce_and_append_member' do
|
4
|
+
subject { object.coerce_and_append_member(collection, entry) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new('stuff') }
|
7
|
+
let(:collection) { mock('collection') }
|
8
|
+
let(:entry) { mock('entry') }
|
9
|
+
|
10
|
+
specify do
|
11
|
+
expect { subject }.to raise_error(
|
12
|
+
NotImplementedError,
|
13
|
+
"Virtus::Attribute::Collection#coerce_and_append_member has not been implemented"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
@@ -1,26 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::Collection, '#coerce' do
|
4
|
-
subject {
|
4
|
+
subject { object.coerce(value) }
|
5
5
|
|
6
|
-
let(:
|
7
|
-
let(:
|
8
|
-
let(:
|
9
|
-
let(:
|
6
|
+
let(:described_class) { Class.new(Virtus::Attribute::Collection) }
|
7
|
+
let(:object) { described_class.new(:things) }
|
8
|
+
let(:value) { [ entry ] }
|
9
|
+
let(:entry) { mock('entry') }
|
10
10
|
|
11
11
|
before do
|
12
|
+
described_class.primitive ::Array
|
12
13
|
value.should_receive(:respond_to?).with(:inject).and_return(respond_to_inject)
|
13
14
|
end
|
14
15
|
|
15
16
|
context 'when coerced value responds to #inject' do
|
16
17
|
let(:respond_to_inject) { true }
|
17
18
|
|
18
|
-
|
19
|
+
context 'when the object has not implemented #coerce_and_append_member' do
|
20
|
+
specify { expect { subject }.to raise_error(NotImplementedError, "#{object.class}#coerce_and_append_member has not been implemented") }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when the object has implemented #coerce_and_append_member' do
|
24
|
+
before do
|
25
|
+
def object.coerce_and_append_member(collection, entry)
|
26
|
+
collection << entry
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it { should eql(value) }
|
31
|
+
end
|
19
32
|
end
|
20
33
|
|
21
34
|
context 'when coerced value does not respond to #inject' do
|
22
35
|
let(:respond_to_inject) { false }
|
23
36
|
|
24
|
-
|
37
|
+
it { should eql(value) }
|
25
38
|
end
|
26
39
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::Collection::MemberCoercion, '#coerce_and_append_member' do
|
4
|
+
subject { object.coerce_and_append_member(collection, entry) }
|
5
|
+
|
6
|
+
let(:attribute_class) do
|
7
|
+
Class.new(Virtus::Attribute::Collection) do
|
8
|
+
include Virtus::Attribute::Collection::MemberCoercion
|
9
|
+
self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:object) { attribute_class.new('stuff', :member_type => Integer) }
|
14
|
+
let(:collection) { [] }
|
15
|
+
let(:entry) { '1' }
|
16
|
+
|
17
|
+
it { should eql([ 1 ]) }
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::Collection, '#member_type' do
|
4
|
+
subject { object.member_type }
|
5
|
+
|
6
|
+
context 'when specified' do
|
7
|
+
let(:object) { Virtus::Attribute::Set.new('stuff', :member_type => Integer) }
|
8
|
+
|
9
|
+
it { should be(Integer) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when not specified' do
|
13
|
+
let(:object) { Virtus::Attribute::Set.new('stuff') }
|
14
|
+
|
15
|
+
it { should be(Virtus::Attribute::Object) }
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue, '.build' do
|
4
|
+
subject { described_class.build(attribute, default) }
|
5
|
+
|
6
|
+
let(:attribute) { mock('attribute') }
|
7
|
+
|
8
|
+
context 'when default is a symbol' do
|
9
|
+
let(:default) { :symbol }
|
10
|
+
|
11
|
+
it { should be_instance_of(Virtus::Attribute::DefaultValue::FromSymbol) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when default is a callable' do
|
15
|
+
let(:default) { Proc.new {} }
|
16
|
+
|
17
|
+
it { should be_instance_of(Virtus::Attribute::DefaultValue::FromCallable) }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when default is a clonable' do
|
21
|
+
let(:default) { "I can be cloned" }
|
22
|
+
|
23
|
+
it { should be_instance_of(Virtus::Attribute::DefaultValue::FromClonable) }
|
24
|
+
end
|
25
|
+
end
|
@@ -9,49 +9,6 @@ describe Virtus::Attribute::DefaultValue, '#evaluate' do
|
|
9
9
|
let(:instance) { mock('instance') }
|
10
10
|
let(:response) { stub('response') }
|
11
11
|
|
12
|
-
context 'when the value is callable' do
|
13
|
-
before { value.stub(:call => response) }
|
14
|
-
|
15
|
-
specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromCallable) }
|
16
|
-
|
17
|
-
it { should be(response) }
|
18
|
-
|
19
|
-
it 'calls the value with the instance and attribute' do
|
20
|
-
value.should_receive(:call).with(instance, attribute)
|
21
|
-
subject
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when the value is cloneable' do
|
26
|
-
let(:clone) { stub('clone') }
|
27
|
-
|
28
|
-
before { value.stub(:clone => clone) }
|
29
|
-
|
30
|
-
specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromClonable) }
|
31
|
-
|
32
|
-
it { should be(clone) }
|
33
|
-
|
34
|
-
it 'clones the value' do
|
35
|
-
value.should_receive(:clone).with(no_args)
|
36
|
-
subject
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'when the value is a method name' do
|
41
|
-
let(:instance) { mock('instance', value => retval) }
|
42
|
-
let(:value) { :set_default }
|
43
|
-
let(:retval) { stub('retval') }
|
44
|
-
|
45
|
-
specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromSymbol) }
|
46
|
-
|
47
|
-
it { should be(retval) }
|
48
|
-
|
49
|
-
it 'calls the method' do
|
50
|
-
instance.should_receive(value).with(no_args)
|
51
|
-
subject
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
12
|
# smallest number that is Bignum across major ruby impls
|
56
13
|
bignum = 0x7fffffffffffffff + 1
|
57
14
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromCallable, '.handle?' do
|
4
|
+
subject { described_class.handle?(attribute, default) }
|
5
|
+
|
6
|
+
let(:attribute) { mock('attribute') }
|
7
|
+
|
8
|
+
context 'with a callable' do
|
9
|
+
let(:default) { Proc.new {} }
|
10
|
+
|
11
|
+
it { should be(true) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with a non-callable' do
|
15
|
+
let(:default) { '' }
|
16
|
+
|
17
|
+
it { should be(false) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromCallable, '#evaluate' do
|
4
|
+
subject { object.evaluate(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(attribute, value) }
|
7
|
+
let(:attribute) { mock('attribute') }
|
8
|
+
let(:value) { mock('value') }
|
9
|
+
let(:instance) { mock('instance') }
|
10
|
+
let(:response) { stub('response') }
|
11
|
+
|
12
|
+
before { value.stub(:call => response) }
|
13
|
+
|
14
|
+
it { should be(response) }
|
15
|
+
|
16
|
+
it 'calls the value with the instance and attribute' do
|
17
|
+
value.should_receive(:call).with(instance, attribute)
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromClonable, '.handle?' do
|
4
|
+
subject { described_class.handle?(attribute, default) }
|
5
|
+
|
6
|
+
let(:attribute) { mock('attribute') }
|
7
|
+
|
8
|
+
context 'with a clonable' do
|
9
|
+
let(:default) { 'clonable' }
|
10
|
+
|
11
|
+
it { should be(true) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with a non-clonable' do
|
15
|
+
let(:default) { 1 }
|
16
|
+
|
17
|
+
it { should be(false) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromClonable, '#evaluate' do
|
4
|
+
subject { object.evaluate(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(attribute, value) }
|
7
|
+
let(:attribute) { mock('attribute') }
|
8
|
+
let(:value) { mock('value') }
|
9
|
+
let(:instance) { mock('instance') }
|
10
|
+
let(:response) { mock('response') }
|
11
|
+
let(:clone) { mock('clone') }
|
12
|
+
|
13
|
+
before { value.stub(:clone => clone) }
|
14
|
+
|
15
|
+
it { should be(clone) }
|
16
|
+
|
17
|
+
it 'clones the value' do
|
18
|
+
value.should_receive(:clone).with(no_args)
|
19
|
+
subject
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromSymbol, '.handle?' do
|
4
|
+
subject { described_class.handle?(attribute, default) }
|
5
|
+
|
6
|
+
let(:attribute) { mock('attribute') }
|
7
|
+
|
8
|
+
context 'with a symbol' do
|
9
|
+
let(:default) { :method_name }
|
10
|
+
|
11
|
+
it { should be(true) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with a non-symbol' do
|
15
|
+
let(:default) { 'method_name' }
|
16
|
+
|
17
|
+
it { should be(false) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromSymbol, '#evaluate' do
|
4
|
+
subject { object.evaluate(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(attribute, value) }
|
7
|
+
let(:attribute) { mock('attribute') }
|
8
|
+
let(:instance) { mock('instance', value => retval) }
|
9
|
+
let(:value) { :set_default }
|
10
|
+
let(:retval) { mock('retval') }
|
11
|
+
|
12
|
+
it { should be(retval) }
|
13
|
+
|
14
|
+
it 'calls the method' do
|
15
|
+
instance.should_receive(value).with(no_args)
|
16
|
+
subject
|
17
|
+
end
|
18
|
+
end
|
@@ -31,8 +31,9 @@ describe Virtus::AttributeSet, '#[]=' do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
context 'with a duplicate attribute' do
|
34
|
-
let(:
|
35
|
-
let(:
|
34
|
+
let(:original) { mock('original', :name => name) }
|
35
|
+
let(:attributes) { [ original ] }
|
36
|
+
let(:attribute) { mock('attribute', :name => name) }
|
36
37
|
|
37
38
|
it { should equal(attribute) }
|
38
39
|
|
@@ -41,11 +42,11 @@ describe Virtus::AttributeSet, '#[]=' do
|
|
41
42
|
end
|
42
43
|
|
43
44
|
it 'allows #[] to access the attribute with a symbol' do
|
44
|
-
expect { subject }.to change { object['name'] }.from(
|
45
|
+
expect { subject }.to change { object['name'] }.from(original).to(attribute)
|
45
46
|
end
|
46
47
|
|
47
48
|
it 'allows #[] to access the attribute with a string' do
|
48
|
-
expect { subject }.to change { object[:name] }.from(
|
49
|
+
expect { subject }.to change { object[:name] }.from(original).to(attribute)
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'allows #reset to track overridden attributes' do
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::AttributesAccessor, '#define_reader_method' do
|
4
|
+
subject { described_class.new('Test') }
|
5
|
+
|
6
|
+
let(:attribute) { mock('attribute') }
|
7
|
+
|
8
|
+
if RUBY_VERSION < '1.9'
|
9
|
+
let(:method_name) { 'foo_bar' }
|
10
|
+
else
|
11
|
+
let(:method_name) { :foo_bar }
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
subject.define_reader_method(attribute, method_name, visibility)
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with public visibility" do
|
19
|
+
let(:visibility) { :public }
|
20
|
+
|
21
|
+
its(:public_instance_methods) { should include(method_name) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with private visibility" do
|
25
|
+
let(:visibility) { :private }
|
26
|
+
|
27
|
+
its(:private_instance_methods) { should include(method_name) }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with protected visibility" do
|
31
|
+
let(:visibility) { :protected }
|
32
|
+
|
33
|
+
its(:protected_instance_methods) { should include(method_name) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|