virtus 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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' ] }