aqua 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|