virtus2 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +39 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +18 -0
- data/Changelog.md +258 -0
- data/Gemfile +10 -0
- data/Guardfile +19 -0
- data/LICENSE +20 -0
- data/README.md +630 -0
- data/Rakefile +15 -0
- data/TODO.md +6 -0
- data/lib/virtus/attribute/accessor.rb +103 -0
- data/lib/virtus/attribute/boolean.rb +55 -0
- data/lib/virtus/attribute/builder.rb +182 -0
- data/lib/virtus/attribute/coercer.rb +45 -0
- data/lib/virtus/attribute/coercible.rb +20 -0
- data/lib/virtus/attribute/collection.rb +103 -0
- data/lib/virtus/attribute/default_value/from_callable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_clonable.rb +35 -0
- data/lib/virtus/attribute/default_value/from_symbol.rb +35 -0
- data/lib/virtus/attribute/default_value.rb +51 -0
- data/lib/virtus/attribute/embedded_value.rb +67 -0
- data/lib/virtus/attribute/enum.rb +45 -0
- data/lib/virtus/attribute/hash.rb +130 -0
- data/lib/virtus/attribute/lazy_default.rb +18 -0
- data/lib/virtus/attribute/nullify_blank.rb +24 -0
- data/lib/virtus/attribute/strict.rb +26 -0
- data/lib/virtus/attribute.rb +245 -0
- data/lib/virtus/attribute_set.rb +240 -0
- data/lib/virtus/builder/hook_context.rb +51 -0
- data/lib/virtus/builder.rb +133 -0
- data/lib/virtus/class_inclusions.rb +48 -0
- data/lib/virtus/class_methods.rb +90 -0
- data/lib/virtus/coercer.rb +41 -0
- data/lib/virtus/configuration.rb +72 -0
- data/lib/virtus/const_missing_extensions.rb +18 -0
- data/lib/virtus/extensions.rb +105 -0
- data/lib/virtus/instance_methods.rb +218 -0
- data/lib/virtus/model.rb +68 -0
- data/lib/virtus/module_extensions.rb +88 -0
- data/lib/virtus/support/equalizer.rb +128 -0
- data/lib/virtus/support/options.rb +113 -0
- data/lib/virtus/support/type_lookup.rb +109 -0
- data/lib/virtus/value_object.rb +150 -0
- data/lib/virtus/version.rb +3 -0
- data/lib/virtus.rb +310 -0
- data/spec/integration/attributes_attribute_spec.rb +28 -0
- data/spec/integration/building_module_spec.rb +90 -0
- data/spec/integration/collection_member_coercion_spec.rb +96 -0
- data/spec/integration/custom_attributes_spec.rb +42 -0
- data/spec/integration/custom_collection_attributes_spec.rb +101 -0
- data/spec/integration/default_values_spec.rb +87 -0
- data/spec/integration/defining_attributes_spec.rb +86 -0
- data/spec/integration/embedded_value_spec.rb +50 -0
- data/spec/integration/extending_objects_spec.rb +35 -0
- data/spec/integration/hash_attributes_coercion_spec.rb +54 -0
- data/spec/integration/inheritance_spec.rb +42 -0
- data/spec/integration/injectible_coercers_spec.rb +48 -0
- data/spec/integration/mass_assignment_with_accessors_spec.rb +44 -0
- data/spec/integration/overriding_virtus_spec.rb +46 -0
- data/spec/integration/required_attributes_spec.rb +25 -0
- data/spec/integration/struct_as_embedded_value_spec.rb +28 -0
- data/spec/integration/using_modules_spec.rb +55 -0
- data/spec/integration/value_object_with_custom_constructor_spec.rb +42 -0
- data/spec/integration/virtus/instance_level_attributes_spec.rb +23 -0
- data/spec/integration/virtus/value_object_spec.rb +99 -0
- data/spec/shared/constants_helpers.rb +9 -0
- data/spec/shared/freeze_method_behavior.rb +40 -0
- data/spec/shared/idempotent_method_behaviour.rb +5 -0
- data/spec/shared/options_class_method.rb +19 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +43 -0
- data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +25 -0
- data/spec/unit/virtus/attribute/class_methods/build_spec.rb +180 -0
- data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +32 -0
- data/spec/unit/virtus/attribute/coerce_spec.rb +129 -0
- data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +105 -0
- data/spec/unit/virtus/attribute/collection/coerce_spec.rb +74 -0
- data/spec/unit/virtus/attribute/collection/value_coerced_predicate_spec.rb +31 -0
- data/spec/unit/virtus/attribute/comparison_spec.rb +20 -0
- data/spec/unit/virtus/attribute/custom_collection_spec.rb +29 -0
- data/spec/unit/virtus/attribute/defined_spec.rb +20 -0
- data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +70 -0
- data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +91 -0
- data/spec/unit/virtus/attribute/get_spec.rb +32 -0
- data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +106 -0
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +92 -0
- data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +20 -0
- data/spec/unit/virtus/attribute/rename_spec.rb +16 -0
- data/spec/unit/virtus/attribute/required_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute/set_default_value_spec.rb +107 -0
- data/spec/unit/virtus/attribute/set_spec.rb +29 -0
- data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +19 -0
- data/spec/unit/virtus/attribute_set/append_spec.rb +47 -0
- data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +36 -0
- data/spec/unit/virtus/attribute_set/each_spec.rb +65 -0
- data/spec/unit/virtus/attribute_set/element_reference_spec.rb +17 -0
- data/spec/unit/virtus/attribute_set/element_set_spec.rb +64 -0
- data/spec/unit/virtus/attribute_set/merge_spec.rb +34 -0
- data/spec/unit/virtus/attribute_set/reset_spec.rb +71 -0
- data/spec/unit/virtus/attribute_spec.rb +229 -0
- data/spec/unit/virtus/attributes_reader_spec.rb +41 -0
- data/spec/unit/virtus/attributes_writer_spec.rb +51 -0
- data/spec/unit/virtus/class_methods/finalize_spec.rb +67 -0
- data/spec/unit/virtus/class_methods/new_spec.rb +39 -0
- data/spec/unit/virtus/config_spec.rb +13 -0
- data/spec/unit/virtus/element_reader_spec.rb +21 -0
- data/spec/unit/virtus/element_writer_spec.rb +19 -0
- data/spec/unit/virtus/freeze_spec.rb +41 -0
- data/spec/unit/virtus/model_spec.rb +197 -0
- data/spec/unit/virtus/module_spec.rb +174 -0
- data/spec/unit/virtus/set_default_attributes_spec.rb +32 -0
- data/spec/unit/virtus/value_object_spec.rb +138 -0
- data/virtus2.gemspec +26 -0
- metadata +225 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Define equality, equivalence and inspection methods
|
4
|
+
class Equalizer < Module
|
5
|
+
|
6
|
+
# Initialize an Equalizer with the given keys
|
7
|
+
#
|
8
|
+
# Will use the keys with which it is initialized to define #cmp?,
|
9
|
+
# #hash, and #inspect
|
10
|
+
#
|
11
|
+
# @param [String] name
|
12
|
+
#
|
13
|
+
# @param [Array<Symbol>] keys
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def initialize(name, keys = [])
|
19
|
+
@name = name.dup.freeze
|
20
|
+
@keys = keys.dup
|
21
|
+
define_methods
|
22
|
+
include_comparison_methods
|
23
|
+
end
|
24
|
+
|
25
|
+
# Append a key and compile the equality methods
|
26
|
+
#
|
27
|
+
# @return [Equalizer] self
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
def <<(key)
|
31
|
+
@keys << key
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Define the equalizer methods based on #keys
|
38
|
+
#
|
39
|
+
# @return [undefined]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
def define_methods
|
43
|
+
define_cmp_method
|
44
|
+
define_hash_method
|
45
|
+
define_inspect_method
|
46
|
+
end
|
47
|
+
|
48
|
+
# Define an #cmp? method based on the instance's values identified by #keys
|
49
|
+
#
|
50
|
+
# @return [undefined]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
def define_cmp_method
|
54
|
+
keys = @keys
|
55
|
+
define_method(:cmp?) do |comparator, other|
|
56
|
+
keys.all? { |key| send(key).send(comparator, other.send(key)) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Define a #hash method based on the instance's values identified by #keys
|
61
|
+
#
|
62
|
+
# @return [undefined]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def define_hash_method
|
66
|
+
keys = @keys
|
67
|
+
define_method(:hash) do
|
68
|
+
keys.map { |key| send(key) }.push(self.class).hash
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Define an inspect method that reports the values of the instance's keys
|
73
|
+
#
|
74
|
+
# @return [undefined]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
def define_inspect_method
|
78
|
+
name, keys = @name, @keys
|
79
|
+
define_method(:inspect) do
|
80
|
+
"#<#{name}#{keys.map { |key| " #{key}=#{send(key).inspect}" }.join}>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Include the #eql? and #== methods
|
85
|
+
#
|
86
|
+
# @return [undefined]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def include_comparison_methods
|
90
|
+
module_eval { include Methods }
|
91
|
+
end
|
92
|
+
|
93
|
+
# The comparison methods
|
94
|
+
module Methods
|
95
|
+
|
96
|
+
# Compare the object with other object for equality
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# object.eql?(other) # => true or false
|
100
|
+
#
|
101
|
+
# @param [Object] other
|
102
|
+
# the other object to compare with
|
103
|
+
#
|
104
|
+
# @return [Boolean]
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def eql?(other)
|
108
|
+
instance_of?(other.class) && cmp?(__method__, other)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Compare the object with other object for equivalency
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# object == other # => true or false
|
115
|
+
#
|
116
|
+
# @param [Object] other
|
117
|
+
# the other object to compare with
|
118
|
+
#
|
119
|
+
# @return [Boolean]
|
120
|
+
#
|
121
|
+
# @api public
|
122
|
+
def ==(other)
|
123
|
+
other.kind_of?(self.class) && cmp?(__method__, other)
|
124
|
+
end
|
125
|
+
|
126
|
+
end # module Methods
|
127
|
+
end # class Equalizer
|
128
|
+
end # module Virtus
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# A module that adds class and instance level options
|
4
|
+
module Options
|
5
|
+
|
6
|
+
# Returns default options hash for a given attribute class
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Virtus::Attribute::String.options
|
10
|
+
# # => {:primitive => String}
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
# a hash of default option values
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
def options
|
17
|
+
accepted_options.each_with_object({}) do |option_name, options|
|
18
|
+
option_value = send(option_name)
|
19
|
+
options[option_name] = option_value unless option_value.nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an array of valid options
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# Virtus::Attribute::String.accepted_options
|
27
|
+
# # => [:primitive, :accessor, :reader, :writer]
|
28
|
+
#
|
29
|
+
# @return [Array]
|
30
|
+
# the array of valid option names
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def accepted_options
|
34
|
+
@accepted_options ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
# Defines which options are valid for a given attribute class
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# class MyAttribute < Virtus::Attribute
|
41
|
+
# accept_options :foo, :bar
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# @return [self]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def accept_options(*new_options)
|
48
|
+
add_accepted_options(new_options)
|
49
|
+
new_options.each { |option| define_option_method(option) }
|
50
|
+
descendants.each { |descendant| descendant.add_accepted_options(new_options) }
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
# Adds a reader/writer method for the give option name
|
57
|
+
#
|
58
|
+
# @return [undefined]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
def define_option_method(option)
|
62
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
63
|
+
def self.#{option}(value = Undefined) # def self.primitive(value = Undefined)
|
64
|
+
@#{option} = nil unless defined?(@#{option}) # @primitive = nil unless defined?(@primitive)
|
65
|
+
return @#{option} if value.equal?(Undefined) # return @primitive if value.equal?(Undefined)
|
66
|
+
@#{option} = value # @primitive = value
|
67
|
+
self # self
|
68
|
+
end # end
|
69
|
+
RUBY
|
70
|
+
end
|
71
|
+
|
72
|
+
# Sets default options
|
73
|
+
#
|
74
|
+
# @param [#each] new_options
|
75
|
+
# options to be set
|
76
|
+
#
|
77
|
+
# @return [self]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def set_options(new_options)
|
81
|
+
new_options.each { |pair| send(*pair) }
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds new options that an attribute class can accept
|
86
|
+
#
|
87
|
+
# @param [#to_ary] new_options
|
88
|
+
# new options to be added
|
89
|
+
#
|
90
|
+
# @return [self]
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
def add_accepted_options(new_options)
|
94
|
+
accepted_options.concat(new_options)
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Adds descendant to descendants array and inherits default options
|
101
|
+
#
|
102
|
+
# @param [Class] descendant
|
103
|
+
#
|
104
|
+
# @return [undefined]
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
def inherited(descendant)
|
108
|
+
super
|
109
|
+
descendant.add_accepted_options(accepted_options).set_options(options)
|
110
|
+
end
|
111
|
+
|
112
|
+
end # module Options
|
113
|
+
end # module Virtus
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# A module that adds type lookup to a class
|
4
|
+
module TypeLookup
|
5
|
+
|
6
|
+
TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
|
7
|
+
|
8
|
+
# Set cache ivar on the model
|
9
|
+
#
|
10
|
+
# @param [Class] model
|
11
|
+
#
|
12
|
+
# @return [undefined]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.extended(model)
|
16
|
+
model.instance_variable_set('@type_lookup_cache', {})
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a descendant based on a name or class
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# MyClass.determine_type('String') # => MyClass::String
|
23
|
+
#
|
24
|
+
# @param [Class, #to_s] class_or_name
|
25
|
+
# name of a class or a class itself
|
26
|
+
#
|
27
|
+
# @return [Class]
|
28
|
+
# a descendant
|
29
|
+
#
|
30
|
+
# @return [nil]
|
31
|
+
# nil if the type cannot be determined by the class_or_name
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def determine_type(class_or_name)
|
35
|
+
@type_lookup_cache[class_or_name] ||= determine_type_and_cache(class_or_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the default primitive supported
|
39
|
+
#
|
40
|
+
# @return [Class]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def primitive
|
44
|
+
raise NotImplementedError, "#{self}.primitive must be implemented"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def determine_type_and_cache(class_or_name)
|
51
|
+
case class_or_name
|
52
|
+
when singleton_class
|
53
|
+
determine_type_from_descendant(class_or_name)
|
54
|
+
when Class
|
55
|
+
determine_type_from_primitive(class_or_name)
|
56
|
+
else
|
57
|
+
determine_type_from_string(class_or_name.to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return the class given a descendant
|
62
|
+
#
|
63
|
+
# @param [Class] descendant
|
64
|
+
#
|
65
|
+
# @return [Class]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def determine_type_from_descendant(descendant)
|
69
|
+
descendant if descendant < self
|
70
|
+
end
|
71
|
+
|
72
|
+
# Return the class given a primitive
|
73
|
+
#
|
74
|
+
# @param [Class] primitive
|
75
|
+
#
|
76
|
+
# @return [Class]
|
77
|
+
#
|
78
|
+
# @return [nil]
|
79
|
+
# nil if the type cannot be determined by the primitive
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
def determine_type_from_primitive(primitive)
|
83
|
+
type = nil
|
84
|
+
descendants.select(&:primitive).reverse_each do |descendant|
|
85
|
+
descendant_primitive = descendant.primitive
|
86
|
+
next unless primitive <= descendant_primitive
|
87
|
+
type = descendant if type.nil? or type.primitive > descendant_primitive
|
88
|
+
end
|
89
|
+
type
|
90
|
+
end
|
91
|
+
|
92
|
+
# Return the class given a string
|
93
|
+
#
|
94
|
+
# @param [String] string
|
95
|
+
#
|
96
|
+
# @return [Class]
|
97
|
+
#
|
98
|
+
# @return [nil]
|
99
|
+
# nil if the type cannot be determined by the string
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
def determine_type_from_string(string)
|
103
|
+
if string =~ TYPE_FORMAT and const_defined?(string, *EXTRA_CONST_ARGS)
|
104
|
+
const_get(string, *EXTRA_CONST_ARGS)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end # module TypeLookup
|
109
|
+
end # module Virtus
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Include this Module for Value Object semantics
|
4
|
+
#
|
5
|
+
# The idea is that instances should be immutable and compared based on state
|
6
|
+
# (rather than identity, as is typically the case)
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class GeoLocation
|
10
|
+
# include Virtus::ValueObject
|
11
|
+
# attribute :latitude, Float
|
12
|
+
# attribute :longitude, Float
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# location = GeoLocation.new(:latitude => 10, :longitude => 100)
|
16
|
+
# same_location = GeoLocation.new(:latitude => 10, :longitude => 100)
|
17
|
+
# location == same_location #=> true
|
18
|
+
# hash = { location => :foo }
|
19
|
+
# hash[same_location] #=> :foo
|
20
|
+
module ValueObject
|
21
|
+
|
22
|
+
# Callback to configure including Class as a Value Object
|
23
|
+
#
|
24
|
+
# Including Class will include Virtus and have additional
|
25
|
+
# value object semantics defined in this module
|
26
|
+
#
|
27
|
+
# @return [Undefined]
|
28
|
+
#
|
29
|
+
# TODO: stacking modules is getting painful
|
30
|
+
# time for Facets' module_inheritance, ActiveSupport::Concern or the like
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def self.included(base)
|
34
|
+
Virtus.warn "Virtus::ValueObject is deprecated and will be removed in 1.0.0 #{caller.first}"
|
35
|
+
|
36
|
+
base.instance_eval do
|
37
|
+
include Virtus
|
38
|
+
include InstanceMethods
|
39
|
+
extend ClassMethods
|
40
|
+
extend AllowedWriterMethods
|
41
|
+
private :attributes=
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private_class_method :included
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
|
49
|
+
# ValueObjects are immutable and can't be cloned
|
50
|
+
#
|
51
|
+
# They always represent the same value
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
#
|
55
|
+
# value_object.clone === value_object # => true
|
56
|
+
#
|
57
|
+
# @return [self]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def clone
|
61
|
+
self
|
62
|
+
end
|
63
|
+
alias dup clone
|
64
|
+
|
65
|
+
# Create a new ValueObject by combining the passed attribute hash with
|
66
|
+
# the instances attributes.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
#
|
70
|
+
# number = PhoneNumber.new(kind: "mobile", number: "123-456-78-90")
|
71
|
+
# number.with(number: "987-654-32-10")
|
72
|
+
# # => #<PhoneNumber kind="mobile" number="987-654-32-10">
|
73
|
+
#
|
74
|
+
# @return [Object]
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def with(attribute_updates)
|
78
|
+
self.class.new(attribute_set.get(self).merge(attribute_updates))
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
module AllowedWriterMethods
|
84
|
+
# The list of writer methods that can be mass-assigned to in #attributes=
|
85
|
+
#
|
86
|
+
# @return [Set]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def allowed_writer_methods
|
90
|
+
@allowed_writer_methods ||=
|
91
|
+
begin
|
92
|
+
allowed_writer_methods = super
|
93
|
+
allowed_writer_methods += attribute_set.map{|attr| "#{attr.name}="}
|
94
|
+
allowed_writer_methods.to_set.freeze
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
module ClassMethods
|
100
|
+
|
101
|
+
# Define an attribute on the receiver
|
102
|
+
#
|
103
|
+
# The Attribute will have private writer methods (eg., immutable instances)
|
104
|
+
# and be used in equality/equivalence comparisons
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# class GeoLocation
|
108
|
+
# include Virtus::ValueObject
|
109
|
+
#
|
110
|
+
# attribute :latitude, Float
|
111
|
+
# attribute :longitude, Float
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# @see Virtus::ClassMethods.attribute
|
115
|
+
#
|
116
|
+
# @return [self]
|
117
|
+
#
|
118
|
+
# @api public
|
119
|
+
def attribute(name, type, options = {})
|
120
|
+
equalizer << name
|
121
|
+
super name, type, options.merge(:writer => :private)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Define and include a module that provides Value Object semantics
|
125
|
+
#
|
126
|
+
# Included module will have #inspect, #eql?, #== and #hash
|
127
|
+
# methods whose definition is based on the _keys_ argument
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# virtus_class.equalizer
|
131
|
+
#
|
132
|
+
# @return [Equalizer]
|
133
|
+
# An Equalizer module which defines #inspect, #eql?, #== and #hash
|
134
|
+
# for instances of this class
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def equalizer
|
138
|
+
@equalizer ||=
|
139
|
+
begin
|
140
|
+
equalizer = Virtus::Equalizer.new(name || inspect)
|
141
|
+
include equalizer
|
142
|
+
equalizer
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end # module ClassMethods
|
147
|
+
|
148
|
+
end # module ValueObject
|
149
|
+
|
150
|
+
end # module Virtus
|