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.
Files changed (52) hide show
  1. data/.gitignore +4 -0
  2. data/.rvmrc +1 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE +20 -0
  6. data/README.markdown +83 -0
  7. data/Rakefile +27 -0
  8. data/VERSION +1 -0
  9. data/lib/virtus.rb +61 -0
  10. data/lib/virtus/attributes/array.rb +8 -0
  11. data/lib/virtus/attributes/attribute.rb +214 -0
  12. data/lib/virtus/attributes/boolean.rb +39 -0
  13. data/lib/virtus/attributes/date.rb +44 -0
  14. data/lib/virtus/attributes/date_time.rb +43 -0
  15. data/lib/virtus/attributes/decimal.rb +24 -0
  16. data/lib/virtus/attributes/float.rb +20 -0
  17. data/lib/virtus/attributes/hash.rb +8 -0
  18. data/lib/virtus/attributes/integer.rb +20 -0
  19. data/lib/virtus/attributes/numeric.rb +9 -0
  20. data/lib/virtus/attributes/object.rb +8 -0
  21. data/lib/virtus/attributes/string.rb +11 -0
  22. data/lib/virtus/attributes/time.rb +45 -0
  23. data/lib/virtus/attributes/typecast/numeric.rb +32 -0
  24. data/lib/virtus/attributes/typecast/time.rb +27 -0
  25. data/lib/virtus/class_methods.rb +60 -0
  26. data/lib/virtus/instance_methods.rb +80 -0
  27. data/lib/virtus/support/chainable.rb +15 -0
  28. data/spec/integration/virtus/class_methods/attribute_spec.rb +63 -0
  29. data/spec/integration/virtus/class_methods/attributes_spec.rb +24 -0
  30. data/spec/integration/virtus/class_methods/const_missing_spec.rb +44 -0
  31. data/spec/spec_helper.rb +20 -0
  32. data/spec/unit/shared/attribute.rb +157 -0
  33. data/spec/unit/virtus/attributes/array_spec.rb +9 -0
  34. data/spec/unit/virtus/attributes/attribute_spec.rb +13 -0
  35. data/spec/unit/virtus/attributes/boolean_spec.rb +97 -0
  36. data/spec/unit/virtus/attributes/date_spec.rb +52 -0
  37. data/spec/unit/virtus/attributes/date_time_spec.rb +65 -0
  38. data/spec/unit/virtus/attributes/decimal_spec.rb +98 -0
  39. data/spec/unit/virtus/attributes/float_spec.rb +98 -0
  40. data/spec/unit/virtus/attributes/hash_spec.rb +9 -0
  41. data/spec/unit/virtus/attributes/integer_spec.rb +98 -0
  42. data/spec/unit/virtus/attributes/numeric/class_methods/descendants_spec.rb +15 -0
  43. data/spec/unit/virtus/attributes/object/class_methods/descendants_spec.rb +16 -0
  44. data/spec/unit/virtus/attributes/string_spec.rb +20 -0
  45. data/spec/unit/virtus/attributes/time_spec.rb +71 -0
  46. data/spec/unit/virtus/class_methods/new_spec.rb +41 -0
  47. data/spec/unit/virtus/determine_type_spec.rb +20 -0
  48. data/spec/unit/virtus/instance_methods/attribute_get_spec.rb +22 -0
  49. data/spec/unit/virtus/instance_methods/attribute_set_spec.rb +30 -0
  50. data/spec/unit/virtus/instance_methods/attributes_spec.rb +37 -0
  51. data/virtus.gemspec +95 -0
  52. 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,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attributes::Hash do
4
+ it_should_behave_like 'Attribute' do
5
+ let(:attribute_name) { :settings }
6
+ let(:attribute_value) { Hash[:one => 1] }
7
+ let(:attribute_value_other) { Hash[:two => 2] }
8
+ end
9
+ 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