motion_virtus 1.0.0.beta0
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.
- checksums.yaml +15 -0
- data/README.md +445 -0
- data/lib/motion_virtus.rb +13 -0
- data/lib/project/attribute/accessor/builder.rb +69 -0
- data/lib/project/attribute/accessor/lazy_accessor.rb +39 -0
- data/lib/project/attribute/accessor.rb +100 -0
- data/lib/project/attribute/accessor_method.rb +73 -0
- data/lib/project/attribute/array.rb +24 -0
- data/lib/project/attribute/boolean.rb +52 -0
- data/lib/project/attribute/class.rb +23 -0
- data/lib/project/attribute/coercer.rb +43 -0
- data/lib/project/attribute/collection/coercible_writer.rb +83 -0
- data/lib/project/attribute/collection.rb +56 -0
- data/lib/project/attribute/date.rb +36 -0
- data/lib/project/attribute/date_time.rb +38 -0
- data/lib/project/attribute/decimal.rb +23 -0
- data/lib/project/attribute/default_value/from_callable.rb +37 -0
- data/lib/project/attribute/default_value/from_clonable.rb +37 -0
- data/lib/project/attribute/default_value/from_symbol.rb +37 -0
- data/lib/project/attribute/default_value.rb +49 -0
- data/lib/project/attribute/embedded_value/open_struct_coercer.rb +43 -0
- data/lib/project/attribute/embedded_value/struct_coercer.rb +42 -0
- data/lib/project/attribute/embedded_value.rb +69 -0
- data/lib/project/attribute/float.rb +30 -0
- data/lib/project/attribute/hash/coercible_writer.rb +78 -0
- data/lib/project/attribute/hash.rb +66 -0
- data/lib/project/attribute/integer.rb +27 -0
- data/lib/project/attribute/numeric.rb +25 -0
- data/lib/project/attribute/object.rb +13 -0
- data/lib/project/attribute/reader.rb +39 -0
- data/lib/project/attribute/set.rb +22 -0
- data/lib/project/attribute/string.rb +24 -0
- data/lib/project/attribute/symbol.rb +23 -0
- data/lib/project/attribute/time.rb +36 -0
- data/lib/project/attribute/writer/coercible.rb +45 -0
- data/lib/project/attribute/writer.rb +73 -0
- data/lib/project/attribute.rb +292 -0
- data/lib/project/attribute_set.rb +260 -0
- data/lib/project/class_inclusions.rb +41 -0
- data/lib/project/class_methods.rb +102 -0
- data/lib/project/configuration.rb +65 -0
- data/lib/project/const_missing_extensions.rb +16 -0
- data/lib/project/extensions.rb +101 -0
- data/lib/project/instance_methods.rb +165 -0
- data/lib/project/module_builder.rb +92 -0
- data/lib/project/module_extensions.rb +72 -0
- data/lib/project/stubs/date.rb +2 -0
- data/lib/project/stubs/date_time.rb +2 -0
- data/lib/project/stubs/decimal.rb +2 -0
- data/lib/project/stubs/ostruct.rb +149 -0
- data/lib/project/stubs/set.rb +767 -0
- data/lib/project/stubs.rb +5 -0
- data/lib/project/support/equalizer.rb +147 -0
- data/lib/project/support/options.rb +114 -0
- data/lib/project/support/type_lookup.rb +109 -0
- data/lib/project/value_object.rb +139 -0
- data/lib/project/version.rb +3 -0
- data/lib/project/virtus.rb +128 -0
- metadata +158 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# A Configuration instance
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
# Access the coerce setting for this instance
|
7
|
+
attr_accessor :coerce
|
8
|
+
|
9
|
+
# Build new configuration instance using the passed block
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# Configuration.build do |config|
|
13
|
+
# config.coerce = false
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @return [Configuration]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def self.build(&block)
|
20
|
+
new.call(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initialized a configuration instance
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
def initialize
|
29
|
+
@coerce = true
|
30
|
+
@coercer = Coercible::Coercer.new
|
31
|
+
end
|
32
|
+
|
33
|
+
# Provide access to the attributes and methods via the passed block
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# configuration.call do |config|
|
37
|
+
# config.coerce = false
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @return [self]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def call(&block)
|
44
|
+
block.call(self) if block_given?
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Access the coercer for this instance and optional configure a
|
49
|
+
# new coercer with the passed block
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# configuration.coercer do |config|
|
53
|
+
# config.string.boolean_map = { true => '1', false => '0' }
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# @return [Coercer]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def coercer(&block)
|
60
|
+
@coercer = Coercible::Coercer.new(&block) if block_given?
|
61
|
+
@coercer
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Configuration
|
65
|
+
end # module Virtus
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Virtus
|
2
|
+
module ConstMissingExtensions
|
3
|
+
|
4
|
+
# Hooks into const missing process to determine types of attributes
|
5
|
+
#
|
6
|
+
# @param [String] name
|
7
|
+
#
|
8
|
+
# @return [Class]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def const_missing(name)
|
12
|
+
Attribute.determine_type(name) or super
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Extensions common for both classes and instances
|
4
|
+
module Extensions
|
5
|
+
WRITER_METHOD_REGEXP = /=\z/.freeze
|
6
|
+
INVALID_WRITER_METHODS = %w[ == != === []= attributes= ].to_set.freeze
|
7
|
+
|
8
|
+
# A hook called when an object is extended with Virtus
|
9
|
+
#
|
10
|
+
# @param [Object] object
|
11
|
+
#
|
12
|
+
# @return [undefined]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.extended(object)
|
16
|
+
super
|
17
|
+
object.instance_eval do
|
18
|
+
extend InstanceMethods
|
19
|
+
extend attribute_set
|
20
|
+
end
|
21
|
+
end
|
22
|
+
private_class_method :extended
|
23
|
+
|
24
|
+
# Defines an attribute on an object's class
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# class Book
|
28
|
+
# include Virtus
|
29
|
+
#
|
30
|
+
# attribute :title, String
|
31
|
+
# attribute :author, String
|
32
|
+
# attribute :published_at, DateTime
|
33
|
+
# attribute :page_count, Integer
|
34
|
+
# attribute :index # defaults to Object
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @param [Symbol] name
|
38
|
+
# the name of an attribute
|
39
|
+
#
|
40
|
+
# @param [Class] type
|
41
|
+
# the type class of an attribute
|
42
|
+
#
|
43
|
+
# @param [#to_hash] options
|
44
|
+
# the extra options hash
|
45
|
+
#
|
46
|
+
# @return [self]
|
47
|
+
#
|
48
|
+
# @see Attribute.build
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def attribute(name, type, options = {})
|
52
|
+
attribute = Attribute.build(name, type, merge_options(options))
|
53
|
+
virtus_add_attribute(attribute)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# The list of writer methods that can be mass-assigned to in #attributes=
|
58
|
+
#
|
59
|
+
# @return [Set]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def allowed_writer_methods
|
63
|
+
@allowed_writer_methods ||=
|
64
|
+
begin
|
65
|
+
allowed_writer_methods = allowed_methods.grep(WRITER_METHOD_REGEXP).to_set
|
66
|
+
allowed_writer_methods -= INVALID_WRITER_METHODS
|
67
|
+
allowed_writer_methods.freeze
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Return an attribute set for that instance
|
74
|
+
#
|
75
|
+
# @return [AttributeSet]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def attribute_set
|
79
|
+
@attribute_set ||= AttributeSet.new
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add an attribute to the attribute set
|
83
|
+
#
|
84
|
+
# @return [AttributeSet]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def virtus_add_attribute(attribute)
|
88
|
+
attribute_set << attribute
|
89
|
+
end
|
90
|
+
|
91
|
+
# Merge default options
|
92
|
+
#
|
93
|
+
# @return [Hash]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
def merge_options(options)
|
97
|
+
{ :coerce => Virtus.coerce }.merge(options)
|
98
|
+
end
|
99
|
+
|
100
|
+
end # module Extensions
|
101
|
+
end # module Virtus
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Instance methods that are added when you include Virtus
|
4
|
+
module InstanceMethods
|
5
|
+
|
6
|
+
# Set attributes during initialization of an object
|
7
|
+
#
|
8
|
+
# @param [#to_hash] attributes
|
9
|
+
# the attributes hash to be set
|
10
|
+
#
|
11
|
+
# @return [undefined]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def initialize(attributes = nil)
|
15
|
+
attribute_set.set(self, attributes) if attributes
|
16
|
+
set_default_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a value of the attribute with the given name
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# class User
|
23
|
+
# include Virtus
|
24
|
+
#
|
25
|
+
# attribute :name, String
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# user = User.new(:name => 'John')
|
29
|
+
# user[:name] # => "John"
|
30
|
+
#
|
31
|
+
# @param [Symbol] name
|
32
|
+
# a name of an attribute
|
33
|
+
#
|
34
|
+
# @return [Object]
|
35
|
+
# a value of an attribute
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def [](name)
|
39
|
+
public_send(name)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets a value of the attribute with the given name
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# class User
|
46
|
+
# include Virtus
|
47
|
+
#
|
48
|
+
# attribute :name, String
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# user = User.new
|
52
|
+
# user[:name] = "John" # => "John"
|
53
|
+
# user.name # => "John"
|
54
|
+
#
|
55
|
+
# @param [Symbol] name
|
56
|
+
# a name of an attribute
|
57
|
+
#
|
58
|
+
# @param [Object] value
|
59
|
+
# a value to be set
|
60
|
+
#
|
61
|
+
# @return [Object]
|
62
|
+
# the value set on an object
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def []=(name, value)
|
66
|
+
public_send("#{name}=", value)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns a hash of all publicly accessible attributes
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# class User
|
73
|
+
# include Virtus
|
74
|
+
#
|
75
|
+
# attribute :name, String
|
76
|
+
# attribute :age, Integer
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# user = User.new(:name => 'John', :age => 28)
|
80
|
+
# user.attributes # => { :name => 'John', :age => 28 }
|
81
|
+
#
|
82
|
+
# @return [Hash]
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
def attributes
|
86
|
+
attribute_set.get(self, &:public_reader?)
|
87
|
+
end
|
88
|
+
alias_method :to_hash, :attributes
|
89
|
+
|
90
|
+
# Mass-assign attribute values
|
91
|
+
#
|
92
|
+
# Keys in the +attributes+ param can be symbols or strings.
|
93
|
+
# All referenced Attribute writer methods *will* be called.
|
94
|
+
# Non-attribute setter methods on the receiver *will* be called.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# class User
|
98
|
+
# include Virtus
|
99
|
+
#
|
100
|
+
# attribute :name, String
|
101
|
+
# attribute :age, Integer
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# user = User.new
|
105
|
+
# user.attributes = { :name => 'John', 'age' => 28 }
|
106
|
+
#
|
107
|
+
# @param [#to_hash] attributes
|
108
|
+
# a hash of attribute names and values to set on the receiver
|
109
|
+
#
|
110
|
+
# @return [Hash]
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
def attributes=(attributes)
|
114
|
+
attribute_set.set(self, attributes)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Freeze object
|
118
|
+
#
|
119
|
+
# @return [self]
|
120
|
+
#
|
121
|
+
# @api public
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
#
|
125
|
+
# class User
|
126
|
+
# include Virtus
|
127
|
+
#
|
128
|
+
# attribute :name, String
|
129
|
+
# attribute :age, Integer
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# user = User.new(:name => 'John', :age => 28)
|
133
|
+
# user.frozen? # => false
|
134
|
+
# user.freeze
|
135
|
+
# user.frozen? # => true
|
136
|
+
#
|
137
|
+
# @api public
|
138
|
+
def freeze
|
139
|
+
set_default_attributes
|
140
|
+
super
|
141
|
+
end
|
142
|
+
|
143
|
+
# Set default attributes
|
144
|
+
#
|
145
|
+
# @return [self]
|
146
|
+
#
|
147
|
+
# @api private
|
148
|
+
def set_default_attributes
|
149
|
+
attribute_set.set_defaults(self)
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# The list of allowed public methods
|
156
|
+
#
|
157
|
+
# @return [Array<String>]
|
158
|
+
#
|
159
|
+
# @api private
|
160
|
+
def allowed_methods
|
161
|
+
public_methods.map(&:to_s)
|
162
|
+
end
|
163
|
+
|
164
|
+
end # module InstanceMethods
|
165
|
+
end # module Virtus
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Class to build a Virtus module with it's own configuration
|
4
|
+
#
|
5
|
+
# This allows for individual Virtus modules to be included in
|
6
|
+
# classes and not impacted by the global Virtus configuration,
|
7
|
+
# which is implemented using Virtus::Configuration.
|
8
|
+
class ModuleBuilder
|
9
|
+
|
10
|
+
# Return module
|
11
|
+
#
|
12
|
+
# @return [Module]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :module
|
16
|
+
|
17
|
+
# Return configuration
|
18
|
+
#
|
19
|
+
# @return [Configuration]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
attr_reader :configuration
|
23
|
+
|
24
|
+
# Builds a new Virtus module
|
25
|
+
#
|
26
|
+
# The block is passed to Virtus::Configuration
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# ModuleBuilder.call do |config|
|
30
|
+
# # config settings
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @return [Module]
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def self.call(&block)
|
37
|
+
config = Configuration.build(&block)
|
38
|
+
builder = new(config)
|
39
|
+
builder.add_included_hook
|
40
|
+
builder.module
|
41
|
+
end
|
42
|
+
|
43
|
+
# Initializes a new ModuleBuilder
|
44
|
+
#
|
45
|
+
# @param [Configuration] configuration
|
46
|
+
#
|
47
|
+
# @param [Module] mod
|
48
|
+
#
|
49
|
+
# @return [undefined]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def initialize(configuration, mod = Module.new)
|
53
|
+
@configuration = configuration
|
54
|
+
@module = mod.send(:include, Virtus)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds the .included hook to the anonymous module which then defines the
|
58
|
+
# .attribute method to override the default.
|
59
|
+
#
|
60
|
+
# @return [Module]
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
def add_included_hook
|
64
|
+
attribute_proc = attribute_method(configuration)
|
65
|
+
|
66
|
+
self.module.define_singleton_method :included do |object|
|
67
|
+
super(object)
|
68
|
+
object.send :define_singleton_method, :attribute, attribute_proc
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Wrapper for the attribute method that is used in .add_included_hook
|
73
|
+
# The coercer is passed in the unused key :configured_coercer to allow the
|
74
|
+
# property encapsulation by Virtus::Attribute::Coercer, where the
|
75
|
+
# coercion method is known.
|
76
|
+
#
|
77
|
+
# @return [Proc(lambda)]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def attribute_method(configuration)
|
81
|
+
lambda do |name, type, options = {}|
|
82
|
+
module_options = {
|
83
|
+
:coerce => configuration.coerce,
|
84
|
+
:configured_coercer => configuration.coercer
|
85
|
+
}
|
86
|
+
|
87
|
+
super(name, type, module_options.merge(options))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end # class ModuleBuilder
|
92
|
+
end # module Virtus
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Virtus
|
2
|
+
|
3
|
+
# Virtus module that can define attributes for later inclusion
|
4
|
+
#
|
5
|
+
module ModuleExtensions
|
6
|
+
include ConstMissingExtensions
|
7
|
+
|
8
|
+
# Define an attribute in the module
|
9
|
+
#
|
10
|
+
# @see Virtus::Extensions#attribute
|
11
|
+
#
|
12
|
+
# @return [self]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def attribute(*args)
|
16
|
+
attribute_definitions << args
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Extend an object with Virtus methods and define attributes
|
23
|
+
#
|
24
|
+
# @param [Object] object
|
25
|
+
#
|
26
|
+
# @return [undefined]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def extended(object)
|
30
|
+
super
|
31
|
+
object.extend(Virtus)
|
32
|
+
define_attributes(object)
|
33
|
+
object.set_default_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extend a class with Virtus methods and define attributes
|
37
|
+
#
|
38
|
+
# @param [Object] object
|
39
|
+
#
|
40
|
+
# @return [undefined]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def included(object)
|
44
|
+
super
|
45
|
+
object.module_eval { include Virtus }
|
46
|
+
define_attributes(object)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return attribute definitions
|
50
|
+
#
|
51
|
+
# @return [Array<Hash>]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def attribute_definitions
|
55
|
+
@_attribute_definitions ||= []
|
56
|
+
end
|
57
|
+
|
58
|
+
# Define attributes on a class or instance
|
59
|
+
#
|
60
|
+
# @param [Object,Class] object
|
61
|
+
#
|
62
|
+
# @return [undefined]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def define_attributes(object)
|
66
|
+
attribute_definitions.each do |attribute_args|
|
67
|
+
object.attribute(*attribute_args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end # module ModuleExtensions
|
72
|
+
end # module Virtus
|
@@ -0,0 +1,149 @@
|
|
1
|
+
#
|
2
|
+
# = ostruct.rb: OpenStruct implementation
|
3
|
+
#
|
4
|
+
# Author:: Yukihiro Matsumoto
|
5
|
+
# Documentation:: Gavin Sinclair
|
6
|
+
#
|
7
|
+
# OpenStruct allows the creation of data objects with arbitrary attributes.
|
8
|
+
# See OpenStruct for an example.
|
9
|
+
#
|
10
|
+
|
11
|
+
#
|
12
|
+
# OpenStruct allows you to create data objects and set arbitrary attributes.
|
13
|
+
# For example:
|
14
|
+
#
|
15
|
+
# require 'ostruct'
|
16
|
+
#
|
17
|
+
# record = OpenStruct.new
|
18
|
+
# record.name = "John Smith"
|
19
|
+
# record.age = 70
|
20
|
+
# record.pension = 300
|
21
|
+
#
|
22
|
+
# puts record.name # -> "John Smith"
|
23
|
+
# puts record.address # -> nil
|
24
|
+
#
|
25
|
+
# It is like a hash with a different way to access the data. In fact, it is
|
26
|
+
# implemented with a hash, and you can initialize it with one.
|
27
|
+
#
|
28
|
+
# hash = { "country" => "Australia", :population => 20_000_000 }
|
29
|
+
# data = OpenStruct.new(hash)
|
30
|
+
#
|
31
|
+
# p data # -> <OpenStruct country="Australia" population=20000000>
|
32
|
+
#
|
33
|
+
class OpenStruct
|
34
|
+
#
|
35
|
+
# Create a new OpenStruct object. The optional +hash+, if given, will
|
36
|
+
# generate attributes and values. For example.
|
37
|
+
#
|
38
|
+
# require 'ostruct'
|
39
|
+
# hash = { "country" => "Australia", :population => 20_000_000 }
|
40
|
+
# data = OpenStruct.new(hash)
|
41
|
+
#
|
42
|
+
# p data # -> <OpenStruct country="Australia" population=20000000>
|
43
|
+
#
|
44
|
+
# By default, the resulting OpenStruct object will have no attributes.
|
45
|
+
#
|
46
|
+
def initialize(hash=nil)
|
47
|
+
@table = {}
|
48
|
+
if hash
|
49
|
+
for k,v in hash
|
50
|
+
@table[k.to_sym] = v
|
51
|
+
new_ostruct_member(k)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Duplicate an OpenStruct object members.
|
57
|
+
def initialize_copy(orig)
|
58
|
+
super
|
59
|
+
@table = @table.dup
|
60
|
+
end
|
61
|
+
|
62
|
+
def marshal_dump
|
63
|
+
@table
|
64
|
+
end
|
65
|
+
def marshal_load(x)
|
66
|
+
@table = x
|
67
|
+
@table.each_key{|key| new_ostruct_member(key)}
|
68
|
+
end
|
69
|
+
|
70
|
+
def modifiable
|
71
|
+
if self.frozen?
|
72
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(2)
|
73
|
+
end
|
74
|
+
@table
|
75
|
+
end
|
76
|
+
protected :modifiable
|
77
|
+
|
78
|
+
def new_ostruct_member(name)
|
79
|
+
name = name.to_sym
|
80
|
+
unless self.respond_to?(name)
|
81
|
+
self.__singleton_class.class_eval do
|
82
|
+
define_method(name) { @table[name] }
|
83
|
+
define_method("#{name}=") { |x| modifiable[name] = x }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
name
|
87
|
+
end
|
88
|
+
|
89
|
+
def method_missing(mid, *args) # :nodoc:
|
90
|
+
mname = mid.id2name
|
91
|
+
len = args.length
|
92
|
+
if mname.chomp!('=')
|
93
|
+
if len != 1
|
94
|
+
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
|
95
|
+
end
|
96
|
+
modifiable[new_ostruct_member(mname)] = args[0]
|
97
|
+
elsif len == 0
|
98
|
+
@table[mid]
|
99
|
+
else
|
100
|
+
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Remove the named field from the object.
|
106
|
+
#
|
107
|
+
def delete_field(name)
|
108
|
+
@table.delete name.to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
InspectKey = :__inspect_key__ # :nodoc:
|
112
|
+
|
113
|
+
#
|
114
|
+
# Returns a string containing a detailed summary of the keys and values.
|
115
|
+
#
|
116
|
+
def inspect
|
117
|
+
str = "#<#{self.class}"
|
118
|
+
|
119
|
+
ids = (Thread.current[InspectKey] ||= [])
|
120
|
+
if ids.include?(object_id)
|
121
|
+
return str << ' ...>'
|
122
|
+
end
|
123
|
+
|
124
|
+
ids << object_id
|
125
|
+
begin
|
126
|
+
first = true
|
127
|
+
for k,v in @table
|
128
|
+
str << "," unless first
|
129
|
+
first = false
|
130
|
+
str << " #{k}=#{v.inspect}"
|
131
|
+
end
|
132
|
+
return str << '>'
|
133
|
+
ensure
|
134
|
+
ids.pop
|
135
|
+
end
|
136
|
+
end
|
137
|
+
alias :to_s :inspect
|
138
|
+
|
139
|
+
attr_reader :table # :nodoc:
|
140
|
+
protected :table
|
141
|
+
|
142
|
+
#
|
143
|
+
# Compare this object and +other+ for equality.
|
144
|
+
#
|
145
|
+
def ==(other)
|
146
|
+
return false unless(other.kind_of?(OpenStruct))
|
147
|
+
return @table == other.table
|
148
|
+
end
|
149
|
+
end
|