novelys_mongo_mapper 0.6.10
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 +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +38 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper.rb +124 -0
- data/lib/mongo_mapper/descendant_appends.rb +44 -0
- data/lib/mongo_mapper/document.rb +423 -0
- data/lib/mongo_mapper/dynamic_finder.rb +74 -0
- data/lib/mongo_mapper/embedded_document.rb +67 -0
- data/lib/mongo_mapper/finder_options.rb +127 -0
- data/lib/mongo_mapper/plugins.rb +14 -0
- data/lib/mongo_mapper/plugins/associations.rb +104 -0
- data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
- data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
- data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
- data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
- data/lib/mongo_mapper/plugins/callbacks.rb +76 -0
- data/lib/mongo_mapper/plugins/clone.rb +13 -0
- data/lib/mongo_mapper/plugins/descendants.rb +16 -0
- data/lib/mongo_mapper/plugins/dirty.rb +119 -0
- data/lib/mongo_mapper/plugins/equality.rb +23 -0
- data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
- data/lib/mongo_mapper/plugins/inspect.rb +14 -0
- data/lib/mongo_mapper/plugins/keys.rb +300 -0
- data/lib/mongo_mapper/plugins/logger.rb +17 -0
- data/lib/mongo_mapper/plugins/pagination.rb +85 -0
- data/lib/mongo_mapper/plugins/protected.rb +41 -0
- data/lib/mongo_mapper/plugins/rails.rb +45 -0
- data/lib/mongo_mapper/plugins/serialization.rb +105 -0
- data/lib/mongo_mapper/plugins/validations.rb +62 -0
- data/lib/mongo_mapper/support.rb +214 -0
- data/mongo_mapper.gemspec +179 -0
- data/performance/read_write.rb +52 -0
- data/specs.watchr +51 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
- data/test/functional/associations/test_in_array_proxy.rb +309 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
- data/test/functional/associations/test_many_documents_proxy.rb +431 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
- data/test/functional/associations/test_one_proxy.rb +161 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +27 -0
- data/test/functional/test_callbacks.rb +81 -0
- data/test/functional/test_dirty.rb +163 -0
- data/test/functional/test_document.rb +1245 -0
- data/test/functional/test_embedded_document.rb +125 -0
- data/test/functional/test_identity_map.rb +508 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_modifiers.rb +252 -0
- data/test/functional/test_pagination.rb +93 -0
- data/test/functional/test_protected.rb +139 -0
- data/test/functional/test_string_id_compatibility.rb +67 -0
- data/test/functional/test_validations.rb +329 -0
- data/test/models.rb +232 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +55 -0
- data/test/unit/associations/test_base.rb +207 -0
- data/test/unit/associations/test_proxy.rb +105 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_descendant_appends.rb +71 -0
- data/test/unit/test_document.rb +231 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +663 -0
- data/test/unit/test_finder_options.rb +329 -0
- data/test/unit/test_keys.rb +169 -0
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_pagination.rb +127 -0
- data/test/unit/test_plugins.rb +50 -0
- data/test/unit/test_rails.rb +123 -0
- data/test/unit/test_rails_compatibility.rb +52 -0
- data/test/unit/test_serialization.rb +51 -0
- data/test/unit/test_support.rb +354 -0
- data/test/unit/test_time_zones.rb +39 -0
- data/test/unit/test_validations.rb +544 -0
- metadata +248 -0
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module MongoMapper
         | 
| 2 | 
            +
              module Plugins
         | 
| 3 | 
            +
                module Associations
         | 
| 4 | 
            +
                  class ManyEmbeddedProxy < EmbeddedCollection
         | 
| 5 | 
            +
                    def replace(values)
         | 
| 6 | 
            +
                      @_values = values.map do |v|
         | 
| 7 | 
            +
                        v.respond_to?(:attributes) ? v.attributes : v
         | 
| 8 | 
            +
                      end
         | 
| 9 | 
            +
                      reset
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    private
         | 
| 13 | 
            +
                      def find_target
         | 
| 14 | 
            +
                        (@_values || []).map do |v|
         | 
| 15 | 
            +
                          child = klass.load(v)
         | 
| 16 | 
            +
                          assign_root_document(child)
         | 
| 17 | 
            +
                          child
         | 
| 18 | 
            +
                        end
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            module MongoMapper
         | 
| 2 | 
            +
              module Plugins
         | 
| 3 | 
            +
                module Associations
         | 
| 4 | 
            +
                  class OneProxy < Proxy
         | 
| 5 | 
            +
                    undef_method :object_id
         | 
| 6 | 
            +
                    
         | 
| 7 | 
            +
                    def build(attrs={})
         | 
| 8 | 
            +
                      instantiate_target(:new, attrs)
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def create(attrs={})
         | 
| 12 | 
            +
                      instantiate_target(:create, attrs)
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def create!(attrs={})
         | 
| 16 | 
            +
                      instantiate_target(:create!, attrs)
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def replace(doc)
         | 
| 20 | 
            +
                      load_target
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      if !target.nil? && target != doc
         | 
| 23 | 
            +
                        if options[:dependent] && !target.new?
         | 
| 24 | 
            +
                          case options[:dependent]
         | 
| 25 | 
            +
                            when :delete
         | 
| 26 | 
            +
                              target.delete
         | 
| 27 | 
            +
                            when :destroy
         | 
| 28 | 
            +
                              target.destroy
         | 
| 29 | 
            +
                            when :nullify
         | 
| 30 | 
            +
                              target[foreign_key] = nil
         | 
| 31 | 
            +
                              target.save
         | 
| 32 | 
            +
                          end
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      reset
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      unless doc.nil?
         | 
| 39 | 
            +
                        owner.save if owner.new?
         | 
| 40 | 
            +
                        doc[foreign_key] = owner.id
         | 
| 41 | 
            +
                        doc.save if doc.new?
         | 
| 42 | 
            +
                        loaded
         | 
| 43 | 
            +
                        @target = doc
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    protected
         | 
| 48 | 
            +
                      def find_target
         | 
| 49 | 
            +
                        target_class.first(reflection.finder_options.merge(foreign_key => owner.id))
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      def instantiate_target(instantiator, attrs={})
         | 
| 53 | 
            +
                        @target = target_class.send(instantiator, attrs.update(foreign_key => owner.id))
         | 
| 54 | 
            +
                        loaded
         | 
| 55 | 
            +
                        @target
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      def target_class
         | 
| 59 | 
            +
                        @target_class ||= options[:class] || (options[:class_name] || reflection.name.to_s.camelize).constantize
         | 
| 60 | 
            +
                      end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      def foreign_key
         | 
| 63 | 
            +
                        options[:foreign_key] || owner.class.name.foreign_key
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,118 @@ | |
| 1 | 
            +
            module MongoMapper
         | 
| 2 | 
            +
              module Plugins
         | 
| 3 | 
            +
                module Associations
         | 
| 4 | 
            +
                  class Proxy
         | 
| 5 | 
            +
                    alias :proxy_respond_to? :respond_to?
         | 
| 6 | 
            +
                    alias :proxy_extend :extend
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    attr_reader :owner, :reflection, :target
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    alias :proxy_owner :owner
         | 
| 13 | 
            +
                    alias :proxy_target :target
         | 
| 14 | 
            +
                    alias :proxy_reflection :reflection
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    delegate :klass, :to => :proxy_reflection
         | 
| 17 | 
            +
                    delegate :options, :to => :proxy_reflection
         | 
| 18 | 
            +
                    delegate :collection, :to => :klass
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    def initialize(owner, reflection)
         | 
| 21 | 
            +
                      @owner, @reflection, @loaded = owner, reflection, false
         | 
| 22 | 
            +
                      Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
         | 
| 23 | 
            +
                      reset
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def inspect
         | 
| 27 | 
            +
                      load_target
         | 
| 28 | 
            +
                      target.inspect
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    def loaded?
         | 
| 32 | 
            +
                      @loaded
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def loaded
         | 
| 36 | 
            +
                      @loaded = true
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def nil?
         | 
| 40 | 
            +
                      load_target
         | 
| 41 | 
            +
                      target.nil?
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def blank?
         | 
| 45 | 
            +
                      load_target
         | 
| 46 | 
            +
                      target.blank?
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    def present?
         | 
| 50 | 
            +
                      load_target
         | 
| 51 | 
            +
                      target.present?
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    def reload
         | 
| 55 | 
            +
                      reset
         | 
| 56 | 
            +
                      load_target
         | 
| 57 | 
            +
                      self unless target.nil?
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    def replace(v)
         | 
| 61 | 
            +
                      raise NotImplementedError
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def reset
         | 
| 65 | 
            +
                      @loaded = false
         | 
| 66 | 
            +
                      @target = nil
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    def respond_to?(*args)
         | 
| 70 | 
            +
                      proxy_respond_to?(*args) || (load_target && target.respond_to?(*args))
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    def send(method, *args)
         | 
| 74 | 
            +
                      if proxy_respond_to?(method)
         | 
| 75 | 
            +
                        super
         | 
| 76 | 
            +
                      else
         | 
| 77 | 
            +
                        load_target
         | 
| 78 | 
            +
                        target.send(method, *args)
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    def ===(other)
         | 
| 83 | 
            +
                      load_target
         | 
| 84 | 
            +
                      other === target
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    protected
         | 
| 88 | 
            +
                      def method_missing(method, *args, &block)
         | 
| 89 | 
            +
                        if load_target
         | 
| 90 | 
            +
                          if block_given?
         | 
| 91 | 
            +
                            target.send(method, *args)  { |*block_args| block.call(*block_args) }
         | 
| 92 | 
            +
                          else
         | 
| 93 | 
            +
                            target.send(method, *args)
         | 
| 94 | 
            +
                          end
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      def load_target
         | 
| 99 | 
            +
                        @target = find_target unless loaded?
         | 
| 100 | 
            +
                        loaded
         | 
| 101 | 
            +
                        @target
         | 
| 102 | 
            +
                      rescue MongoMapper::DocumentNotFound
         | 
| 103 | 
            +
                        reset
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                      def find_target
         | 
| 107 | 
            +
                        raise NotImplementedError
         | 
| 108 | 
            +
                      end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                      def flatten_deeper(array)
         | 
| 111 | 
            +
                        array.collect do |element|
         | 
| 112 | 
            +
                          (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element
         | 
| 113 | 
            +
                        end.flatten
         | 
| 114 | 
            +
                      end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
            end
         | 
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            module MongoMapper
         | 
| 2 | 
            +
              module Plugins
         | 
| 3 | 
            +
                module Callbacks
         | 
| 4 | 
            +
                  METHODS = ["before_save", "after_save", 
         | 
| 5 | 
            +
                  "before_create", "after_create", 
         | 
| 6 | 
            +
                  "before_update", "after_update", 
         | 
| 7 | 
            +
                  "before_validation", "after_validation", 
         | 
| 8 | 
            +
                  "before_validation_on_create", "after_validation_on_create", 
         | 
| 9 | 
            +
                  "before_validation_on_update", "after_validation_on_update", 
         | 
| 10 | 
            +
                  "before_destroy", "after_destroy"]
         | 
| 11 | 
            +
                  
         | 
| 12 | 
            +
                  def self.configure(model)
         | 
| 13 | 
            +
                    model.class_eval do
         | 
| 14 | 
            +
                      include ActiveSupport::Callbacks
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      define_callbacks *METHODS
         | 
| 17 | 
            +
                      
         | 
| 18 | 
            +
                      # If Rails 3, support the new callback syntax
         | 
| 19 | 
            +
                      if metaclass.method_defined?(:set_callback)
         | 
| 20 | 
            +
                        METHODS.each do |method|
         | 
| 21 | 
            +
                          class_eval <<-"end_eval", __FILE__, __LINE__
         | 
| 22 | 
            +
                            def self.#{method}(*methods, &block)
         | 
| 23 | 
            +
                              set_callback :#{method}, *methods, &block
         | 
| 24 | 
            +
                            end
         | 
| 25 | 
            +
                          end_eval
         | 
| 26 | 
            +
                        end
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                  
         | 
| 31 | 
            +
                  module InstanceMethods
         | 
| 32 | 
            +
                    def valid?
         | 
| 33 | 
            +
                      action = new? ? 'create' : 'update'
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      run_callbacks(:before_validation)
         | 
| 36 | 
            +
                      run_callbacks("before_validation_on_#{action}".to_sym)
         | 
| 37 | 
            +
                      result = super
         | 
| 38 | 
            +
                      run_callbacks("after_validation_on_#{action}".to_sym)
         | 
| 39 | 
            +
                      run_callbacks(:after_validation)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      result
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def destroy
         | 
| 45 | 
            +
                      run_callbacks(:before_destroy)
         | 
| 46 | 
            +
                      result = super
         | 
| 47 | 
            +
                      run_callbacks(:after_destroy)
         | 
| 48 | 
            +
                      result
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    private
         | 
| 52 | 
            +
                      def create_or_update(*args)
         | 
| 53 | 
            +
                        run_callbacks(:before_save)
         | 
| 54 | 
            +
                        if result = super
         | 
| 55 | 
            +
                          run_callbacks(:after_save)
         | 
| 56 | 
            +
                        end
         | 
| 57 | 
            +
                        result
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      def create(*args)
         | 
| 61 | 
            +
                        run_callbacks(:before_create)
         | 
| 62 | 
            +
                        result = super
         | 
| 63 | 
            +
                        run_callbacks(:after_create)
         | 
| 64 | 
            +
                        result
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      def update(*args)
         | 
| 68 | 
            +
                        run_callbacks(:before_update)
         | 
| 69 | 
            +
                        result = super
         | 
| 70 | 
            +
                        run_callbacks(:after_update)
         | 
| 71 | 
            +
                        result
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
            end
         | 
| @@ -0,0 +1,119 @@ | |
| 1 | 
            +
            module MongoMapper
         | 
| 2 | 
            +
              module Plugins
         | 
| 3 | 
            +
                module Dirty
         | 
| 4 | 
            +
                  module InstanceMethods
         | 
| 5 | 
            +
                    def method_missing(method, *args, &block)
         | 
| 6 | 
            +
                      if method.to_s =~ /(_changed\?|_change|_will_change!|_was)$/
         | 
| 7 | 
            +
                        method_suffix = $1
         | 
| 8 | 
            +
                        key = method.to_s.gsub(method_suffix, '')
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                        if key_names.include?(key)
         | 
| 11 | 
            +
                          case method_suffix
         | 
| 12 | 
            +
                            when '_changed?'
         | 
| 13 | 
            +
                              key_changed?(key)
         | 
| 14 | 
            +
                            when '_change'
         | 
| 15 | 
            +
                              key_change(key)
         | 
| 16 | 
            +
                            when '_will_change!'
         | 
| 17 | 
            +
                              key_will_change!(key)
         | 
| 18 | 
            +
                            when '_was'
         | 
| 19 | 
            +
                              key_was(key)
         | 
| 20 | 
            +
                          end
         | 
| 21 | 
            +
                        else
         | 
| 22 | 
            +
                          super
         | 
| 23 | 
            +
                        end
         | 
| 24 | 
            +
                      else
         | 
| 25 | 
            +
                        super
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def changed?
         | 
| 30 | 
            +
                      !changed_keys.empty?
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def changed
         | 
| 34 | 
            +
                      changed_keys.keys
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def changes
         | 
| 38 | 
            +
                      changed.inject({}) { |h, key| h[key] = key_change(key); h }
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def initialize(*args)
         | 
| 42 | 
            +
                      super
         | 
| 43 | 
            +
                      changed_keys.clear if args.first.blank? || !new?
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    def save(*args)
         | 
| 47 | 
            +
                      if status = super
         | 
| 48 | 
            +
                        changed_keys.clear
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                      status
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    def save!(*args)
         | 
| 54 | 
            +
                      status = super
         | 
| 55 | 
            +
                      changed_keys.clear
         | 
| 56 | 
            +
                      status
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    def reload(*args)
         | 
| 60 | 
            +
                      document = super
         | 
| 61 | 
            +
                      changed_keys.clear
         | 
| 62 | 
            +
                      document
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    private
         | 
| 66 | 
            +
                      def clone_key_value(key)
         | 
| 67 | 
            +
                        value = read_key(key)
         | 
| 68 | 
            +
                        value.duplicable? ? value.clone : value
         | 
| 69 | 
            +
                      rescue TypeError, NoMethodError
         | 
| 70 | 
            +
                        value
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                      def changed_keys
         | 
| 74 | 
            +
                        @changed_keys ||= {}
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                      def key_changed?(key)
         | 
| 78 | 
            +
                        changed_keys.include?(key)
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                      def key_change(key)
         | 
| 82 | 
            +
                        [changed_keys[key], __send__(key)] if key_changed?(key)
         | 
| 83 | 
            +
                      end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      def key_was(key)
         | 
| 86 | 
            +
                        key_changed?(key) ? changed_keys[key] : __send__(key)
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                      def key_will_change!(key)
         | 
| 90 | 
            +
                        changed_keys[key] = clone_key_value(key)
         | 
| 91 | 
            +
                      end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                      def write_key(key, value)
         | 
| 94 | 
            +
                        key = key.to_s
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                        if changed_keys.include?(key)
         | 
| 97 | 
            +
                          old = changed_keys[key]
         | 
| 98 | 
            +
                          changed_keys.delete(key) unless value_changed?(key, old, value)
         | 
| 99 | 
            +
                        else
         | 
| 100 | 
            +
                          old = clone_key_value(key)
         | 
| 101 | 
            +
                          changed_keys[key] = old if value_changed?(key, old, value)
         | 
| 102 | 
            +
                        end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                        super(key, value)
         | 
| 105 | 
            +
                      end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                      def value_changed?(key_name, old, value)
         | 
| 108 | 
            +
                        key = keys[key_name]
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                        if key.number? && value.blank?
         | 
| 111 | 
            +
                          value = nil
         | 
| 112 | 
            +
                        end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                        old != value
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
            end
         |