lib/aqua/object/pack.rb in aqua-0.1.6 vs lib/aqua/object/pack.rb in aqua-0.2.0
- old
+ new
@@ -9,15 +9,15 @@
extend HiddenAttributes::ClassMethods
include HiddenAttributes::InstanceMethods
extend ClassMethods
include InstanceMethods
- unless instance_methods.include?( 'id=' ) || new.instance_variables.include?( '@id' )
+ unless instance_methods.include?( 'id=' ) # || new.instance_variables.include?( '@id' )
attr_accessor :id
end
- hide_attributes :_store, :__pack, :id, :_rev
+ hide_attributes :_store, :__pack, :id, :_rev, :_translator
end
end
module HiddenAttributes
def self.included( klass )
@@ -63,12 +63,12 @@
def _storable_attributes
(instance_variables||[]) - self.class._hidden_attributes
end
end # InstanceMethods
end
-
- module ClassMethods
+
+ module ClassMethods
end # ClassMethods
module InstanceMethods
# TODO: option for transaction on children documents, all or nothing
@@ -89,92 +89,52 @@
def _pack
class_name = self.class.to_s
self.__pack = Storage.new
self.__pack.id = @id if @id
self.__pack[:_rev] = _rev if _rev
- self.__pack[:keys] = []
- self.__pack[:stubs] = []
- self.__pack.merge!( _pack_object( self ) )
- _pack_singletons
+ self.__pack.merge!( _translator.pack_object( self ) )
+ _pack_attachments
__pack
end
+
+ def _pack_attachments
+ _translator.attachments.each do |file|
+ self.__pack.attachments.add( file.filename, file )
+ end
+ end
+
+ # Translator object responsible for packing the object and keeping track of externally
+ # stored records and also attachments
+ # @return [Translator]
+ #
+ # @api private
+ def _translator
+ @_translator ||= Translator.new( self )
+ end
# Details from configuration options for the objects class about embedability.
# @return [true, false, Hash] If true then it should be embedded in the object at hand.
# If false, then it should be saved externally. If a hash, with the key :stub and a related
# value that is an array of methods, then the object should be saved externally,
# with a few cached methods as defined in the array.
#
# @api private
- def _embed_me
- self.class._aqua_opts[:embed]
+ def _stubbed_methods
+ meths = !_embedded? && self.class._aqua_opts[:embed] && self.class._aqua_opts[:embed][:stub]
+ meths ? [meths].flatten : []
end
-
- # 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
- #
- # @param Object to pack
- # @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
- #
+
+ # Details from configuration options for the objects class about embedability.
+ # @return [true, false] If true then it should be embedded in the base object.
+ # If false, then it should be saved externally.
+ #
# @api private
- def _pack_object( obj )
- klass = obj.class
- if klass == String
- obj
- elsif obj.respond_to?(:to_aqua) # Types requiring initialization
- obj.to_aqua( self )
- elsif obj.aquatic? && obj != self
- if obj._embed_me == true
- obj._pack
- else
- _build_stub( obj )
- end
- else # other object without initializations
- _pack_vanilla( obj )
- end
- end
-
- # Packs the ivars for a given object.
- #
- # @param Object to pack
- # @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
- #
- # @api private
- def _pack_ivars( obj )
- return_hash = {}
- vars = obj.respond_to?(:_storable_attributes) ? obj._storable_attributes : obj.instance_variables
- vars.each do |ivar_name|
- ivar = obj.instance_variable_get( ivar_name )
- return_hash[ivar_name] = _pack_object( ivar ) unless ivar.nil?
- end
- return_hash
- end
-
- # Handles the case of an hash-like object with keys that are objects
- #
- # @param Object to pack
- # @return [Integer] Index of the object in the keys array, used by the hash packer to name the key
- #
- # @api private
- def _build_object_key( obj )
- index = self.__pack[:keys].length
- self.__pack[:keys] << _pack_object( obj )
- index # return key
+ def _embedded?
+ self.class._aqua_opts[:embed] == true
end
- # Adds an attachment to the __pack document. Before save the attachments are encoded into the doc
- #
- # @param [String] filename used as the key/index
- # @param [File, Tempfile] file to be attached
- #
- # @api semi-public
- def _pack_file( filename, file )
- __pack.attachments.add( filename, file )
- end
-
-
- attr_accessor :_warnings
+ attr_accessor :_warnings, :_rev
# Private/protected methods are all prefaced by an underscore to prevent
# clogging the object instance space. Some of the public ones above are too!
protected
@@ -185,11 +145,11 @@
#
# _rev is needed for CouchDB store, since updates require the rev information. We could
# do without this accessor, but it would mean that an extra get request would have to be
# made with each PUT request so that the latest _rev could be obtained.
#
- attr_accessor :_store, :__pack, :_rev
+ attr_accessor :_store, :__pack
private
def _commit( mask_exception = true )
result = true
@@ -204,65 +164,17 @@
end
end
if result
self.id = __pack.id
self._rev = __pack.rev
- _clear_accessors
+ _clear_aqua_accessors
self
else
result
end
end
- # Object packing methods ------------
-
- # Packs the an object requiring no initialization.
- #
- # @param Object to pack
- # @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
- #
- # @api private
- def _pack_vanilla( obj )
- {
- 'class' => obj.class.to_s,
- 'ivars' => _pack_ivars( obj )
- }
- end
-
- # Packs the stub for an externally saved object.
- #
- # @param Object to pack
- # @return [Mash] Indifferent hash that is the data/metadata deconstruction of an object.
- #
- # @api private
- def _build_stub( obj )
- index = self.__pack[:stubs].length
- stub = { :class => obj.class.to_s, :id => obj }
- # deal with cached methods
- if obj._embed_me && obj._embed_me.keys && stub_methods = obj._embed_me[:stub]
- stub[:methods] = {}
- if stub_methods.class == Symbol || stub_methods.class == String
- stub_method = stub_methods.to_sym
- stub[:methods][stub_method] = obj.send( stub_method )
- else # is an array of values
- stub_methods.each do |meth|
- stub_method = meth.to_sym
- stub[:methods][stub_method] = obj.send( stub_method )
- end
- end
- end
- # add the stub
- self.__pack[:stubs] << stub
- # return a hash
- {'class' => 'Aqua::Stub', 'init' => "/STUB_#{index}"}
- end
-
- def _pack_singletons
- # TODO: figure out 1.8 and 1.9 compatibility issues.
- # Also learn the library usage, without any docs :(
- end
-
# Saves all self and nested object requiring independent saves
#
# @return [Object, false] Returns false on failure and self on success.
#
# @api private
@@ -274,33 +186,45 @@
# Saves nested object requiring independent saves. Adds warning messages to _warnings, when a save fails.
#
# @api private
def _commit_externals
- __pack[:stubs].each_with_index do |obj_hash, index|
- obj = obj_hash[:id]
+ _translator.externals.each do |obj, path|
if obj.commit
- obj_hash[:id] = obj.id
+ _update_external_id( path, obj.id )
else
- if obj.id
- self._warnings << "Unable to save latest version of #{obj.inspect}, stubbed at index #{index}"
- obj_hash[:id] = obj.id if obj.id
- else
- self._warnings << "Unable to save #{obj.inspect}, stubbed at index #{index}"
- end
+ self._warnings << ( obj.id ?
+ "Unable to save latest version of #{obj.inspect}, stubbed at #{path}" :
+ "Unable to save #{obj.inspect}, stubbed at #{path}"
+ )
end
end
end
+
+ # When external objects are saved to the base object, ids need to be updated after save
+ # This is the method used to locate the original id and updated it
+ #
+ # @param [String] path to external
+ # @param [String] id to save
+ #
+ # @api private
+ def _update_external_id( path, new_id )
+ __pack.instance_eval "self#{path}['init']['id'] = '#{new_id}'"
+ end
+
# clears the __pack and _store accessors to save on memory after each pack and unpack
#
# @api private
- def _clear_accessors
+ def _clear_aqua_accessors
self.__pack = nil
self._store = nil
+ @_translator = nil
end
public
end # InstanceMethods
end # Pack
+
+
end # Aqua
\ No newline at end of file