virtus 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -15
- data/History.txt +10 -0
- data/TODO +4 -0
- data/VERSION +1 -1
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/roodi.yml +6 -6
- data/config/site.reek +5 -5
- data/lib/virtus.rb +21 -11
- data/lib/virtus/attribute.rb +92 -59
- data/lib/virtus/attribute/array.rb +4 -3
- data/lib/virtus/attribute/boolean.rb +21 -9
- data/lib/virtus/attribute/date.rb +5 -3
- data/lib/virtus/attribute/date_time.rb +5 -3
- data/lib/virtus/attribute/decimal.rb +5 -3
- data/lib/virtus/attribute/float.rb +5 -3
- data/lib/virtus/attribute/hash.rb +4 -3
- data/lib/virtus/attribute/integer.rb +5 -3
- data/lib/virtus/attribute/numeric.rb +5 -3
- data/lib/virtus/attribute/object.rb +4 -4
- data/lib/virtus/attribute/string.rb +7 -9
- data/lib/virtus/attribute/time.rb +5 -3
- data/lib/virtus/attribute_set.rb +151 -0
- data/lib/virtus/class_methods.rb +19 -10
- data/lib/virtus/instance_methods.rb +51 -27
- data/lib/virtus/support/descendants_tracker.rb +30 -0
- data/lib/virtus/typecast/boolean.rb +7 -5
- data/lib/virtus/typecast/numeric.rb +13 -8
- data/lib/virtus/typecast/string.rb +24 -0
- data/lib/virtus/typecast/time.rb +7 -5
- data/spec/integration/virtus/class_methods/attribute_spec.rb +17 -5
- data/spec/integration/virtus/class_methods/attributes_spec.rb +2 -5
- data/spec/shared/idempotent_method_behaviour.rb +5 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/unit/shared/attribute.rb +6 -155
- data/spec/unit/shared/attribute/accept_options.rb +55 -0
- data/spec/unit/shared/attribute/accepted_options.rb +11 -0
- data/spec/unit/shared/attribute/complex.rb +15 -0
- data/spec/unit/shared/attribute/get.rb +29 -0
- data/spec/unit/shared/attribute/options.rb +7 -0
- data/spec/unit/shared/attribute/set.rb +42 -0
- data/spec/unit/virtus/attribute/boolean_spec.rb +1 -2
- data/spec/unit/virtus/attribute/date_spec.rb +1 -2
- data/spec/unit/virtus/attribute/date_time_spec.rb +1 -2
- data/spec/unit/virtus/attribute/decimal_spec.rb +1 -2
- data/spec/unit/virtus/attribute/float_spec.rb +1 -2
- data/spec/unit/virtus/attribute/integer_spec.rb +1 -2
- data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +2 -2
- data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +6 -4
- data/spec/unit/virtus/attribute/string_spec.rb +1 -2
- data/spec/unit/virtus/attribute/time_spec.rb +1 -2
- data/spec/unit/virtus/attribute_set/append_spec.rb +35 -0
- data/spec/unit/virtus/attribute_set/each_spec.rb +60 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +13 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +35 -0
- data/spec/unit/virtus/attribute_set/merge_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/parent_spec.rb +11 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +60 -0
- data/spec/unit/virtus/class_methods/attribute_spec.rb +11 -0
- data/spec/unit/virtus/descendants_tracker/descendants_spec.rb +22 -0
- data/spec/unit/virtus/descendants_tracker/inherited_spec.rb +24 -0
- data/spec/unit/virtus/determine_type_spec.rb +21 -9
- data/spec/unit/virtus/instance_methods/{attribute_get_spec.rb → element_reference_spec.rb} +4 -2
- data/spec/unit/virtus/instance_methods/{attribute_set_spec.rb → element_set_spec.rb} +5 -7
- data/virtus.gemspec +35 -13
- metadata +96 -14
- data/lib/virtus/support/chainable.rb +0 -13
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -12,6 +13,6 @@ module Virtus
|
|
12
13
|
class Array < Object
|
13
14
|
primitive ::Array
|
14
15
|
complex true
|
15
|
-
end #
|
16
|
-
end #
|
17
|
-
end # Virtus
|
16
|
+
end # class Array
|
17
|
+
end # class Attribute
|
18
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Bolean attribute allows true or false values to be set
|
4
5
|
# Additionally it adds boolean reader method, like "admin?"
|
5
6
|
#
|
@@ -19,10 +20,16 @@ module Virtus
|
|
19
20
|
|
20
21
|
# Returns if the given value is either true or false
|
21
22
|
#
|
23
|
+
# @example
|
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
|
+
#
|
22
29
|
# @return [TrueClass,FalseClass]
|
23
30
|
#
|
24
|
-
# @api
|
25
|
-
def primitive?(value)
|
31
|
+
# @api semipublic
|
32
|
+
def self.primitive?(value)
|
26
33
|
value.equal?(true) || value.equal?(false)
|
27
34
|
end
|
28
35
|
|
@@ -45,16 +52,21 @@ module Virtus
|
|
45
52
|
def add_reader_method(model)
|
46
53
|
super
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
#{reader_visibility}
|
55
|
+
name = self.name
|
56
|
+
method_name = "#{name}?"
|
51
57
|
|
52
|
-
|
58
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
59
|
+
module AttributeMethods
|
60
|
+
def #{method_name}
|
53
61
|
#{name}
|
54
62
|
end
|
55
63
|
end
|
64
|
+
include AttributeMethods
|
56
65
|
RUBY
|
66
|
+
|
67
|
+
model.send(reader_visibility, method_name)
|
57
68
|
end
|
58
|
-
|
59
|
-
|
60
|
-
end #
|
69
|
+
|
70
|
+
end # class Boolean
|
71
|
+
end # class Attribute
|
72
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -30,6 +31,7 @@ module Virtus
|
|
30
31
|
def typecast_to_primitive(value)
|
31
32
|
Typecast::Time.to_date(value)
|
32
33
|
end
|
33
|
-
|
34
|
-
|
35
|
-
end #
|
34
|
+
|
35
|
+
end # class Date
|
36
|
+
end # class Attribute
|
37
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -29,6 +30,7 @@ module Virtus
|
|
29
30
|
def typecast_to_primitive(value)
|
30
31
|
Typecast::Time.to_datetime(value)
|
31
32
|
end
|
32
|
-
|
33
|
-
|
34
|
-
end #
|
33
|
+
|
34
|
+
end # class DateTim
|
35
|
+
end # class Attribute
|
36
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class ExchangeRate
|
@@ -19,6 +20,7 @@ module Virtus
|
|
19
20
|
def typecast_to_primitive(value)
|
20
21
|
Typecast::Numeric.to_d(value)
|
21
22
|
end
|
22
|
-
|
23
|
-
|
24
|
-
end #
|
23
|
+
|
24
|
+
end # class Decimal
|
25
|
+
end # class Attribute
|
26
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class ExchangeRate
|
@@ -28,6 +29,7 @@ module Virtus
|
|
28
29
|
def typecast_to_primitive(value)
|
29
30
|
Typecast::Numeric.to_f(value)
|
30
31
|
end
|
31
|
-
|
32
|
-
|
33
|
-
end #
|
32
|
+
|
33
|
+
end # class Float
|
34
|
+
end # class Attribute
|
35
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -13,6 +14,6 @@ module Virtus
|
|
13
14
|
class Hash < Object
|
14
15
|
primitive ::Hash
|
15
16
|
complex true
|
16
|
-
end #
|
17
|
-
end #
|
18
|
-
end # Virtus
|
17
|
+
end # class Hash
|
18
|
+
end # class Attribute
|
19
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -25,6 +26,7 @@ module Virtus
|
|
25
26
|
def typecast_to_primitive(value)
|
26
27
|
Typecast::Numeric.to_i(value)
|
27
28
|
end
|
28
|
-
|
29
|
-
|
30
|
-
end #
|
29
|
+
|
30
|
+
end # class Integer
|
31
|
+
end # class Attribute
|
32
|
+
end # module Virtus
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Base class for all numerical attributes
|
4
5
|
class Numeric < Object
|
6
|
+
primitive ::Numeric
|
5
7
|
accept_options :min, :max
|
6
|
-
end # Numeric
|
7
|
-
end #
|
8
|
-
end # Virtus
|
8
|
+
end # class Numeric
|
9
|
+
end # class Attribute
|
10
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage
|
4
5
|
#
|
5
6
|
# class User
|
@@ -16,16 +17,13 @@ module Virtus
|
|
16
17
|
class String < Object
|
17
18
|
primitive ::String
|
18
19
|
|
19
|
-
# Typecast
|
20
|
-
#
|
21
|
-
# @param [Object]
|
22
|
-
#
|
23
|
-
# @return [String]
|
20
|
+
# @see Virtus::Typecast::String.call
|
24
21
|
#
|
25
22
|
# @api private
|
26
23
|
def typecast_to_primitive(value)
|
27
|
-
value
|
24
|
+
Virtus::Typecast::String.call(value)
|
28
25
|
end
|
29
|
-
|
30
|
-
|
31
|
-
end #
|
26
|
+
|
27
|
+
end # class String
|
28
|
+
end # class Attribute
|
29
|
+
end # module Virtus
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Virtus
|
2
2
|
class Attribute
|
3
|
+
|
3
4
|
# Example usage:
|
4
5
|
#
|
5
6
|
# class Post
|
@@ -29,6 +30,7 @@ module Virtus
|
|
29
30
|
def typecast_to_primitive(value)
|
30
31
|
Typecast::Time.to_time(value)
|
31
32
|
end
|
32
|
-
|
33
|
-
|
34
|
-
end #
|
33
|
+
|
34
|
+
end # class Time
|
35
|
+
end # class Attribute
|
36
|
+
end # module Virtus
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# A set of Attribute objects
|
4
|
+
class AttributeSet
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# Return the parent attributes
|
8
|
+
#
|
9
|
+
# @return [AttributeSet]
|
10
|
+
# the parent attributes
|
11
|
+
#
|
12
|
+
# @return [nil]
|
13
|
+
# nil if there are no parent attributes
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
attr_reader :parent
|
17
|
+
|
18
|
+
# Initialize an AttributeSet
|
19
|
+
#
|
20
|
+
# @param [AttributeSet] parent
|
21
|
+
# @param [Array] attributes
|
22
|
+
#
|
23
|
+
# @return [undefined]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def initialize(parent = nil, attributes = [])
|
27
|
+
@parent = parent
|
28
|
+
@attributes = attributes.dup
|
29
|
+
@index = {}
|
30
|
+
reset
|
31
|
+
end
|
32
|
+
|
33
|
+
# Iterate over each attribute in the set
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# attribute_set = AttributeSet.new(attributes, parent)
|
37
|
+
# attribute_set.each { |attribute| ... }
|
38
|
+
#
|
39
|
+
# @yield [attribute]
|
40
|
+
#
|
41
|
+
# @yieldparam [Attribute] attribute
|
42
|
+
# each attribute in the set
|
43
|
+
#
|
44
|
+
# @return [self]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def each
|
48
|
+
return to_enum unless block_given?
|
49
|
+
@index.each_value { |attribute| yield attribute }
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adds the attributes to the set
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# attribute_set.merge(attributes)
|
57
|
+
#
|
58
|
+
# @param [Array<Attribute>] attributes
|
59
|
+
#
|
60
|
+
# @return [self]
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
def merge(attributes)
|
64
|
+
attributes.each { |attribute| self << attribute }
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adds an attribute to the set
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# attribute_set << attribute
|
72
|
+
#
|
73
|
+
# @param [Attribute] attribute
|
74
|
+
#
|
75
|
+
# @return [self]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def <<(attribute)
|
79
|
+
self[attribute.name] = attribute
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get an attribute by name
|
84
|
+
#
|
85
|
+
# @example
|
86
|
+
# attribute_set[:name] # => Attribute object
|
87
|
+
#
|
88
|
+
# @param [Symbol] name
|
89
|
+
#
|
90
|
+
# @return [Attribute]
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
def [](name)
|
94
|
+
@index[name]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set an attribute by name
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# attribute_set[:name] = attribute
|
101
|
+
#
|
102
|
+
# @param [Symbol] name
|
103
|
+
# @param [Attribute] attribute
|
104
|
+
#
|
105
|
+
# @return [Attribute]
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def []=(name, attribute)
|
109
|
+
delete(name)
|
110
|
+
@attributes << attribute
|
111
|
+
@index[name] = attribute
|
112
|
+
end
|
113
|
+
|
114
|
+
# Reset the index when the parent is updated
|
115
|
+
#
|
116
|
+
# @return [self]
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
def reset
|
120
|
+
parent = self.parent
|
121
|
+
merge_index(parent) if parent
|
122
|
+
merge_index(@attributes)
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Delete the Attribute by name
|
129
|
+
#
|
130
|
+
# @param [Symbol] name
|
131
|
+
#
|
132
|
+
# @return [undefined]
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def delete(name)
|
136
|
+
@attributes.delete(@index.delete(name))
|
137
|
+
end
|
138
|
+
|
139
|
+
# Add the attributes to the index
|
140
|
+
#
|
141
|
+
# @param [Array<Attribute>] attributes
|
142
|
+
#
|
143
|
+
# @return [undefined]
|
144
|
+
#
|
145
|
+
# @api private
|
146
|
+
def merge_index(attributes)
|
147
|
+
attributes.each { |attribute| @index[attribute.name] = attribute }
|
148
|
+
end
|
149
|
+
|
150
|
+
end # class AttributeSet
|
151
|
+
end # module Virtus
|
data/lib/virtus/class_methods.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Virtus
|
2
|
+
|
2
3
|
# Class methods that are added when you include Virtus
|
3
4
|
module ClassMethods
|
5
|
+
|
4
6
|
# Defines an attribute on an object's class
|
5
7
|
#
|
6
8
|
# @example
|
@@ -19,20 +21,22 @@ module Virtus
|
|
19
21
|
# @param [Class] type
|
20
22
|
# the type class of an attribute
|
21
23
|
#
|
22
|
-
# @param [
|
24
|
+
# @param [#to_hash] options
|
23
25
|
# the extra options hash
|
24
26
|
#
|
25
|
-
# @return [
|
27
|
+
# @return [self]
|
26
28
|
#
|
27
29
|
# @api public
|
28
30
|
def attribute(name, type, options = {})
|
29
|
-
|
30
|
-
attribute = attribute_klass.new(name, options)
|
31
|
+
attribute = Virtus.determine_type(type).new(name, options)
|
31
32
|
|
32
33
|
attribute.add_reader_method(self)
|
33
34
|
attribute.add_writer_method(self)
|
34
35
|
|
35
|
-
attributes
|
36
|
+
attributes << attribute
|
37
|
+
descendants.each { |descendant| descendant.attributes.reset }
|
38
|
+
|
39
|
+
self
|
36
40
|
end
|
37
41
|
|
38
42
|
# Returns all the attributes defined on a Class
|
@@ -54,16 +58,20 @@ module Virtus
|
|
54
58
|
#
|
55
59
|
# @api public
|
56
60
|
def attributes
|
57
|
-
@attributes ||=
|
61
|
+
@attributes ||= begin
|
62
|
+
superclass = self.superclass
|
63
|
+
parent = superclass.attributes if superclass.respond_to?(:attributes)
|
64
|
+
AttributeSet.new(parent)
|
65
|
+
end
|
58
66
|
end
|
59
67
|
|
60
|
-
|
68
|
+
private
|
61
69
|
|
62
70
|
# Hooks into const missing process to determine types of attributes
|
63
71
|
#
|
64
72
|
# It is used when an attribute is defined and a global class like String
|
65
73
|
# or Integer is provided as the type which needs to be mapped to
|
66
|
-
# Virtus::
|
74
|
+
# Virtus::Attribute::String and Virtus::Attribute::Integer
|
67
75
|
#
|
68
76
|
# @param [String] name
|
69
77
|
#
|
@@ -73,5 +81,6 @@ module Virtus
|
|
73
81
|
def const_missing(name)
|
74
82
|
Virtus.determine_type(name) || super
|
75
83
|
end
|
76
|
-
|
77
|
-
end #
|
84
|
+
|
85
|
+
end # module ClassMethods
|
86
|
+
end # module Virtus
|