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