virtus 0.0.5 → 0.0.6

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 (113) hide show
  1. data/.travis.yml +9 -2
  2. data/.yardopts +1 -0
  3. data/History.md +51 -0
  4. data/{README.markdown → README.md} +63 -7
  5. data/TODO +2 -4
  6. data/VERSION +1 -1
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +5 -6
  10. data/config/site.reek +3 -3
  11. data/examples/custom_coercion_spec.rb +50 -0
  12. data/examples/default_values_spec.rb +21 -0
  13. data/lib/virtus.rb +21 -6
  14. data/lib/virtus/attribute.rb +113 -253
  15. data/lib/virtus/attribute/array.rb +6 -3
  16. data/lib/virtus/attribute/boolean.rb +9 -28
  17. data/lib/virtus/attribute/date.rb +9 -12
  18. data/lib/virtus/attribute/date_time.rb +10 -12
  19. data/lib/virtus/attribute/decimal.rb +4 -11
  20. data/lib/virtus/attribute/float.rb +4 -11
  21. data/lib/virtus/attribute/hash.rb +5 -3
  22. data/lib/virtus/attribute/integer.rb +4 -11
  23. data/lib/virtus/attribute/numeric.rb +1 -0
  24. data/lib/virtus/attribute/object.rb +1 -0
  25. data/lib/virtus/attribute/string.rb +4 -11
  26. data/lib/virtus/attribute/time.rb +9 -16
  27. data/lib/virtus/class_methods.rb +42 -7
  28. data/lib/virtus/coercion.rb +32 -0
  29. data/lib/virtus/coercion/date.rb +26 -0
  30. data/lib/virtus/coercion/date_time.rb +26 -0
  31. data/lib/virtus/coercion/decimal.rb +40 -0
  32. data/lib/virtus/coercion/false_class.rb +24 -0
  33. data/lib/virtus/coercion/float.rb +24 -0
  34. data/lib/virtus/coercion/hash.rb +82 -0
  35. data/lib/virtus/coercion/integer.rb +60 -0
  36. data/lib/virtus/coercion/numeric.rb +66 -0
  37. data/lib/virtus/coercion/object.rb +25 -0
  38. data/lib/virtus/coercion/string.rb +155 -0
  39. data/lib/virtus/coercion/symbol.rb +24 -0
  40. data/lib/virtus/coercion/time.rb +26 -0
  41. data/lib/virtus/coercion/time_coercions.rb +85 -0
  42. data/lib/virtus/coercion/true_class.rb +24 -0
  43. data/lib/virtus/instance_methods.rb +7 -0
  44. data/lib/virtus/support/descendants_tracker.rb +1 -1
  45. data/lib/virtus/support/options.rb +114 -0
  46. data/lib/virtus/support/type_lookup.rb +95 -0
  47. data/spec/integration/virtus/attributes/attribute/{typecast_spec.rb → set_spec.rb} +7 -7
  48. data/spec/unit/shared/attribute.rb +3 -3
  49. data/spec/unit/shared/attribute/accept_options.rb +0 -18
  50. data/spec/unit/shared/attribute/accepted_options.rb +0 -6
  51. data/spec/unit/shared/attribute/get.rb +32 -17
  52. data/spec/unit/shared/attribute/inspect.rb +7 -0
  53. data/spec/unit/shared/attribute/primitive.rb +15 -0
  54. data/spec/unit/shared/attribute/set.rb +16 -21
  55. data/spec/unit/virtus/attribute/array_spec.rb +18 -3
  56. data/spec/unit/virtus/attribute/boolean_spec.rb +8 -6
  57. data/spec/unit/virtus/attribute/date_spec.rb +8 -6
  58. data/spec/unit/virtus/attribute/date_time_spec.rb +8 -6
  59. data/spec/unit/virtus/attribute/decimal_spec.rb +18 -6
  60. data/spec/unit/virtus/attribute/float_spec.rb +19 -7
  61. data/spec/unit/virtus/attribute/hash_spec.rb +5 -3
  62. data/spec/unit/virtus/attribute/integer_spec.rb +10 -8
  63. data/spec/unit/virtus/attribute/string_spec.rb +10 -8
  64. data/spec/unit/virtus/attribute/time_spec.rb +8 -6
  65. data/spec/unit/virtus/class_methods/attributes_spec.rb +11 -0
  66. data/spec/unit/virtus/coercion/class_name_reference_spec.rb +17 -0
  67. data/spec/unit/virtus/coercion/date/class_methods/to_datetime_spec.rb +30 -0
  68. data/spec/unit/virtus/coercion/date/class_methods/to_string_spec.rb +12 -0
  69. data/spec/unit/virtus/coercion/date/class_methods/to_time_spec.rb +12 -0
  70. data/spec/unit/virtus/coercion/date_time/class_methods/to_date_spec.rb +30 -0
  71. data/spec/unit/virtus/coercion/date_time/class_methods/to_string_spec.rb +12 -0
  72. data/spec/unit/virtus/coercion/date_time/class_methods/to_time_spec.rb +30 -0
  73. data/spec/unit/virtus/coercion/decimal/class_methods/to_float_spec.rb +12 -0
  74. data/spec/unit/virtus/coercion/decimal/class_methods/to_integer_spec.rb +12 -0
  75. data/spec/unit/virtus/coercion/decimal/class_methods/to_string_spec.rb +12 -0
  76. data/spec/unit/virtus/coercion/false_class/class_methods/to_string_spec.rb +12 -0
  77. data/spec/unit/virtus/coercion/float/class_methods/to_decimal_spec.rb +12 -0
  78. data/spec/unit/virtus/coercion/float/class_methods/to_integer_spec.rb +12 -0
  79. data/spec/unit/virtus/coercion/float/class_methods/to_string_spec.rb +12 -0
  80. data/spec/unit/virtus/coercion/hash/class_methods/to_array_spec.rb +12 -0
  81. data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +31 -0
  82. data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +31 -0
  83. data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +31 -0
  84. data/spec/unit/virtus/coercion/integer/class_methods/to_boolean_spec.rb +25 -0
  85. data/spec/unit/virtus/coercion/integer/class_methods/to_decimal_spec.rb +12 -0
  86. data/spec/unit/virtus/coercion/integer/class_methods/to_float_spec.rb +12 -0
  87. data/spec/unit/virtus/coercion/integer/class_methods/to_string_spec.rb +12 -0
  88. data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +33 -0
  89. data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +29 -0
  90. data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +23 -0
  91. data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +50 -0
  92. data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +23 -0
  93. data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +21 -0
  94. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +21 -0
  95. data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +50 -0
  96. data/spec/unit/virtus/coercion/symbol/class_methods/to_string_spec.rb +12 -0
  97. data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +12 -0
  98. data/spec/unit/virtus/instance_methods/attributes_spec.rb +7 -0
  99. data/spec/unit/virtus/options/accept_options_spec.rb +38 -0
  100. data/spec/unit/virtus/options/accepted_options_spec.rb +21 -0
  101. data/spec/unit/virtus/options/options_spec.rb +11 -0
  102. data/spec/unit/virtus/type_lookup/determine_type_spec.rb +68 -0
  103. data/spec/unit/virtus/type_lookup/primitive_spec.rb +9 -0
  104. data/virtus.gemspec +70 -17
  105. metadata +78 -27
  106. data/History.txt +0 -38
  107. data/lib/virtus/typecast/boolean.rb +0 -29
  108. data/lib/virtus/typecast/numeric.rb +0 -87
  109. data/lib/virtus/typecast/string.rb +0 -24
  110. data/lib/virtus/typecast/time.rb +0 -192
  111. data/spec/unit/shared/attribute/complex.rb +0 -15
  112. data/spec/unit/shared/attribute/options.rb +0 -7
  113. data/spec/unit/virtus/attribute/attribute_spec.rb +0 -12
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::Integer, '.to_decimal' do
4
+ subject { object.to_decimal(fixnum) }
5
+
6
+ let(:object) { described_class }
7
+ let(:fixnum) { 1 }
8
+
9
+ it { should be_instance_of(BigDecimal) }
10
+
11
+ it { should eql(BigDecimal('1.0')) }
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::Integer, '.to_float' do
4
+ subject { object.to_float(fixnum) }
5
+
6
+ let(:object) { described_class }
7
+ let(:fixnum) { 1 }
8
+
9
+ it { should be_instance_of(Float) }
10
+
11
+ it { should eql(1.0) }
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::Integer, '.to_string' do
4
+ subject { object.to_string(integer) }
5
+
6
+ let(:object) { described_class }
7
+ let(:integer) { 1 }
8
+
9
+ it { should be_instance_of(String) }
10
+
11
+ it { should eql('1') }
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::Object, '.method_missing' do
4
+ subject { described_class.send(method_name, value) }
5
+
6
+ let(:value) { '1' }
7
+
8
+ shared_examples_for 'no method error' do
9
+ specify do
10
+ expect { subject }.to raise_error(NoMethodError)
11
+ end
12
+ end
13
+
14
+ context 'with a non-typecast method' do
15
+ let(:method_name) { 'not_here' }
16
+
17
+ it_behaves_like 'no method error'
18
+ end
19
+
20
+ Virtus::Attribute::Object.descendants.each do |attribute|
21
+ let(:method_name) { attribute.coercion_method }
22
+
23
+ context "with #{attribute.coercion_method.inspect} and an input value" do
24
+ it { should equal(value) }
25
+ end
26
+
27
+ context "with #{attribute.coercion_method.inspect} and without an input value" do
28
+ subject { described_class.send(method_name) }
29
+
30
+ it_behaves_like 'no method error'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_boolean' do
4
+ subject { object.to_boolean(string) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ %w[ 1 t true T TRUE ].each do |value|
9
+ context "with #{value.inspect}" do
10
+ let(:string) { value }
11
+
12
+ it { should be(true) }
13
+ end
14
+ end
15
+
16
+ %w[ 0 f false F FALSE ].each do |value|
17
+ context "with #{value.inspect}" do
18
+ let(:string) { value }
19
+
20
+ it { should be(false) }
21
+ end
22
+ end
23
+
24
+ context 'with an invalid boolean string' do
25
+ let(:string) { 'non-boolean' }
26
+
27
+ it { should equal(string) }
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_date' do
4
+ subject { object.to_date(string) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ context 'with a valid date string' do
9
+ let(:string) { "July, 22th, 2011" }
10
+
11
+ it { should be_instance_of(Date) }
12
+
13
+ its(:year) { should == 2011 }
14
+ its(:month) { should == 7 }
15
+ its(:day) { should == 22 }
16
+ end
17
+
18
+ context 'with an invalid date string' do
19
+ let(:string) { 'non-date' }
20
+
21
+ it { should equal(string) }
22
+ end
23
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_datetime' do
4
+ subject { object.to_datetime(string) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ shared_examples_for 'a correct datetime object' do
9
+ it { should be_instance_of(DateTime) }
10
+
11
+ its(:year) { should == year }
12
+ its(:month) { should == month }
13
+ its(:day) { should == day }
14
+ its(:hour) { should == hour }
15
+ its(:min) { should == min }
16
+ its(:sec) { should == sec }
17
+ end
18
+
19
+ context 'with a valid date string' do
20
+ let(:year) { 2011 }
21
+ let(:month) { 7 }
22
+ let(:day) { 22 }
23
+
24
+ context 'not including time part' do
25
+ let(:string) { "July, #{day}th, #{year}" }
26
+
27
+ let(:hour) { 0 }
28
+ let(:min) { 0 }
29
+ let(:sec) { 0 }
30
+
31
+ it_behaves_like 'a correct datetime object'
32
+ end
33
+
34
+ context 'including time part' do
35
+ let(:string) { "July, #{day}, #{year}, #{hour}:#{min}:#{sec}" }
36
+
37
+ let(:hour) { 13 }
38
+ let(:min) { 44 }
39
+ let(:sec) { 50 }
40
+
41
+ it_behaves_like 'a correct datetime object'
42
+ end
43
+ end
44
+
45
+ context 'with an invalid date time string' do
46
+ let(:string) { 'non-datetime' }
47
+
48
+ it { should equal(string) }
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_decimal' do
4
+ subject { object.to_decimal(string) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ { '1' => BigDecimal('1.0'), '1.0' => BigDecimal('1.0'), '.1' => BigDecimal('0.1') }.each do |value, expected|
9
+ context "with #{value.inspect}" do
10
+ let(:string) { value }
11
+
12
+ it { should be_instance_of(BigDecimal) }
13
+
14
+ it { should eql(expected) }
15
+ end
16
+ end
17
+
18
+ context 'with an invalid decimal string' do
19
+ let(:string) { 'non-decimal' }
20
+
21
+ it { should equal(string) }
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_float' do
4
+ subject { described_class.to_float(string) }
5
+
6
+ { '1' => 1.0, '1.0' => 1.0, '.1' => 0.1 }.each do |value, expected|
7
+ context "with #{value.inspect}" do
8
+ let(:string) { value }
9
+
10
+ it { should be_instance_of(Float) }
11
+
12
+ it { should eql(expected) }
13
+ end
14
+ end
15
+
16
+ context 'with an invalid float string' do
17
+ let(:string) { 'non-float' }
18
+
19
+ it { should equal(string) }
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_integer' do
4
+ subject { described_class.to_integer(string) }
5
+
6
+ { '1' => 1, '1.0' => 1, '.1' => 0 }.each do |value, expected|
7
+ context "with #{value.inspect}" do
8
+ let(:string) { value }
9
+
10
+ it { should be_kind_of(Integer) }
11
+
12
+ it { should eql(expected) }
13
+ end
14
+ end
15
+
16
+ context 'with an invalid integer string' do
17
+ let(:string) { 'non-integer' }
18
+
19
+ it { should equal(string) }
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::String, '.to_time' do
4
+ subject { object.to_time(string) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ shared_examples_for 'a correct time object' do
9
+ it { should be_instance_of(Time) }
10
+
11
+ its(:year) { should == year }
12
+ its(:month) { should == month }
13
+ its(:day) { should == day }
14
+ its(:hour) { should == hour }
15
+ its(:min) { should == min }
16
+ its(:sec) { should == sec }
17
+ end
18
+
19
+ context 'with a valid time string' do
20
+ let(:year) { 2011 }
21
+ let(:month) { 7 }
22
+ let(:day) { 22 }
23
+
24
+ context 'not including time part' do
25
+ let(:string) { "July, #{day}th, #{year}" }
26
+
27
+ let(:hour) { 0 }
28
+ let(:min) { 0 }
29
+ let(:sec) { 0 }
30
+
31
+ it_behaves_like 'a correct time object'
32
+ end
33
+
34
+ context 'including time part' do
35
+ let(:string) { "July, #{day}, #{year}, #{hour}:#{min}:#{sec}" }
36
+
37
+ let(:hour) { 13 }
38
+ let(:min) { 44 }
39
+ let(:sec) { 50 }
40
+
41
+ it_behaves_like 'a correct time object'
42
+ end
43
+ end
44
+
45
+ context 'with an invalid date time string' do
46
+ let(:string) { '2999' }
47
+
48
+ it { should equal(string) }
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::Symbol, '.to_string' do
4
+ subject { object.to_string(symbol) }
5
+
6
+ let(:object) { described_class }
7
+ let(:symbol) { :piotr }
8
+
9
+ it { should be_instance_of(String) }
10
+
11
+ it { should eql('piotr') }
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Coercion::TrueClass, '.to_string' do
4
+ subject { object.to_string(true_class) }
5
+
6
+ let(:object) { described_class }
7
+ let(:true_class) { true }
8
+
9
+ it { should be_instance_of(String) }
10
+
11
+ it { should eql('true') }
12
+ end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
+ # TODO: split this into separate files - solnic
3
4
  describe Virtus do
4
5
  let(:model) do
5
6
  Class.new do
@@ -25,6 +26,12 @@ describe Virtus do
25
26
  end
26
27
  end
27
28
 
29
+ describe '#to_hash' do
30
+ it 'returns attributes' do
31
+ object.to_hash.should == object.attributes
32
+ end
33
+ end
34
+
28
35
  describe "#attributes=" do
29
36
  before do
30
37
  object.attributes = attributes
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Options, '.accept_options' do
4
+ let(:object) do
5
+ Class.new do
6
+ extend Virtus::DescendantsTracker
7
+ extend Virtus::Options
8
+ end
9
+ end
10
+
11
+ let(:descendant) { Class.new(object) }
12
+ let(:new_option) { :width }
13
+
14
+ specify { object.should respond_to(:accept_options) }
15
+ specify { descendant.should respond_to(:accept_options) }
16
+
17
+ before :all do
18
+ object.accepted_options.should_not include(new_option)
19
+ descendant.accepted_options.should_not include(new_option)
20
+ object.accept_options(new_option)
21
+ end
22
+
23
+ it "sets new accepted options on itself" do
24
+ object.accepted_options.should include(new_option)
25
+ end
26
+
27
+ it "sets new accepted option on its descendants" do
28
+ descendant.accepted_options.should include(new_option)
29
+ end
30
+
31
+ it "creates option accessors" do
32
+ object.should respond_to(new_option)
33
+ end
34
+
35
+ it "creates option accessors on descendant" do
36
+ descendant.should respond_to(new_option)
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Options, '.accepted_options' do
4
+ subject { object.accepted_options }
5
+
6
+ specify { object.should respond_to(:accepted_options) }
7
+
8
+ let(:object) do
9
+ Class.new do
10
+ extend Virtus::DescendantsTracker
11
+ extend Virtus::Options
12
+ end
13
+ end
14
+
15
+ let(:options) { [ :width, :height ] }
16
+
17
+ before { object.accept_options(*options) }
18
+
19
+ it { should be_instance_of(Array) }
20
+ it { should include(*options) }
21
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Options, '.options' do
4
+ subject { object.options }
5
+
6
+ specify { object.should respond_to(:options) }
7
+
8
+ let(:object) { Class.new { extend Virtus::Options } }
9
+
10
+ it { should be_instance_of(Hash) }
11
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::TypeLookup, '#determine_type' do
4
+ subject { object.determine_type(class_or_name) }
5
+
6
+ let(:object) do
7
+ Class.new do
8
+ extend Virtus::DescendantsTracker
9
+ extend Virtus::TypeLookup
10
+ end
11
+ end
12
+
13
+ let(:descendant) do
14
+ object.const_set :String, Class.new(object) {
15
+ def self.primitive
16
+ ::String
17
+ end
18
+ }
19
+ end
20
+
21
+ context 'with a TypeLookup class' do
22
+ context 'when the argument is a descendant of the object' do
23
+ let(:class_or_name) { descendant }
24
+
25
+ it { should equal(descendant) }
26
+ end
27
+
28
+ context 'when the argument is the same object' do
29
+ let(:class_or_name) { object }
30
+
31
+ it { should be_nil }
32
+ end
33
+ end
34
+
35
+ context 'with a class' do
36
+ context 'when the argument is a known primitive' do
37
+ let(:class_or_name) { ::String }
38
+
39
+ it { should equal(descendant) }
40
+ end
41
+
42
+ context 'when the argument is an unknown class' do
43
+ let(:class_or_name) { Class.new }
44
+
45
+ it { should be_nil }
46
+ end
47
+ end
48
+
49
+ context 'with an instance' do
50
+ context 'when the argument is the name of a known constant' do
51
+ let(:class_or_name) { 'String' }
52
+
53
+ it { should equal(descendant) }
54
+ end
55
+
56
+ context 'when the argument does not stringify to a valid constant name' do
57
+ let(:class_or_name) { Object.new }
58
+
59
+ it { should be_nil }
60
+ end
61
+
62
+ context 'when the argument is not the name of a known constant' do
63
+ let(:class_or_name) { 'Unknown' }
64
+
65
+ it { should be_nil }
66
+ end
67
+ end
68
+ end