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