Class: Aqua::Translator

Inherits:
Object
  • Object
show all
Defined in:
lib/aqua/object/translator.rb

Overview

Packing of objects needs to save an object as well as query it. Therefore this packer module gives a lot of class methods and mirrored instance methods that pack various types of objects. The instance methods aggregate all of the attachments and externals that need to be mapped back to the base object after they are saved. The class methods return an array with the packaging in the first element and the attachment and externals in subsequent elements

Defined Under Namespace

Classes: Opts, Rat

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_object, base_id = nil) ⇒ Translator

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Although most of what the translator does happens via class level methods, instantiation happens to maintain a reference to the base object when packing/saving externals and attachments. It is also needed in the unpack process to locate attachments.

Parameters:

  • (Aquatic Object)


36
37
38
# File 'lib/aqua/object/translator.rb', line 36

def initialize( base_object, base_id=nil )
  load_base( base_object, base_id )
end

Instance Attribute Details

#attachmentsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

An array of attachments that have to be stored by the base object



25
26
27
# File 'lib/aqua/object/translator.rb', line 25

def attachments
  @attachments ||= []
end

#base_idObject

Returns the value of attribute base_id.



8
9
10
# File 'lib/aqua/object/translator.rb', line 8

def base_id
  @base_id
end

#base_objectObject

Returns the value of attribute base_object.



8
9
10
# File 'lib/aqua/object/translator.rb', line 8

def base_object
  @base_object
end

#externalsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Hash-like object that gathers all the externally saved aquatic objects. Pattern for storage is

external_object => 'pack_path_to_object'

where the string ‘pack_path_to_object’ gets instance evaled in order to update the id in the pack after a new external is saved



19
20
21
# File 'lib/aqua/object/translator.rb', line 19

def externals
  @externals ||= {}
end

Class Method Details

.classesObject

PACKING —————————————————————————————



226
227
228
# File 'lib/aqua/object/translator.rb', line 226

def self.classes
  @classes ||= {}
end

.get_class(class_str) ⇒ Object



230
231
232
# File 'lib/aqua/object/translator.rb', line 230

def self.get_class( class_str )
  classes[class_str] ||= class_str.constantize  if class_str
end

.new_from_doc(doc, opts) ⇒ Object



257
258
259
260
261
262
263
264
# File 'lib/aqua/object/translator.rb', line 257

def self.new_from_doc( doc, opts  )
  klass = get_class( doc['class'] )
  obj = if (init = doc['init']) &&  klass.respond_to?( :aqua_init )
    klass.aqua_init( init, opts )
  else
    klass.new   
  end 
end

.pack_ivars(obj, path = '') ⇒ Mash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Packs the ivars for a given object.

Parameters:

  • Object

    to pack

  • path (String) (defaults to: '')

    to this particular object within the parent object

Returns:

  • (Mash)

    Indifferent hash that is the data/metadata deconstruction of an object.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/aqua/object/translator.rb', line 62

def self.pack_ivars( obj, path='' )
  path = "#{path}['ivars']"
  rat = Rat.new
  vars = obj.respond_to?(:_storable_attributes) ? obj._storable_attributes : obj.instance_variables
  vars.each do |ivar_name| 
    ivar = obj.instance_variable_get( ivar_name ) 
    ivar_path = path + "['#{ivar_name}']" 
    if ivar
      if ivar == obj # self referential TODO: this will only work direct descendants :(
        ivar_rat = pack_to_stub( ivar, ivar_path )
        rat.hord( ivar_rat, ivar_name ) 
      else   
        ivar_rat = pack_object( ivar, ivar_path )
        rat.hord( ivar_rat, ivar_name )
      end
    end         
  end
  rat
end

.pack_object(obj, path = '') ⇒ Mash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Packs an object into data and meta data. Works recursively sending out to array, hash, etc.

object packers, which send their values back to _pack_object

Parameters:

  • Object

    to pack

  • path, (String)

    so that unsaved externals can find and set their id after creation

Returns:

  • (Mash)

    Indifferent hash that is the data/metadata deconstruction of an object.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/aqua/object/translator.rb', line 95

def self.pack_object( obj, path='' )
  klass = obj.class
  if obj.respond_to?(:to_aqua) # probably requires special initialization not just ivar assignment
    obj.to_aqua( path )
  elsif obj.aquatic? 
    if obj._embedded? || path == ''
      pack_vanilla( obj, path )
    else
      pack_to_stub( obj, path)
    end
  else
    pack_vanilla( obj, path )
  end    
end

.pack_to_stub(obj, path = '') ⇒ Mash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Packs the stub for an externally saved object.

Parameters:

  • Object

    to pack

  • path (String) (defaults to: '')

    to this part of the object

Returns:

  • (Mash)

    Indifferent hash that is the data/metadata deconstruction of an object.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/aqua/object/translator.rb', line 141

def self.pack_to_stub( obj, path='' )
  rat = Rat.new( {'class' => 'Aqua::Stub'} ) 
  stub_rat = Rat.new({'class' => obj.class.to_s, 'id' => obj.id || '' }, {obj => path} )
  # deal with cached methods
  unless (stub_methods = obj._stubbed_methods).empty?
    stub_rat.pack['methods'] = {}
    stub_methods.each do |meth|
      meth = meth.to_s
      method_rat = pack_object( obj.send( meth ) )
      stub_rat.hord( method_rat, ['methods', "#{meth}"])
    end    
  end
  rat.hord( stub_rat, 'init' )
end

.pack_vanilla(obj, path = '') ⇒ Mash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Packs the an object requiring no initialization.

Parameters:

  • Object

    to pack

Returns:

  • (Mash)

    Indifferent hash that is the data/metadata deconstruction of an object.



121
122
123
124
125
126
# File 'lib/aqua/object/translator.rb', line 121

def self.pack_vanilla( obj, path='' )
  rat = Rat.new( { 'class' => obj.class.to_s } ) 
  ivar_rat = pack_ivars( obj, path )
  rat.hord( ivar_rat, 'ivars' ) unless ivar_rat.pack.empty?
  rat
end

.unpack_ivars(obj, doc, opts) ⇒ Object



283
284
285
286
287
288
289
290
291
292
# File 'lib/aqua/object/translator.rb', line 283

def self.unpack_ivars( obj, doc, opts ) 
  # add the ivars  
  if ivars = doc['ivars']
    ivars.each do |ivar_name, ivar_hash|
      opts.path += "#{ivar_name}"
      unpacked_ivar = unpack_object( ivar_hash, opts ) 
      obj.instance_variable_set( ivar_name, unpacked_ivar )
    end  
  end   
end

.unpack_object(doc, opts = Opts.new) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/aqua/object/translator.rb', line 240

def self.unpack_object( doc, opts=Opts.new )
  if doc.respond_to?(:from_aqua)
    doc.from_aqua # these are basic types, like nil, strings, true/false, or symbols
  else 
    obj = new_from_doc( doc, opts )
    
    # mixin the id and rev
    if obj.aquatic? && !obj._embedded?
      obj.id = doc.id if doc.id
      obj._rev = doc.rev if doc.rev
    end   
    
    unpack_ivars( obj, doc, opts )
    obj  
  end    
end

Instance Method Details

#load_base(base_object, base_id = nil) ⇒ Object

Method used by initialized and reload_object to set necessary base object data



41
42
43
44
# File 'lib/aqua/object/translator.rb', line 41

def load_base( base_object, base_id=nil)
  self.base_object = base_object
  self.base_id = base_object.id || base_id
end

#packObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This is a wrapper method that takes the a class method and aggregates externals and attachments



48
49
50
51
52
53
# File 'lib/aqua/object/translator.rb', line 48

def pack
  rat = yield 
  self.externals.merge!( rat.externals )
  self.attachments += rat.attachments
  rat.pack
end

#pack_ivars(obj, path = '') ⇒ Object

The instance version is wrapped by the #pack method. It otherwise performs the class method



83
84
85
# File 'lib/aqua/object/translator.rb', line 83

def pack_ivars( obj, path='' )
  pack { self.class.pack_ivars( obj ) }
end

#pack_object(obj, path = '') ⇒ Object

The instance version is wrapped by the #pack method. It otherwise performs the class method



111
112
113
# File 'lib/aqua/object/translator.rb', line 111

def pack_object( obj, path='' )
  pack { self.class.pack_object( obj ) }
end

#pack_to_stub(obj, path = '') ⇒ Object

The instance version is wrapped by the #pack method. It otherwise performs the class method



157
158
159
# File 'lib/aqua/object/translator.rb', line 157

def pack_to_stub( obj, path='' )
  pack { self.class.pack_to_stub( obj ) }
end

#pack_vanilla(obj, path = '') ⇒ Object

The instance version is wrapped by the #pack method. It otherwise performs the class method



129
130
131
# File 'lib/aqua/object/translator.rb', line 129

def pack_vanilla( obj, path='' )
  pack { self.class.pack_vanilla( obj ) }
end

#reload_from_init(doc) ⇒ Object



266
267
268
269
270
271
272
273
# File 'lib/aqua/object/translator.rb', line 266

def reload_from_init( doc )
  if init = doc['init']
    raise NotImplementedError, "Class #{base_object.class} does not implement a #replace method and can't be reloaded" unless base_object.respond_to?( :replace )
    init_unpacked = unpack_object( {'class' => init.class.to_s, 'init' => init } ) 
    base_object.replace( init_unpacked )
  end
  base_object 
end

#reload_object(obj, doc) ⇒ Object



294
295
296
297
298
299
300
# File 'lib/aqua/object/translator.rb', line 294

def reload_object( obj, doc )
  load_base( obj ) # loads this object as the base for the translator
  reload_from_init( doc ) 
  # todo: clear all non-hidden ivars too 
  unpack_ivars( doc )
  base_object._rev = doc.rev if doc.rev
end

#unpack_ivars(doc) ⇒ Object



275
276
277
278
279
280
# File 'lib/aqua/object/translator.rb', line 275

def unpack_ivars( doc )
  opts = Opts.new
  opts.base_object  = self.base_object
  opts.base_id      = self.base_object.id || self.base_id
  self.class.unpack_ivars( base_object, doc, opts )
end

#unpack_object(doc, opts = Opts.new) ⇒ Object



234
235
236
237
238
# File 'lib/aqua/object/translator.rb', line 234

def unpack_object( doc, opts=Opts.new ) 
  opts.base_object  = self.base_object
  opts.base_id      = self.base_object.id || self.base_id
  self.class.unpack_object( doc, opts )
end