Skip to content

Instantly share code, notes, and snippets.

@abriening
Created October 22, 2009 12:31
Show Gist options
  • Save abriening/215925 to your computer and use it in GitHub Desktop.
Save abriening/215925 to your computer and use it in GitHub Desktop.
##
# 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