Module: ActiveRecord::IdentityMap
- Included in:
- Base
- Defined in:
- 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
- .add(record) ⇒ Object
- .clear ⇒ Object
- .enabled ⇒ Object (also: enabled?)
- .enabled=(flag) ⇒ Object
- .get(klass, primary_key) ⇒ Object
- .remove(record) ⇒ Object
- .remove_by_id(symbolized_sti_name, id) ⇒ Object
- .repository ⇒ Object
- .use ⇒ Object
- .without ⇒ Object
Instance Method Summary collapse
-
#reinit_with(coder) ⇒ Object
Reinitialize an Identity Map model object from
coder
.
Class Method Details
.add(record) ⇒ Object
92 93 94 |
# File 'lib/active_record/identity_map.rb', line 92 def add(record) repository[record.class.symbolized_sti_name][record.id] = record if contain_all_columns?(record) end |
.clear ⇒ Object
104 105 106 |
# File 'lib/active_record/identity_map.rb', line 104 def clear repository.clear end |
.enabled ⇒ Object Also known as: enabled?
51 52 53 |
# File 'lib/active_record/identity_map.rb', line 51 def enabled Thread.current[:identity_map_enabled] end |
.enabled=(flag) ⇒ Object
47 48 49 |
# File 'lib/active_record/identity_map.rb', line 47 def enabled=(flag) Thread.current[:identity_map_enabled] = flag end |
.get(klass, primary_key) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File '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 |
.remove(record) ⇒ Object
96 97 98 |
# File 'lib/active_record/identity_map.rb', line 96 def remove(record) repository[record.class.symbolized_sti_name].delete(record.id) end |
.remove_by_id(symbolized_sti_name, id) ⇒ Object
100 101 102 |
# File 'lib/active_record/identity_map.rb', line 100 def remove_by_id(symbolized_sti_name, id) repository[symbolized_sti_name].delete(id) end |
.repository ⇒ Object
56 57 58 |
# File 'lib/active_record/identity_map.rb', line 56 def repository Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} } end |
.use ⇒ Object
60 61 62 63 64 65 66 67 |
# File '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 |
.without ⇒ Object
69 70 71 72 73 74 75 |
# File '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
#reinit_with(coder) ⇒ Object
Reinitialize an Identity Map model object from coder
. coder
must contain the attributes necessary for initializing an empty model object.
118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/active_record/identity_map.rb', line 118 def reinit_with(coder) @attributes_cache = {} dirty = @changed_attributes.keys attributes = self.class.initialize_attributes(coder['attributes'].except(*dirty)) @attributes.update(attributes) @changed_attributes.update(coder['attributes'].slice(*dirty)) @changed_attributes.delete_if{|k,v| v.eql? @attributes[k]} run_callbacks :find self end |