virtus 0.0.10 → 0.1.0
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/.gitignore +33 -7
- data/.travis.yml +4 -74
- data/Changelog.md +11 -2
- data/Gemfile +10 -9
- data/README.md +85 -1
- data/TODO +18 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/roodi.yml +3 -3
- data/config/site.reek +8 -3
- data/lib/virtus.rb +5 -0
- data/lib/virtus/attribute.rb +137 -30
- data/lib/virtus/attribute/array.rb +17 -1
- data/lib/virtus/attribute/boolean.rb +8 -13
- data/lib/virtus/attribute/collection.rb +96 -0
- data/lib/virtus/attribute/default_value.rb +11 -7
- data/lib/virtus/attribute/embedded_value.rb +70 -0
- data/lib/virtus/attribute/set.rb +25 -0
- data/lib/virtus/attribute_set.rb +18 -16
- data/lib/virtus/attributes_accessor.rb +66 -0
- data/lib/virtus/class_methods.rb +50 -28
- data/lib/virtus/coercion/array.rb +23 -0
- data/lib/virtus/coercion/string.rb +10 -4
- data/lib/virtus/instance_methods.rb +38 -31
- data/lib/virtus/support/options.rb +6 -8
- data/lib/virtus/support/type_lookup.rb +4 -11
- data/lib/virtus/version.rb +1 -1
- data/spec/integration/collection_member_coercion_spec.rb +75 -0
- data/spec/integration/custom_attributes_spec.rb +49 -0
- data/spec/integration/default_values_spec.rb +32 -0
- data/spec/integration/defining_attributes_spec.rb +79 -0
- data/spec/integration/embedded_value_spec.rb +50 -0
- data/spec/integration/overriding_virtus_spec.rb +46 -0
- data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
- data/spec/rcov.opts +1 -0
- data/spec/shared/constants_helpers.rb +9 -0
- data/spec/shared/options_class_method.rb +19 -0
- data/spec/spec_helper.rb +20 -7
- data/spec/unit/virtus/attribute/array/coerce_spec.rb +13 -0
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +85 -0
- data/spec/unit/virtus/attribute/boolean/define_reader_method_spec.rb +15 -0
- data/spec/unit/virtus/attribute/boolean/value_coerced_spec.rb +97 -0
- data/spec/unit/virtus/attribute/boolean_spec.rb +2 -81
- data/spec/unit/virtus/attribute/class/coerce_spec.rb +13 -0
- data/spec/unit/virtus/attribute/class_methods/accessor_spec.rb +12 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +37 -0
- data/spec/unit/virtus/attribute/class_methods/coercion_method_spec.rb +9 -0
- data/spec/unit/virtus/attribute/class_methods/default_spec.rb +9 -0
- data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +31 -1
- data/spec/unit/virtus/attribute/class_methods/merge_options_spec.rb +11 -0
- data/spec/unit/virtus/attribute/class_methods/primitive_spec.rb +9 -0
- data/spec/unit/virtus/attribute/class_methods/reader_spec.rb +9 -0
- data/spec/unit/virtus/attribute/class_methods/writer_spec.rb +9 -0
- data/spec/unit/virtus/attribute/coerce_spec.rb +30 -0
- data/spec/unit/virtus/attribute/coercion_method_spec.rb +12 -0
- data/spec/unit/virtus/attribute/collection/class_methods/merge_options_spec.rb +40 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +26 -0
- data/spec/unit/virtus/attribute/date/coerce_spec.rb +47 -0
- data/spec/unit/virtus/attribute/date/value_coerced_spec.rb +46 -0
- data/spec/unit/virtus/attribute/date_time/coerce_spec.rb +68 -0
- data/spec/unit/virtus/attribute/decimal/coerce_spec.rb +117 -0
- data/spec/unit/virtus/attribute/default_spec.rb +32 -0
- data/spec/unit/virtus/attribute/default_value/attribute_spec.rb +11 -0
- data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +4 -2
- data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +51 -0
- data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +9 -6
- data/spec/unit/virtus/attribute/default_value/value_spec.rb +11 -0
- data/spec/unit/virtus/attribute/define_accessor_methods_spec.rb +26 -0
- data/spec/unit/virtus/attribute/define_reader_method_spec.rb +24 -0
- data/spec/unit/virtus/attribute/define_writer_method_spec.rb +24 -0
- data/spec/unit/virtus/attribute/embedded_value/class_methods/merge_options_spec.rb +17 -0
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +50 -0
- data/spec/unit/virtus/attribute/float/coerce_spec.rb +117 -0
- data/spec/unit/virtus/attribute/get_spec.rb +80 -0
- data/spec/unit/virtus/attribute/inspect_spec.rb +27 -0
- data/spec/unit/virtus/attribute/instance_variable_name_spec.rb +12 -0
- data/spec/unit/virtus/attribute/integer/coerce_spec.rb +105 -0
- data/spec/unit/virtus/attribute/name_spec.rb +12 -0
- data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +1 -1
- data/spec/unit/virtus/attribute/numeric/class_methods/max_spec.rb +9 -0
- data/spec/unit/virtus/attribute/numeric/class_methods/min_spec.rb +9 -0
- data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +9 -7
- data/spec/unit/virtus/attribute/options_spec.rb +14 -0
- data/spec/unit/virtus/attribute/public_reader_spec.rb +24 -0
- data/spec/unit/virtus/attribute/public_writer_spec.rb +24 -0
- data/spec/unit/virtus/attribute/reader_visibility_spec.rb +24 -0
- data/spec/unit/virtus/attribute/set/coerce_spec.rb +13 -0
- data/spec/unit/virtus/attribute/set_spec.rb +49 -0
- data/spec/unit/virtus/attribute/string/coerce_spec.rb +11 -0
- data/spec/unit/virtus/attribute/time/coerce_spec.rb +67 -0
- data/spec/unit/virtus/attribute/value_coerced_spec.rb +19 -0
- data/spec/unit/virtus/attribute/writer_visibility_spec.rb +24 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +12 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +4 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +29 -9
- data/spec/unit/virtus/attributes_accessor/inspect_spec.rb +9 -0
- data/spec/unit/virtus/class_methods/attribute_spec.rb +23 -5
- data/spec/unit/virtus/class_methods/attributes_spec.rb +3 -5
- data/spec/unit/virtus/class_methods/const_missing_spec.rb +27 -0
- data/spec/unit/virtus/class_methods/inherited_spec.rb +21 -0
- data/spec/unit/virtus/coercion/array/to_set_spec.rb +12 -0
- data/spec/unit/virtus/coercion/date/class_methods/to_date_spec.rb +10 -0
- data/spec/unit/virtus/coercion/date_time/class_methods/to_datetime_spec.rb +10 -0
- data/spec/unit/virtus/coercion/hash/class_methods/to_date_spec.rb +10 -3
- data/spec/unit/virtus/coercion/hash/class_methods/to_datetime_spec.rb +10 -3
- data/spec/unit/virtus/coercion/hash/class_methods/to_time_spec.rb +10 -3
- data/spec/unit/virtus/coercion/object/class_methods/method_missing_spec.rb +8 -8
- data/spec/unit/virtus/coercion/string/class_methods/to_boolean_spec.rb +2 -2
- data/spec/unit/virtus/coercion/string/class_methods/to_constant_spec.rb +1 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_date_spec.rb +1 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_datetime_spec.rb +13 -13
- data/spec/unit/virtus/coercion/string/class_methods/to_decimal_spec.rb +25 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_float_spec.rb +25 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +30 -1
- data/spec/unit/virtus/coercion/string/class_methods/to_time_spec.rb +13 -13
- data/spec/unit/virtus/coercion/time/class_methods/to_time_spec.rb +10 -0
- data/spec/unit/virtus/coercion/true_class/class_methods/to_string_spec.rb +1 -1
- data/spec/unit/virtus/instance_methods/attributes_spec.rb +77 -20
- data/spec/unit/virtus/instance_methods/element_reference_spec.rb +1 -1
- data/spec/unit/virtus/instance_methods/element_set_spec.rb +2 -2
- data/spec/unit/virtus/instance_methods/to_hash_spec.rb +23 -0
- data/spec/unit/virtus/options/accept_options_spec.rb +10 -11
- data/spec/unit/virtus/options/accepted_options_spec.rb +1 -1
- data/spec/unit/virtus/options/options_spec.rb +27 -4
- data/tasks/metrics/ci.rake +2 -1
- data/tasks/metrics/heckle.rake +207 -0
- data/tasks/spec.rake +14 -7
- data/virtus.gemspec +1 -1
- metadata +111 -97
- data/VERSION +0 -1
- data/examples/custom_coercion_spec.rb +0 -50
- data/examples/default_values_spec.rb +0 -21
- data/examples/override_attribute_methods_spec.rb +0 -40
- data/spec/integration/virtus/attributes/attribute/set_spec.rb +0 -36
- data/spec/integration/virtus/class_methods/attribute_spec.rb +0 -82
- data/spec/integration/virtus/class_methods/attributes_spec.rb +0 -22
- data/spec/integration/virtus/class_methods/const_missing_spec.rb +0 -44
- data/spec/unit/shared/attribute.rb +0 -7
- data/spec/unit/shared/attribute/accept_options.rb +0 -37
- data/spec/unit/shared/attribute/accepted_options.rb +0 -5
- data/spec/unit/shared/attribute/get.rb +0 -44
- data/spec/unit/shared/attribute/inspect.rb +0 -7
- data/spec/unit/shared/attribute/set.rb +0 -37
- data/spec/unit/virtus/attribute/array_spec.rb +0 -24
- data/spec/unit/virtus/attribute/class_spec.rb +0 -24
- data/spec/unit/virtus/attribute/date_spec.rb +0 -59
- data/spec/unit/virtus/attribute/date_time_spec.rb +0 -87
- data/spec/unit/virtus/attribute/decimal_spec.rb +0 -109
- data/spec/unit/virtus/attribute/float_spec.rb +0 -109
- data/spec/unit/virtus/attribute/hash_spec.rb +0 -11
- data/spec/unit/virtus/attribute/integer_spec.rb +0 -99
- data/spec/unit/virtus/attribute/string_spec.rb +0 -21
- data/spec/unit/virtus/attribute/time_spec.rb +0 -82
- data/spec/unit/virtus/class_methods/new_spec.rb +0 -41
|
@@ -12,10 +12,26 @@ module Virtus
|
|
|
12
12
|
#
|
|
13
13
|
# post = Post.new(:tags => %w(red green blue))
|
|
14
14
|
#
|
|
15
|
-
class Array <
|
|
15
|
+
class Array < Collection
|
|
16
16
|
primitive ::Array
|
|
17
17
|
coercion_method :to_array
|
|
18
18
|
|
|
19
|
+
# Coerce a member of a source collection and append it to the target collection
|
|
20
|
+
#
|
|
21
|
+
# @param [Array, Set] collection
|
|
22
|
+
# target collection to which the coerced member should be appended
|
|
23
|
+
#
|
|
24
|
+
# @param [Object] entry
|
|
25
|
+
# the member that should be coerced and appended
|
|
26
|
+
#
|
|
27
|
+
# @return [Array, Set]
|
|
28
|
+
# collection with the coerced member appended to it
|
|
29
|
+
#
|
|
30
|
+
# @api private
|
|
31
|
+
def coerce_and_append_member(collection, entry)
|
|
32
|
+
collection << @member_type_instance.coerce(entry)
|
|
33
|
+
end
|
|
34
|
+
|
|
19
35
|
end # class Array
|
|
20
36
|
end # class Attribute
|
|
21
37
|
end # module Virtus
|
|
@@ -21,19 +21,20 @@ module Virtus
|
|
|
21
21
|
# Returns if the given value is either true or false
|
|
22
22
|
#
|
|
23
23
|
# @example
|
|
24
|
-
# Virtus::Attribute::Boolean.
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
24
|
+
# boolean = Virtus::Attribute::Boolean.new(:bool)
|
|
25
|
+
# boolean.value_coerced?(true) # => true
|
|
26
|
+
# boolean.value_coerced?(false) # => true
|
|
27
|
+
# boolean.value_coerced?(1) # => false
|
|
28
|
+
# boolean.value_coerced?('true') # => false
|
|
28
29
|
#
|
|
29
30
|
# @return [Boolean]
|
|
30
31
|
#
|
|
31
32
|
# @api public
|
|
32
|
-
def
|
|
33
|
+
def value_coerced?(value)
|
|
33
34
|
value.equal?(true) || value.equal?(false)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
# Creates
|
|
37
|
+
# Creates an attribute reader method as a query
|
|
37
38
|
#
|
|
38
39
|
# @param [Module] mod
|
|
39
40
|
#
|
|
@@ -42,13 +43,7 @@ module Virtus
|
|
|
42
43
|
# @api private
|
|
43
44
|
def define_reader_method(mod)
|
|
44
45
|
super
|
|
45
|
-
|
|
46
|
-
reader_method_name = "#{name}?"
|
|
47
|
-
attribute = self
|
|
48
|
-
|
|
49
|
-
mod.send(:define_method, reader_method_name) { attribute.get(self) }
|
|
50
|
-
mod.send(reader_visibility, reader_method_name)
|
|
51
|
-
|
|
46
|
+
mod.define_reader_method(self, "#{name}?", reader_visibility)
|
|
52
47
|
self
|
|
53
48
|
end
|
|
54
49
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Attribute
|
|
3
|
+
|
|
4
|
+
# Abstract superclass for collection Attributes.
|
|
5
|
+
#
|
|
6
|
+
# Handles coercing members to the designated member type.
|
|
7
|
+
#
|
|
8
|
+
# @abstract
|
|
9
|
+
class Collection < Object
|
|
10
|
+
|
|
11
|
+
# The type to which members of this collection will be coerced
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
#
|
|
15
|
+
# class Post
|
|
16
|
+
# include Virtus
|
|
17
|
+
#
|
|
18
|
+
# attribute :tags, Array[String]
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Post.attributes[:tags].member_type # => Virtus::Attribute::String
|
|
22
|
+
#
|
|
23
|
+
# @return [Virtus::Attribute]
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
attr_reader :member_type
|
|
27
|
+
|
|
28
|
+
# Handles collection with member_type syntax
|
|
29
|
+
#
|
|
30
|
+
# @param [Class]
|
|
31
|
+
#
|
|
32
|
+
# @param [Hash]
|
|
33
|
+
#
|
|
34
|
+
# @return [Hash]
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
def self.merge_options(type, options)
|
|
38
|
+
if !type.respond_to?(:size)
|
|
39
|
+
options
|
|
40
|
+
elsif type.size > 1
|
|
41
|
+
raise NotImplementedError, "build SumType from list of types (#{type.inspect})"
|
|
42
|
+
else
|
|
43
|
+
options.merge(:member_type => type.first)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Init an instance of Virtus::Attribute::Collection
|
|
48
|
+
#
|
|
49
|
+
# @api private
|
|
50
|
+
def initialize(*)
|
|
51
|
+
super
|
|
52
|
+
@member_type = @options.fetch(:member_type, Virtus::Attribute::Object)
|
|
53
|
+
@member_type_instance = Attribute.build(@name, @member_type)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Coerce a collection with members
|
|
57
|
+
#
|
|
58
|
+
# @param [Object]
|
|
59
|
+
#
|
|
60
|
+
# @return [Object]
|
|
61
|
+
#
|
|
62
|
+
# @api private
|
|
63
|
+
def coerce(value)
|
|
64
|
+
coerced = super
|
|
65
|
+
return coerced unless coerced.respond_to?(:inject)
|
|
66
|
+
coerced.inject(new_collection) do |collection, entry|
|
|
67
|
+
coerce_and_append_member(collection, entry)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Return an instance of the collection
|
|
72
|
+
#
|
|
73
|
+
# @return [Enumerable]
|
|
74
|
+
#
|
|
75
|
+
# @api private
|
|
76
|
+
def new_collection
|
|
77
|
+
self.class.primitive.new
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Coerce entry and add it to the collection
|
|
81
|
+
#
|
|
82
|
+
# @abstract
|
|
83
|
+
#
|
|
84
|
+
# @raise NotImplementedError
|
|
85
|
+
#
|
|
86
|
+
# @return [undefined]
|
|
87
|
+
#
|
|
88
|
+
# @api private
|
|
89
|
+
def coerce_and_append_member(collection, entry)
|
|
90
|
+
raise NotImplementedError,
|
|
91
|
+
"#{self.class}#coerce_and_append_member has not been implemented"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end # class Array
|
|
95
|
+
end # class Attribute
|
|
96
|
+
end # module Virtus
|
|
@@ -3,8 +3,8 @@ module Virtus
|
|
|
3
3
|
|
|
4
4
|
# Class representing the default value option
|
|
5
5
|
class DefaultValue
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
SINGLETON_CLASSES = [ ::NilClass, ::TrueClass, ::FalseClass,
|
|
7
|
+
::Numeric, ::Symbol ].freeze
|
|
8
8
|
|
|
9
9
|
# Returns the attribute associated with this default value instance
|
|
10
10
|
#
|
|
@@ -42,8 +42,8 @@ module Virtus
|
|
|
42
42
|
def evaluate(instance)
|
|
43
43
|
if callable?
|
|
44
44
|
call(instance)
|
|
45
|
-
elsif
|
|
46
|
-
value.
|
|
45
|
+
elsif cloneable?
|
|
46
|
+
value.clone
|
|
47
47
|
else
|
|
48
48
|
value
|
|
49
49
|
end
|
|
@@ -71,13 +71,17 @@ module Virtus
|
|
|
71
71
|
value.respond_to?(:call)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
# Returns whether or not the value is
|
|
74
|
+
# Returns whether or not the value is cloneable
|
|
75
75
|
#
|
|
76
76
|
# # return [TrueClass, FalseClass]
|
|
77
77
|
#
|
|
78
78
|
# @api private
|
|
79
|
-
def
|
|
80
|
-
|
|
79
|
+
def cloneable?
|
|
80
|
+
case value
|
|
81
|
+
when *SINGLETON_CLASSES then false
|
|
82
|
+
else
|
|
83
|
+
true
|
|
84
|
+
end
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
end # class DefaultValue
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
|
|
3
|
+
module Virtus
|
|
4
|
+
class Attribute
|
|
5
|
+
|
|
6
|
+
# EmbeddedValue
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
#
|
|
10
|
+
# class Address
|
|
11
|
+
# include Virtus
|
|
12
|
+
#
|
|
13
|
+
# attribute :street, String
|
|
14
|
+
# attribute :zipcode, String
|
|
15
|
+
# attribute :city, String
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# class User
|
|
19
|
+
# include Virtus
|
|
20
|
+
#
|
|
21
|
+
# attribute :address, Address
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# user = User.new(:address => {
|
|
25
|
+
# :street => 'Street 1/2', :zipcode => '12345', :city => 'NYC' })
|
|
26
|
+
#
|
|
27
|
+
class EmbeddedValue < Object
|
|
28
|
+
|
|
29
|
+
# @see Attribute.merge_options
|
|
30
|
+
#
|
|
31
|
+
# @return [Hash]
|
|
32
|
+
# an updated options hash for configuring an EmbeddedValue instance
|
|
33
|
+
#
|
|
34
|
+
# @api private
|
|
35
|
+
def self.merge_options(type, options)
|
|
36
|
+
options.merge(:model => type)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Sets @model ivar
|
|
40
|
+
#
|
|
41
|
+
# @see Virtus::Attribute#initialize
|
|
42
|
+
#
|
|
43
|
+
# @return [undefined]
|
|
44
|
+
#
|
|
45
|
+
# @api private
|
|
46
|
+
def initialize(name, options = {})
|
|
47
|
+
super
|
|
48
|
+
@model = options.fetch(:model, OpenStruct)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Coerce attributes into a virtus object
|
|
52
|
+
#
|
|
53
|
+
# @param [Hash,Virtus]
|
|
54
|
+
#
|
|
55
|
+
# @return [Virtus]
|
|
56
|
+
#
|
|
57
|
+
# @api private
|
|
58
|
+
def coerce(attributes_or_object)
|
|
59
|
+
value = if attributes_or_object.kind_of?(::Hash)
|
|
60
|
+
@model.new(attributes_or_object)
|
|
61
|
+
else
|
|
62
|
+
attributes_or_object
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
super(value)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end # class EmbeddedValue
|
|
69
|
+
end # class Attribute
|
|
70
|
+
end # module Virtus
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
class Attribute
|
|
3
|
+
|
|
4
|
+
# Set
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# class Post
|
|
8
|
+
# include Virtus
|
|
9
|
+
#
|
|
10
|
+
# attribute :tags, Set
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# post = Post.new(:tags => %w(red green blue))
|
|
14
|
+
#
|
|
15
|
+
class Set < Collection
|
|
16
|
+
primitive ::Set
|
|
17
|
+
coercion_method :to_set
|
|
18
|
+
|
|
19
|
+
def coerce_and_append_member(collection, entry)
|
|
20
|
+
collection << @member_type_instance.coerce(entry)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # class Set
|
|
24
|
+
end # class Attribute
|
|
25
|
+
end # module Virtus
|
data/lib/virtus/attribute_set.rb
CHANGED
|
@@ -24,9 +24,10 @@ module Virtus
|
|
|
24
24
|
#
|
|
25
25
|
# @api private
|
|
26
26
|
def initialize(parent = nil, attributes = [])
|
|
27
|
-
@parent
|
|
28
|
-
@attributes
|
|
29
|
-
@index
|
|
27
|
+
@parent = parent
|
|
28
|
+
@attributes = attributes.dup
|
|
29
|
+
@index = {}
|
|
30
|
+
@string_index = {}
|
|
30
31
|
reset
|
|
31
32
|
end
|
|
32
33
|
|
|
@@ -91,7 +92,7 @@ module Virtus
|
|
|
91
92
|
#
|
|
92
93
|
# @api public
|
|
93
94
|
def [](name)
|
|
94
|
-
@index[name]
|
|
95
|
+
@index.fetch(name) { @string_index[name] }
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
# Set an attribute by name
|
|
@@ -106,9 +107,8 @@ module Virtus
|
|
|
106
107
|
#
|
|
107
108
|
# @api public
|
|
108
109
|
def []=(name, attribute)
|
|
109
|
-
delete(name)
|
|
110
110
|
@attributes << attribute
|
|
111
|
-
|
|
111
|
+
update_index(name, attribute)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
# Reset the index when the parent is updated
|
|
@@ -118,33 +118,35 @@ module Virtus
|
|
|
118
118
|
# @api private
|
|
119
119
|
def reset
|
|
120
120
|
parent = self.parent
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
merge_attributes(parent) if parent
|
|
122
|
+
merge_attributes(@attributes)
|
|
123
123
|
self
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
private
|
|
127
127
|
|
|
128
|
-
#
|
|
128
|
+
# Merge the attributes into the index
|
|
129
129
|
#
|
|
130
|
-
# @param [
|
|
130
|
+
# @param [Array<Attribute>] attributes
|
|
131
131
|
#
|
|
132
132
|
# @return [undefined]
|
|
133
133
|
#
|
|
134
134
|
# @api private
|
|
135
|
-
def
|
|
136
|
-
|
|
135
|
+
def merge_attributes(attributes)
|
|
136
|
+
attributes.each { |attribute| update_index(attribute.name, attribute) }
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
-
#
|
|
139
|
+
# Update the symbol and string indexes with the attribute
|
|
140
140
|
#
|
|
141
|
-
# @param [
|
|
141
|
+
# @param [Symbol] name
|
|
142
|
+
#
|
|
143
|
+
# @param [Attribute] attribute
|
|
142
144
|
#
|
|
143
145
|
# @return [undefined]
|
|
144
146
|
#
|
|
145
147
|
# @api private
|
|
146
|
-
def
|
|
147
|
-
|
|
148
|
+
def update_index(name, attribute)
|
|
149
|
+
@index[name] = @string_index[name.to_s.freeze] = attribute
|
|
148
150
|
end
|
|
149
151
|
|
|
150
152
|
end # class AttributeSet
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Virtus
|
|
2
|
+
|
|
3
|
+
# Host attribute accessor methods
|
|
4
|
+
class AttributesAccessor < Module
|
|
5
|
+
|
|
6
|
+
# The inspect value of this Module
|
|
7
|
+
#
|
|
8
|
+
# This provides meaningful output when inspecting the ancestors
|
|
9
|
+
# of a class/module that includes this module
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
#
|
|
13
|
+
# class ClassWithAttributes
|
|
14
|
+
# include Virtus
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# mod = ClassWithAttributes.send(:virtus_setup_attributes_accessor_module)
|
|
18
|
+
# mod.inspect
|
|
19
|
+
#
|
|
20
|
+
# @return [String]
|
|
21
|
+
#
|
|
22
|
+
# @api public
|
|
23
|
+
attr_reader :inspect
|
|
24
|
+
|
|
25
|
+
# Initialize a module for hosting Attribute access methods
|
|
26
|
+
#
|
|
27
|
+
# @param [Symbol, String] name
|
|
28
|
+
#
|
|
29
|
+
# @api private
|
|
30
|
+
def initialize(name)
|
|
31
|
+
super()
|
|
32
|
+
@inspect = "#{name}::AttributesAccessor"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Defines an attribute reader method
|
|
36
|
+
#
|
|
37
|
+
# @param [Attribute] attribute
|
|
38
|
+
# @param [Symbol] method_name
|
|
39
|
+
# @param [Symbol] visibility
|
|
40
|
+
#
|
|
41
|
+
# @return [self]
|
|
42
|
+
#
|
|
43
|
+
# @api private
|
|
44
|
+
def define_reader_method(attribute, method_name, visibility)
|
|
45
|
+
define_method(method_name) { attribute.get(self) }
|
|
46
|
+
send(visibility, method_name)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Defines an attribute writer method
|
|
51
|
+
#
|
|
52
|
+
# @param [Attribute] attribute
|
|
53
|
+
# @param [Symbol] method_name
|
|
54
|
+
# @param [Symbol] visibility
|
|
55
|
+
#
|
|
56
|
+
# @return [self]
|
|
57
|
+
#
|
|
58
|
+
# @api private
|
|
59
|
+
def define_writer_method(attribute, method_name, visibility)
|
|
60
|
+
define_method(method_name) { |value| attribute.set(self, value) }
|
|
61
|
+
send(visibility, method_name)
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|