connection_manager 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -1
- data/Gemfile +0 -1
- data/LICENSE.txt +1 -1
- data/README.md +115 -55
- data/Rakefile +2 -0
- data/connection_manager.gemspec +9 -3
- data/lib/connection_manager/connection_builder.rb +81 -0
- data/lib/connection_manager/connection_manager_railtie.rb +4 -3
- data/lib/connection_manager/helpers/abstract_adapter_helper.rb +40 -0
- data/lib/connection_manager/helpers/connection_helpers.rb +105 -0
- data/lib/connection_manager/{cross_schema_patch.rb → patches/cross_schema_patch.rb} +2 -3
- data/lib/connection_manager/replication.rb +165 -0
- data/lib/connection_manager/shards.rb +25 -0
- data/lib/connection_manager/using.rb +95 -0
- data/lib/connection_manager/version.rb +1 -1
- data/lib/connection_manager.rb +19 -8
- data/spec/factories.rb +7 -11
- data/spec/helpers/database_spec_helper.rb +52 -41
- data/spec/helpers/models_spec_helper.rb +23 -0
- data/spec/jdbcmysql_database.yml +55 -0
- data/spec/lib/connection_builder_spec.rb +29 -0
- data/spec/lib/connection_helpers_spec.rb +74 -0
- data/spec/lib/replication_spec.rb +134 -0
- data/spec/lib/shards_spec.rb +40 -0
- data/spec/lib/using_spec.rb +77 -0
- data/spec/mysql2_database.yml +28 -2
- data/spec/spec_helper.rb +20 -6
- metadata +42 -39
- data/Gemfile.lock +0 -56
- data/lib/connection_manager/associations.rb +0 -28
- data/lib/connection_manager/connections.rb +0 -124
- data/lib/connection_manager/method_recorder.rb +0 -57
- data/lib/connection_manager/secondary_connection_builder.rb +0 -180
- data/spec/integration/secondary_connection_builder_spec.rb +0 -115
- data/spec/lib/associations_spec.rb +0 -30
- data/spec/lib/connections_spec.rb +0 -92
- data/spec/lib/method_recorder_spec.rb +0 -43
- data/spec/lib/secondary_connection_builder_spec.rb +0 -77
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
# ConnectionManager
|
2
|
-
Replication and
|
2
|
+
Multi-Database, Replication and Sharding for ActiveRecord.
|
3
3
|
|
4
|
-
##
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
## Background
|
5
|
+
ActiveRecord, for quite some time now, has supported multiple database connections
|
6
|
+
through the use of #establish_connection and connection classes [more info](http://api.rubyonrails.org/classes/ActiveRecord/Base.html)
|
7
|
+
Multiple databases, replication and shards can be implemented directly in rails without
|
8
|
+
patching, but a gem helps to reduce redundant code and ensure consistency.
|
9
|
+
ConnectionManager replaces all the connection classes and subclasses required
|
10
|
+
for multiple database support in Rails with a few class methods and simple
|
11
|
+
database.yml configuration. Since ConnectionManager does not alter
|
12
|
+
ActiveRecord's connection pool, thread safety is not a concern.
|
13
|
+
|
14
|
+
## Upgrading to 0.3
|
15
|
+
|
16
|
+
0.3 is a complete overhaul and will cause compatibility issues for folks who upgrade using the previous replication setup.
|
17
|
+
Fortunately, for most folks the only change they have to do is specify the their slaves
|
18
|
+
and masters in the database.yml and set build_connection_class to true to have
|
19
|
+
ActiveRecord build their connection classes. See the example database.yml below.
|
10
20
|
|
11
21
|
## Installation
|
12
22
|
|
@@ -16,119 +26,169 @@ ConnectionManager is available through [Rubygems](https://rubygems.org/gems/conn
|
|
16
26
|
|
17
27
|
## Rails 3 setup (No Rails 2 at this time)
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
Add connection_manager to you gemfile:
|
30
|
+
|
31
|
+
gem 'connection_manager'
|
21
32
|
|
22
|
-
|
33
|
+
Run bundle install:
|
34
|
+
|
35
|
+
bundle install
|
36
|
+
|
37
|
+
### Example database.yml
|
23
38
|
|
24
39
|
common: &common
|
25
40
|
adapter: mysql2
|
26
41
|
username: root
|
27
42
|
password: *****
|
28
|
-
|
29
|
-
pool: 100
|
43
|
+
pool: 20
|
30
44
|
connect_timeout: 20
|
31
45
|
timeout: 900
|
32
46
|
socket: /tmp/mysql.sock
|
47
|
+
build_connection_class: true
|
33
48
|
|
34
49
|
development:
|
35
50
|
<<: *common
|
36
51
|
database: test_app
|
52
|
+
slaves: [slave_1_test_app_development, slave_2_test_app_development]
|
37
53
|
|
38
54
|
slave_1_test_app_development:
|
39
55
|
<<: *common
|
40
56
|
database: test_app
|
57
|
+
readonly: true
|
41
58
|
|
42
59
|
slave_2_test_app_development:
|
43
60
|
<<: *common
|
44
61
|
database: test_app
|
62
|
+
readonly: true
|
45
63
|
|
46
64
|
user_data_development
|
47
65
|
<<: *common
|
48
66
|
database: user_data
|
67
|
+
slaves: [slave_1_user_data_development, slave_2_user_data_development]
|
49
68
|
|
50
69
|
slave_1_user_data_development
|
51
70
|
<<: *common
|
52
71
|
database: user_data
|
72
|
+
readonly: true
|
53
73
|
|
54
74
|
slave_2_user_data_development
|
55
75
|
<<: *common
|
56
76
|
database: user_data
|
77
|
+
readonly: true
|
57
78
|
|
58
79
|
In the above database.yml the Master databases are listed as "development" and "user_data_development".
|
59
|
-
|
60
|
-
|
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 :)
|
80
|
+
Replication databases are defined as normally connections and are added to the 'replications:' option for
|
81
|
+
their master. The readonly option ensures all ActiveRecord objects returned from this connection are ALWAYS readonly.
|
63
82
|
|
64
83
|
|
65
|
-
##
|
84
|
+
## Building Connection Classes
|
66
85
|
|
67
|
-
|
68
|
-
|
86
|
+
### Manually
|
87
|
+
ConnectionManager provides establish_managed_connection for build connection
|
88
|
+
classes and connection to multiple databases.
|
69
89
|
|
70
|
-
|
90
|
+
class MyConnection < ActiveRecord::Base
|
91
|
+
establish_managed_connection("my_database_#{Rails.env}", :readonly => true)
|
92
|
+
end
|
93
|
+
|
94
|
+
class User < MyConnection
|
95
|
+
end
|
96
|
+
|
97
|
+
MyConnection => MyConnection(abstract)
|
98
|
+
@user = User.first
|
99
|
+
@user.readonly? => true
|
71
100
|
|
72
|
-
|
101
|
+
The establish_managed_connection method, runs establish_connection with the supplied
|
102
|
+
database.yml key, sets abstract_class to true, and (since :readonly is set to true) ensures
|
103
|
+
all ActiveRecord objects build using this connection class are readonly. If readonly is set
|
104
|
+
to true in the database.yml, passing the readonly option is not necessary.
|
73
105
|
|
74
|
-
ConnectionManager::Connections.all => ["TestAppConnection", "Slave1TestAppConnection", "Slave2TestAppConnection"]
|
75
106
|
|
76
|
-
|
77
|
-
|
78
|
-
|
107
|
+
### Automatically
|
108
|
+
ActiveRecord can build all your connection classes for you.
|
109
|
+
The connection class names will be based on the database.yml keys.ActiveRecord will
|
110
|
+
build connection classes for all the entries in the database.yml where
|
111
|
+
"build_connection_class" is true, and match the current environment settings
|
79
112
|
|
113
|
+
## Using
|
80
114
|
|
81
|
-
|
82
|
-
|
115
|
+
The using method allows you specify the connection class to use
|
116
|
+
for query. The return objects will have the correct model name, but the instance's
|
117
|
+
class's superclass will be the connection class and all database actions performed
|
118
|
+
on the instance will use the connection class's connection.
|
83
119
|
|
84
|
-
|
85
|
-
|
86
|
-
|
120
|
+
User.using("Slave1Connection").first
|
121
|
+
|
122
|
+
search = User.where(disabled => true)
|
123
|
+
@legacy_users = search.using("Shard1Connection").all #=> [<User::Shard1ConnectionDup...>,<User::Shard1ConnectionDup..]
|
124
|
+
@legacy_users.first.save #=> uses the Shard1Connection connection
|
125
|
+
|
126
|
+
@new_users = search.page(params[:page]).all => [<User...>,<User...>]
|
87
127
|
|
88
128
|
## Replication
|
89
129
|
|
90
|
-
Simply add 'replicated' to your model
|
130
|
+
Simply add 'replicated' to your model.
|
91
131
|
|
92
|
-
class User <
|
132
|
+
class User < UserDataConnection
|
93
133
|
has_one :job
|
94
134
|
has_many :teams
|
95
135
|
replicated # implement replication
|
96
136
|
# model code ...
|
97
137
|
end
|
98
138
|
|
99
|
-
The replicated method
|
100
|
-
|
139
|
+
The replicated method builds models who inherit from the main model.
|
140
|
+
User::Slave1UserDataConnectionDup.superclass => Slave1UserDataConnection(abstract)
|
141
|
+
User::Slave1UserDataDup.first => returns results from slave_1_user_data_development
|
142
|
+
User::Slave2UserDataDup.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_2_user_data_development
|
101
143
|
|
102
|
-
|
144
|
+
Finally, ConnectionManager creates an additional class method that shifts through your
|
145
|
+
available slave connections each time it is called using a different connection on each action.
|
103
146
|
|
104
|
-
User
|
105
|
-
User
|
147
|
+
User.slaves.first => returns results from slave_1_use_data_development
|
148
|
+
User.slaves.last => => returns results from slave_2_use_data_development
|
149
|
+
User.slaves.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_1_user_data_development
|
150
|
+
User.slaves.where(['created_at BETWEEN ? and ?',Time.now - 5.days, Time.now]).all => returns results from slave_2_user_data_development
|
106
151
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
User.slave_1.first => returns results from slave_1_user_data_development
|
111
|
-
User.slave_2.where(['created_at BETWEEN ? and ?',Time.now - 3.hours, Time.now]).all => returns results from slave_2_user_data_development
|
152
|
+
Replicated defaults to the slaves replication type,so if you have only masters and a combination
|
153
|
+
of masters and slaves for replication, you have set the replication type to masters
|
112
154
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
155
|
+
class User < UserDataConnection
|
156
|
+
replicated #slaves replication
|
157
|
+
replicated :type => :masters, :name => 'masters' # masters replication
|
158
|
+
end
|
159
|
+
|
160
|
+
## Sharding
|
161
|
+
|
162
|
+
After tinkering with some solutions for shards, I've come to a similar conclusion as [DataFabric] (https://github.com/mperham/data_fabric):
|
163
|
+
"Sharding should be implemented at the application level". The #shards method is very basic and
|
164
|
+
while it may be useful to most folks, it should really serve as an example of a possible solutions
|
165
|
+
to your shard requirements.
|
166
|
+
|
167
|
+
class LegacyUser < UserShardConnection
|
168
|
+
end
|
169
|
+
|
170
|
+
class User < ActiveRecord::Base
|
171
|
+
self.shard_class_names = ["LegacyUser"]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Calls the supplied block on all the shards available to User, including the User model itself.
|
175
|
+
User.shards{ |shard| shard.where(:user_name => "some_user").all} => [<User ...>,<LegacyUser ...>]
|
176
|
+
|
177
|
+
## Migrations
|
178
|
+
|
179
|
+
Nothing implement now to help but there are lots of potential solutions [here] (http://stackoverflow.com/questions/1404620/using-rails-migration-on-different-database-than-standard-production-or-devel)
|
120
180
|
|
121
|
-
##
|
122
|
-
*
|
123
|
-
* cross schema joins - 2.2 AS BETA tested with Mysql2 ONLY
|
181
|
+
## TODOs
|
182
|
+
* Maybe add migration support for Rails AR implementations.
|
124
183
|
|
125
|
-
## Other
|
184
|
+
## Other ActiveRecord Connection gems
|
185
|
+
* [DataFabric] (https://github.com/mperham/data_fabric)
|
126
186
|
* [Octopus](https://github.com/tchandy/octopus)
|
127
187
|
|
128
188
|
## Contributing to ConnectionManager
|
129
189
|
|
130
|
-
* Check out the latest master to make sure the feature
|
131
|
-
* Check out the issue tracker to make sure someone already
|
190
|
+
* Check out the latest master to make sure the feature has not been implemented or the bug hasn't been fixed yet
|
191
|
+
* Check out the issue tracker to make sure someone already has not requested it and/or contributed it
|
132
192
|
* Fork the project
|
133
193
|
* Start a feature/bugfix branch
|
134
194
|
* Commit and push until you are happy with your contribution
|
data/Rakefile
CHANGED
data/connection_manager.gemspec
CHANGED
@@ -18,10 +18,16 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
s.add_runtime_dependency 'activerecord', '~> 3.0'
|
21
|
-
s.add_development_dependency 'sqlite3'
|
22
21
|
s.add_development_dependency 'rspec'
|
23
22
|
s.add_development_dependency 'autotest'
|
24
23
|
s.add_development_dependency 'mocha'
|
25
24
|
s.add_development_dependency 'factory_girl'
|
26
|
-
s.add_development_dependency '
|
27
|
-
|
25
|
+
s.add_development_dependency 'activesupport', '~> 3.0'
|
26
|
+
|
27
|
+
if(defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
28
|
+
s.add_development_dependency 'jruby-openssl'
|
29
|
+
s.add_development_dependency 'activerecord-jdbcmysql-adapter'
|
30
|
+
else
|
31
|
+
s.add_development_dependency "mysql2"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
module ConnectionManager
|
3
|
+
module ConnectionBuilder
|
4
|
+
|
5
|
+
# Get the current environment if defined
|
6
|
+
# Check for Rails, check for RACK_ENV, default to 'development'
|
7
|
+
def ar_env
|
8
|
+
return Rails.env if defined?(Rails)
|
9
|
+
return RACK_ENV if defined?(RACK_ENV)
|
10
|
+
return ENV["AR_ENV"] if ENV["AR_ENV"]
|
11
|
+
"development"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Grab only thoses connections that correspond to the current env. If env
|
15
|
+
# is blank it grabs all the connection keys
|
16
|
+
#
|
17
|
+
# If you current environment valid database keys can be:
|
18
|
+
# * development
|
19
|
+
# * other_database_development
|
20
|
+
# * slave_database_development
|
21
|
+
def configuration_keys
|
22
|
+
ActiveRecord::Base.configurations.keys.select{|n| n.match(ar_env_regex)}
|
23
|
+
end
|
24
|
+
|
25
|
+
def database_yml_attributes(name_from_yml)
|
26
|
+
found = ActiveRecord::Base.configurations[name_from_yml]
|
27
|
+
ActiveRecord::Base.configurations[name_from_yml].symbolize_keys if found
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns currently loaded configuations where :build_connection is true
|
31
|
+
def database_keys_for_auto_build
|
32
|
+
ab_configs = []
|
33
|
+
configuration_keys.each do |key|
|
34
|
+
ab_configs << key if (database_yml_attributes(key)[:build_connection_class] == true)
|
35
|
+
end
|
36
|
+
ab_configs
|
37
|
+
end
|
38
|
+
|
39
|
+
# Builds connection classes using the database keys provided; expects an array.
|
40
|
+
def build_connection_classes(database_keys_to_use=database_keys_for_auto_build)
|
41
|
+
database_keys_to_use.each do |key|
|
42
|
+
build_connection_class(connection_class_name(key),key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Build connection classes base on the supplied class name and connection
|
47
|
+
# key from database.yml
|
48
|
+
def build_connection_class(class_name,connection_key)
|
49
|
+
begin
|
50
|
+
class_name.constantize
|
51
|
+
rescue NameError
|
52
|
+
klass = Class.new(ActiveRecord::Base)
|
53
|
+
new_connection_class = Object.const_set(class_name, klass)
|
54
|
+
new_connection_class.establish_managed_connection(connection_key)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def ar_env_regex
|
60
|
+
return @ar_env_regex if @ar_env_regex
|
61
|
+
s = "#{ar_env}$"
|
62
|
+
@ar_env_regex = Regexp.new("(#{s})")
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a string to be used for the class name. Removes the current env.
|
66
|
+
def clean_yml_key(name)
|
67
|
+
new_name = "#{name}".gsub(ar_env_regex,'')
|
68
|
+
new_name = "Base"if new_name.blank?
|
69
|
+
new_name.gsub(/\_$/,'')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Given an connection key name from the database.yml, returns the string
|
73
|
+
# equivelent of the class name for that entry.
|
74
|
+
def connection_class_name(name_from_yml)
|
75
|
+
new_class_name = clean_yml_key(name_from_yml)
|
76
|
+
new_class_name = new_class_name.gsub(/\_/,' ').titleize.gsub(/ /,'')
|
77
|
+
new_class_name << "Connection"
|
78
|
+
new_class_name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module ConnectionManager
|
2
2
|
class ConnectionManagerRailtie < ::Rails::Railtie
|
3
|
-
initializer "connection_manager.
|
4
|
-
|
3
|
+
initializer "connection_manager.build_connection_classes" do
|
4
|
+
ActiveRecord::Base.build_connection_classes
|
5
5
|
end
|
6
6
|
end
|
7
|
-
end
|
7
|
+
end
|
8
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ConnectionManager
|
2
|
+
module AbstractAdapterHelper
|
3
|
+
def config
|
4
|
+
@config
|
5
|
+
end
|
6
|
+
|
7
|
+
def using_em_adapter?
|
8
|
+
(config[:adapter].match(/^em\_/) && defined?(EM) && EM::reactor_running?)
|
9
|
+
end
|
10
|
+
|
11
|
+
def readonly?
|
12
|
+
(config[:readonly] == true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def replicated?
|
16
|
+
(!slave_keys.blank? || !master_keys.blank?)
|
17
|
+
end
|
18
|
+
|
19
|
+
def database_name
|
20
|
+
config[:database]
|
21
|
+
end
|
22
|
+
|
23
|
+
def replication_keys(type=:slaves)
|
24
|
+
return slave_keys if type == :slaves
|
25
|
+
master_keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def slave_keys
|
29
|
+
slave_keys = []
|
30
|
+
slave_keys = config[:slaves].collect{|r| r.to_sym} if config[:slaves]
|
31
|
+
slave_keys
|
32
|
+
end
|
33
|
+
|
34
|
+
def master_keys
|
35
|
+
master_keys = []
|
36
|
+
master_keys = config[:masters].collect{|r| r.to_sym} if config[:masters]
|
37
|
+
master_keys
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
module ConnectionManager
|
3
|
+
module ConnectionHelpers
|
4
|
+
@@managed_connections = HashWithIndifferentAccess.new
|
5
|
+
|
6
|
+
# Returns the database_name of the connection unless set otherwise
|
7
|
+
def database_name
|
8
|
+
@database_name = "#{connection.database_name.to_s}" unless @database_name
|
9
|
+
@database_name
|
10
|
+
end
|
11
|
+
|
12
|
+
alias :schema_name :database_name
|
13
|
+
|
14
|
+
# Returns true if this is a readonly only a readonly model
|
15
|
+
# If the connection.readonly? then the model that uses the connection
|
16
|
+
# must be readonly.
|
17
|
+
def readonly?
|
18
|
+
((@readonly == true)||connection.readonly?)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Allow setting of readonly at the model level
|
22
|
+
def readonly=readonly
|
23
|
+
@readonly = readonly
|
24
|
+
end
|
25
|
+
|
26
|
+
# A place to store managed connections
|
27
|
+
def managed_connections
|
28
|
+
@@managed_connections
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_managed_connections(yml_key,value)
|
32
|
+
@@managed_connections[yml_key] ||= []
|
33
|
+
@@managed_connections[yml_key] << value unless @@managed_connections[yml_key].include?(value)
|
34
|
+
@@managed_connections
|
35
|
+
end
|
36
|
+
|
37
|
+
def managed_connection_classes
|
38
|
+
managed_connections.values.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
def yml_key
|
42
|
+
@yml_key
|
43
|
+
end
|
44
|
+
|
45
|
+
# Tell Active Record to use a different database/schema on this model.
|
46
|
+
# You may call #use_database when your schemas reside on the same database server
|
47
|
+
# and you do not want to create extra connection class and database.yml entries.
|
48
|
+
#
|
49
|
+
# Options:
|
50
|
+
# * :table_name_prefix - the prefix required for making cross database/schema
|
51
|
+
# joins for you database managmenet system. By default table_name_prefix is the
|
52
|
+
# database/schema name followed by a period EX: "my_database."
|
53
|
+
# * :table_name - the table name for the model if it does not match ActiveRecord
|
54
|
+
# naming conventions
|
55
|
+
#
|
56
|
+
# EX: class LegacyUser < ActiveRecord::Base
|
57
|
+
# use_database('DBUser', :table_name => 'UserData')
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# LegacyUser.limit(1).to_sql => "SELECT * FROM `BDUser`.`UserData` LIMIT 1
|
61
|
+
#
|
62
|
+
def use_database(database_name,opts={})
|
63
|
+
@database_name = database_name
|
64
|
+
opts[:table_name_prefix] ||= "#{database_name}."
|
65
|
+
opts[:table_name] ||= self.table_name.to_s.split('.').last
|
66
|
+
self.table_name = opts[:table_name]
|
67
|
+
self.table_name_prefix = opts[:table_name_prefix]
|
68
|
+
end
|
69
|
+
|
70
|
+
alias :use_schema :use_database
|
71
|
+
|
72
|
+
# Establishes and checks in a connection, noramlly for abstract classes aka connection classes.
|
73
|
+
#
|
74
|
+
# Options:
|
75
|
+
# * :abstract_class - used the set #abstract_class, default is true
|
76
|
+
# * :readonly - force all instances to readonly
|
77
|
+
# * :class_name - name of connection class name, default is current class's name
|
78
|
+
# * :table_name_prefix - prefix to append to table name for cross database joins,
|
79
|
+
# default is the "#{self.database_name}."
|
80
|
+
# EX:
|
81
|
+
# class MyConnection < ActiveRecord::Base
|
82
|
+
# establish_managed_connection :key_from_db_yml,{:readonly => true}
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
def establish_managed_connection(yml_key,opts={})
|
86
|
+
@yml_key = yml_key
|
87
|
+
opts = {:class_name => self.name,
|
88
|
+
:abstract_class => true}.merge(opts)
|
89
|
+
establish_connection(yml_key)
|
90
|
+
self.abstract_class = opts[:abstract_class]
|
91
|
+
set_to_readonly if (readonly? || opts[:readonly] || self.connection.readonly?)
|
92
|
+
add_managed_connections(yml_key,opts[:class_name])
|
93
|
+
use_database(self.database_name,opts)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Override ActiveRecord::Base instance method readonly? to force
|
97
|
+
# readonly connections.
|
98
|
+
def set_to_readonly
|
99
|
+
self.readonly = true
|
100
|
+
define_method(:readonly?) do
|
101
|
+
true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -5,8 +5,7 @@ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
7
7
|
class Mysql2Adapter < AbstractAdapter
|
8
|
-
|
9
|
-
def real_tables(name = nil, database = nil) #:nodoc:
|
8
|
+
def new_tables(database = nil) #:nodoc:
|
10
9
|
sql = ["SHOW TABLES", database].compact.join(' IN ')
|
11
10
|
execute(sql, 'SCHEMA').collect do |field|
|
12
11
|
field.first
|
@@ -21,7 +20,7 @@ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
|
21
20
|
table = schema
|
22
21
|
schema = nil
|
23
22
|
end
|
24
|
-
|
23
|
+
new_tables(schema).include? table
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|