Module: ActiveRecord::IdentityMap

Defined in:
activerecord/lib/active_record/identity_map.rb

Overview

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 enable IdentityMap, set config.active_record.identity_map = true in your config/application.rb file.

IdentityMap is disabled by default and still in development (i.e. use it with care).

Associations

Active Record Identity Map does not track associations yet. For example:

comment = @post.comments.first
comment.post = nil
@post.comments.include?(comment) #=> true

Ideally, the example above would return false, removing the comment object from the post association when the association is nullified. This may cause side effects, as in the situation below, if Identity Map is enabled:

Post.has_many :comments, :dependent => :destroy

comment = @post.comments.first
comment.post = nil
comment.save
Post.destroy(@post.id)

Without using Identity Map, the code above will destroy the @post object leaving the comment object intact. However, once we enable Identity Map, the post loaded by Post.destroy is exactly the same object as the object @post. As the object @post still has the comment object in @post.comments, once Identity Map is enabled, the comment object will be accidently removed.

This inconsistency is meant to be fixed in future Rails releases.

Defined Under Namespace

Classes: Middleware

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Object) add(record)



92
93
94
# File 'activerecord/lib/active_record/identity_map.rb', line 92

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

+ (Object) clear



104
105
106
# File 'activerecord/lib/active_record/identity_map.rb', line 104

def clear
  repository.clear
end

+ (Object) enabled Also known as: enabled?



51
52
53
# File 'activerecord/lib/active_record/identity_map.rb', line 51

def enabled
  Thread.current[:identity_map_enabled]
end

+ (Object) enabled=(flag)



47
48
49
# File 'activerecord/lib/active_record/identity_map.rb', line 47

def enabled=(flag)
  Thread.current[:identity_map_enabled] = flag
end

+ (Object) get(klass, primary_key)



77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'activerecord/lib/active_record/identity_map.rb', line 77

def get(klass, primary_key)
  record = repository[klass.symbolized_sti_name][primary_key]

  if record.is_a?(klass)
    ActiveSupport::Notifications.instrument("identity.active_record",
      :line => "From Identity Map (id: #{primary_key})",
      :name => "#{klass} Loaded",
      :connection_id => object_id)

    record
  else
    nil
  end
end

+ (Object) remove(record)



96
97
98
# File 'activerecord/lib/active_record/identity_map.rb', line 96

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

+ (Object) remove_by_id(symbolized_sti_name, id)



100
101
102
# File 'activerecord/lib/active_record/identity_map.rb', line 100

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

+ (Object) repository



56
57
58
# File 'activerecord/lib/active_record/identity_map.rb', line 56

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

+ (Object) use



60
61
62
63
64
65
66
67
# File 'activerecord/lib/active_record/identity_map.rb', line 60

def use
  old, self.enabled = enabled, true

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

+ (Object) without



69
70
71
72
73
74
75
# File 'activerecord/lib/active_record/identity_map.rb', line 69

def without
  old, self.enabled = enabled, false

  yield if block_given?
ensure
  self.enabled = old
end

Instance Method Details

- (Object) reinit_with(coder)

Reinitialize an Identity Map model object from coder. coder must contain the attributes necessary for initializing an empty model object.



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'activerecord/lib/active_record/identity_map.rb', line 112

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]}

  set_serialized_attributes

  run_callbacks :find

  self
end