module DbCharmer module ActiveRecord module MultiDbProxy # Simple proxy class that switches connections and then proxies all the calls # This class is used to implement chained on_db calls class OnDbProxy < ActiveSupport::BasicObject # We need to do this because in Rails 2.3 BasicObject does not remove object_id method, which is stupid undef_method(:object_id) if instance_methods.member?('object_id') def initialize(proxy_target, slave) @proxy_target = proxy_target @slave = slave end private def method_missing(meth, *args, &block) # Switch connection and proxy the method call @proxy_target.on_db(@slave) do |proxy_target| res = proxy_target.__send__(meth, *args, &block) # If result is a scope/association, return a new proxy for it, otherwise return the result itself (res.proxy?) ? OnDbProxy.new(res, @slave) : res end end end module ClassMethods def on_db(con, proxy_target = nil) proxy_target ||= self # Chain call return OnDbProxy.new(proxy_target, con) unless block_given? # Block call begin self.db_charmer_connection_level += 1 old_proxy = db_charmer_connection_proxy switch_connection_to(con, DbCharmer.connections_should_exist?) yield(proxy_target) ensure switch_connection_to(old_proxy) self.db_charmer_connection_level -= 1 end end end module InstanceMethods def on_db(con, proxy_target = nil, &block) proxy_target ||= self self.class.on_db(con, proxy_target, &block) end end module MasterSlaveClassMethods def on_slave(con = nil, proxy_target = nil, &block) con ||= db_charmer_random_slave raise ArgumentError, "No slaves found in the class and no slave connection given" unless con on_db(con, proxy_target, &block) end def on_master(proxy_target = nil, &block) on_db(db_charmer_default_connection, proxy_target, &block) end def first_level_on_slave first_level = db_charmer_top_level_connection? && on_master.connection.open_transactions.zero? if first_level && db_charmer_force_slave_reads? && db_charmer_slaves.any? on_slave { yield } else yield end end end end end end