virtus 0.0.3 → 0.0.4
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 +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
|