aqua 0.1.6 → 0.2.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 +2 -1
- data/Aqua.gemspec +14 -11
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/aqua.rb +5 -7
- data/lib/aqua/object/config.rb +2 -3
- data/lib/aqua/object/initializers.rb +309 -0
- data/lib/aqua/object/pack.rb +56 -132
- data/lib/aqua/object/query.rb +30 -2
- data/lib/aqua/object/stub.rb +60 -95
- data/lib/aqua/object/tank.rb +1 -0
- data/lib/aqua/object/translator.rb +313 -0
- data/lib/aqua/object/unpack.rb +26 -227
- data/lib/aqua/store/couch_db/couch_db.rb +1 -0
- data/lib/aqua/store/couch_db/database.rb +1 -1
- data/lib/aqua/store/couch_db/design_document.rb +126 -2
- data/lib/aqua/store/couch_db/result_set.rb +36 -0
- data/lib/aqua/store/couch_db/storage_methods.rb +182 -17
- data/lib/aqua/store/storage.rb +4 -48
- data/lib/aqua/support/mash.rb +2 -3
- data/lib/aqua/support/set.rb +4 -16
- data/spec/object/object_fixtures/array_udder.rb +1 -1
- data/spec/object/object_fixtures/persistent.rb +0 -2
- data/spec/object/pack_spec.rb +137 -517
- data/spec/object/query_spec.rb +36 -6
- data/spec/object/stub_spec.rb +10 -9
- data/spec/object/translator_packing_spec.rb +402 -0
- data/spec/object/translator_unpacking_spec.rb +262 -0
- data/spec/object/unpack_spec.rb +162 -320
- data/spec/spec_helper.rb +18 -0
- data/spec/store/couchdb/design_document_spec.rb +148 -7
- data/spec/store/couchdb/result_set_spec.rb +95 -0
- data/spec/store/couchdb/storage_methods_spec.rb +150 -10
- metadata +13 -9
- data/lib/aqua/support/initializers.rb +0 -216
- data/spec/object/object_fixtures/grounded.rb +0 -13
- data/spec/object/object_fixtures/sugar.rb +0 -4
data/lib/aqua/object/tank.rb
CHANGED
@@ -0,0 +1,313 @@
|
|
1
|
+
module Aqua
|
2
|
+
# Packing of objects needs to save an object as well as query it. Therefore this packer module gives a lot
|
3
|
+
# of class methods and mirrored instance methods that pack various types of objects. The instance methods
|
4
|
+
# aggregate all of the attachments and externals that need to be mapped back to the base object after they
|
5
|
+
# are saved. The class methods return an array with the packaging in the first element and the attachment
|
6
|
+
# and externals in subsequent elements
|
7
|
+
class Translator
|
8
|
+
attr_accessor :base_object, :base_id
|
9
|
+
|
10
|
+
# PACKING ---------------------------------------------------------------------------------------
|
11
|
+
# ===============================================================================================
|
12
|
+
attr_writer :externals, :attachments
|
13
|
+
|
14
|
+
# Hash-like object that gathers all the externally saved aquatic objects. Pattern for storage is
|
15
|
+
# external_object => 'pack_path_to_object'
|
16
|
+
# where the string 'pack_path_to_object' gets instance evaled in order to update the id in the pack
|
17
|
+
# after a new external is saved
|
18
|
+
# @api private
|
19
|
+
def externals
|
20
|
+
@externals ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# An array of attachments that have to be stored by the base object
|
24
|
+
# @api private
|
25
|
+
def attachments
|
26
|
+
@attachments ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
# Although most of what the translator does happens via class level methods, instantiation happens
|
30
|
+
# to maintain a reference to the base object when packing/saving externals and attachments. It is
|
31
|
+
# also needed in the unpack process to locate attachments.
|
32
|
+
#
|
33
|
+
# @param [Aquatic Object]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def initialize( base_object, base_id=nil )
|
37
|
+
load_base( base_object, base_id )
|
38
|
+
end
|
39
|
+
|
40
|
+
# Method used by initialized and reload_object to set necessary base object data
|
41
|
+
def load_base( base_object, base_id=nil)
|
42
|
+
self.base_object = base_object
|
43
|
+
self.base_id = base_object.id || base_id
|
44
|
+
end
|
45
|
+
|
46
|
+
# This is a wrapper method that takes the a class method and aggregates externals and attachments
|
47
|
+
# @api private
|
48
|
+
def pack
|
49
|
+
rat = yield
|
50
|
+
self.externals.merge!( rat.externals )
|
51
|
+
self.attachments += rat.attachments
|
52
|
+
rat.pack
|
53
|
+
end
|
54
|
+
|
55
|
+
# Packs the ivars for a given object.
|
56
|
+
#
|
57
|
+
# @param Object to pack
|
58
|
+
# @param [String] path to this particular object within the parent object
|
59
|
+
# @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def self.pack_ivars( obj, path='' )
|
63
|
+
path = "#{path}['ivars']"
|
64
|
+
rat = Rat.new
|
65
|
+
vars = obj.respond_to?(:_storable_attributes) ? obj._storable_attributes : obj.instance_variables
|
66
|
+
vars.each do |ivar_name|
|
67
|
+
ivar = obj.instance_variable_get( ivar_name )
|
68
|
+
ivar_path = path + "['#{ivar_name}']"
|
69
|
+
if ivar
|
70
|
+
if ivar == obj # self referential TODO: this will only work direct descendants :(
|
71
|
+
ivar_rat = pack_to_stub( ivar, ivar_path )
|
72
|
+
rat.hord( ivar_rat, ivar_name )
|
73
|
+
else
|
74
|
+
ivar_rat = pack_object( ivar, ivar_path )
|
75
|
+
rat.hord( ivar_rat, ivar_name )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rat
|
80
|
+
end
|
81
|
+
|
82
|
+
# The instance version is wrapped by the #pack method. It otherwise performs the class method
|
83
|
+
def pack_ivars( obj, path='' )
|
84
|
+
pack { self.class.pack_ivars( obj ) }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Packs an object into data and meta data. Works recursively sending out to array, hash, etc.
|
88
|
+
# object packers, which send their values back to _pack_object
|
89
|
+
#
|
90
|
+
# @param Object to pack
|
91
|
+
# @param [String] path, so that unsaved externals can find and set their id after creation
|
92
|
+
# @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def self.pack_object( obj, path='' )
|
96
|
+
klass = obj.class
|
97
|
+
if obj.respond_to?(:to_aqua) # probably requires special initialization not just ivar assignment
|
98
|
+
obj.to_aqua( path )
|
99
|
+
elsif obj.aquatic?
|
100
|
+
if obj._embedded? || path == ''
|
101
|
+
pack_vanilla( obj, path )
|
102
|
+
else
|
103
|
+
pack_to_stub( obj, path)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
pack_vanilla( obj, path )
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# The instance version is wrapped by the #pack method. It otherwise performs the class method
|
111
|
+
def pack_object( obj, path='' )
|
112
|
+
pack { self.class.pack_object( obj ) }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Packs the an object requiring no initialization.
|
116
|
+
#
|
117
|
+
# @param Object to pack
|
118
|
+
# @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
def self.pack_vanilla( obj, path='' )
|
122
|
+
rat = Rat.new( { 'class' => obj.class.to_s } )
|
123
|
+
ivar_rat = pack_ivars( obj, path )
|
124
|
+
rat.hord( ivar_rat, 'ivars' ) unless ivar_rat.pack.empty?
|
125
|
+
rat
|
126
|
+
end
|
127
|
+
|
128
|
+
# The instance version is wrapped by the #pack method. It otherwise performs the class method
|
129
|
+
def pack_vanilla( obj, path='' )
|
130
|
+
pack { self.class.pack_vanilla( obj ) }
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Packs the stub for an externally saved object.
|
135
|
+
#
|
136
|
+
# @param Object to pack
|
137
|
+
# @param [String] path to this part of the object
|
138
|
+
# @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
|
139
|
+
#
|
140
|
+
# @api private
|
141
|
+
def self.pack_to_stub( obj, path='' )
|
142
|
+
rat = Rat.new( {'class' => 'Aqua::Stub'} )
|
143
|
+
stub_rat = Rat.new({'class' => obj.class.to_s, 'id' => obj.id || '' }, {obj => path} )
|
144
|
+
# deal with cached methods
|
145
|
+
unless (stub_methods = obj._stubbed_methods).empty?
|
146
|
+
stub_rat.pack['methods'] = {}
|
147
|
+
stub_methods.each do |meth|
|
148
|
+
meth = meth.to_s
|
149
|
+
method_rat = pack_object( obj.send( meth ) )
|
150
|
+
stub_rat.hord( method_rat, ['methods', "#{meth}"])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
rat.hord( stub_rat, 'init' )
|
154
|
+
end
|
155
|
+
|
156
|
+
# The instance version is wrapped by the #pack method. It otherwise performs the class method
|
157
|
+
def pack_to_stub( obj, path='' )
|
158
|
+
pack { self.class.pack_to_stub( obj ) }
|
159
|
+
end
|
160
|
+
|
161
|
+
# def pack_singletons
|
162
|
+
# # TODO: figure out 1.8 and 1.9 compatibility issues.
|
163
|
+
# # Also learn the library usage, without any docs :(
|
164
|
+
# end
|
165
|
+
|
166
|
+
# Rat class is used by the translators packing side and aggregates methods for merging
|
167
|
+
# packed representation, externals and attachments
|
168
|
+
class Rat
|
169
|
+
attr_accessor :pack, :externals, :attachments
|
170
|
+
def initialize( pack=Mash.new, externals=Mash.new, attachments=[] )
|
171
|
+
self.pack = pack
|
172
|
+
self.externals = externals
|
173
|
+
self.attachments = attachments
|
174
|
+
end
|
175
|
+
|
176
|
+
# merges the two rats
|
177
|
+
def eat( other_rat )
|
178
|
+
if self.pack.respond_to?(:keys)
|
179
|
+
self.pack.merge!( other_rat.pack )
|
180
|
+
else
|
181
|
+
self.pack << other_rat.pack # this is a special case for array init rats
|
182
|
+
end
|
183
|
+
self.externals.merge!( other_rat.externals )
|
184
|
+
self.attachments += other_rat.attachments
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# outputs and resets the accessor
|
189
|
+
def barf( accessor )
|
190
|
+
case accessor
|
191
|
+
when :pack, 'pack'
|
192
|
+
meal = self.pack
|
193
|
+
self.pack = {}
|
194
|
+
when :externals, 'externals'
|
195
|
+
meal = self.externals
|
196
|
+
self.externals = {}
|
197
|
+
else
|
198
|
+
meal = self.attachments
|
199
|
+
self.attachments = []
|
200
|
+
end
|
201
|
+
meal
|
202
|
+
end
|
203
|
+
|
204
|
+
def hord( other_rat, index)
|
205
|
+
if [String, Symbol].include?( index.class )
|
206
|
+
self.pack[index] = other_rat.barf(:pack)
|
207
|
+
else # for nested hording
|
208
|
+
eval_string = index.inject("self.pack") do |result, element|
|
209
|
+
element = "'#{element}'" if element.class == String
|
210
|
+
result += "[#{element}]"
|
211
|
+
end
|
212
|
+
value = other_rat.barf(:pack)
|
213
|
+
instance_eval "#{eval_string} = #{value.inspect}"
|
214
|
+
end
|
215
|
+
self.eat( other_rat )
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
219
|
+
def ==( other_rat )
|
220
|
+
self.pack == other_rat.pack && self.externals == other_rat.externals && self.attachments == other_rat.attachments
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# PACKING ---------------------------------------------------------------------------------------
|
225
|
+
# ===============================================================================================
|
226
|
+
def self.classes
|
227
|
+
@classes ||= {}
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.get_class( class_str )
|
231
|
+
classes[class_str] ||= class_str.constantize if class_str
|
232
|
+
end
|
233
|
+
|
234
|
+
def unpack_object( doc, opts=Opts.new )
|
235
|
+
opts.base_object = self.base_object
|
236
|
+
opts.base_id = self.base_object.id || self.base_id
|
237
|
+
self.class.unpack_object( doc, opts )
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.unpack_object( doc, opts=Opts.new )
|
241
|
+
if doc.respond_to?(:from_aqua)
|
242
|
+
doc.from_aqua # these are basic types, like nil, strings, true/false, or symbols
|
243
|
+
else
|
244
|
+
obj = new_from_doc( doc, opts )
|
245
|
+
|
246
|
+
# mixin the id and rev
|
247
|
+
if obj.aquatic? && !obj._embedded?
|
248
|
+
obj.id = doc.id if doc.id
|
249
|
+
obj._rev = doc.rev if doc.rev
|
250
|
+
end
|
251
|
+
|
252
|
+
unpack_ivars( obj, doc, opts )
|
253
|
+
obj
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.new_from_doc( doc, opts )
|
258
|
+
klass = get_class( doc['class'] )
|
259
|
+
obj = if (init = doc['init']) && klass.respond_to?( :aqua_init )
|
260
|
+
klass.aqua_init( init, opts )
|
261
|
+
else
|
262
|
+
klass.new
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def reload_from_init( doc )
|
267
|
+
if init = doc['init']
|
268
|
+
raise NotImplementedError, "Class #{base_object.class} does not implement a #replace method and can't be reloaded" unless base_object.respond_to?( :replace )
|
269
|
+
init_unpacked = unpack_object( {'class' => init.class.to_s, 'init' => init } )
|
270
|
+
base_object.replace( init_unpacked )
|
271
|
+
end
|
272
|
+
base_object
|
273
|
+
end
|
274
|
+
|
275
|
+
def unpack_ivars( doc )
|
276
|
+
opts = Opts.new
|
277
|
+
opts.base_object = self.base_object
|
278
|
+
opts.base_id = self.base_object.id || self.base_id
|
279
|
+
self.class.unpack_ivars( base_object, doc, opts )
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
def self.unpack_ivars( obj, doc, opts )
|
284
|
+
# add the ivars
|
285
|
+
if ivars = doc['ivars']
|
286
|
+
ivars.each do |ivar_name, ivar_hash|
|
287
|
+
opts.path += "#{ivar_name}"
|
288
|
+
unpacked_ivar = unpack_object( ivar_hash, opts )
|
289
|
+
obj.instance_variable_set( ivar_name, unpacked_ivar )
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def reload_object( obj, doc )
|
295
|
+
load_base( obj ) # loads this object as the base for the translator
|
296
|
+
reload_from_init( doc )
|
297
|
+
# todo: clear all non-hidden ivars too
|
298
|
+
unpack_ivars( doc )
|
299
|
+
base_object._rev = doc.rev if doc.rev
|
300
|
+
end
|
301
|
+
|
302
|
+
class Opts
|
303
|
+
attr_accessor :base_object
|
304
|
+
attr_accessor :base_id
|
305
|
+
attr_writer :path
|
306
|
+
|
307
|
+
def path
|
308
|
+
@path ||= ''
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
end # Translator
|
313
|
+
end # Aqua
|
data/lib/aqua/object/unpack.rb
CHANGED
@@ -4,7 +4,7 @@ module Aqua::Unpack
|
|
4
4
|
klass.class_eval do
|
5
5
|
extend ClassMethods
|
6
6
|
include InstanceMethods
|
7
|
-
end
|
7
|
+
end
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
@@ -14,240 +14,39 @@ module Aqua::Unpack
|
|
14
14
|
#
|
15
15
|
# @api public
|
16
16
|
def load( id )
|
17
|
-
|
18
|
-
|
19
|
-
instance.reload
|
20
|
-
instance
|
17
|
+
doc = _get_store( id )
|
18
|
+
build( doc, id )
|
21
19
|
end
|
20
|
+
|
21
|
+
# Retrieves objects storage from its engine.
|
22
|
+
# @return [Storage]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
def _get_store( id )
|
26
|
+
doc = self::Storage.get( id )
|
27
|
+
raise ArgumentError, "#{self} with id of #{doc_id} was not found" unless doc
|
28
|
+
doc
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a new object from the doc; It is used by queries which return a set of docs.
|
32
|
+
# Also used by load to do the same thing ...
|
33
|
+
# @param [Document, Hash, Mash] converted object
|
34
|
+
def build( doc, id=nil )
|
35
|
+
translator = Aqua::Translator.new( new, id )
|
36
|
+
translator.unpack_object( doc )
|
37
|
+
end
|
22
38
|
end
|
23
39
|
|
24
40
|
module InstanceMethods
|
25
|
-
# Reloads database information into the object.
|
26
|
-
# @param [optional true, false] Default is true. If true the exceptions will be swallowed and
|
27
|
-
# false will be returned. If false, then any exceptions raised will stop the show.
|
28
|
-
# @return [Object, false] Will return false or raise error on failure and self on success.
|
29
|
-
#
|
30
|
-
# @api public
|
31
|
-
def reload( mask_exceptions = true )
|
32
|
-
if id.nil?
|
33
|
-
if mask_exceptions
|
34
|
-
false
|
35
|
-
else
|
36
|
-
raise ObjectNotFound, "#{self.class} instance must have an id to be reloaded"
|
37
|
-
end
|
38
|
-
else
|
39
|
-
begin
|
40
|
-
_reload
|
41
|
-
rescue Exception => e
|
42
|
-
if mask_exceptions
|
43
|
-
false
|
44
|
-
else
|
45
|
-
raise e
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
41
|
# Reloads database information into the object, and raises an error on failure.
|
52
42
|
# @return [Object] Will return raise error on failure and return self on success.
|
53
43
|
#
|
54
44
|
# @api public
|
55
|
-
def reload
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# Actual mechanism for reloading an object from stored data.
|
61
|
-
# @return [Object] Will return raise error on failure and return self on success.
|
62
|
-
#
|
63
|
-
# @api private
|
64
|
-
def _reload
|
65
|
-
_get_store
|
66
|
-
_unpack
|
67
|
-
_clear_store
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
# Retrieves objects storage from its engine.
|
72
|
-
# @return [Storage]
|
73
|
-
#
|
74
|
-
# @api private
|
75
|
-
def _get_store
|
76
|
-
# this is kind of klunky, should refactor
|
77
|
-
self._store = self.class::Storage.new(:id => self.id).retrieve
|
78
|
-
end
|
79
|
-
|
80
|
-
# Unpacks an object from hash representation of data and metadata
|
81
|
-
# @return [Storage]
|
82
|
-
# @todo Refactor to move more of this into individual classes
|
83
|
-
#
|
84
|
-
# @api private
|
85
|
-
def _unpack
|
86
|
-
if init = _unpack_initialization( _store )
|
87
|
-
replace( init )
|
88
|
-
end
|
89
|
-
if ivars = _store[:ivars]
|
90
|
-
_unpack_ivars( self, ivars )
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Makes @_store nil to converve on memory
|
95
|
-
#
|
96
|
-
# @api private
|
97
|
-
def _clear_store
|
98
|
-
@_store = nil
|
99
|
-
end
|
100
|
-
|
101
|
-
# Unpacks an object's instance variables
|
102
|
-
# @todo Refactor to move more of this into individual classes
|
103
|
-
#
|
104
|
-
# @api private
|
105
|
-
def _unpack_ivars( obj, data )
|
106
|
-
data.each do |ivar_name, data_package|
|
107
|
-
unpacked = if data_package.class == String
|
108
|
-
data_package
|
109
|
-
else
|
110
|
-
_unpack_object( data_package )
|
111
|
-
end
|
112
|
-
obj.instance_variable_set( ivar_name, unpacked )
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Unpacks an the initialization object from a hash into a real object.
|
117
|
-
# @return [Object] Generally a hash, array or string
|
118
|
-
# @todo Refactor to move more of this into individual classes
|
119
|
-
#
|
120
|
-
# @api private
|
121
|
-
def _unpack_initialization( obj )
|
122
|
-
if init = obj[:init]
|
123
|
-
init_class = init.class
|
124
|
-
if init_class == String
|
125
|
-
if init.match(/\A\/STUB_(\d*)\z/)
|
126
|
-
_unpack_stub( $1.to_i )
|
127
|
-
elsif init.match(/\A\/FILE_(.*)\z/)
|
128
|
-
_unpack_file( $1, obj )
|
129
|
-
else
|
130
|
-
init
|
131
|
-
end
|
132
|
-
elsif init.class == Array
|
133
|
-
_unpack_array( init )
|
134
|
-
else
|
135
|
-
_unpack_hash( init )
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Retrieves and unpacks a stubbed object from its separate storage area
|
141
|
-
# @return [Aqua::Stub] Delegate object for externally saved class
|
142
|
-
# @param [Fixnum] Array index for the stub details, garnered from the key name
|
143
|
-
#
|
144
|
-
# @api private
|
145
|
-
def _unpack_stub( index )
|
146
|
-
hash = _store[:stubs][index]
|
147
|
-
Aqua::Stub.new( hash )
|
148
|
-
end
|
149
|
-
|
150
|
-
# Retrieves and unpacks a stubbed object from its separate storage area
|
151
|
-
# @param [String] File name, and attachment id
|
152
|
-
# @return [Aqua::FileStub] Delegate object for file attachments
|
153
|
-
#
|
154
|
-
# @api private
|
155
|
-
def _unpack_file( name, obj )
|
156
|
-
hash = {
|
157
|
-
:parent => self,
|
158
|
-
:id => name,
|
159
|
-
:methods => obj[:methods]
|
160
|
-
}
|
161
|
-
Aqua::FileStub.new( hash )
|
162
|
-
end
|
163
|
-
|
164
|
-
# Unpacks an Array.
|
165
|
-
# @return [Object] Generally a hash, array or string
|
166
|
-
# @todo Refactor ?? move more of this into support/initializers Array
|
167
|
-
#
|
168
|
-
# @api private
|
169
|
-
def _unpack_array( obj )
|
170
|
-
arr = []
|
171
|
-
obj.each do |value|
|
172
|
-
value = _unpack_object( value ) unless value.class == String
|
173
|
-
arr << value
|
174
|
-
end
|
175
|
-
arr
|
176
|
-
end
|
177
|
-
|
178
|
-
# Unpacks a Hash.
|
179
|
-
# @return [Object] Generally a hash, array or string
|
180
|
-
# @todo Refactor ?? move more of this into support/initializers Hash
|
181
|
-
#
|
182
|
-
# @api private
|
183
|
-
def _unpack_hash( obj )
|
184
|
-
hash = {}
|
185
|
-
obj.each do |raw_key, value|
|
186
|
-
value = _unpack_object( value ) unless value.class == String
|
187
|
-
if raw_key.match(/\A(:)/)
|
188
|
-
key = raw_key.gsub($1, '').to_sym
|
189
|
-
elsif raw_key.match(/\A\/OBJECT_(\d*)\z/)
|
190
|
-
key = _unpack_object( self._store[:keys][$1.to_i] )
|
191
|
-
else
|
192
|
-
key = raw_key
|
193
|
-
end
|
194
|
-
hash[key] = value
|
195
|
-
end
|
196
|
-
hash
|
197
|
-
end
|
198
|
-
|
199
|
-
# The real workhorse behind object construction: it recursively rebuilds objects based on
|
200
|
-
# whether the passed in object is an Array, String or a Hash (true/false too now).
|
201
|
-
# A hash that has the class key
|
202
|
-
# is an object representation. If it does not have a hash key then it is an ordinary hash.
|
203
|
-
# An array will either have strings or object representations values.
|
204
|
-
#
|
205
|
-
# @param [Hash, Mash] Representation of the data in the aqua meta format
|
206
|
-
# @return [Object] The object represented by the data
|
207
|
-
#
|
208
|
-
# @api private
|
209
|
-
def _unpack_object( store_pack )
|
210
|
-
package_class = store_pack.class
|
211
|
-
if package_class == String || store_pack == true || store_pack == false
|
212
|
-
store_pack
|
213
|
-
elsif package_class == Array
|
214
|
-
_unpack_array( store_pack )
|
215
|
-
else # package_class == Hash -or- Mash
|
216
|
-
if store_pack['class']
|
217
|
-
# Constantize the objects class
|
218
|
-
obj_class = store_pack['class'].constantize rescue nil
|
219
|
-
|
220
|
-
# build from initialization
|
221
|
-
init = _unpack_initialization( store_pack )
|
222
|
-
return_object = if init
|
223
|
-
[Aqua::Stub, Aqua::FileStub].include?( obj_class ) ? init : obj_class.aqua_init( init )
|
224
|
-
end
|
225
|
-
|
226
|
-
# Build uninitialized object
|
227
|
-
if return_object.nil?
|
228
|
-
if obj_class
|
229
|
-
return_object = obj_class.new
|
230
|
-
else
|
231
|
-
# should log an error internally
|
232
|
-
return_object = OpenStruct.new
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# add the ivars
|
237
|
-
if ivars = store_pack['ivars']
|
238
|
-
ivars.delete('@table') if obj_class.ancestors.include?( OpenStruct )
|
239
|
-
_unpack_ivars( return_object, ivars )
|
240
|
-
end
|
241
|
-
|
242
|
-
return_object
|
243
|
-
else # not a packaged object, just a hash, so unpack
|
244
|
-
_unpack_hash( hash )
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
end
|
249
|
-
|
250
|
-
public
|
45
|
+
def reload
|
46
|
+
doc = self.class._get_store( id )
|
47
|
+
_translator.reload_object( self, doc )
|
48
|
+
self
|
49
|
+
end
|
251
50
|
end
|
252
51
|
|
253
52
|
end
|