Skip to content
This repository
tree: 024bc70bfe
Fetching contributors…

Cannot retrieve contributors at this time

file 111 lines (90 sloc) 2.684 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
require "active_support/weak_hash"

module ActiveRecord
  # = Active Record Identity Map
  #
  # Ensures that each object gets loaded only once by keeping every loaded
  # object in a map. Looks up objects using the map when referring to them.
  #
  # More information on Identity Map pattern:
  # http://www.martinfowler.com/eaaCatalog/identityMap.html
  #
  # == Configuration
  #
  # In order to disable IdentityMap, set <tt>config.active_record.identity_map = false</tt>
  # in your <tt>config/application.rb</tt> file.
  #
  # IdentityMap is enabled by default.
  #
  module IdentityMap
    extend ActiveSupport::Concern

    class << self
      attr_accessor :enabled

      def repository
        Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = ActiveSupport::WeakHash.new }
      end

      def use
        old, self.enabled = self.enabled, true

        yield if block_given?
      ensure
        self.enabled = old
        ActiveRecord::IdentityMap.clear
      end

      def without
        old, self.enabled = self.enabled, false

        yield if block_given?
      ensure
        self.enabled = old
      end

      def get(klass, primary_key)
        if obj = repository[klass.symbolized_base_class][primary_key]
          return obj if obj.id == primary_key && klass == obj.class
        end

        nil
      end

      def add(record)
        repository[record.class.symbolized_base_class][record.id] = record
      end

      def remove(record)
        repository[record.class.symbolized_base_class].delete(record.id)
      end

      def remove_by_id(symbolized_base_class, id)
        repository[symbolized_base_class].delete(id)
      end

      def clear
        repository.clear
      end

      alias enabled? enabled
      alias identity_map= enabled=
    end

    self.enabled = true

    module InstanceMethods
      # Reinitialize an Identity Map model object from +coder+.
      # +coder+ must contain the attributes necessary for initializing an empty
      # model object.
      def reinit_with(coder)
        @attributes_cache = {}
        dirty = @changed_attributes.keys
        @attributes.update(coder['attributes'].except(*dirty))
        @changed_attributes.update(coder['attributes'].slice(*dirty))
        @changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}

        _run_find_callbacks

        self
      end
    end

    module ClassMethods
      def identity_map
        ActiveRecord::IdentityMap
      end
    end

    class Middleware
      def initialize(app)
        @app = app
      end

      def call(env)
        ActiveRecord::IdentityMap.use do
          @app.call(env)
        end
      end
    end
  end
end
Something went wrong with that request. Please try again.