virtus 0.0.4 → 0.0.5
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/Gemfile +4 -2
- data/History.txt +7 -0
- data/README.markdown +7 -7
- data/Rakefile +5 -10
- data/VERSION +1 -1
- data/config/flay.yml +1 -1
- data/config/roodi.yml +3 -3
- data/lib/virtus.rb +5 -31
- data/lib/virtus/attribute.rb +118 -46
- data/lib/virtus/attribute/boolean.rb +19 -15
- data/lib/virtus/attribute/date_time.rb +2 -0
- data/lib/virtus/attribute/decimal.rb +2 -0
- data/lib/virtus/attribute/float.rb +2 -0
- data/lib/virtus/attribute/integer.rb +2 -0
- data/lib/virtus/attribute/string.rb +2 -0
- data/lib/virtus/attribute/time.rb +8 -1
- data/lib/virtus/class_methods.rb +4 -5
- data/lib/virtus/instance_methods.rb +9 -7
- data/lib/virtus/support/descendants_tracker.rb +21 -7
- data/lib/virtus/typecast/boolean.rb +7 -7
- data/lib/virtus/typecast/numeric.rb +8 -8
- data/lib/virtus/typecast/string.rb +2 -2
- data/lib/virtus/typecast/time.rb +49 -21
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +49 -0
- data/spec/unit/virtus/descendants_tracker/{inherited_spec.rb → add_descendant_spec.rb} +2 -2
- data/virtus.gemspec +7 -6
- metadata +27 -53
- data/.gitignore +0 -7
- data/spec/unit/virtus/determine_type_spec.rb +0 -32
@@ -13,7 +13,7 @@ module Virtus
|
|
13
13
|
# end
|
14
14
|
#
|
15
15
|
# post = Post.new(:published => false)
|
16
|
-
# post.published?
|
16
|
+
# post.published? # => false
|
17
17
|
#
|
18
18
|
class Boolean < Object
|
19
19
|
primitive TrueClass
|
@@ -21,14 +21,14 @@ module Virtus
|
|
21
21
|
# Returns if the given value is either true or false
|
22
22
|
#
|
23
23
|
# @example
|
24
|
-
# Virtus::Attribute::Boolean.primitive?(true)
|
25
|
-
# Virtus::Attribute::Boolean.primitive?(false)
|
26
|
-
# Virtus::Attribute::Boolean.primitive?(1)
|
27
|
-
# Virtus::Attribute::Boolean.primitive?('true')
|
24
|
+
# Virtus::Attribute::Boolean.primitive?(true) # => true
|
25
|
+
# Virtus::Attribute::Boolean.primitive?(false) # => true
|
26
|
+
# Virtus::Attribute::Boolean.primitive?(1) # => false
|
27
|
+
# Virtus::Attribute::Boolean.primitive?('true') # => false
|
28
28
|
#
|
29
|
-
# @return [
|
29
|
+
# @return [Boolean]
|
30
30
|
#
|
31
|
-
# @api
|
31
|
+
# @api public
|
32
32
|
def self.primitive?(value)
|
33
33
|
value.equal?(true) || value.equal?(false)
|
34
34
|
end
|
@@ -37,7 +37,7 @@ module Virtus
|
|
37
37
|
#
|
38
38
|
# @see Virtus::Typecast::Boolean.call
|
39
39
|
#
|
40
|
-
# @return [
|
40
|
+
# @return [Boolean]
|
41
41
|
#
|
42
42
|
# @api private
|
43
43
|
def typecast_to_primitive(value)
|
@@ -46,7 +46,9 @@ module Virtus
|
|
46
46
|
|
47
47
|
# Creates standard and boolean attribute reader methods
|
48
48
|
#
|
49
|
-
# @
|
49
|
+
# @param [Class] model
|
50
|
+
#
|
51
|
+
# @return [self]
|
50
52
|
#
|
51
53
|
# @api private
|
52
54
|
def add_reader_method(model)
|
@@ -56,15 +58,17 @@ module Virtus
|
|
56
58
|
method_name = "#{name}?"
|
57
59
|
|
58
60
|
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
59
|
-
module AttributeMethods
|
60
|
-
def #{method_name}
|
61
|
-
#{name}
|
62
|
-
end
|
63
|
-
end
|
64
|
-
include AttributeMethods
|
61
|
+
module AttributeMethods # module AttributeMethods
|
62
|
+
def #{method_name} # def active?
|
63
|
+
#{name} # @active
|
64
|
+
end # end
|
65
|
+
end # end
|
66
|
+
include AttributeMethods # include AttributeMethods
|
65
67
|
RUBY
|
66
68
|
|
67
69
|
model.send(reader_visibility, method_name)
|
70
|
+
|
71
|
+
self
|
68
72
|
end
|
69
73
|
|
70
74
|
end # class Boolean
|
@@ -16,7 +16,12 @@ module Virtus
|
|
16
16
|
#
|
17
17
|
# # typecasting from a hash
|
18
18
|
# Post.new(:published_at => {
|
19
|
-
# :year
|
19
|
+
# :year => 2011,
|
20
|
+
# :month => 6,
|
21
|
+
# :day => 9,
|
22
|
+
# :hour => 11,
|
23
|
+
# :minutes => 8
|
24
|
+
# })
|
20
25
|
#
|
21
26
|
# # typecasting from an object which implements #to_time
|
22
27
|
# Post.new(:published_at => DateTime.now)
|
@@ -26,6 +31,8 @@ module Virtus
|
|
26
31
|
|
27
32
|
# @see Virtus::Typecast::Time.to_time
|
28
33
|
#
|
34
|
+
# @return [Time]
|
35
|
+
#
|
29
36
|
# @api private
|
30
37
|
def typecast_to_primitive(value)
|
31
38
|
Typecast::Time.to_time(value)
|
data/lib/virtus/class_methods.rb
CHANGED
@@ -28,7 +28,7 @@ module Virtus
|
|
28
28
|
#
|
29
29
|
# @api public
|
30
30
|
def attribute(name, type, options = {})
|
31
|
-
attribute =
|
31
|
+
attribute = Attribute.determine_type(type).new(name, options)
|
32
32
|
|
33
33
|
attribute.add_reader_method(self)
|
34
34
|
attribute.add_writer_method(self)
|
@@ -49,12 +49,11 @@ module Virtus
|
|
49
49
|
# attribute :age, Integer
|
50
50
|
# end
|
51
51
|
#
|
52
|
-
# User.attributes
|
52
|
+
# User.attributes # =>
|
53
53
|
#
|
54
54
|
# TODO: implement inspect so the output is not cluttered - solnic
|
55
55
|
#
|
56
|
-
# @return [
|
57
|
-
# an attributes hash indexed by attribute names
|
56
|
+
# @return [AttributeSet]
|
58
57
|
#
|
59
58
|
# @api public
|
60
59
|
def attributes
|
@@ -79,7 +78,7 @@ module Virtus
|
|
79
78
|
#
|
80
79
|
# @api private
|
81
80
|
def const_missing(name)
|
82
|
-
|
81
|
+
Attribute.determine_type(name) || super
|
83
82
|
end
|
84
83
|
|
85
84
|
end # module ClassMethods
|
@@ -8,7 +8,7 @@ module Virtus
|
|
8
8
|
# @param [#to_hash] attributes
|
9
9
|
# the attributes hash to be set
|
10
10
|
#
|
11
|
-
# @return [
|
11
|
+
# @return [undefined]
|
12
12
|
#
|
13
13
|
# @api private
|
14
14
|
def initialize(attributes = {})
|
@@ -25,7 +25,7 @@ module Virtus
|
|
25
25
|
# end
|
26
26
|
#
|
27
27
|
# user = User.new(:name => 'John')
|
28
|
-
# user[:name]
|
28
|
+
# user[:name] # => "John"
|
29
29
|
#
|
30
30
|
# @param [Symbol] name
|
31
31
|
# a name of an attribute
|
@@ -48,8 +48,8 @@ module Virtus
|
|
48
48
|
# end
|
49
49
|
#
|
50
50
|
# user = User.new
|
51
|
-
# user[:name] = "
|
52
|
-
# user.name
|
51
|
+
# user[:name] = "John" # => "John"
|
52
|
+
# user.name # => "John"
|
53
53
|
#
|
54
54
|
# @param [Symbol] name
|
55
55
|
# a name of an attribute
|
@@ -76,10 +76,9 @@ module Virtus
|
|
76
76
|
# end
|
77
77
|
#
|
78
78
|
# user = User.new(:name => 'John', :age => 28)
|
79
|
-
# user.attributes
|
79
|
+
# user.attributes # => { :name => 'John', :age => 28 }
|
80
80
|
#
|
81
81
|
# @return [Hash]
|
82
|
-
# the attributes
|
83
82
|
#
|
84
83
|
# @api public
|
85
84
|
def attributes
|
@@ -110,7 +109,6 @@ module Virtus
|
|
110
109
|
# a hash of attribute values to be set on an object
|
111
110
|
#
|
112
111
|
# @return [Hash]
|
113
|
-
# the attributes
|
114
112
|
#
|
115
113
|
# @api public
|
116
114
|
def attributes=(attributes)
|
@@ -125,6 +123,8 @@ module Virtus
|
|
125
123
|
#
|
126
124
|
# @see Virtus::InstanceMethods#[]
|
127
125
|
#
|
126
|
+
# @return [Object]
|
127
|
+
#
|
128
128
|
# @api private
|
129
129
|
def attribute_get(name)
|
130
130
|
__send__(name)
|
@@ -134,6 +134,8 @@ module Virtus
|
|
134
134
|
#
|
135
135
|
# @see Virtus::InstanceMethods#[]=
|
136
136
|
#
|
137
|
+
# @return [Object]
|
138
|
+
#
|
137
139
|
# @api private
|
138
140
|
def attribute_set(name, value)
|
139
141
|
__send__("#{name}=", value)
|
@@ -3,27 +3,41 @@ module Virtus
|
|
3
3
|
# A module that adds descendant tracking to a class
|
4
4
|
module DescendantsTracker
|
5
5
|
|
6
|
-
#
|
6
|
+
# Return the descendants of this class
|
7
|
+
#
|
8
|
+
# @return [Array<Class>]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def descendants
|
12
|
+
@descendants ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add the descendant to this class and the superclass
|
7
16
|
#
|
8
17
|
# @param [Class] descendant
|
9
18
|
#
|
10
19
|
# @return [self]
|
11
20
|
#
|
12
21
|
# @api private
|
13
|
-
def
|
22
|
+
def add_descendant(descendant)
|
14
23
|
superclass = self.superclass
|
15
|
-
superclass.
|
24
|
+
superclass.add_descendant(descendant) if superclass.respond_to?(:add_descendant)
|
16
25
|
descendants.unshift(descendant)
|
17
26
|
self
|
18
27
|
end
|
19
28
|
|
20
|
-
|
29
|
+
private
|
30
|
+
|
31
|
+
# Hook called when class is inherited
|
21
32
|
#
|
22
|
-
# @
|
33
|
+
# @param [Class] descendant
|
34
|
+
#
|
35
|
+
# @return [self]
|
23
36
|
#
|
24
37
|
# @api private
|
25
|
-
def
|
26
|
-
|
38
|
+
def inherited(descendant)
|
39
|
+
super
|
40
|
+
add_descendant(descendant)
|
27
41
|
end
|
28
42
|
|
29
43
|
end # module DescendantsTracker
|
@@ -5,23 +5,23 @@ module Virtus
|
|
5
5
|
# See TRUE_VALUES and FALSE_VALUES constants for a reference.
|
6
6
|
class Boolean
|
7
7
|
|
8
|
-
TRUE_VALUES = [ 1
|
9
|
-
FALSE_VALUES = [ 0
|
8
|
+
TRUE_VALUES = %w[ 1 t true ].freeze
|
9
|
+
FALSE_VALUES = %w[ 0 f false ].freeze
|
10
10
|
BOOLEAN_MAP = Hash[ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
|
11
11
|
|
12
12
|
# Typecast value to TrueClass or FalseClass
|
13
13
|
#
|
14
14
|
# @example
|
15
|
-
# Virtus::Typecast::Boolean.call('T')
|
16
|
-
# Virtus::Typecast::Boolean.call('F')
|
15
|
+
# Virtus::Typecast::Boolean.call('T') # => true
|
16
|
+
# Virtus::Typecast::Boolean.call('F') # => false
|
17
17
|
#
|
18
|
-
# @param [
|
18
|
+
# @param [#to_s]
|
19
19
|
#
|
20
|
-
# @return [
|
20
|
+
# @return [Boolean]
|
21
21
|
#
|
22
22
|
# @api public
|
23
23
|
def self.call(value)
|
24
|
-
BOOLEAN_MAP.fetch(value, value)
|
24
|
+
BOOLEAN_MAP.fetch(value.to_s.downcase, value)
|
25
25
|
end
|
26
26
|
|
27
27
|
end # class Boolean
|
@@ -9,8 +9,8 @@ module Virtus
|
|
9
9
|
# Typecast value to integer
|
10
10
|
#
|
11
11
|
# @example
|
12
|
-
# Virtus::Typecast::Numeric.to_i('1')
|
13
|
-
# Virtus::Typecast::Numeric.to_i(1.2)
|
12
|
+
# Virtus::Typecast::Numeric.to_i('1') # => 1
|
13
|
+
# Virtus::Typecast::Numeric.to_i(1.2) # => 1
|
14
14
|
#
|
15
15
|
# @param [Object] value
|
16
16
|
#
|
@@ -24,8 +24,8 @@ module Virtus
|
|
24
24
|
# Typecast value to float
|
25
25
|
#
|
26
26
|
# @example
|
27
|
-
# Virtus::Typecast::Numeric.to_f('1.2')
|
28
|
-
# Virtus::Typecast::Numeric.to_f(1)
|
27
|
+
# Virtus::Typecast::Numeric.to_f('1.2') # => 1.2
|
28
|
+
# Virtus::Typecast::Numeric.to_f(1) # => 1.0
|
29
29
|
#
|
30
30
|
# @param [Object] value
|
31
31
|
#
|
@@ -39,8 +39,8 @@ module Virtus
|
|
39
39
|
# Typecast value to decimal
|
40
40
|
#
|
41
41
|
# @example
|
42
|
-
# Virtus::Typecast::Numeric.to_d('1.2')
|
43
|
-
# Virtus::Typecast::Numeric.to_d(1)
|
42
|
+
# Virtus::Typecast::Numeric.to_d('1.2') # => #<BigDecimal:b72157d4,'0.12E1',8(8)>
|
43
|
+
# Virtus::Typecast::Numeric.to_d(1) # => #<BigDecimal:b7212e08,'0.1E1',4(8)>
|
44
44
|
#
|
45
45
|
# @param [Object] value
|
46
46
|
#
|
@@ -55,8 +55,6 @@ module Virtus
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
private
|
59
|
-
|
60
58
|
# Match numeric string
|
61
59
|
#
|
62
60
|
# @param [#to_str, Numeric] value
|
@@ -82,6 +80,8 @@ module Virtus
|
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
83
|
+
private_class_method :call
|
84
|
+
|
85
85
|
end # class Numeric
|
86
86
|
end # module Typecast
|
87
87
|
end # module Virtus
|
@@ -7,8 +7,8 @@ module Virtus
|
|
7
7
|
# Typecast value to a string
|
8
8
|
#
|
9
9
|
# @example
|
10
|
-
# Virtus::Typecast::String.call(1)
|
11
|
-
# Virtus::Typecast::String.call([])
|
10
|
+
# Virtus::Typecast::String.call(1) # => "1"
|
11
|
+
# Virtus::Typecast::String.call([]) # => "[]"
|
12
12
|
#
|
13
13
|
# @param [Object] value
|
14
14
|
#
|
data/lib/virtus/typecast/time.rb
CHANGED
@@ -19,7 +19,7 @@ module Virtus
|
|
19
19
|
# Virtus::Typecast::Time.to_time('2011/06/09 12:01')
|
20
20
|
# # => Thu Jun 09 12:01:00 +0200 2011
|
21
21
|
#
|
22
|
-
# @param [
|
22
|
+
# @param [#to_hash, #to_s] value
|
23
23
|
# value to be typecast
|
24
24
|
#
|
25
25
|
# @return [Time]
|
@@ -38,7 +38,7 @@ module Virtus
|
|
38
38
|
# Virtus::Typecast::Time.to_date('2011/06/09')
|
39
39
|
# # => #<Date: 4911443/2,0,2299161>
|
40
40
|
#
|
41
|
-
# @param [
|
41
|
+
# @param [#to_hash, #to_s] value
|
42
42
|
# value to be typecast
|
43
43
|
#
|
44
44
|
# @return [Date]
|
@@ -57,7 +57,7 @@ module Virtus
|
|
57
57
|
# Virtus::Typecast::Time.to_datetime('2011/06/09 12:01')
|
58
58
|
# # => #<DateTime: 3536239681/1440,0,2299161>
|
59
59
|
#
|
60
|
-
# @param [
|
60
|
+
# @param [#to_hash, #to_s] value
|
61
61
|
# value to be typecast
|
62
62
|
#
|
63
63
|
# @return [DateTime]
|
@@ -68,15 +68,21 @@ module Virtus
|
|
68
68
|
call(value, :to_datetime)
|
69
69
|
end
|
70
70
|
|
71
|
-
|
72
|
-
|
71
|
+
# Coerce the value into a Date, Time or DateTime object
|
72
|
+
#
|
73
|
+
# @param [#to_hash, #to_s] value
|
74
|
+
#
|
75
|
+
# @param [Symbol] method
|
76
|
+
#
|
77
|
+
# @return [Object]
|
78
|
+
#
|
73
79
|
# @api private
|
74
80
|
def self.call(value, method)
|
75
81
|
return value.send(method) if value.respond_to?(method)
|
76
82
|
|
77
83
|
begin
|
78
|
-
if value.
|
79
|
-
from_hash(value, method)
|
84
|
+
if value.respond_to?(:to_hash)
|
85
|
+
from_hash(value.to_hash, method)
|
80
86
|
else
|
81
87
|
from_string(value.to_s, method)
|
82
88
|
end
|
@@ -85,70 +91,90 @@ module Virtus
|
|
85
91
|
end
|
86
92
|
end
|
87
93
|
|
94
|
+
private_class_method :call
|
95
|
+
|
96
|
+
# Coerce the string into a Date, Time or DateTime object
|
97
|
+
#
|
98
|
+
# @param [String] value
|
99
|
+
#
|
100
|
+
# @param [Symbol] method
|
101
|
+
#
|
102
|
+
# @return [Object]
|
103
|
+
#
|
88
104
|
# @api private
|
89
105
|
def self.from_string(value, method)
|
90
|
-
METHOD_TO_CLASS[method].parse(value
|
106
|
+
METHOD_TO_CLASS[method].parse(value)
|
91
107
|
end
|
92
108
|
|
109
|
+
private_class_method :from_string
|
110
|
+
|
111
|
+
# Coerce the Hash into a Date, Time or DateTime object
|
112
|
+
#
|
113
|
+
# @param [Hash] value
|
114
|
+
#
|
115
|
+
# @param [Symbol] method
|
116
|
+
#
|
117
|
+
# @return [Object]
|
118
|
+
#
|
93
119
|
# @api private
|
94
120
|
def self.from_hash(value, method)
|
95
121
|
send("hash_#{method}", value)
|
96
122
|
end
|
97
123
|
|
124
|
+
private_class_method :from_hash
|
125
|
+
|
98
126
|
# Creates a Time instance from a Hash
|
99
127
|
#
|
100
128
|
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
101
129
|
#
|
102
|
-
# @param [Hash
|
103
|
-
# value to be typecast
|
130
|
+
# @param [Hash] value
|
104
131
|
#
|
105
132
|
# @return [Time]
|
106
|
-
# Time constructed from hash
|
107
133
|
#
|
108
134
|
# @api private
|
109
135
|
def self.hash_to_time(value)
|
110
136
|
::Time.local(*extract(value))
|
111
137
|
end
|
112
138
|
|
139
|
+
private_class_method :hash_to_time
|
140
|
+
|
113
141
|
# Creates a Date instance from a Hash
|
114
142
|
#
|
115
143
|
# Valid keys are: :year, :month, :day, :hour
|
116
144
|
#
|
117
|
-
# @param [Hash
|
118
|
-
# value to be typecast
|
145
|
+
# @param [Hash] value
|
119
146
|
#
|
120
147
|
# @return [Date]
|
121
|
-
# Date constructed from hash
|
122
148
|
#
|
123
149
|
# @api private
|
124
150
|
def self.hash_to_date(value)
|
125
151
|
::Date.new(*extract(value).first(3))
|
126
152
|
end
|
127
153
|
|
154
|
+
private_class_method :hash_to_date
|
155
|
+
|
128
156
|
# Creates a DateTime instance from a Hash
|
129
157
|
#
|
130
158
|
# Valid keys are: :year, :month, :day, :hour, :min, :sec
|
131
159
|
#
|
132
|
-
# @param [Hash
|
133
|
-
# value to be typecast
|
160
|
+
# @param [Hash] value
|
134
161
|
#
|
135
162
|
# @return [DateTime]
|
136
|
-
# DateTime constructed from hash
|
137
163
|
#
|
138
164
|
# @api private
|
139
165
|
def self.hash_to_datetime(value)
|
140
166
|
::DateTime.new(*extract(value))
|
141
167
|
end
|
142
168
|
|
143
|
-
|
169
|
+
private_class_method :hash_to_datetime
|
170
|
+
|
171
|
+
# Extracts the given args from a Hash
|
144
172
|
#
|
145
173
|
# If a value does not exist, it uses the value of Time.now
|
146
174
|
#
|
147
|
-
# @param [Hash
|
148
|
-
# value to extract time args from
|
175
|
+
# @param [Hash] value
|
149
176
|
#
|
150
177
|
# @return [Array]
|
151
|
-
# Extracted values
|
152
178
|
#
|
153
179
|
# @api private
|
154
180
|
def self.extract(value)
|
@@ -159,6 +185,8 @@ module Virtus
|
|
159
185
|
end
|
160
186
|
end
|
161
187
|
|
188
|
+
private_class_method :extract
|
189
|
+
|
162
190
|
end # class Time
|
163
191
|
end # module Typecast
|
164
192
|
end # module Virtus
|