connection_manager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +49 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +135 -0
  6. data/Rakefile +1 -0
  7. data/connection_manager.gemspec +24 -0
  8. data/lib/connection_manager.rb +9 -0
  9. data/lib/connection_manager/associations.rb +29 -0
  10. data/lib/connection_manager/connection_manager_railtie.rb +9 -0
  11. data/lib/connection_manager/connections.rb +110 -0
  12. data/lib/connection_manager/replication_builder.rb +135 -0
  13. data/lib/connection_manager/version.rb +4 -0
  14. data/spec/spec.opts +4 -0
  15. data/spec/spec_helper.rb +13 -0
  16. data/test_app/.gitignore +15 -0
  17. data/test_app/.rspec +1 -0
  18. data/test_app/Gemfile +14 -0
  19. data/test_app/Gemfile.lock +140 -0
  20. data/test_app/README +261 -0
  21. data/test_app/Rakefile +7 -0
  22. data/test_app/app/models/.gitkeep +0 -0
  23. data/test_app/app/models/basket.rb +4 -0
  24. data/test_app/app/models/fruit.rb +6 -0
  25. data/test_app/app/models/fruit_basket.rb +4 -0
  26. data/test_app/app/models/region.rb +3 -0
  27. data/test_app/app/models/type.rb +2 -0
  28. data/test_app/config.ru +4 -0
  29. data/test_app/config/application.rb +52 -0
  30. data/test_app/config/boot.rb +6 -0
  31. data/test_app/config/database.yml +42 -0
  32. data/test_app/config/environment.rb +5 -0
  33. data/test_app/config/environments/development.rb +30 -0
  34. data/test_app/config/environments/production.rb +60 -0
  35. data/test_app/config/environments/test.rb +39 -0
  36. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  37. data/test_app/config/initializers/inflections.rb +10 -0
  38. data/test_app/config/initializers/load_connection_manager.rb +6 -0
  39. data/test_app/config/initializers/mime_types.rb +5 -0
  40. data/test_app/config/initializers/secret_token.rb +7 -0
  41. data/test_app/config/initializers/session_store.rb +8 -0
  42. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  43. data/test_app/config/locales/en.yml +5 -0
  44. data/test_app/config/routes.rb +58 -0
  45. data/test_app/db/migrate/20111127040654_create_fruits.rb +9 -0
  46. data/test_app/db/migrate/20111127040720_create_baskets.rb +9 -0
  47. data/test_app/db/migrate/20111127040846_create_fruit_baskets.rb +9 -0
  48. data/test_app/db/migrate/20111127040915_create_regions.rb +9 -0
  49. data/test_app/db/migrate/20111127060322_create_types.rb +9 -0
  50. data/test_app/db/schema.rb +16 -0
  51. data/test_app/db/seeds.rb +7 -0
  52. data/test_app/log/.gitkeep +0 -0
  53. data/test_app/script/rails +6 -0
  54. data/test_app/spec/connection_manager/associations_spec.rb +29 -0
  55. data/test_app/spec/connection_manager/connections_spec.rb +51 -0
  56. data/test_app/spec/connection_manager/replication_builder_spec.rb +33 -0
  57. data/test_app/spec/factories/baskets.rb +7 -0
  58. data/test_app/spec/factories/fruit_baskets.rb +8 -0
  59. data/test_app/spec/factories/fruits.rb +8 -0
  60. data/test_app/spec/factories/regions.rb +7 -0
  61. data/test_app/spec/factories/types.rb +7 -0
  62. data/test_app/spec/models/type_spec.rb +5 -0
  63. data/test_app/spec/spec.opts +4 -0
  64. data/test_app/spec/spec_helper.rb +42 -0
  65. data/test_app/test_app +0 -0
  66. metadata +158 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .DS_Store
5
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rails-connection_manager.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ activerecord-connection_manager (0.0.1)
5
+ activerecord (~> 3.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ZenTest (4.6.2)
11
+ activemodel (3.1.3)
12
+ activesupport (= 3.1.3)
13
+ builder (~> 3.0.0)
14
+ i18n (~> 0.6)
15
+ activerecord (3.1.3)
16
+ activemodel (= 3.1.3)
17
+ activesupport (= 3.1.3)
18
+ arel (~> 2.2.1)
19
+ tzinfo (~> 0.3.29)
20
+ activesupport (3.1.3)
21
+ multi_json (~> 1.0)
22
+ arel (2.2.1)
23
+ autotest (4.4.6)
24
+ ZenTest (>= 4.4.1)
25
+ builder (3.0.0)
26
+ diff-lcs (1.1.3)
27
+ i18n (0.6.0)
28
+ metaclass (0.0.1)
29
+ mocha (0.10.0)
30
+ metaclass (~> 0.0.1)
31
+ multi_json (1.0.3)
32
+ rspec (2.7.0)
33
+ rspec-core (~> 2.7.0)
34
+ rspec-expectations (~> 2.7.0)
35
+ rspec-mocks (~> 2.7.0)
36
+ rspec-core (2.7.1)
37
+ rspec-expectations (2.7.0)
38
+ diff-lcs (~> 1.1.2)
39
+ rspec-mocks (2.7.0)
40
+ tzinfo (0.3.31)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ activerecord-connection_manager!
47
+ autotest
48
+ mocha
49
+ rspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Joshua T. Mckinney
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # connection_manager
2
+ Replication and Multi-Database ActiveRecord add on.
3
+
4
+ ## Goals
5
+ * Take the lib I've been using finally make something out of it ;)
6
+ * Use connection classes instead of establish_connection on every model to ensure connection pooling
7
+ * Focus connection management at the model level
8
+ * Use the default database.yml as single point for all database configurations (no extra .yml files)
9
+ * When slave objects are used in html helpers like link_to and form_for the created urls that match the master
10
+
11
+ ## Installation
12
+
13
+ connection_manager is available through [Rubygems](https://rubygems.org/gems/connection_manager) and can be installed via:
14
+
15
+ $ gem install connection_manager
16
+
17
+ ## Rails 3 setup (Rails 2 untested at this time please let me know if it works for you )
18
+
19
+ connection_manager assumes the primary connection for the model is the master. For standard
20
+ models using the default connection this means the main rails database connection is the master.
21
+
22
+ Example database.yml
23
+
24
+ common: &common
25
+ adapter: mysql2
26
+ username: root
27
+ password: *****
28
+ database_timezone: local
29
+ pool: 100
30
+ connect_timeout: 20
31
+ timeout: 900
32
+ socket: /tmp/mysql.sock
33
+
34
+ development:
35
+ <<: *common
36
+ database: test_app
37
+
38
+ slave_1_test_app_development:
39
+ <<: *common
40
+ database: portal_production
41
+
42
+ slave_2_test_app_development:
43
+ <<: *common
44
+ database: test_app
45
+
46
+ user_data_development
47
+ <<: *common
48
+ database: user_data
49
+
50
+ slave_1_user_data_development
51
+ <<: *common
52
+ database: user_data
53
+
54
+ slave_2_user_data_development
55
+ <<: *common
56
+ database: user_data
57
+
58
+ In the above database.yml the Master databases are listed as "development" and "user_data_development".
59
+ As you can see the replication database name follow a strict standard for the connection names.
60
+ For slave_1_test_app_development, "slave" is the name of the replication, "1" is the count, "test_app"
61
+ is the databases name and finally the "development" is the environment. (Of course in your database.yml
62
+ each slave would have a different connection to is replication :)
63
+
64
+
65
+ ### Setup Multiple Databases
66
+
67
+ At startup connection_manager builds connection classes to ConnectionManager::Connections
68
+ using the connections described in your database.yml based on the current rails environment.
69
+
70
+ You can use a different master by having the model inherit from one of your ConnectionManager::Connections.
71
+
72
+ To view your ConnectionManager::Connections, at the Rails console type:
73
+
74
+ ruby-1.9.2-p290 :001 > ConnectionManager::Connections.all => ["TestAppConnection", "Slave1TestAppConnection", "Slave2TestAppConnection"]
75
+
76
+ If your using the example database.yml your array would look like this:
77
+ ["TestAppConnection", "Slave1TestAppConnection", "Slave2TestAppConnection",
78
+ "UserDataConnection", "Slave1UserDataConnection", "Slave2UserDataConnection"]
79
+
80
+
81
+
82
+ To use one of your ConnectionManager::Connections for your models default/master database
83
+ setup you model like the following
84
+
85
+ class User < ConnectionManager::Connections::UserDataConnection
86
+ # model code ...
87
+ end
88
+
89
+ ### Replication
90
+
91
+ simply add 'replicated' to you model beneath any defined associations
92
+
93
+ class User < ConnectionManager::Connections::UserDataConnection
94
+ has_one :job
95
+ has_many :teams
96
+ replicated # implement replication
97
+ # model code ...
98
+ end
99
+
100
+ The replicated method addeds subclass whose names match the replication connection name and count.
101
+ Based on the above example database.yml User class would now have User::Slave1 and User::Slave2.
102
+
103
+ You can treat your subclass like normal activerecord objects.
104
+ User::Slave1.first => returns results from slave_1_use_data_development
105
+ User::Slave2.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_2_use_data_development
106
+
107
+ For a more elegant implementation, connection_manager also add class methods to you main model following the
108
+ same naming standard as the subclass creation.
109
+ User.slave_1.first => returns results from slave_1_use_data_development
110
+ User.slave_2.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_2_use_data_development
111
+
112
+ Finally connection_manager creates an addional class method that shifts through your
113
+ available slave connections each time it is called using a different connection on each action.
114
+ User.slave.first => returns results from slave_1_use_data_development
115
+ User.slave.last => => returns results from slave_2_use_data_development
116
+ User.slave.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_1_use_data_development
117
+ User.slave.where(['created_at BETWEEN ? and ?',Time.now - 5.days, Time.now]).all => returns results from slave_2_use_data_development
118
+
119
+ ## TODO's
120
+ * add more to readme
121
+ * more specs
122
+ * sharding
123
+
124
+ ## Other activerecord Connection gems
125
+ * [Octopus](https://github.com/tchandy/octopus)
126
+
127
+ ## Contributing to connection_manager
128
+
129
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
130
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
131
+ * Fork the project
132
+ * Start a feature/bugfix branch
133
+ * Commit and push until you are happy with your contribution
134
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
135
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "connection_manager/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "connection_manager"
7
+ s.version = ConnectionManager::VERSION
8
+ s.authors = ["Joshua Mckinney"]
9
+ s.email = ["joshmckin@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Simplifies connecting to Muliple and Replication databases with rails and active_record}
12
+ s.description = %q{Simplifies connecting to Muliple and Replication databases with rails and active_record}
13
+
14
+ s.rubyforge_project = "connection_manager"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_runtime_dependency 'activerecord', '~> 3.0'
21
+ s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'autotest'
23
+ s.add_development_dependency 'mocha'
24
+ end
@@ -0,0 +1,9 @@
1
+ require "connection_manager/version"
2
+
3
+ module ConnectionManager
4
+ require 'connection_manager/connections'
5
+ require 'connection_manager/associations'
6
+ require 'connection_manager/replication_builder'
7
+ require 'connection_manager/connection_manager_railtie.rb' if defined?(Rails)
8
+ end
9
+
@@ -0,0 +1,29 @@
1
+ module ConnectionManager
2
+ module Associations
3
+ @defined_associations
4
+
5
+ # Stores defined associtions and their options
6
+ def defined_associations
7
+ @defined_associations ||= {}
8
+ end
9
+
10
+ def belongs_to(*options)
11
+ defined_associations[:belongs_to] ||= []
12
+ defined_associations[:belongs_to] << options
13
+ super
14
+ end
15
+
16
+ def has_many(*options)
17
+ defined_associations[:has_many] ||= []
18
+ defined_associations[:has_many] << options
19
+ super
20
+ end
21
+
22
+ def has_one(*options)
23
+ defined_associations[:has_one] ||= []
24
+ defined_associations[:has_one] << options
25
+ super
26
+ end
27
+ end
28
+ end
29
+ ActiveRecord::Base.extend(ConnectionManager::Associations)
@@ -0,0 +1,9 @@
1
+ module ConnectionManager
2
+ class ConnectionManagerRailtie < ::Rails::Railtie
3
+ initializer "connection_manager.setup" do |app|
4
+
5
+ ConnectionManager::Connections.initialize
6
+ require 'connection_manager/connections'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,110 @@
1
+ module ConnectionManager
2
+ class Connections
3
+ class << self
4
+ @connection_keys
5
+ @all
6
+ @replication_connections
7
+ @env
8
+
9
+ def env
10
+ @env ||= fetch_env
11
+ @env
12
+ end
13
+
14
+ def env=env
15
+ @env = env
16
+ end
17
+
18
+ # Get the current Rails environment if defined
19
+ # TODO add sinatra
20
+ def fetch_env
21
+ Rails.env if defined?(Rails)
22
+ end
23
+
24
+ # Grab only thoses connections that correspond to the current env. If env
25
+ # is blank it grabs all the connection keys
26
+ def connection_keys
27
+ @connection_keys ||= ActiveRecord::Base.configurations.keys.
28
+ select{|n| n.match(Regexp.new("(#{env}$)"))}
29
+ end
30
+
31
+ # Contains all the connection classes built
32
+ def all
33
+ @all ||= []
34
+ end
35
+
36
+ # Holds connections
37
+ def replication_connections
38
+ @replication_connections ||= {}
39
+ end
40
+
41
+ # Returns the database value given a connection key from the database.yml
42
+ def database_name_from_yml(name_from_yml)
43
+ ActiveRecord::Base.configurations[name_from_yml]['database'].to_s
44
+ end
45
+
46
+ # Given an connection key name from the database.yml, returns the string
47
+ # equivelent of the class name for that entry.
48
+ def connection_class_name(name_from_yml)
49
+ name_from_yml = name_from_yml #clean up sqlite database names
50
+ new_class_name = name_from_yml.gsub(Regexp.new("#{env}$"),'')
51
+ new_class_name = database_name_from_yml(name_from_yml) if new_class_name.blank?
52
+
53
+ #cleanup sqlite database names
54
+ if new_class_name.gsub!(/(^db\/)|(\.sqlite3$)/,'')
55
+ new_class_name.gsub!(Regexp.new("(\\_#{env}$)"),'')
56
+ end
57
+
58
+ new_class_name = new_class_name.gsub(/\_/,' ').titleize.gsub(/ /,'')
59
+ new_class_name << "Connection"
60
+ new_class_name
61
+ end
62
+
63
+
64
+
65
+ def add_replication_connection(name_from_yml,new_connection)
66
+ rep_name = name_from_yml.split("_")[0]
67
+ db_name = database_name_from_yml(name_from_yml)
68
+ if db_name.gsub!(/(^db\/)|(\.sqlite3$)/,'')
69
+ db_name.gsub!(Regexp.new("(\\_#{env}$)"),'')
70
+ end
71
+ rep_name << "_#{db_name}"
72
+ rep_name = rep_name.to_sym
73
+ replication_connections[rep_name] ||= []
74
+ replication_connections[rep_name] << new_connection
75
+ replication_connections
76
+ end
77
+
78
+
79
+ def connections_for_replication(rep_collection_key)
80
+ replication_connections[rep_collection_key.to_sym]
81
+ end
82
+
83
+ # Sets class instance attributes, then builds connection classes, while populating
84
+ # available_connctions and replication_connection
85
+ def initialize(options={})
86
+ options.each do |k,v|
87
+ send("#{k.to_s}=",v)
88
+ end
89
+ connection_keys.each do |connection|
90
+ new_connection = connection_class_name(connection)
91
+ add_replication_connection(connection,new_connection)
92
+ build_connection_class(new_connection,connection)
93
+ end
94
+ all
95
+ end
96
+
97
+ # Addes a conneciton subclass to AvailableConnections using the supplied
98
+ # class name and connection key from database.yml
99
+ def build_connection_class(class_name,connection_key)
100
+ class_eval <<-STR, __FILE__, __LINE__
101
+ class #{class_name} < ActiveRecord::Base
102
+ self.abstract_class = true
103
+ establish_connection("#{connection_key}")
104
+ end
105
+ STR
106
+ all << class_name
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,135 @@
1
+ module ConnectionManager
2
+ module ReplicationBuilder
3
+
4
+ def database_name(db_name=nil)
5
+ db_name = "#{connection.instance_variable_get(:@config)[:database].to_s}" if db_name.blank?
6
+ db_name.gsub!(/(\.sqlite3$)/,'')
7
+ db_name = db_name.split("/").last
8
+ db_name
9
+ end
10
+
11
+ def replication_association_options(method,association,class_name,options={})
12
+ new_options = {}.merge(options)
13
+ if new_options[:class_name].blank?
14
+ new_options[:class_name] = "#{association.to_s.singularize.classify}::#{class_name}"
15
+ else
16
+ new_options[:class_name] = "#{new_options[:class_name]}::#{class_name}"
17
+ end
18
+
19
+ if [:has_one,:has_many].include?(method) && new_options[:foreign_key].blank?
20
+ new_options[:foreign_key] = "#{table_name.singularize}_id"
21
+ end
22
+ new_options
23
+ end
24
+
25
+ def build_replication_associations(class_name)
26
+ str = ""
27
+ defined_associations.each do |method,defs|
28
+ unless defs.blank?
29
+ defs.each do |association,options|
30
+ options = {} if options.blank?
31
+ unless options[:no_readonly] || options[:class_name].to_s.match("::#{class_name}")
32
+ str << "#{method.to_s} :#{association}, #{replication_association_options(method,association,class_name,options)};"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ str
38
+ end
39
+
40
+ def replication_connection_classes(options)
41
+ if options[:using] && options[:using].is_a?(Array)
42
+ connection_classes = options[:using].collection{|c| Connections.connection_class_name(c)}
43
+ else
44
+ connection_classes = Connections.connections_for_replication("#{options[:name].to_s}_#{database_name}")
45
+ end
46
+ connection_classes
47
+ end
48
+
49
+ # Adds subclass with the class name of the type provided in the options, which
50
+ # defaults to 'slave' if blank, that uses the connection from a connection class.
51
+ # If :database option is blank?, replicated will assume the database.yml has
52
+ # slave connections defined as: slave_database_name_test or slave_1_database_name_test,
53
+ # where slave_1 is the replication instance, 'database_name' is the actual
54
+ # name of the database and '_test' is the Rails environment
55
+ def replicated(*settings)
56
+ options = {:name => "slave", :readonly => true}.merge(settings.extract_options!)
57
+ connection_classes = replication_connection_classes(options)
58
+ if connection_classes.blank?
59
+ raise ArgumentError, " a replication connection was not found. Check your database.yml."
60
+ else
61
+ connection_methods = []
62
+ connection_classes.each do |c|
63
+ under_scored = c.underscore
64
+ method_name = under_scored.split("_")[0]
65
+ method_name = method_name.insert(method_name.index(/\d/),"_")
66
+ class_name = method_name.classify
67
+ connection_methods << method_name.to_sym
68
+ build_replication_class(class_name,c,options)
69
+ build_single_replication_method(method_name,class_name)
70
+ end
71
+ end
72
+ build_full_replication_method(options,connection_methods)
73
+
74
+ end
75
+
76
+ # Creats a subclass that inherets from the model. The default model_name
77
+ # class method is overriden to return the super's name, which ensures rails
78
+ # helpers like link_to called on a replication stance generate a url for the
79
+ # master database. If options include readonly, build_replication_class also
80
+ # overrides the rails "readonly?" method to ensure saves are prevented.
81
+ # Replication class can be called directly for operaitons.
82
+ # Usage:
83
+ # => User::Slave1.where(:id => 1).first => returns results from slave_1 database
84
+ # => User::Slave2.where(:id => 2).first => returns results from slave_1 database
85
+ def build_replication_class(class_name,connection_name,options)
86
+ class_eval <<-STR, __FILE__, __LINE__
87
+ class #{class_name} < #{self.name}
88
+ #{build_replication_associations(class_name)}
89
+ class << self
90
+ delegate :connection, :to => Connections::#{connection_name}
91
+ def model_name
92
+ @_model_name ||= ActiveModel::Name.new(#{model_name})
93
+ end
94
+ end
95
+ #{'def readonly?; true; end;' if (options[:name] == "readonly" || options[:readonly])}
96
+ end
97
+ STR
98
+ end
99
+
100
+ # Adds as class method to call a specific replication conneciton.
101
+ # Usage:
102
+ # => User.slave_1.where(:id => 2).first => returns results from slave_1 database
103
+ # => User.slave_2.where(:id => 2).first => returns results from slave_1 database
104
+ def build_single_replication_method(method_name,class_name)
105
+ class_eval <<-STR, __FILE__, __LINE__
106
+ class << self
107
+ def #{method_name}
108
+ self::#{class_name}
109
+ end
110
+ end
111
+ STR
112
+ end
113
+
114
+ # add a class method that shifts through available connections methods
115
+ # on each call.
116
+ # Usage:
117
+ # => User.slave.where(:id => 2).first => can return results from slave_1 or slave_2
118
+ def build_full_replication_method(options,connection_methods)
119
+ class_eval <<-STR, __FILE__, __LINE__
120
+ @connection_methods = #{connection_methods}
121
+ class << self
122
+ def #{options[:name].to_s}
123
+ current = @connection_methods.shift
124
+ @connection_methods << current
125
+ send(current)
126
+ end
127
+ def connection_methods
128
+ @connection_methods
129
+ end
130
+ end
131
+ STR
132
+ end
133
+ end
134
+ end
135
+ ActiveRecord::Base.extend(ConnectionManager::ReplicationBuilder)