connection_manager 0.3.11 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -1
- data/README.md +1 -1
- data/connection_manager.gemspec +4 -4
- data/lib/connection_manager.rb +2 -0
- data/lib/connection_manager/patches/cross_schema_patch.rb +5 -5
- data/lib/connection_manager/patches/query_methods_patch.rb +15 -0
- data/lib/connection_manager/patches/reflections_patch.rb +19 -0
- data/lib/connection_manager/using.rb +2 -1
- data/lib/connection_manager/version.rb +1 -1
- data/spec/lib/replication_spec.rb +47 -32
- data/spec/lib/shards_spec.rb +5 -5
- data/spec/lib/using_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -2
- metadata +22 -8
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -24,7 +24,7 @@ ConnectionManager is available through [Rubygems](https://rubygems.org/gems/conn
|
|
24
24
|
|
25
25
|
$ gem install connection_manager
|
26
26
|
|
27
|
-
## Rails 3 setup (No Rails 2 at this time)
|
27
|
+
## Rails 3/4 setup (No Rails 2 at this time)
|
28
28
|
|
29
29
|
Add connection_manager to you gemfile:
|
30
30
|
|
data/connection_manager.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Joshua Mckinney"]
|
9
9
|
s.email = ["joshmckin@gmail.com"]
|
10
10
|
s.homepage = ""
|
11
|
-
s.summary = %q{Simplifies connecting to
|
12
|
-
s.description = %q{Simplifies connecting to
|
11
|
+
s.summary = %q{Simplifies connecting to Multiple and Replication databases with rails and active_record}
|
12
|
+
s.description = %q{Simplifies connecting to Multiple and Replication databases with rails and active_record}
|
13
13
|
|
14
14
|
s.rubyforge_project = "connection_manager"
|
15
15
|
|
@@ -17,12 +17,12 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
|
-
s.add_runtime_dependency 'activerecord', '
|
20
|
+
s.add_runtime_dependency 'activerecord', '>= 3.0', '<= 4.0'
|
21
21
|
s.add_development_dependency 'rspec'
|
22
22
|
s.add_development_dependency 'autotest'
|
23
23
|
s.add_development_dependency 'mocha'
|
24
24
|
s.add_development_dependency 'factory_girl'
|
25
|
-
s.add_development_dependency 'activesupport', '
|
25
|
+
s.add_development_dependency 'activesupport', '>= 3.0', '<= 4.0'
|
26
26
|
|
27
27
|
if(defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
28
28
|
s.add_development_dependency 'jruby-openssl'
|
data/lib/connection_manager.rb
CHANGED
@@ -10,6 +10,8 @@ module ConnectionManager
|
|
10
10
|
require 'connection_manager/shards'
|
11
11
|
require 'connection_manager/replication'
|
12
12
|
require 'connection_manager/patches/cross_schema_patch'
|
13
|
+
require 'connection_manager/patches/reflections_patch'
|
14
|
+
require 'connection_manager/patches/query_methods_patch'
|
13
15
|
require 'connection_manager/connection_manager_railtie' if defined?(Rails)
|
14
16
|
|
15
17
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include,(ConnectionManager::AbstractAdapterHelper))
|
@@ -5,12 +5,12 @@ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 2
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
7
7
|
class Mysql2Adapter < ((ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 2) ? AbstractMysqlAdapter : AbstractAdapter)
|
8
|
-
|
8
|
+
|
9
9
|
# Force all tables to be cached for the life connection
|
10
10
|
def cached_tables
|
11
11
|
@cached_tables ||= {}
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def tables(name = nil, database = nil, like =nil)
|
15
15
|
return cached_tables[database] if cached_tables[database] && like.nil?
|
16
16
|
cached_tables[database] ||= []
|
@@ -21,9 +21,9 @@ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 2
|
|
21
21
|
result = execute(sql, 'SCHEMA')
|
22
22
|
cached_tables[database] = (cached_tables[database] | result.collect { |field| field[0] }).compact
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
alias :new_tables :tables
|
26
|
-
|
26
|
+
|
27
27
|
def table_exists?(name)
|
28
28
|
return false unless name
|
29
29
|
name = name.to_s
|
@@ -37,4 +37,4 @@ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 2
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module QueryMethods
|
3
|
+
private
|
4
|
+
# For some reason on .include or a custom join
|
5
|
+
# Arel drops the table name prefix on slave classes
|
6
|
+
def build_select(arel, selects)
|
7
|
+
unless selects.empty?
|
8
|
+
@implicit_readonly = false
|
9
|
+
arel.project(*selects)
|
10
|
+
else
|
11
|
+
arel.project("#{quoted_table_name}.*")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
if ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR <= 0
|
2
|
+
# Sometimes for some reason the quoted_table_name instance methods
|
3
|
+
# drops the schema. If the quoted table name does not include a '.'
|
4
|
+
# want to retrieve the quoted_table_name from the class and reset
|
5
|
+
module ActiveRecord
|
6
|
+
# = Active Record Reflection
|
7
|
+
module Reflection # :nodoc:
|
8
|
+
class AssociationReflection < MacroReflection
|
9
|
+
def table_name
|
10
|
+
@table_name = klass.table_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def quoted_table_name
|
14
|
+
@quoted_table_name = klass.quoted_table_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -25,7 +25,8 @@ module ConnectionManager
|
|
25
25
|
|
26
26
|
# Modifies the dup class to use the connection class connection.
|
27
27
|
# We want to use the current class table name, but the connection
|
28
|
-
# class database as the prefix
|
28
|
+
# class database as the prefix, useful when shards but normally
|
29
|
+
# should be the same. We also want the superclass method to
|
29
30
|
# return the connection class as AR sometimes uses the the superclass
|
30
31
|
# connection
|
31
32
|
def build_dup_class(connection_class_name)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
describe ConnectionManager::Replication do
|
3
|
-
|
3
|
+
|
4
4
|
describe '#database_name' do
|
5
5
|
it "should return the name of the database the model is using" do
|
6
6
|
Fruit.current_database_name.should eql('cm_test')
|
@@ -12,45 +12,61 @@ describe ConnectionManager::Replication do
|
|
12
12
|
Fruit.connection.stubs(:replication_keys).returns([])
|
13
13
|
lambda { Fruit.replicated }.should raise_error
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it "should not raise an exception if no connections are empty, but connection.replication_keys are not blank" do
|
17
|
-
Fruit.connection.stubs(:replication_keys).returns([:slave_1_cm_test])
|
17
|
+
Fruit.connection.stubs(:replication_keys).returns([:slave_1_cm_test])
|
18
18
|
lambda { Fruit.replicated }.should_not raise_error
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
context "the methods created from #replicated" do
|
22
22
|
it "should create a method with the name given for the :name option" do
|
23
23
|
Fruit.replicated(:name => 'foozle')
|
24
24
|
Fruit.respond_to?(:foozle).should be_true
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
it "should create a method in ActiveRecord::QueryMethods with the name given for the :name option" do
|
28
28
|
f = FactoryGirl.create(:fruit)
|
29
29
|
Fruit.replicated(:name => 'fizzle')
|
30
30
|
ActiveRecord::QueryMethods.instance_methods.include?(:fizzle).should be_true
|
31
31
|
Fruit.where(:id => f.id).fizzle.first.should_not eql(Fruit.where(:id => f.id).first)
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
it "should return an ActiveRecord::Relation" do
|
35
35
|
Fruit.replicated(:name => 'slaves')
|
36
36
|
Fruit.slaves.should be_kind_of(ActiveRecord::Relation)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
it "should have a different connection" do
|
40
40
|
Fruit.replicated
|
41
41
|
Fruit.slaves.connection.config.should_not eql(Fruit.connection.config)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
|
+
it "should work" do
|
45
|
+
Fruit.replicated
|
46
|
+
Fruit.slaves.joins(:region).joins("LEFT OUTER JOIN `fruit_baskets` ON `fruit_baskets`.`fruit_id` = `cm_test`.`fruits`.`id`").to_sql.should eql(Fruit.joins(:region).joins("LEFT OUTER JOIN `fruit_baskets` ON `fruit_baskets`.`fruit_id` = `cm_test`.`fruits`.`id`").to_sql)
|
47
|
+
end
|
48
|
+
|
44
49
|
it "should produce the same query string" do
|
45
50
|
Fruit.replicated
|
46
51
|
Fruit.slaves.joins(:region).to_sql.should eql(Fruit.joins(:region).to_sql)
|
47
52
|
Fruit.slaves.joins(:fruit_baskets).to_sql.should eql(Fruit.joins(:fruit_baskets).to_sql)
|
48
|
-
Fruit.slaves.joins(:region).joins("LEFT OUTER JOIN `fruit_baskets` ON `fruit_baskets`.`fruit_id` = `cm_test`.`fruits`.`id`").to_sql.should eql(Fruit.joins(:region).joins("LEFT OUTER JOIN `fruit_baskets` ON `fruit_baskets`.`fruit_id` = `cm_test`.`fruits`.`id`").to_sql)
|
49
53
|
Fruit.slaves.includes(:fruit_baskets).to_sql.should eql(Fruit.includes(:fruit_baskets).to_sql)
|
50
54
|
Fruit.slaves.includes(:region).to_sql.should eql(Fruit.includes(:region).to_sql)
|
51
55
|
end
|
56
|
+
|
57
|
+
context '#slaves' do
|
58
|
+
it "should have the same quoted_table_name" do
|
59
|
+
Fruit.replicated
|
60
|
+
|
61
|
+
Fruit.slaves.quoted_table_name.should eql(Fruit.quoted_table_name)
|
62
|
+
end
|
63
|
+
it "should have the same table_name_prefix"do
|
64
|
+
Fruit.replicated
|
65
|
+
Fruit.slaves.table_name_prefix.should eql(Fruit.table_name_prefix)
|
66
|
+
end
|
67
|
+
end
|
52
68
|
end
|
53
|
-
|
69
|
+
|
54
70
|
context "the objects return from a query" do
|
55
71
|
# The default connection is the original connection for the model
|
56
72
|
it "should have the connection as the replication" do
|
@@ -58,10 +74,10 @@ describe ConnectionManager::Replication do
|
|
58
74
|
FactoryGirl.create(:fruit)
|
59
75
|
Fruit.slaves.first.connection.config.should eql(Fruit.slaves.connection.config)
|
60
76
|
Fruit.slaves.first.should_not be_nil
|
61
|
-
end
|
77
|
+
end
|
62
78
|
end
|
63
79
|
end
|
64
|
-
|
80
|
+
|
65
81
|
describe'#replicated?' do
|
66
82
|
it "should be false if not replicated" do
|
67
83
|
Fruit.replicated?.should be_false
|
@@ -71,34 +87,34 @@ describe ConnectionManager::Replication do
|
|
71
87
|
Fruit.replicated?.should be_true
|
72
88
|
end
|
73
89
|
end
|
74
|
-
|
75
|
-
|
90
|
+
|
91
|
+
|
76
92
|
|
77
93
|
#ConnectionManager::Connections.build_connection_classes(:env => 'test')
|
78
94
|
class CmFooSlaveConnection < ActiveRecord::Base
|
79
95
|
establish_managed_connection(:slave_1_cm_test)
|
80
96
|
end
|
81
|
-
|
97
|
+
|
82
98
|
class CmUserConnection < ActiveRecord::Base
|
83
99
|
establish_managed_connection(:cm_user_test)
|
84
100
|
end
|
85
|
-
|
101
|
+
|
86
102
|
class SlaveCmUserConnection < ActiveRecord::Base
|
87
103
|
establish_managed_connection(:slave_1_cm_user_test)
|
88
104
|
end
|
89
|
-
|
105
|
+
|
90
106
|
class User < CmUserConnection
|
91
107
|
has_many :foos
|
92
108
|
has_many(:foo_slaves, :class_name => "Foo::Slaves")
|
93
109
|
replicated('SlaveCmUserConnection')
|
94
|
-
|
110
|
+
|
95
111
|
end
|
96
|
-
|
112
|
+
|
97
113
|
class Foo < ActiveRecord::Base
|
98
114
|
belongs_to :user
|
99
115
|
replicated("CmFooSlaveConnection")
|
100
116
|
end
|
101
|
-
|
117
|
+
|
102
118
|
context "eager loading (#includes)" do
|
103
119
|
before :each do
|
104
120
|
@user = User.new(:name => "Testing")
|
@@ -106,38 +122,37 @@ describe ConnectionManager::Replication do
|
|
106
122
|
@foo = Foo.new(:user_id => @user.id)
|
107
123
|
@foo.save
|
108
124
|
end
|
109
|
-
|
125
|
+
|
110
126
|
# We'd like this to happen magically some day. Possible in 3.2
|
111
127
|
it "should eager load with replication instances" #do
|
112
|
-
# user = User.slaves.includes(:foos).where(:id => @user.id).first
|
113
|
-
# user.foos.first.should_not be_kind_of(Foo)
|
114
|
-
# end
|
115
|
-
|
128
|
+
# user = User.slaves.includes(:foos).where(:id => @user.id).first
|
129
|
+
# user.foos.first.should_not be_kind_of(Foo)
|
130
|
+
# end
|
131
|
+
|
116
132
|
context "specifically defined replication association" do
|
117
133
|
it "should eager load with replication instances" do
|
118
134
|
user = User.slaves.includes(:foo_slaves).where(:id => @user.id).first
|
119
135
|
user.foo_slaves.first.should_not be_kind_of(Foo)
|
120
136
|
end
|
121
|
-
end
|
137
|
+
end
|
122
138
|
end
|
123
|
-
context "cross database joins" do
|
139
|
+
context "cross database joins" do
|
124
140
|
before :each do
|
125
141
|
@user = User.new(:name => "Testing")
|
126
142
|
@user.save
|
127
143
|
@foo = Foo.new(:user_id => @user.id)
|
128
144
|
@foo.save
|
129
145
|
end
|
130
|
-
|
146
|
+
|
131
147
|
it "should work" do
|
132
|
-
@user.foos.blank?.should be_false
|
148
|
+
@user.foos.blank?.should be_false
|
133
149
|
found = Foo.select('users.name AS user_name').joins(:user).where(:id => @foo.id).first
|
134
150
|
found.user_name.blank?.should be_false
|
135
151
|
end
|
136
|
-
|
152
|
+
|
137
153
|
it "should work with replication" do
|
138
154
|
found = Foo.slaves.select('foos.*, users.name AS user_name').joins(:user).where(:id => @foo.id).first
|
139
155
|
found.user.blank?.should be_false
|
140
156
|
end
|
141
|
-
end
|
157
|
+
end
|
142
158
|
end
|
143
|
-
|
data/spec/lib/shards_spec.rb
CHANGED
@@ -23,18 +23,18 @@ describe ConnectionManager::Shards do
|
|
23
23
|
|
24
24
|
it "should not matter how the where statement is formated" do
|
25
25
|
fruit = FactoryGirl.create(:fruit)
|
26
|
-
|
26
|
+
afruit = Fruit.shards do |shard|
|
27
27
|
shard.where(:id => fruit.id).first
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
bfruit = Fruit.shards do |shard|
|
30
31
|
shard.where(['id = ?', fruit.id]).first
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
+
cfruit = Fruit.shards do |shard|
|
34
35
|
shard.where('id = ?', fruit.id).first
|
35
36
|
end
|
36
|
-
|
37
|
-
(a == b && b == c).should be_true
|
37
|
+
(afruit == bfruit && bfruit == cfruit).should be_true
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/spec/lib/using_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe ConnectionManager::Using do
|
|
10
10
|
|
11
11
|
it "should add sub class to current class with the name of the connection" do
|
12
12
|
Fruit.send(:fetch_duplicate_class,"CmFooSlaveConnection")
|
13
|
-
lambda { "Fruit::CmFooSlaveConnectionDup".constantize}.should_not raise_error
|
13
|
+
lambda { "Fruit::CmFooSlaveConnectionDup".constantize}.should_not raise_error
|
14
14
|
end
|
15
15
|
|
16
16
|
describe '#using' do
|
data/spec/spec_helper.rb
CHANGED
@@ -20,11 +20,11 @@ RSpec.configure do |config|
|
|
20
20
|
config.mock_with :mocha
|
21
21
|
# Loads database.yml and establishes primary connection
|
22
22
|
# Create tables when tests are completed
|
23
|
-
config.before(:
|
23
|
+
config.before(:suite) {
|
24
24
|
require 'helpers/models_spec_helper.rb'
|
25
25
|
}
|
26
26
|
# Drops tables when tests are completed
|
27
|
-
config.after(:
|
27
|
+
config.after(:suite){
|
28
28
|
TestDB.clean
|
29
29
|
}
|
30
30
|
# Make sure every test is isolated.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: connection_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,30 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '3.0'
|
22
|
+
- - <=
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '4.0'
|
22
25
|
type: :runtime
|
23
26
|
prerelease: false
|
24
27
|
version_requirements: !ruby/object:Gem::Requirement
|
25
28
|
none: false
|
26
29
|
requirements:
|
27
|
-
- -
|
30
|
+
- - ! '>='
|
28
31
|
- !ruby/object:Gem::Version
|
29
32
|
version: '3.0'
|
33
|
+
- - <=
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '4.0'
|
30
36
|
- !ruby/object:Gem::Dependency
|
31
37
|
name: rspec
|
32
38
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,17 +102,23 @@ dependencies:
|
|
96
102
|
requirement: !ruby/object:Gem::Requirement
|
97
103
|
none: false
|
98
104
|
requirements:
|
99
|
-
- -
|
105
|
+
- - ! '>='
|
100
106
|
- !ruby/object:Gem::Version
|
101
107
|
version: '3.0'
|
108
|
+
- - <=
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.0'
|
102
111
|
type: :development
|
103
112
|
prerelease: false
|
104
113
|
version_requirements: !ruby/object:Gem::Requirement
|
105
114
|
none: false
|
106
115
|
requirements:
|
107
|
-
- -
|
116
|
+
- - ! '>='
|
108
117
|
- !ruby/object:Gem::Version
|
109
118
|
version: '3.0'
|
119
|
+
- - <=
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '4.0'
|
110
122
|
- !ruby/object:Gem::Dependency
|
111
123
|
name: mysql2
|
112
124
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,7 +135,7 @@ dependencies:
|
|
123
135
|
- - ! '>='
|
124
136
|
- !ruby/object:Gem::Version
|
125
137
|
version: '0'
|
126
|
-
description: Simplifies connecting to
|
138
|
+
description: Simplifies connecting to Multiple and Replication databases with rails
|
127
139
|
and active_record
|
128
140
|
email:
|
129
141
|
- joshmckin@gmail.com
|
@@ -144,6 +156,8 @@ files:
|
|
144
156
|
- lib/connection_manager/helpers/abstract_adapter_helper.rb
|
145
157
|
- lib/connection_manager/helpers/connection_helpers.rb
|
146
158
|
- lib/connection_manager/patches/cross_schema_patch.rb
|
159
|
+
- lib/connection_manager/patches/query_methods_patch.rb
|
160
|
+
- lib/connection_manager/patches/reflections_patch.rb
|
147
161
|
- lib/connection_manager/replication.rb
|
148
162
|
- lib/connection_manager/shards.rb
|
149
163
|
- lib/connection_manager/using.rb
|
@@ -184,7 +198,7 @@ rubyforge_project: connection_manager
|
|
184
198
|
rubygems_version: 1.8.25
|
185
199
|
signing_key:
|
186
200
|
specification_version: 3
|
187
|
-
summary: Simplifies connecting to
|
201
|
+
summary: Simplifies connecting to Multiple and Replication databases with rails and
|
188
202
|
active_record
|
189
203
|
test_files:
|
190
204
|
- spec/factories.rb
|