virtus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.markdown +83 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/lib/virtus.rb +61 -0
- data/lib/virtus/attributes/array.rb +8 -0
- data/lib/virtus/attributes/attribute.rb +214 -0
- data/lib/virtus/attributes/boolean.rb +39 -0
- data/lib/virtus/attributes/date.rb +44 -0
- data/lib/virtus/attributes/date_time.rb +43 -0
- data/lib/virtus/attributes/decimal.rb +24 -0
- data/lib/virtus/attributes/float.rb +20 -0
- data/lib/virtus/attributes/hash.rb +8 -0
- data/lib/virtus/attributes/integer.rb +20 -0
- data/lib/virtus/attributes/numeric.rb +9 -0
- data/lib/virtus/attributes/object.rb +8 -0
- data/lib/virtus/attributes/string.rb +11 -0
- data/lib/virtus/attributes/time.rb +45 -0
- data/lib/virtus/attributes/typecast/numeric.rb +32 -0
- data/lib/virtus/attributes/typecast/time.rb +27 -0
- data/lib/virtus/class_methods.rb +60 -0
- data/lib/virtus/instance_methods.rb +80 -0
- data/lib/virtus/support/chainable.rb +15 -0
- data/spec/integration/virtus/class_methods/attribute_spec.rb +63 -0
- data/spec/integration/virtus/class_methods/attributes_spec.rb +24 -0
- data/spec/integration/virtus/class_methods/const_missing_spec.rb +44 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/shared/attribute.rb +157 -0
- data/spec/unit/virtus/attributes/array_spec.rb +9 -0
- data/spec/unit/virtus/attributes/attribute_spec.rb +13 -0
- data/spec/unit/virtus/attributes/boolean_spec.rb +97 -0
- data/spec/unit/virtus/attributes/date_spec.rb +52 -0
- data/spec/unit/virtus/attributes/date_time_spec.rb +65 -0
- data/spec/unit/virtus/attributes/decimal_spec.rb +98 -0
- data/spec/unit/virtus/attributes/float_spec.rb +98 -0
- data/spec/unit/virtus/attributes/hash_spec.rb +9 -0
- data/spec/unit/virtus/attributes/integer_spec.rb +98 -0
- data/spec/unit/virtus/attributes/numeric/class_methods/descendants_spec.rb +15 -0
- data/spec/unit/virtus/attributes/object/class_methods/descendants_spec.rb +16 -0
- data/spec/unit/virtus/attributes/string_spec.rb +20 -0
- data/spec/unit/virtus/attributes/time_spec.rb +71 -0
- data/spec/unit/virtus/class_methods/new_spec.rb +41 -0
- data/spec/unit/virtus/determine_type_spec.rb +20 -0
- data/spec/unit/virtus/instance_methods/attribute_get_spec.rb +22 -0
- data/spec/unit/virtus/instance_methods/attribute_set_spec.rb +30 -0
- data/spec/unit/virtus/instance_methods/attributes_spec.rb +37 -0
- data/virtus.gemspec +95 -0
- metadata +131 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Date do
|
4
|
+
it_should_behave_like 'Attribute' do
|
5
|
+
let(:attribute_name) { :created_on }
|
6
|
+
let(:attribute_value) { Date.today }
|
7
|
+
let(:attribute_value_other) { (Date.today+1).to_s }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#typecast' do
|
11
|
+
let(:model) { Class.new { include Virtus } }
|
12
|
+
let(:attribute) { model.attribute(:bday, Virtus::Attributes::Date) }
|
13
|
+
|
14
|
+
let(:year) { 2011 }
|
15
|
+
let(:month) { 4 }
|
16
|
+
let(:day) { 7 }
|
17
|
+
|
18
|
+
subject { attribute.typecast(value) }
|
19
|
+
|
20
|
+
shared_examples_for "a correct date" do
|
21
|
+
it { should be_kind_of(Date) }
|
22
|
+
its(:year) { should == year }
|
23
|
+
its(:month) { should == month }
|
24
|
+
its(:day) { should == day }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with a time' do
|
28
|
+
it_should_behave_like "a correct date" do
|
29
|
+
let(:value) { Time.local(year, month, day) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a hash' do
|
34
|
+
it_should_behave_like "a correct date" do
|
35
|
+
let(:value) do
|
36
|
+
{ :year => year, :month => month, :day => day }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a string' do
|
42
|
+
it_should_behave_like "a correct date" do
|
43
|
+
let(:value) { "April #{day}th, #{year}" }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a on-date value' do
|
48
|
+
let(:value) { 'non-date' }
|
49
|
+
it { should == value }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::DateTime do
|
4
|
+
it_should_behave_like 'Attribute' do
|
5
|
+
let(:attribute_name) { :created_at }
|
6
|
+
let(:attribute_value) { DateTime.now }
|
7
|
+
let(:attribute_value_other) { DateTime.now.to_s }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#typecast' do
|
11
|
+
let(:model) { Class.new { include Virtus } }
|
12
|
+
let(:attribute) { model.attribute(:bday, Virtus::Attributes::DateTime) }
|
13
|
+
|
14
|
+
let(:year) { 2011 }
|
15
|
+
let(:month) { 4 }
|
16
|
+
let(:day) { 7 }
|
17
|
+
let(:hour) { 1 }
|
18
|
+
let(:min) { 26 }
|
19
|
+
let(:sec) { 49 }
|
20
|
+
|
21
|
+
subject { attribute.typecast(value) }
|
22
|
+
|
23
|
+
shared_examples_for "a correct date time" do
|
24
|
+
it { should be_kind_of(DateTime) }
|
25
|
+
its(:year) { should == year }
|
26
|
+
its(:month) { should == month }
|
27
|
+
its(:day) { should == day }
|
28
|
+
its(:hour) { should == hour }
|
29
|
+
its(:min) { should == min }
|
30
|
+
its(:sec) { should == sec }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with a hash' do
|
34
|
+
it_should_behave_like "a correct date time" do
|
35
|
+
let(:value) do
|
36
|
+
{ :year => year, :month => month, :day => day,
|
37
|
+
:hour => hour, :min => min, :sec => sec }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with a string' do
|
43
|
+
context "without hour, min and sec" do
|
44
|
+
let(:hour) { 0 }
|
45
|
+
let(:min) { 0 }
|
46
|
+
let(:sec) { 0 }
|
47
|
+
|
48
|
+
it_should_behave_like "a correct date time" do
|
49
|
+
let(:value) { "April #{day}th, #{year}" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with hour, min and sec" do
|
54
|
+
it_should_behave_like "a correct date time" do
|
55
|
+
let(:value) { "April #{day}th, #{year}, #{hour}:#{min}:#{sec}" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with a on-date value' do
|
61
|
+
let(:value) { 'non-date' }
|
62
|
+
it { should == value }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Decimal do
|
4
|
+
it_should_behave_like 'Attribute' do
|
5
|
+
let(:attribute_name) { :price }
|
6
|
+
let(:attribute_value) { BigDecimal("12.3456789") }
|
7
|
+
let(:attribute_value_other) { "12.3456789" }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#typecast' do
|
11
|
+
let(:model) { Class.new { include Virtus } }
|
12
|
+
let(:attribute) { model.attribute(:price, Virtus::Attributes::Decimal) }
|
13
|
+
|
14
|
+
subject { attribute.typecast(value) }
|
15
|
+
|
16
|
+
context "with 24.0 big decimal" do
|
17
|
+
let(:value) { BigDecimal('24.0') }
|
18
|
+
it { should == value }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a zero string integer' do
|
22
|
+
let(:value) { '0' }
|
23
|
+
it { should == BigDecimal('0.0') }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a positive string integer' do
|
27
|
+
let(:value) { '24' }
|
28
|
+
it { should == BigDecimal('24.0') }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a negative string integer' do
|
32
|
+
let(:value) { '-24' }
|
33
|
+
it { should == BigDecimal('-24.0') }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a zero string float' do
|
37
|
+
let(:value) { '0.0' }
|
38
|
+
it { should == BigDecimal('0.0') }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a positive string float' do
|
42
|
+
let(:value) { '24.35' }
|
43
|
+
it { should == BigDecimal('24.35') }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a negative string float' do
|
47
|
+
let(:value) { '-24.35' }
|
48
|
+
it { should == BigDecimal('-24.35') }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with a zero string float, with no leading digits' do
|
52
|
+
let(:value) { '.0' }
|
53
|
+
it { should == BigDecimal('0.0') }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with a positive string float, with no leading digits' do
|
57
|
+
let(:value) { '0.41' }
|
58
|
+
it { should == BigDecimal('0.41') }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with a zero integer' do
|
62
|
+
let(:value) { 0 }
|
63
|
+
it { should == BigDecimal('0.0') }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with a positive integer' do
|
67
|
+
let(:value) { 24 }
|
68
|
+
it { should == BigDecimal('24.0') }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a negative integer' do
|
72
|
+
let(:value) { -24 }
|
73
|
+
it { should == BigDecimal('-24.0') }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with a zero float' do
|
77
|
+
let(:value) { 0.0 }
|
78
|
+
it { should == BigDecimal('0.0') }
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with a positive float' do
|
82
|
+
let(:value) { 24.35 }
|
83
|
+
it { should == BigDecimal('24.35') }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with a negative float' do
|
87
|
+
let(:value) { -24.35 }
|
88
|
+
it { should == BigDecimal('-24.35') }
|
89
|
+
end
|
90
|
+
|
91
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |non_num_value|
|
92
|
+
context "with a non-numeric value = #{non_num_value.inspect}" do
|
93
|
+
let(:value) { non_num_value }
|
94
|
+
it { should == non_num_value }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Float do
|
4
|
+
it_should_behave_like 'Attribute' do
|
5
|
+
let(:attribute_name) { :score }
|
6
|
+
let(:attribute_value) { 12.34 }
|
7
|
+
let(:attribute_value_other) { "12.34" }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#typecast' do
|
11
|
+
let(:model) { Class.new { include Virtus } }
|
12
|
+
let(:attribute) { model.attribute(:score, Virtus::Attributes::Float) }
|
13
|
+
|
14
|
+
subject { attribute.typecast(value) }
|
15
|
+
|
16
|
+
context "with a float" do
|
17
|
+
let(:value) { 24.0 }
|
18
|
+
it { should == value }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a zero string integer' do
|
22
|
+
let(:value) { '0' }
|
23
|
+
it { should == 0.0 }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a positive string integer' do
|
27
|
+
let(:value) { '24' }
|
28
|
+
it { should == 24.0 }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a negative string integer' do
|
32
|
+
let(:value) { '-24' }
|
33
|
+
it { should == -24.0 }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a zero string float' do
|
37
|
+
let(:value) { '0.0' }
|
38
|
+
it { should == 0.0 }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a positive string float' do
|
42
|
+
let(:value) { '24.35' }
|
43
|
+
it { should == 24.35 }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a negative string float' do
|
47
|
+
let(:value) { '-24.35' }
|
48
|
+
it { should == -24.35 }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with a zero string float, with no leading digits' do
|
52
|
+
let(:value) { '.0' }
|
53
|
+
it { should == 0.0 }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with a positive string float, with no leading digits' do
|
57
|
+
let(:value) { '.41' }
|
58
|
+
it { should == 0.41 }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with a zero integer' do
|
62
|
+
let(:value) { 0 }
|
63
|
+
it { should == 0.0 }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with a positive integer' do
|
67
|
+
let(:value) { 24 }
|
68
|
+
it { should == 24.0 }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a negative integer' do
|
72
|
+
let(:value) { -24 }
|
73
|
+
it { should == -24.0 }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with a zero decimal' do
|
77
|
+
let(:value) { BigDecimal('0.0') }
|
78
|
+
it { should == 0.0 }
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with a positive decimal' do
|
82
|
+
let(:value) { BigDecimal('24.35') }
|
83
|
+
it { should == 24.35 }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with a negative decimal' do
|
87
|
+
let(:value) { BigDecimal('-24.35') }
|
88
|
+
it { should == -24.35 }
|
89
|
+
end
|
90
|
+
|
91
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |non_num_value|
|
92
|
+
context "does not typecast non-numeric value #{non_num_value.inspect}" do
|
93
|
+
let(:value) { non_num_value }
|
94
|
+
it { should == non_num_value }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Integer do
|
4
|
+
it_should_behave_like 'Attribute' do
|
5
|
+
let(:attribute_name) { :age }
|
6
|
+
let(:attribute_value) { 28 }
|
7
|
+
let(:attribute_value_other) { "28" }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#typecast' do
|
11
|
+
let(:model) { Class.new { include Virtus } }
|
12
|
+
let(:attribute) { model.attribute(:age, Virtus::Attributes::Integer) }
|
13
|
+
|
14
|
+
subject { attribute.typecast(value) }
|
15
|
+
|
16
|
+
context "with an integer" do
|
17
|
+
let(:value) { 24 }
|
18
|
+
it { should == value }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a zero string integer' do
|
22
|
+
let(:value) { 24 }
|
23
|
+
it { should == value }
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a positive string integer' do
|
27
|
+
let(:value) { '24' }
|
28
|
+
it { should == 24 }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a negative string integer' do
|
32
|
+
let(:value) { '-24' }
|
33
|
+
it { should == -24 }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a zero string float' do
|
37
|
+
let(:value) { 0.0 }
|
38
|
+
it { should == 0 }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a positive string float' do
|
42
|
+
let(:value) { '24.35' }
|
43
|
+
it { should == 24 }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a negative string float' do
|
47
|
+
let(:value) { '-24.35' }
|
48
|
+
it { should == -24 }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with a zero string float, with no leading digits' do
|
52
|
+
let(:value) { '.0' }
|
53
|
+
it { should == 0 }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with a positive string float, with no leading digits' do
|
57
|
+
let(:value) { '.41' }
|
58
|
+
it { should == 0 }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with a zero float' do
|
62
|
+
let(:value) { 0.0 }
|
63
|
+
it { should == 0 }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with a positive float' do
|
67
|
+
let(:value) { 24.35 }
|
68
|
+
it { should == 24 }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a negative float' do
|
72
|
+
let(:value) { -24.35 }
|
73
|
+
it { should == -24 }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with a zero decimal' do
|
77
|
+
let(:value) { BigDecimal('0.0') }
|
78
|
+
it { should == 0 }
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with a positive decimal' do
|
82
|
+
let(:value) { BigDecimal('24.35') }
|
83
|
+
it { should == 24 }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with a negative decimal' do
|
87
|
+
let(:value) { BigDecimal('-24.35') }
|
88
|
+
it { should == -24 }
|
89
|
+
end
|
90
|
+
|
91
|
+
[ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |non_num_value|
|
92
|
+
context "does not typecast non-numeric value #{non_num_value.inspect}" do
|
93
|
+
let(:value) { non_num_value }
|
94
|
+
it { should == non_num_value }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Numeric, '.descendants' do
|
4
|
+
subject { described_class.descendants }
|
5
|
+
|
6
|
+
let(:known_descendants) do
|
7
|
+
[ Virtus::Attributes::Decimal,
|
8
|
+
Virtus::Attributes::Float,
|
9
|
+
Virtus::Attributes::Integer ]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return all known attribute classes" do
|
13
|
+
subject.should == known_descendants
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Virtus::Attributes::Object, '.descendants' do
|
4
|
+
subject { described_class.descendants }
|
5
|
+
|
6
|
+
let(:known_descendants) do
|
7
|
+
[ Virtus::Attributes::Array, Virtus::Attributes::Boolean,
|
8
|
+
Virtus::Attributes::Date, Virtus::Attributes::DateTime,
|
9
|
+
Virtus::Attributes::Numeric, Virtus::Attributes::Hash,
|
10
|
+
Virtus::Attributes::String, Virtus::Attributes::Time ]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return all known attribute classes" do
|
14
|
+
subject.should == known_descendants
|
15
|
+
end
|
16
|
+
end
|