virtus 0.0.2 → 0.0.3

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 (85) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -1
  3. data/Gemfile +20 -1
  4. data/History.txt +21 -0
  5. data/README.markdown +2 -2
  6. data/Rakefile +1 -2
  7. data/VERSION +1 -1
  8. data/config/flay.yml +3 -0
  9. data/config/flog.yml +2 -0
  10. data/config/roodi.yml +18 -0
  11. data/config/site.reek +91 -0
  12. data/config/yardstick.yml +2 -0
  13. data/lib/virtus.rb +51 -45
  14. data/lib/virtus/attribute.rb +301 -0
  15. data/lib/virtus/attribute/array.rb +17 -0
  16. data/lib/virtus/attribute/boolean.rb +60 -0
  17. data/lib/virtus/attribute/date.rb +35 -0
  18. data/lib/virtus/attribute/date_time.rb +34 -0
  19. data/lib/virtus/attribute/decimal.rb +24 -0
  20. data/lib/virtus/attribute/float.rb +33 -0
  21. data/lib/virtus/attribute/hash.rb +18 -0
  22. data/lib/virtus/attribute/integer.rb +30 -0
  23. data/lib/virtus/{attributes → attribute}/numeric.rb +2 -3
  24. data/lib/virtus/{attributes → attribute}/object.rb +2 -1
  25. data/lib/virtus/attribute/string.rb +31 -0
  26. data/lib/virtus/attribute/time.rb +34 -0
  27. data/lib/virtus/class_methods.rb +25 -8
  28. data/lib/virtus/instance_methods.rb +48 -9
  29. data/lib/virtus/support/chainable.rb +4 -6
  30. data/lib/virtus/typecast/boolean.rb +27 -0
  31. data/lib/virtus/typecast/numeric.rb +82 -0
  32. data/lib/virtus/typecast/time.rb +162 -0
  33. data/spec/integration/virtus/attributes/attribute/typecast_spec.rb +4 -4
  34. data/spec/integration/virtus/class_methods/attribute_spec.rb +1 -1
  35. data/spec/integration/virtus/class_methods/attributes_spec.rb +3 -2
  36. data/spec/integration/virtus/class_methods/const_missing_spec.rb +2 -2
  37. data/spec/rcov.opts +6 -0
  38. data/spec/spec_helper.rb +0 -9
  39. data/spec/unit/shared/attribute.rb +8 -8
  40. data/spec/unit/virtus/{attributes → attribute}/array_spec.rb +1 -1
  41. data/spec/unit/virtus/attribute/attribute_spec.rb +12 -0
  42. data/spec/unit/virtus/{attributes → attribute}/boolean_spec.rb +4 -4
  43. data/spec/unit/virtus/{attributes → attribute}/date_spec.rb +13 -7
  44. data/spec/unit/virtus/{attributes → attribute}/date_time_spec.rb +31 -10
  45. data/spec/unit/virtus/{attributes → attribute}/decimal_spec.rb +18 -18
  46. data/spec/unit/virtus/{attributes → attribute}/float_spec.rb +18 -18
  47. data/spec/unit/virtus/{attributes → attribute}/hash_spec.rb +1 -1
  48. data/spec/unit/virtus/{attributes → attribute}/integer_spec.rb +18 -18
  49. data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +15 -0
  50. data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +16 -0
  51. data/spec/unit/virtus/{attributes → attribute}/string_spec.rb +2 -2
  52. data/spec/unit/virtus/{attributes → attribute}/time_spec.rb +19 -9
  53. data/spec/unit/virtus/class_methods/new_spec.rb +7 -7
  54. data/spec/unit/virtus/determine_type_spec.rb +4 -4
  55. data/spec/unit/virtus/instance_methods/attribute_get_spec.rb +1 -1
  56. data/spec/unit/virtus/instance_methods/attribute_set_spec.rb +2 -2
  57. data/spec/unit/virtus/instance_methods/attributes_spec.rb +2 -2
  58. data/tasks/metrics/ci.rake +7 -0
  59. data/tasks/metrics/flay.rake +41 -0
  60. data/tasks/metrics/flog.rake +43 -0
  61. data/tasks/metrics/heckle.rake +261 -0
  62. data/tasks/metrics/metric_fu.rake +29 -0
  63. data/tasks/metrics/reek.rake +9 -0
  64. data/tasks/metrics/roodi.rake +15 -0
  65. data/tasks/metrics/yardstick.rake +23 -0
  66. data/tasks/spec.rake +26 -0
  67. data/tasks/yard.rake +9 -0
  68. data/virtus.gemspec +48 -33
  69. metadata +51 -41
  70. data/lib/virtus/attributes/array.rb +0 -8
  71. data/lib/virtus/attributes/attribute.rb +0 -214
  72. data/lib/virtus/attributes/boolean.rb +0 -39
  73. data/lib/virtus/attributes/date.rb +0 -44
  74. data/lib/virtus/attributes/date_time.rb +0 -43
  75. data/lib/virtus/attributes/decimal.rb +0 -24
  76. data/lib/virtus/attributes/float.rb +0 -20
  77. data/lib/virtus/attributes/hash.rb +0 -8
  78. data/lib/virtus/attributes/integer.rb +0 -20
  79. data/lib/virtus/attributes/string.rb +0 -11
  80. data/lib/virtus/attributes/time.rb +0 -45
  81. data/lib/virtus/attributes/typecast/numeric.rb +0 -32
  82. data/lib/virtus/attributes/typecast/time.rb +0 -27
  83. data/spec/unit/virtus/attributes/attribute_spec.rb +0 -13
  84. data/spec/unit/virtus/attributes/numeric/class_methods/descendants_spec.rb +0 -15
  85. data/spec/unit/virtus/attributes/object/class_methods/descendants_spec.rb +0 -16
@@ -3,12 +3,10 @@ module Virtus
3
3
  module Chainable
4
4
  MODULES = {}
5
5
 
6
- def chainable(key, &block)
7
- begin
8
- mod = (MODULES[key] ||= Module.new)
9
- include mod
10
- mod
11
- end.module_eval(&block)
6
+ def chainable(key)
7
+ mod = MODULES[key] ||= Module.new
8
+ include mod
9
+ mod.module_eval { yield }
12
10
  end
13
11
  end
14
12
  end
@@ -0,0 +1,27 @@
1
+ module Virtus
2
+ module Typecast
3
+ # Typecast defined values into true or false.
4
+ # See TRUE_VALUES and FALSE_VALUES constants for a reference.
5
+ class Boolean
6
+ TRUE_VALUES = [ 1, '1', 't', 'T', 'true', 'TRUE' ].freeze
7
+ FALSE_VALUES = [ 0, '0', 'f', 'F', 'false', 'FALSE' ].freeze
8
+
9
+ BOOLEAN_MAP = Hash[TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
10
+
11
+ # Typecast value to TrueClass or FalseClass
12
+ #
13
+ # @example
14
+ # Virtus::Typecast::Boolean.call('T') # => true
15
+ # Virtus::Typecast::Boolean.call('F') # => false
16
+ #
17
+ # @param [Integer, String]
18
+ #
19
+ # @return [TrueClass, FalseClass]
20
+ #
21
+ # @api public
22
+ def self.call(value)
23
+ BOOLEAN_MAP.fetch(value, value)
24
+ end
25
+ end # Boolean
26
+ end # Typecast
27
+ end # Virtus
@@ -0,0 +1,82 @@
1
+ module Virtus
2
+ module Typecast
3
+ # Typecast numeric values. Supports Integer, Float and BigDecimal
4
+ class Numeric
5
+ # Typecast value to integer
6
+ #
7
+ # @example
8
+ # Virtus::Typecast::Numeric.to_i('1') # => 1
9
+ # Virtus::Typecast::Numeric.to_i(1.2) # => 1
10
+ #
11
+ # @param [Object]
12
+ #
13
+ # @return [Integer]
14
+ #
15
+ # @api public
16
+ def self.to_i(value)
17
+ call(value, :to_i)
18
+ end
19
+
20
+ # Typecast value to float
21
+ #
22
+ # @example
23
+ # Virtus::Typecast::Numeric.to_f('1.2') # => 1.2
24
+ # Virtus::Typecast::Numeric.to_f(1) # => 1.0
25
+ #
26
+ # @param [Object]
27
+ #
28
+ # @return [Float]
29
+ #
30
+ # @api public
31
+ def self.to_f(value)
32
+ call(value, :to_f)
33
+ end
34
+
35
+ # Typecast value to decimal
36
+ #
37
+ # @example
38
+ # Virtus::Typecast::Numeric.to_d('1.2') # => #<BigDecimal:b72157d4,'0.12E1',8(8)>
39
+ # Virtus::Typecast::Numeric.to_d(1) # => #<BigDecimal:b7212e08,'0.1E1',4(8)>
40
+ #
41
+ # @param [Object]
42
+ #
43
+ # @return [BigDecimal]
44
+ #
45
+ # @api public
46
+ def self.to_d(value)
47
+ if value.kind_of?(::Integer)
48
+ value.to_s.to_d
49
+ else
50
+ call(value, :to_d)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Match numeric string
57
+ #
58
+ # @param [#to_str, Numeric] value
59
+ # value to typecast
60
+ # @param [Symbol] method
61
+ # method to typecast with
62
+ #
63
+ # @return [Numeric]
64
+ # number if matched, value if no match
65
+ #
66
+ # @api private
67
+ def self.call(value, method)
68
+ if value.respond_to?(:to_str)
69
+ if value.to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
70
+ $1.send(method)
71
+ else
72
+ value
73
+ end
74
+ elsif value.respond_to?(method)
75
+ value.send(method)
76
+ else
77
+ value
78
+ end
79
+ end
80
+ end # Numeric
81
+ end # Typecast
82
+ end # Virtus
@@ -0,0 +1,162 @@
1
+ module Virtus
2
+ module Typecast
3
+ # Typecast various values into Date, DateTime or Time
4
+ class Time
5
+ SEGMENTS = [ :year, :month, :day, :hour, :min, :sec ].freeze
6
+
7
+ METHOD_TO_CLASS = {
8
+ :to_time => ::Time,
9
+ :to_date => ::Date,
10
+ :to_datetime => ::DateTime
11
+ }.freeze
12
+
13
+ # Typecasts an arbitrary value to a Time
14
+ #
15
+ # Handles both Hashes and Time instances
16
+ #
17
+ # @example
18
+ # Virtus::Typecast::Time.to_time('2011/06/09 12:01')
19
+ # # => Thu Jun 09 12:01:00 +0200 2011
20
+ #
21
+ # @param [Hash, #to_mash, #to_s] value
22
+ # value to be typecast
23
+ #
24
+ # @return [Time]
25
+ # Time constructed from value
26
+ #
27
+ # @api public
28
+ def self.to_time(value)
29
+ call(value, :to_time)
30
+ end
31
+
32
+ # Typecasts an arbitrary value to a Date
33
+ #
34
+ # Handles both Hashes and Date instances
35
+ #
36
+ # @example
37
+ # Virtus::Typecast::Time.to_date('2011/06/09')
38
+ # # => #<Date: 4911443/2,0,2299161>
39
+ #
40
+ # @param [Hash, #to_mash, #to_s] value
41
+ # value to be typecast
42
+ #
43
+ # @return [Date]
44
+ # Date constructed from value
45
+ #
46
+ # @api public
47
+ def self.to_date(value)
48
+ call(value, :to_date)
49
+ end
50
+
51
+ # Typecasts an arbitrary value to a DateTime
52
+ #
53
+ # Handles both Hashes and DateTime instances
54
+ #
55
+ # @example
56
+ # Virtus::Typecast::Time.to_datetime('2011/06/09 12:01')
57
+ # # => #<DateTime: 3536239681/1440,0,2299161>
58
+ #
59
+ # @param [Hash, #to_mash, #to_s] value
60
+ # value to be typecast
61
+ #
62
+ # @return [DateTime]
63
+ # DateTime constructed from value
64
+ #
65
+ # @api public
66
+ def self.to_datetime(value)
67
+ call(value, :to_datetime)
68
+ end
69
+
70
+ private
71
+
72
+ # @api private
73
+ def self.call(value, method)
74
+ return value.send(method) if value.respond_to?(method)
75
+
76
+ begin
77
+ if value.is_a?(::Hash)
78
+ from_hash(value, method)
79
+ else
80
+ from_string(value.to_s, method)
81
+ end
82
+ rescue ArgumentError
83
+ return value
84
+ end
85
+ end
86
+
87
+ # @api private
88
+ def self.from_string(value, method)
89
+ METHOD_TO_CLASS[method].parse(value.to_s)
90
+ end
91
+
92
+ # @api private
93
+ def self.from_hash(value, method)
94
+ send("hash_#{method}", value)
95
+ end
96
+
97
+ # Creates a Time instance from a Hash
98
+ #
99
+ # Valid keys are: :year, :month, :day, :hour, :min, :sec
100
+ #
101
+ # @param [Hash, #to_mash] value
102
+ # value to be typecast
103
+ #
104
+ # @return [Time]
105
+ # Time constructed from hash
106
+ #
107
+ # @api private
108
+ def self.hash_to_time(value)
109
+ ::Time.local(*extract(value))
110
+ end
111
+
112
+ # Creates a Date instance from a Hash
113
+ #
114
+ # Valid keys are: :year, :month, :day, :hour
115
+ #
116
+ # @param [Hash, #to_mash] value
117
+ # value to be typecast
118
+ #
119
+ # @return [Date]
120
+ # Date constructed from hash
121
+ #
122
+ # @api private
123
+ def self.hash_to_date(value)
124
+ ::Date.new(*extract(value).first(3))
125
+ end
126
+
127
+ # Creates a DateTime instance from a Hash
128
+ #
129
+ # Valid keys are: :year, :month, :day, :hour, :min, :sec
130
+ #
131
+ # @param [Hash, #to_mash] value
132
+ # value to be typecast
133
+ #
134
+ # @return [DateTime]
135
+ # DateTime constructed from hash
136
+ #
137
+ # @api private
138
+ def self.hash_to_datetime(value)
139
+ ::DateTime.new(*extract(value))
140
+ end
141
+
142
+ # Extracts the given args from the hash
143
+ #
144
+ # If a value does not exist, it uses the value of Time.now
145
+ #
146
+ # @param [Hash, #to_mash] value
147
+ # value to extract time args from
148
+ #
149
+ # @return [Array]
150
+ # Extracted values
151
+ #
152
+ # @api private
153
+ def self.extract(value)
154
+ now = ::Time.now
155
+
156
+ SEGMENTS.map do |segment|
157
+ Numeric.to_i(value.fetch(segment, now.send(segment)))
158
+ end
159
+ end
160
+ end # Time
161
+ end # Typecast
162
+ end # Virtus
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::Attributes::Attribute, '#typecast' do
3
+ describe Virtus::Attribute, '#typecast' do
4
4
  let(:attribute_class) do
5
- Class.new(Virtus::Attributes::Integer) do
6
- def typecast(value, object)
5
+ Class.new(Virtus::Attribute::Integer) do
6
+ def typecast(value)
7
7
  super + 1
8
8
  end
9
9
  end
@@ -30,7 +30,7 @@ describe Virtus::Attributes::Attribute, '#typecast' do
30
30
  end
31
31
 
32
32
  it "peforms custom typecasting" do
33
- object.count.should == output_value
33
+ object.count.should eql(output_value)
34
34
  end
35
35
  end
36
36
  end
@@ -25,7 +25,7 @@ describe Virtus::ClassMethods, '.attribute' do
25
25
  end
26
26
 
27
27
  it "should create an attribute of a correct type" do
28
- described_class.attributes[:name].should be_instance_of(Virtus::Attributes::String)
28
+ described_class.attributes[:name].should be_instance_of(Virtus::Attribute::String)
29
29
  end
30
30
 
31
31
  it "creates attribute writer" do
@@ -16,9 +16,10 @@ describe Virtus::ClassMethods, '.attributes' do
16
16
  subject { described_class.attributes }
17
17
 
18
18
  it "returns an attributes hash" do
19
- subject.should == {
19
+ subject.should eql(
20
20
  :name => described_class.attributes[:name],
21
- :age => described_class.attributes[:age] }
21
+ :age => described_class.attributes[:age]
22
+ )
22
23
  end
23
24
  end
24
25
  end
@@ -14,7 +14,7 @@ describe Virtus::ClassMethods, '.const_missing' do
14
14
  end
15
15
 
16
16
  it "should create attribute of the correct type" do
17
- User.attributes[:name].should be_instance_of(Virtus::Attributes::String)
17
+ User.attributes[:name].should be_instance_of(Virtus::Attribute::String)
18
18
  end
19
19
  end
20
20
 
@@ -27,7 +27,7 @@ describe Virtus::ClassMethods, '.const_missing' do
27
27
  end
28
28
 
29
29
  it "should create attribute of the correct type" do
30
- User.attributes[:admin].should be_instance_of(Virtus::Attributes::Boolean)
30
+ User.attributes[:admin].should be_instance_of(Virtus::Attribute::Boolean)
31
31
  end
32
32
  end
33
33
 
@@ -0,0 +1,6 @@
1
+ --exclude-only "spec/,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
@@ -2,15 +2,6 @@ require 'pathname'
2
2
  require 'rubygems'
3
3
  require 'rspec'
4
4
 
5
- if ENV['SPEC_COV'] && RUBY_VERSION >= '1.9.2'
6
- require 'simplecov'
7
-
8
- SimpleCov.start do
9
- add_filter "/spec/"
10
- add_group "lib", "lib"
11
- end
12
- end
13
-
14
5
  require 'virtus'
15
6
 
16
7
  ENV['TZ'] = 'UTC'
@@ -27,7 +27,7 @@ shared_examples_for "Attribute" do
27
27
  end
28
28
 
29
29
  it "accepts base options" do
30
- described_class.accepted_options.should include(*Virtus::Attributes::Attribute::OPTIONS)
30
+ described_class.accepted_options.should include(*Virtus::Attribute::OPTIONS)
31
31
  end
32
32
  end
33
33
 
@@ -65,20 +65,20 @@ shared_examples_for "Attribute" do
65
65
  end
66
66
 
67
67
  context "when new attribute is created" do
68
- subject { sub_attribute.new(attribute_name, model) }
68
+ subject { sub_attribute.new(attribute_name) }
69
69
 
70
70
  it "sets the default value" do
71
- subject.options[option].should == value
71
+ subject.options[option].should eql(value)
72
72
  end
73
73
  end
74
74
 
75
75
  context "when new attribute is created and overrides option's default value" do
76
76
  let(:new_value) { 11 }
77
77
 
78
- subject { sub_attribute.new(attribute_name, model, option => new_value) }
78
+ subject { sub_attribute.new(attribute_name, option => new_value) }
79
79
 
80
80
  it "sets the new value" do
81
- subject.options[option].should == new_value
81
+ subject.options[option].should eql(new_value)
82
82
  end
83
83
  end
84
84
  end
@@ -105,7 +105,7 @@ shared_examples_for "Attribute" do
105
105
  before { attribute.set(object, attribute_value) }
106
106
 
107
107
  it "sets the value in an ivar" do
108
- object.instance_variable_get(attribute.instance_variable_name).should == attribute_value
108
+ object.instance_variable_get(attribute.instance_variable_name).should eql(attribute_value)
109
109
  end
110
110
  end
111
111
 
@@ -127,7 +127,7 @@ shared_examples_for "Attribute" do
127
127
 
128
128
  subject { attribute.get(object) }
129
129
 
130
- it { should == attribute_value }
130
+ it { should eql(attribute_value) }
131
131
  end
132
132
 
133
133
  context "when nil is set" do
@@ -139,7 +139,7 @@ shared_examples_for "Attribute" do
139
139
  end
140
140
  end
141
141
 
142
- describe "#complex" do
142
+ describe "#complex?" do
143
143
  let(:attribute) { model.attribute(attribute_name, described_class, :complex => complex) }
144
144
 
145
145
  subject { attribute.complex? }
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::Attributes::Array do
3
+ describe Virtus::Attribute::Array do
4
4
  it_should_behave_like 'Attribute' do
5
5
  let(:attribute_name) { :colors }
6
6
  let(:attribute_value) { [ 'red', 'green', 'blue' ] }