virtus 0.4.0 → 0.4.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.
- data/.travis.yml +1 -0
- data/Changelog.md +9 -1
- data/Gemfile +1 -1
- data/Guardfile +0 -1
- data/README.md +0 -2
- data/Rakefile +0 -1
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/site.reek +1 -1
- data/lib/virtus.rb +4 -2
- data/lib/virtus/attribute.rb +2 -2
- data/lib/virtus/attribute/boolean.rb +1 -1
- data/lib/virtus/attribute/default_value.rb +4 -14
- data/lib/virtus/attribute/default_value/from_callable.rb +5 -3
- data/lib/virtus/attribute/default_value/from_clonable.rb +4 -4
- data/lib/virtus/attribute/default_value/from_symbol.rb +4 -4
- data/lib/virtus/attribute/embedded_value.rb +0 -2
- data/lib/virtus/class_methods.rb +1 -1
- data/lib/virtus/coercion.rb +1 -1
- data/lib/virtus/coercion/numeric.rb +12 -12
- data/lib/virtus/coercion/object.rb +1 -1
- data/lib/virtus/support/equalizer.rb +129 -0
- data/lib/virtus/support/type_lookup.rb +2 -2
- data/lib/virtus/value_object.rb +0 -5
- data/lib/virtus/version.rb +1 -1
- data/spec/spec_helper.rb +0 -3
- data/spec/unit/virtus/attribute/default_spec.rb +0 -4
- data/spec/unit/virtus/attribute/default_value/call_spec.rb +21 -0
- data/spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb +1 -3
- data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +1 -3
- data/spec/unit/virtus/attribute/default_value/from_callable/call_spec.rb +19 -0
- data/spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb +1 -4
- data/spec/unit/virtus/attribute/default_value/from_clonable/call_spec.rb +20 -0
- data/spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb +1 -2
- data/spec/unit/virtus/attribute/default_value/from_symbol/{evaluate_spec.rb → call_spec.rb} +3 -4
- data/spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb +1 -4
- data/spec/unit/virtus/attribute/default_value/value_spec.rb +2 -3
- data/spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb +0 -1
- data/spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb +0 -1
- data/spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb +0 -1
- data/spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb +0 -1
- data/spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb +0 -1
- data/spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb +10 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb +10 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb +10 -0
- data/spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb +12 -0
- data/spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb +0 -1
- data/spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb +0 -1
- data/spec/unit/virtus/coercion/time_coercions/to_date_spec.rb +33 -0
- data/spec/unit/virtus/coercion/time_coercions/to_datetime_spec.rb +33 -0
- data/spec/unit/virtus/coercion/time_coercions/to_string_spec.rb +18 -0
- data/spec/unit/virtus/coercion/time_coercions/to_time_spec.rb +33 -0
- data/spec/unit/virtus/equalizer/append_spec.rb +82 -0
- data/spec/unit/virtus/equalizer/class_method/new_spec.rb +111 -0
- data/spec/unit/virtus/equalizer/methods/eql_spec.rb +47 -0
- data/spec/unit/virtus/equalizer/methods/equal_value_spec.rb +57 -0
- data/spec/unit/virtus/type_lookup/class_methods/extended_spec.rb +0 -1
- data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +15 -8
- data/spec/unit/virtus/value_object/class_methods/equalizer_spec.rb +1 -1
- data/spec/unit/virtus/value_object/{with_spec.rb → instance_methods/with_spec.rb} +1 -1
- data/tasks/metrics/heckle.rake +2 -2
- data/virtus.gemspec +2 -1
- metadata +29 -23
- data/lib/virtus/value_object/equalizer.rb +0 -125
- data/spec/unit/virtus/attribute/default_value/attribute_spec.rb +0 -11
- data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +0 -22
- data/spec/unit/virtus/attribute/default_value/from_callable/evaluate_spec.rb +0 -21
- data/spec/unit/virtus/attribute/default_value/from_clonable/evaluate_spec.rb +0 -22
- data/spec/unit/virtus/value_object/equalizer/append_spec.rb +0 -61
@@ -84,7 +84,7 @@ module Virtus
|
|
84
84
|
descendants.reverse_each do |descendant|
|
85
85
|
descendant_primitive = descendant.primitive
|
86
86
|
next unless primitive <= descendant_primitive
|
87
|
-
type = descendant if type.nil?
|
87
|
+
type = descendant if type.nil? or type.primitive > descendant_primitive
|
88
88
|
end
|
89
89
|
type
|
90
90
|
end
|
@@ -100,7 +100,7 @@ module Virtus
|
|
100
100
|
#
|
101
101
|
# @api private
|
102
102
|
def determine_type_from_string(string)
|
103
|
-
if string =~ TYPE_FORMAT
|
103
|
+
if string =~ TYPE_FORMAT and const_defined?(string, *EXTRA_CONST_ARGS)
|
104
104
|
const_get(string, *EXTRA_CONST_ARGS)
|
105
105
|
end
|
106
106
|
end
|
data/lib/virtus/value_object.rb
CHANGED
data/lib/virtus/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -11,8 +11,6 @@ describe Virtus::Attribute, '#default' do
|
|
11
11
|
context 'when the default is not specified' do
|
12
12
|
it { should be_instance_of(Virtus::Attribute::DefaultValue) }
|
13
13
|
|
14
|
-
its(:attribute) { should be(object) }
|
15
|
-
|
16
14
|
its(:value) { should be_nil }
|
17
15
|
end
|
18
16
|
|
@@ -25,8 +23,6 @@ describe Virtus::Attribute, '#default' do
|
|
25
23
|
|
26
24
|
it { should be_instance_of(Virtus::Attribute::DefaultValue::FromClonable) }
|
27
25
|
|
28
|
-
its(:attribute) { should be(object) }
|
29
|
-
|
30
26
|
its(:value) { should be(default) }
|
31
27
|
end
|
32
28
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue, '#call' do
|
4
|
+
subject { object.call(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.build(value) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
let(:instance) { mock('instance') }
|
9
|
+
let(:response) { stub('response') }
|
10
|
+
|
11
|
+
# smallest number that is Bignum across major ruby impls
|
12
|
+
bignum = 0x7fffffffffffffff + 1
|
13
|
+
|
14
|
+
[ nil, true, false, 0, 0.0, bignum, :symbol ].each do |value|
|
15
|
+
context "when the value is #{value.inspect} (#{value.class})" do
|
16
|
+
let(:value) { value }
|
17
|
+
|
18
|
+
it { should be(value) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::DefaultValue, '.build' do
|
4
|
-
subject { described_class.build(
|
5
|
-
|
6
|
-
let(:attribute) { mock('attribute') }
|
4
|
+
subject { described_class.build(default) }
|
7
5
|
|
8
6
|
context 'when default is a symbol' do
|
9
7
|
let(:default) { :symbol }
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::DefaultValue, '.new' do
|
4
|
-
subject { described_class.new(
|
5
|
-
|
6
|
-
let(:attribute) { Virtus::Attribute::String.new(:attribute) }
|
4
|
+
subject { described_class.new(value) }
|
7
5
|
|
8
6
|
context 'with a cloneable value' do
|
9
7
|
let(:value) { 'something' }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromCallable, '#call' do
|
4
|
+
subject { object.call(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(value) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
let(:instance) { mock('instance') }
|
9
|
+
let(:response) { stub('response') }
|
10
|
+
|
11
|
+
before { value.stub(:call => response) }
|
12
|
+
|
13
|
+
it { should be(response) }
|
14
|
+
|
15
|
+
it 'calls the value with the instance and attribute' do
|
16
|
+
value.should_receive(:call).with(instance)
|
17
|
+
subject
|
18
|
+
end
|
19
|
+
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::DefaultValue::FromCallable, '.handle?' do
|
4
|
-
subject { described_class.handle?(
|
5
|
-
|
6
|
-
let(:attribute) { mock('attribute') }
|
4
|
+
subject { described_class.handle?(default) }
|
7
5
|
|
8
6
|
context 'with a callable' do
|
9
7
|
let(:default) { Proc.new {} }
|
@@ -17,4 +15,3 @@ describe Virtus::Attribute::DefaultValue::FromCallable, '.handle?' do
|
|
17
15
|
it { should be(false) }
|
18
16
|
end
|
19
17
|
end
|
20
|
-
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromClonable, '#call' do
|
4
|
+
subject { object.call(instance) }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(value) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
let(:instance) { mock('instance') }
|
9
|
+
let(:response) { mock('response') }
|
10
|
+
let(:clone) { mock('clone') }
|
11
|
+
|
12
|
+
before { value.stub(:clone => clone) }
|
13
|
+
|
14
|
+
it { should be(clone) }
|
15
|
+
|
16
|
+
it 'clones the value' do
|
17
|
+
value.should_receive(:clone).with(no_args)
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::DefaultValue::FromClonable, '.handle?' do
|
4
|
-
subject { described_class.handle?(
|
4
|
+
subject { described_class.handle?(default) }
|
5
5
|
|
6
6
|
let(:attribute) { mock('attribute') }
|
7
7
|
|
@@ -17,4 +17,3 @@ describe Virtus::Attribute::DefaultValue::FromClonable, '.handle?' do
|
|
17
17
|
it { should be(false) }
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Virtus::Attribute::DefaultValue::FromSymbol, '#
|
4
|
-
subject { object.
|
3
|
+
describe Virtus::Attribute::DefaultValue::FromSymbol, '#call' do
|
4
|
+
subject { object.call(instance) }
|
5
5
|
|
6
|
-
let(:object) { described_class.new(
|
7
|
-
let(:attribute) { mock('attribute') }
|
6
|
+
let(:object) { described_class.new(value) }
|
8
7
|
let(:instance) { mock('instance', value => retval) }
|
9
8
|
let(:value) { :set_default }
|
10
9
|
let(:retval) { mock('retval') }
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Virtus::Attribute::DefaultValue::FromSymbol, '.handle?' do
|
4
|
-
subject { described_class.handle?(
|
5
|
-
|
6
|
-
let(:attribute) { mock('attribute') }
|
4
|
+
subject { described_class.handle?(default) }
|
7
5
|
|
8
6
|
context 'with a symbol' do
|
9
7
|
let(:default) { :method_name }
|
@@ -17,4 +15,3 @@ describe Virtus::Attribute::DefaultValue::FromSymbol, '.handle?' do
|
|
17
15
|
it { should be(false) }
|
18
16
|
end
|
19
17
|
end
|
20
|
-
|
@@ -3,9 +3,8 @@ require 'spec_helper'
|
|
3
3
|
describe Virtus::Attribute::DefaultValue, '#value' do
|
4
4
|
subject { object.value }
|
5
5
|
|
6
|
-
let(:object)
|
7
|
-
let(:
|
8
|
-
let(:value) { mock('value') }
|
6
|
+
let(:object) { described_class.new(value) }
|
7
|
+
let(:value) { mock('value') }
|
9
8
|
|
10
9
|
it { should be(value) }
|
11
10
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Coercion::Numeric, '.to_string' do
|
4
|
+
subject { object.to_string(numeric) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
let(:numeric) { Rational(2, 2) }
|
8
|
+
|
9
|
+
let(:coerced_value) { RUBY_VERSION < '1.9' ? '1' : '1/1' }
|
10
|
+
|
11
|
+
it { should eql(coerced_value) }
|
12
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Coercion::TimeCoercions, '.to_date' do
|
4
|
+
subject { object.to_date(value) }
|
5
|
+
|
6
|
+
let(:object) { Class.new(Virtus::Coercion::Object) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
|
9
|
+
context 'when the value responds to #to_date' do
|
10
|
+
before do
|
11
|
+
object.extend Virtus::Coercion::TimeCoercions
|
12
|
+
|
13
|
+
value.should_receive(:to_date).and_return(Date.new(2011, 1, 1))
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should be_instance_of(Date) }
|
17
|
+
|
18
|
+
it { should eql(Date.new(2011, 1, 1)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when the value does not respond to #to_date' do
|
22
|
+
before do
|
23
|
+
object.extend Virtus::Coercion::TimeCoercions
|
24
|
+
|
25
|
+
# use a string that Date.parse can handle
|
26
|
+
value.should_receive(:to_s).and_return('2011-01-01')
|
27
|
+
end
|
28
|
+
|
29
|
+
it { should be_instance_of(Date) }
|
30
|
+
|
31
|
+
it { should eql(Date.new(2011, 1, 1)) }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Coercion::TimeCoercions, '.to_datetime' do
|
4
|
+
subject { object.to_datetime(value) }
|
5
|
+
|
6
|
+
let(:object) { Class.new(Virtus::Coercion::Object) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
|
9
|
+
context 'when the value responds to #to_datetime' do
|
10
|
+
before do
|
11
|
+
object.extend Virtus::Coercion::TimeCoercions
|
12
|
+
|
13
|
+
value.should_receive(:to_datetime).and_return(DateTime.new(2011, 1, 1, 0, 0, 0))
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should be_instance_of(DateTime) }
|
17
|
+
|
18
|
+
it { should eql(DateTime.new(2011, 1, 1, 0, 0, 0)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when the value does not respond to #to_datetime' do
|
22
|
+
before do
|
23
|
+
object.extend Virtus::Coercion::TimeCoercions
|
24
|
+
|
25
|
+
# use a string that DateTime.parse can handle
|
26
|
+
value.should_receive(:to_s).and_return('2011-01-01T00:00:00+00:00')
|
27
|
+
end
|
28
|
+
|
29
|
+
it { should be_instance_of(DateTime) }
|
30
|
+
|
31
|
+
it { should eql(DateTime.new(2011, 1, 1, 0, 0, 0)) }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Coercion::TimeCoercions, '.to_string' do
|
4
|
+
subject { object.to_string(value) }
|
5
|
+
|
6
|
+
let(:object) { Class.new(Virtus::Coercion::Object) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
|
9
|
+
before do
|
10
|
+
object.extend Virtus::Coercion::TimeCoercions
|
11
|
+
|
12
|
+
value.should_receive(:to_s).and_return('2011-01-01')
|
13
|
+
end
|
14
|
+
|
15
|
+
it { should be_instance_of(String) }
|
16
|
+
|
17
|
+
it { should eql('2011-01-01') }
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Coercion::TimeCoercions, '.to_time' do
|
4
|
+
subject { object.to_time(value) }
|
5
|
+
|
6
|
+
let(:object) { Class.new(Virtus::Coercion::Object) }
|
7
|
+
let(:value) { mock('value') }
|
8
|
+
|
9
|
+
context 'when the value responds to #to_time' do
|
10
|
+
before do
|
11
|
+
object.extend Virtus::Coercion::TimeCoercions
|
12
|
+
|
13
|
+
value.should_receive(:to_time).and_return(Time.utc(2011, 1, 1))
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should be_instance_of(Time) }
|
17
|
+
|
18
|
+
it { should eql(Time.utc(2011, 1, 1)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when the value does not respond to #to_time' do
|
22
|
+
before do
|
23
|
+
object.extend Virtus::Coercion::TimeCoercions
|
24
|
+
|
25
|
+
# use a string that Time.parse can handle
|
26
|
+
value.should_receive(:to_s).and_return('Sat Jan 01 00:00:00 UTC 2011')
|
27
|
+
end
|
28
|
+
|
29
|
+
it { should be_instance_of(Time) }
|
30
|
+
|
31
|
+
it { should eql(Time.utc(2011, 1, 1)) }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Equalizer, '#<<' do
|
4
|
+
subject { object << :last_name }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(name, attributes) }
|
7
|
+
let(:name) { 'User' }
|
8
|
+
let(:attributes) { [ :first_name ].freeze }
|
9
|
+
let(:first_name) { 'John' }
|
10
|
+
let(:last_name) { 'Doe' }
|
11
|
+
|
12
|
+
let(:klass) do
|
13
|
+
klass = Class.new { attr_accessor :first_name, :last_name }
|
14
|
+
klass.send(:include, object)
|
15
|
+
klass
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:instance) do
|
19
|
+
klass.new.tap do |instance|
|
20
|
+
instance.first_name = first_name
|
21
|
+
instance.last_name = last_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#eql?' do
|
26
|
+
context 'when the objects are similar' do
|
27
|
+
let(:other) { instance.dup }
|
28
|
+
|
29
|
+
it 'adds a key to the comparison' do
|
30
|
+
expect { subject }.to_not change { instance.eql?(other) }.from(true)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when the objects are different' do
|
35
|
+
let(:other) { instance.dup }
|
36
|
+
|
37
|
+
before do
|
38
|
+
other.last_name = 'Smith'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'adds a key to the comparison' do
|
42
|
+
expect { subject }.to change { instance.eql?(other) }.
|
43
|
+
from(true).to(false)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#==' do
|
49
|
+
context 'when the objects are similar' do
|
50
|
+
let(:other) { instance.dup }
|
51
|
+
|
52
|
+
it 'adds a key to the comparison' do
|
53
|
+
expect { subject }.to_not change { instance == other }.from(true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when the objects are different' do
|
58
|
+
let(:other) { instance.dup }
|
59
|
+
|
60
|
+
before do
|
61
|
+
other.last_name = 'Smith'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'adds a key to the comparison' do
|
65
|
+
expect { subject }.to change { instance == other }.
|
66
|
+
from(true).to(false)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'adds a new to #hash' do
|
72
|
+
expect { subject }.to change(instance, :hash).
|
73
|
+
from(klass.hash ^ first_name.hash).
|
74
|
+
to(klass.hash ^ first_name.hash ^ last_name.hash)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'adds a new to #inspect' do
|
78
|
+
expect { subject }.to change(instance, :inspect).
|
79
|
+
from('#<User first_name="John">').
|
80
|
+
to('#<User first_name="John" last_name="Doe">')
|
81
|
+
end
|
82
|
+
end
|