Created
October 22, 2009 12:31
-
-
Save abriening/215925 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# Include this in your app as config/initializers/identity_map.rb | |
# But don't really, this is untested and not used in any production code. | |
# | |
# It is a partial implementation of Martin Fowler's IdentityMap. | |
# http://martinfowler.com/eaaCatalog/identityMap.html | |
# | |
# Combined with QueryCache it is a complete implementation ( almost ). | |
# | |
module IdentityMap | |
def self.included(base) | |
base.extend ClassMethods | |
base.alias_method_chain :create, :identity_map | |
class << base | |
alias_method_chain :instantiate, :identity_map | |
end | |
end | |
def identity_map_key | |
self.class.identity_map_key(self) | |
end | |
def create_with_identity_map | |
result = create_without_identity_map | |
if self.class.identity_map_enabled? && result | |
complete = self.class.column_names.sort == attribute_names | |
self.class.identity_map[identity_map_key] = self if complete | |
end | |
result | |
end | |
# TODO 2009-10-22 | |
# reload will do nothing | |
# define a without identity_map reload | |
module ClassMethods | |
def instantiate_with_identity_map(record) | |
if identity_map_enabled? && record[primary_key] | |
key = identity_map_key(record) | |
if mapped = identity_map[key] | |
# puts "HIT [#{key}]" | |
mapped | |
else | |
# puts "MISS [#{key}]" | |
unmapped = instantiate_without_identity_map(record) | |
complete = column_names.sort == record.keys.sort | |
identity_map[key] = unmapped if complete | |
unmapped | |
end | |
else | |
instantiate_without_identity_map(record) | |
end | |
end | |
def identity_map_enabled? | |
if defined?(@identity_map_enabled) | |
@identity_map_enabled == true | |
elsif superclass.respond_to?(:identity_map_enabled?) | |
superclass.identity_map_enabled? | |
end | |
end | |
def enable_identity_map! | |
@identity_map = {} unless identity_map | |
@identity_map_enabled = true | |
end | |
def disable_identity_map! | |
remove_instance_variable(:@identity_map) if instance_variable_defined?(:@identity_map) | |
@identity_map_enabled = false | |
end | |
def identity_map | |
if defined?(@identity_map) | |
@identity_map | |
elsif superclass.respond_to?(:identity_map) | |
superclass.identity_map | |
end | |
end | |
def identity_map_key(record) | |
[table_name, record[primary_key.to_s]].join('-') | |
end | |
def with_identity_map | |
old, @identity_map_enabled = @identity_map_enabled, true | |
@identity_map ||= {} | |
yield | |
ensure | |
clear_identity_map | |
if old.nil? | |
remove_instance_variable :@identity_map_enabled | |
else | |
@identity_map_enabled = old | |
end | |
end | |
## | |
# allow clearing if you need to instantiate the same records into separate instances | |
# also used for testing | |
# | |
def clear_identity_map | |
@identity_map.clear if @identity_map | |
end | |
def without_identity_map | |
old, @identity_map_enabled = @identity_map_enabled, false | |
yield | |
ensure | |
if old.nil? | |
remove_instance_variable :@identity_map_enabled | |
else | |
@identity_map_enabled = old | |
end | |
end | |
end # ClassMethods | |
end # IdentityMap | |
ActiveRecord::Base.class_eval do | |
include IdentityMap | |
end | |
module ActionController | |
module IdentitiyMap | |
def self.included(base) | |
base.alias_method_chain :perform_action, :identity_map | |
end | |
protected | |
def perform_action_with_identity_map | |
ActiveRecord::Base.with_identity_map do | |
perform_action_without_identity_map | |
end | |
end | |
end | |
end | |
ActionController::Base.class_eval do | |
include ActionController::IdentitiyMap | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment