Permalink
Browse files

Added the ability to specify the slave database.

Calling or not-calling ConnectionProxy.setup! will work the same
if no master or slave connection specified for the current environment.

I also removed trailing spaces and added tests.
  • Loading branch information...
1 parent 176aa0e commit 1bd18ef778bb54f7ef3796e0ebd9a21766ab4ab7 Scott Steadman committed Oct 20, 2008
Showing with 150 additions and 16 deletions.
  1. +16 −2 README
  2. +30 −14 lib/active_reload/connection_proxy.rb
  3. +104 −0 test/connection_proxy_test.rb
View
18 README
@@ -8,7 +8,12 @@ to a master database, and the rest to the slave database.
The ActiveReload::MasterDatabase model uses a 'master_database' setting that
can either be defined for all of your environments, or for each environment as
-a nested declaration:
+a nested declaration.
+
+The ActiveReload::SlaveDatabase model uses a 'slave_database' setting that
+can only be defined per environment.
+
+Example declarations:
# config/database.yml
login: &login
@@ -33,6 +38,15 @@ a nested declaration:
host: master-db-server.local
<<: *login
+ qa:
+ database: qa_master_database_name
+ host: qa-master
+ <<: *login
+ slave_database:
+ database: qa_slave_database_name
+ host: qa-slave
+ <<: *login
+
development: # Does not use masochism
database: development_database_name
<<: *login
@@ -81,4 +95,4 @@ class RandomController < ApplicationController
around_filter ActiveReload::MasterFilter, :only => [:show, :edit, :update]
...
-end
+end
@@ -1,7 +1,15 @@
module ActiveReload
class MasterDatabase < ActiveRecord::Base
self.abstract_class = true
- establish_connection configurations[Rails.env]['master_database'] || :master_database
+ establish_connection configurations[Rails.env]['master_database'] || configurations['master_database'] || Rails.env
+ end
+
+ class SlaveDatabase < ActiveRecord::Base
+ self.abstract_class = true
+ def self.name
+ ActiveRecord::Base.name
+ end
+ establish_connection configurations[Rails.env]['slave_database'] || Rails.env
end
class ConnectionProxy
@@ -10,13 +18,21 @@ def initialize(master, slave)
@master = master.connection
@current = @slave
end
-
+
attr_accessor :slave, :master
def self.setup!
- setup_for ActiveReload::MasterDatabase
+ if slave_defined?
+ setup_for ActiveReload::MasterDatabase, ActiveReload::SlaveDatabase
+ else
+ setup_for ActiveReload::MasterDatabase
+ end
+ end
+
+ def self.slave_defined?
+ ActiveRecord::Base.configurations[Rails.env]['slave_database']
end
-
+
def self.setup_for(master, slave = nil)
slave ||= ActiveRecord::Base
slave.send :include, ActiveRecordConnectionMethods
@@ -33,22 +49,22 @@ def with_master
def set_to_master!
return if @current == @master
-
+
logger.info "Switching to Master"
@current = @master
end
-
+
def set_to_slave!
return if @current == @slave
-
+
logger.info "Switching to Slave"
@current = @slave
end
-
- delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
+
+ delegate :insert, :update, :delete, :create_table, :rename_table, :drop_table, :add_column, :remove_column,
:change_column, :change_column_default, :rename_column, :add_index, :remove_index, :initialize_schema_information,
:dump_schema_information, :execute, :to => :master
-
+
def transaction(start_db_transaction = true, &block)
with_master { @current.transaction(start_db_transaction, &block) }
end
@@ -57,12 +73,12 @@ def method_missing(method, *args, &block)
@current.send(method, *args, &block)
end
end
-
+
module ActiveRecordConnectionMethods
def self.included(base)
base.alias_method_chain :reload, :master
end
-
+
def reload_with_master(*args, &block)
if connection.class.name == "ActiveReload::ConnectionProxy"
connection.with_master { reload_without_master }
@@ -79,7 +95,7 @@ module ObserverExtensions
def self.included(base)
base.alias_method_chain :update, :masterdb
end
-
+
# Send observed_method(object) if the method exists.
def update_with_masterdb(observed_method, object) #:nodoc:
if object.class.connection.respond_to?(:with_master)
@@ -91,4 +107,4 @@ def update_with_masterdb(observed_method, object) #:nodoc:
end
end
end
-end
+end
@@ -0,0 +1,104 @@
+require File.dirname(__FILE__) + '/../../../../config/environment'
+require 'test/unit'
+require 'fileutils'
+require 'pp'
+
+module ActiveReload
+ class ConnectionProxyTest < Test::Unit::TestCase
+
+ MASTER = 'db/masochism_master.sqlite3'
+ SLAVE = 'db/masochism_slave.sqlite3'
+
+ def teardown
+ ActiveRecord::Base.remove_connection
+ FileUtils.rm_f(Rails.root + '/' + MASTER)
+ FileUtils.rm_f(Rails.root + '/' + SLAVE)
+ end
+
+ def test_slave_defined_returns_false_when_slave_not_defined
+ ActiveRecord::Base.configurations = default_config
+ assert_nil ActiveReload::ConnectionProxy.slave_defined?, 'Slave should not be defined'
+ end
+
+ def test_slave_defined_returns_true_when_slave_defined
+ ActiveRecord::Base.configurations = slave_inside_config
+ assert_not_nil ActiveReload::ConnectionProxy.slave_defined?, 'Slave should be defined'
+ end
+
+ def test_default
+ ActiveRecord::Base.configurations = default_config
+ reload
+ ActiveReload::ConnectionProxy.setup!
+
+ ActiveRecord::Base.connection.master.execute('CREATE TABLE foo (id int)')
+ assert_equal ['foo'], ActiveRecord::Base.connection.tables, 'Master and Slave should be the same database'
+ assert_equal ['foo'], ActiveRecord::Base.connection.slave.tables, 'Master and Slave should be the same database'
+ end
+
+ def test_master_database_outside_environment
+ ActiveRecord::Base.configurations = master_outside_config
+ reload
+ ActiveReload::ConnectionProxy.setup!
+
+ ActiveRecord::Base.connection.master.execute('CREATE TABLE foo (id int)')
+ assert_equal [], ActiveRecord::Base.connection.tables, 'Master and Slave should be different databases'
+ assert_equal [], ActiveRecord::Base.connection.slave.tables, 'Master and Slave should be different databases'
+ end
+
+ def test_master_database_within_environment
+ ActiveRecord::Base.configurations = master_inside_config
+ reload
+ ActiveReload::ConnectionProxy.setup!
+
+ ActiveRecord::Base.connection.master.execute('CREATE TABLE foo (id int)')
+ assert_equal [], ActiveRecord::Base.connection.tables, 'Master and Slave should be different databases'
+ assert_equal [], ActiveRecord::Base.connection.slave.tables, 'Master and Slave should be different databases'
+ end
+
+ def test_slave_database_within_environment
+ ActiveRecord::Base.configurations = slave_inside_config
+ reload
+ ActiveReload::ConnectionProxy.setup!
+
+ ActiveRecord::Base.connection.master.execute('CREATE TABLE foo (id int)')
+ assert_equal [], ActiveRecord::Base.connection.tables, 'Master and Slave should be different databases'
+ assert_equal [], ActiveRecord::Base.connection.slave.tables, 'Master and Slave should be different databases'
+ end
+
+ private
+
+ def reload
+ # force establish_connection calls to be re-executed
+ load File.dirname(__FILE__)+'/../lib/active_reload/connection_proxy.rb'
+ end
+
+ def default_config
+ {Rails.env => {'adapter' => 'sqlite3', 'database' => MASTER}}
+ end
+
+ def master_outside_config
+ {
+ Rails.env => {'adapter' => 'sqlite3', 'database' => SLAVE},
+ 'master_database' => {'adapter' => 'sqlite3', 'database' => MASTER}
+ }
+ end
+
+ def master_inside_config
+ {
+ Rails.env => {'adapter' => 'sqlite3', 'database' => SLAVE,
+ 'master_database' => {'adapter' => 'sqlite3', 'database' => MASTER}
+ }
+ }
+ end
+
+ def slave_inside_config
+ {
+ Rails.env => {'adapter' => 'sqlite3', 'database' => MASTER,
+ 'slave_database' => {'adapter' => 'sqlite3', 'database' => SLAVE}
+ }
+ }
+ end
+
+
+ end # class ConnectionProxyTest
+end # module ActiveReload

0 comments on commit 1bd18ef

Please sign in to comment.