connection_manager 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGE.md +4 -0
- data/README.md +2 -9
- data/lib/connection_manager/helpers/abstract_adapter_helper.rb +9 -10
- data/lib/connection_manager/replication.rb +0 -36
- data/lib/connection_manager/using.rb +35 -70
- data/lib/connection_manager/version.rb +1 -1
- data/lib/connection_manager.rb +4 -1
- data/spec/helpers/database_spec_helper.rb +23 -13
- data/spec/helpers/models_spec_helper.rb +12 -8
- data/spec/lib/patches/cross_schema_path_spec.rb +52 -2
- data/spec/lib/replication_spec.rb +0 -69
- data/spec/lib/using_spec.rb +10 -11
- data/spec/spec_helper.rb +6 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTEwZjBmOTI5ZTVjMTI0OGJkZDQwODg0NzcwYTIzZGUxYTI5M2M4NQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZGJjMWY5YWU4Zjc0OTI3MWMxMTczYTAwMTNmYzI0MTY2OGJhYTEyNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjIxODExY2IwYzEzY2Y4MjU3ODAyMGVhZTZmYTM0YTcwODAzMDYxMWYyNjU5
|
10
|
+
NWE3N2M2YzBkYTYxYzk3ZDdiYzA5NzgzYjQ0ODU5Y2E5MmMwMDM5OWU5MTA4
|
11
|
+
MGUzMGNiNjE3NDdiNGYwYzUwYjZlM2NhNWM3ZDAyOWE2ZWQxYzE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NGFiOWQ5NTQyZGYyZmNkYzQ0NWI0ZjVmYjg3NDhmZTE1YjY5MzVlY2Y0NmFh
|
14
|
+
OTY1ZTczNjE5ZWFlZDU4YmQ5OWIxZmUxNmMyYzI2MTg3N2QzNmMwOWIzMmY0
|
15
|
+
NTI1ZGUzNWFiNWJhZDc3ZjQ0NGFjZjEwYTNkYzU1MjhjOWUzZjE=
|
data/CHANGE.md
CHANGED
@@ -5,6 +5,10 @@ HEAD
|
|
5
5
|
=======
|
6
6
|
- None yet!
|
7
7
|
|
8
|
+
1.0.4
|
9
|
+
=======
|
10
|
+
- Stop duping classes for using, use proxy that returns instance of Class query is called on.
|
11
|
+
|
8
12
|
1.0.3
|
9
13
|
=======
|
10
14
|
- Fix issue where ActiveRecord::ConnectionNotEstablished error is raised in Rails app with Engine that requires delayed/backend/active_record
|
data/README.md
CHANGED
@@ -11,20 +11,13 @@ for multiple database support in Rails with a few class methods and simple
|
|
11
11
|
database.yml configuration. Since ConnectionManager does not alter
|
12
12
|
ActiveRecord's connection pool, thread safety is not a concern.
|
13
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.
|
20
|
-
|
21
14
|
## Installation
|
22
15
|
|
23
16
|
ConnectionManager is available through [Rubygems](https://rubygems.org/gems/connection_manager) and can be installed via:
|
24
17
|
|
25
18
|
$ gem install connection_manager
|
26
19
|
|
27
|
-
## Rails 3/4 setup
|
20
|
+
## Rails 3/4 setup
|
28
21
|
|
29
22
|
Add connection_manager to you gemfile:
|
30
23
|
|
@@ -176,7 +169,7 @@ to your shard requirements.
|
|
176
169
|
|
177
170
|
## Caching
|
178
171
|
|
179
|
-
ActiveRecord only caches queries for the ActiveRecord::Base connection.
|
172
|
+
ActiveRecord only caches queries for the ActiveRecord::Base connection. In order to cache queries that
|
180
173
|
originate from classes that used establish_connection you must surround your code with a cache block:
|
181
174
|
|
182
175
|
MyOtherConnectionClass.cache {
|
@@ -6,7 +6,7 @@ module ConnectionManager
|
|
6
6
|
|
7
7
|
# Determines if connection supports cross database queries
|
8
8
|
def cross_database_support?
|
9
|
-
(@config[:cross_database_support] || @config[:adapter].match(/(mysql)|(postgres)|(sqlserver)/i))
|
9
|
+
@cross_database_support ||= (@config[:cross_database_support] || @config[:adapter].match(/(mysql)|(postgres)|(sqlserver)/i))
|
10
10
|
end
|
11
11
|
alias :cross_schema_support? :cross_database_support?
|
12
12
|
|
@@ -32,31 +32,28 @@ module ConnectionManager
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def slave_keys
|
35
|
-
|
36
|
-
[]
|
35
|
+
@slave_keys ||= (@config[:slaves] ? @config[:slaves].collect{|r| r.to_sym} : [] )
|
37
36
|
end
|
38
37
|
|
39
38
|
def master_keys
|
40
|
-
|
41
|
-
[]
|
39
|
+
@master_keys ||= (@config[:masters] ? @config[:masters].collect{|r| r.to_sym} : [])
|
42
40
|
end
|
43
41
|
|
44
42
|
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1
|
45
43
|
|
46
44
|
# Returns the schema for a give table. Returns nil of multiple matches are found
|
47
45
|
def fetch_table_schema(table_name)
|
46
|
+
return nil unless cross_database_support?
|
48
47
|
sql = "SELECT table_schema FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}'"
|
49
|
-
found = nil
|
50
48
|
results = execute(sql, 'SCHEMA')
|
51
49
|
found = results.to_a
|
52
|
-
if (found.length == 1)
|
53
|
-
|
54
|
-
end
|
55
|
-
found
|
50
|
+
return found[0][0] if (found.length == 1)
|
51
|
+
nil
|
56
52
|
end
|
57
53
|
|
58
54
|
# Returns table_schema.table_name for the given table. Returns nil if multiple matches are found
|
59
55
|
def fetch_full_table_name(table_name)
|
56
|
+
return nil unless cross_database_support?
|
60
57
|
return table_name if (table_name.to_s.match(/(^$)|(\.)/))
|
61
58
|
sql = "SELECT CONCAT(table_schema,'.',table_name) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}'"
|
62
59
|
found = nil
|
@@ -73,6 +70,7 @@ module ConnectionManager
|
|
73
70
|
|
74
71
|
# Returns the schema for a give table. Returns nil of multiple matches are found
|
75
72
|
def fetch_table_schema(table_name)
|
73
|
+
return nil unless cross_database_support?
|
76
74
|
sql = "SELECT table_schema FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}'"
|
77
75
|
execute_and_free(sql, 'SCHEMA') do |result|
|
78
76
|
found = result.to_a
|
@@ -83,6 +81,7 @@ module ConnectionManager
|
|
83
81
|
|
84
82
|
# Returns table_schema.table_name for the given table. Returns nil if multiple matches are found
|
85
83
|
def fetch_full_table_name(table_name)
|
84
|
+
return nil unless cross_database_support?
|
86
85
|
return table_name if (table_name.to_s.match(/(^$)|(\.)/))
|
87
86
|
sql = "SELECT CONCAT(table_schema,'.',table_name) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}'"
|
88
87
|
execute_and_free(sql, 'SCHEMA') do |result|
|
@@ -10,10 +10,6 @@ module ConnectionManager
|
|
10
10
|
def replication_methods
|
11
11
|
@replication_methods ||= HashWithIndifferentAccess.new
|
12
12
|
end
|
13
|
-
|
14
|
-
def replicant_classes
|
15
|
-
@replicant_classes ||= HashWithIndifferentAccess.new
|
16
|
-
end
|
17
13
|
|
18
14
|
# Is this class replicated
|
19
15
|
def replicated?
|
@@ -42,7 +38,6 @@ module ConnectionManager
|
|
42
38
|
connections = connection.replication_keys(options[:type]) if connections.blank?
|
43
39
|
set_replications_to_method(connections,options[:name])
|
44
40
|
build_repliciation_class_method(options)
|
45
|
-
build_replication_association_class(options)
|
46
41
|
build_query_method_alias_method(options[:name])
|
47
42
|
build_repliciation_instance_method(options[:name])
|
48
43
|
options[:name]
|
@@ -98,37 +93,6 @@ module ConnectionManager
|
|
98
93
|
end
|
99
94
|
STR
|
100
95
|
end
|
101
|
-
|
102
|
-
# Builds a class within the model with the name of replication method. Use this
|
103
|
-
# class as the :class_name options for associations when it is necessary to
|
104
|
-
# ensure eager loading uses a replication connection.
|
105
|
-
#
|
106
|
-
# EX:
|
107
|
-
#
|
108
|
-
# class Foo < ActiveRecord::Base
|
109
|
-
# belongs_to :user
|
110
|
-
# replicated # use default name .slaves
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# class MyClass < ActiveRecord::Base
|
114
|
-
# has_many :foos
|
115
|
-
# has_many :foo_slaves, :class_name => 'Foo::Slaves'
|
116
|
-
# replicated
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# a = MyClass.include(:foo_slaves).first
|
120
|
-
# a.foo_slaves => [<Foo::Slave1ConnectionDup...>,<Foo::Slave1ConnectionDup...>...]
|
121
|
-
def build_replication_association_class(options)
|
122
|
-
class_eval <<-STR
|
123
|
-
class #{options[:name].titleize}
|
124
|
-
class << self
|
125
|
-
def method_missing(name, *args)
|
126
|
-
#{self.name}.#{options[:name]}.klass.send(name, *args)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
STR
|
131
|
-
end
|
132
96
|
|
133
97
|
# Build a query method with the name of our replication method. This method
|
134
98
|
# uses the relation.klass to fetch the appropriate connection, ensuring the
|
@@ -1,88 +1,53 @@
|
|
1
1
|
require 'active_support/core_ext/module/delegation'
|
2
2
|
module ConnectionManager
|
3
3
|
module Using
|
4
|
-
module
|
4
|
+
module Relation
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
rescue NameError
|
14
|
-
return build_dup_class(connection_class_name)
|
15
|
-
end
|
6
|
+
# Specify connection class to used for query.For
|
7
|
+
# example:
|
8
|
+
#
|
9
|
+
# users = User.using(MySlaveConnection).first
|
10
|
+
def using(connection_class_name)
|
11
|
+
@klass = ConnectionManager::Using::Proxy.new(@klass,connection_class_name)
|
12
|
+
self
|
16
13
|
end
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# connection
|
25
|
-
def build_dup_class(connection_class_name)
|
26
|
-
use_database(self.current_database_name) # make sure we are consistent from super to dup
|
27
|
-
con_class = connection_class_name.constantize
|
28
|
-
db_name = con_class.current_database_name
|
29
|
-
dup_klass = dup
|
30
|
-
dup_klass.class_eval <<-STR
|
31
|
-
self.use_database('#{db_name}',{:table_name => '#{table_name}'})
|
32
|
-
class << self
|
33
|
-
def model_name
|
34
|
-
#{self.name}.model_name
|
35
|
-
end
|
36
|
-
def connection_class
|
37
|
-
#{connection_class_name}
|
38
|
-
end
|
39
|
-
def connection
|
40
|
-
connection_class.connection
|
41
|
-
end
|
42
|
-
def superclass
|
43
|
-
connection_class
|
44
|
-
end
|
45
|
-
end
|
46
|
-
STR
|
47
|
-
|
48
|
-
self.const_set("#{connection_class_name}Dup", dup_klass)
|
49
|
-
"#{self.name}::#{connection_class_name}Dup".constantize
|
16
|
+
class Proxy
|
17
|
+
attr_accessor :klass, :connection_class
|
18
|
+
|
19
|
+
def initialize(klass,connection_class)
|
20
|
+
@klass = klass # the @klass insance from an ActiveRecord::Relation
|
21
|
+
@connection_class = (connection_class.is_a?(String) ? connection_class.constantize : connection_class)
|
50
22
|
end
|
51
|
-
end
|
52
23
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
24
|
+
# Use the connection from the connection class
|
25
|
+
def connection
|
26
|
+
ConnectionManager.logger.info "Using proxy connection: #{@connection_class.name} for #{@klass.name}" if ConnectionManager.logger
|
27
|
+
@connection_class.connection
|
28
|
+
end
|
57
29
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
30
|
+
# Make sure we return the @klass superclass,
|
31
|
+
# which used through the query building code in AR
|
32
|
+
def superclass
|
33
|
+
@klass.superclass
|
34
|
+
end
|
63
35
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def
|
69
|
-
|
70
|
-
self.instance_variable_set(:@arel_table, d.arel_table)
|
71
|
-
self.instance_variable_set(:@klass, d)
|
72
|
-
self
|
36
|
+
# Pass all methods to @klass, this ensures objects
|
37
|
+
# build from the query are the correct class and
|
38
|
+
# any settings in the model like table_name_prefix
|
39
|
+
# are used.
|
40
|
+
def method_missing(name, *args, &blk)
|
41
|
+
@klass.send(name, *args,&blk)
|
73
42
|
end
|
74
|
-
|
75
|
-
def
|
76
|
-
|
77
|
-
rel = clone
|
78
|
-
rel.instance_variable_set(:@arel_table, d.arel_table)
|
79
|
-
rel.instance_variable_set(:@klass, d)
|
80
|
-
rel
|
43
|
+
|
44
|
+
def respond_to?(method_name, include_private = false)
|
45
|
+
@klass.respond_to?(method_name) || super
|
81
46
|
end
|
82
47
|
end
|
83
48
|
end
|
84
49
|
end
|
85
|
-
|
50
|
+
ActiveRecord::Relation.send(:include,ConnectionManager::Using::Relation)
|
86
51
|
module ActiveRecord
|
87
52
|
class Base
|
88
53
|
class << self
|
data/lib/connection_manager.rb
CHANGED
@@ -15,12 +15,15 @@ module ConnectionManager
|
|
15
15
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include,(ConnectionManager::AbstractAdapterHelper))
|
16
16
|
ActiveRecord::Base.extend(ConnectionManager::ConnectionHelpers)
|
17
17
|
ActiveRecord::Base.extend(ConnectionManager::ConnectionBuilder)
|
18
|
-
ActiveRecord::Base.send(:include,ConnectionManager::Using)
|
19
18
|
ActiveRecord::Base.extend(ConnectionManager::Replication)
|
20
19
|
ActiveRecord::Base.extend(ConnectionManager::Shards)
|
21
20
|
|
22
21
|
ActiveSupport.on_load(:active_record) do
|
23
22
|
ActiveRecord::Base.build_connection_classes
|
24
23
|
end
|
24
|
+
|
25
|
+
def self.logger
|
26
|
+
@logger ||= ActiveRecord::Base.logger
|
27
|
+
end
|
25
28
|
end
|
26
29
|
|
@@ -22,12 +22,12 @@ end
|
|
22
22
|
#Put all the test migrations here
|
23
23
|
class TestMigrations < ActiveRecord::Migration
|
24
24
|
# all the ups
|
25
|
-
def self.up
|
26
|
-
ActiveRecord::Base.establish_connection(
|
25
|
+
def self.up
|
26
|
+
ActiveRecord::Base.establish_connection(:test)
|
27
27
|
begin
|
28
28
|
create_table "#{ActiveRecord::Base.schema_name}.foos" do |t|
|
29
29
|
t.string :name
|
30
|
-
t.integer :
|
30
|
+
t.integer :cm_user_id
|
31
31
|
end
|
32
32
|
rescue => e
|
33
33
|
puts "tables failed to create: #{e}"
|
@@ -76,21 +76,31 @@ class TestMigrations < ActiveRecord::Migration
|
|
76
76
|
puts "tables failed to create: #{e}"
|
77
77
|
end
|
78
78
|
|
79
|
-
ActiveRecord::Base.establish_connection(
|
79
|
+
ActiveRecord::Base.establish_connection(:cm_user_test)
|
80
80
|
begin
|
81
|
-
create_table :
|
81
|
+
create_table :cm_users do |t|
|
82
82
|
t.string :name
|
83
83
|
end
|
84
84
|
rescue => e
|
85
85
|
puts "tables failed to create: #{e}"
|
86
86
|
end
|
87
87
|
|
88
|
-
|
88
|
+
# Table is in more than 1 schema
|
89
|
+
begin
|
90
|
+
create_table "#{ActiveRecord::Base.schema_name}.types" do |t|
|
91
|
+
t.string :name
|
92
|
+
t.timestamps
|
93
|
+
end
|
94
|
+
rescue => e
|
95
|
+
puts "tables failed to create: #{e}"
|
96
|
+
end
|
97
|
+
|
98
|
+
ActiveRecord::Base.establish_connection(:test)
|
89
99
|
end
|
90
100
|
|
91
101
|
# all the downs
|
92
102
|
def self.down(connection_name='test',user_connection_name='cm_user_test')
|
93
|
-
ActiveRecord::Base.establish_connection(
|
103
|
+
ActiveRecord::Base.establish_connection(:test)
|
94
104
|
[:foos,:fruits,:baskets,:fruit_baskets,:regions,:types].each do |t|
|
95
105
|
begin
|
96
106
|
drop_table t
|
@@ -98,14 +108,14 @@ class TestMigrations < ActiveRecord::Migration
|
|
98
108
|
puts "tables were not dropped: #{e}"
|
99
109
|
end
|
100
110
|
end
|
101
|
-
ActiveRecord::Base.establish_connection(
|
102
|
-
|
103
|
-
|
111
|
+
ActiveRecord::Base.establish_connection(:cm_user_test)
|
112
|
+
[ :cm_users, :types].each do |t|
|
113
|
+
begin
|
104
114
|
drop_table t
|
115
|
+
rescue => e
|
116
|
+
puts "tables were not dropped: #{e}"
|
105
117
|
end
|
106
|
-
rescue => e
|
107
|
-
puts "tables were not dropped: #{e}"
|
108
118
|
end
|
109
|
-
ActiveRecord::Base.establish_connection(
|
119
|
+
ActiveRecord::Base.establish_connection(:test)
|
110
120
|
end
|
111
121
|
end
|
@@ -31,18 +31,22 @@ class Region < ActiveRecord::Base
|
|
31
31
|
#replicated
|
32
32
|
end
|
33
33
|
|
34
|
-
class Type < ActiveRecord::Base
|
35
|
-
|
36
|
-
end
|
34
|
+
class Type < ActiveRecord::Base;end
|
37
35
|
|
38
36
|
class SouthernFruit < Fruit
|
39
|
-
self.table_name = 'fruits'
|
37
|
+
self.table_name = 'fruits'
|
40
38
|
end
|
41
39
|
|
42
|
-
class
|
43
|
-
|
44
|
-
["Basket", "Fruit", "FruitBasket", "Region","SouthernFruit", "Type"]
|
45
|
-
end
|
40
|
+
class CmUser < ActiveRecord::Base
|
41
|
+
has_many :foos
|
46
42
|
end
|
47
43
|
|
44
|
+
class Foo < ActiveRecord::Base
|
45
|
+
belongs_to :cm_user
|
46
|
+
end
|
48
47
|
|
48
|
+
class ModelsHelper
|
49
|
+
def self.models
|
50
|
+
["Basket", "Fruit", "FruitBasket", "Region","SouthernFruit", "Type", "Foo", "CmUser"]
|
51
|
+
end
|
52
|
+
end
|
@@ -1,5 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
describe
|
2
|
+
describe ActiveRecord::ConnectionAdapters::AbstractAdapter do
|
3
|
+
|
4
|
+
describe '#fetch_table_schema' do
|
5
|
+
context "table is unique in DMS" do
|
6
|
+
it "should return a string consisting of the schema name, a '.' and the table_name" do
|
7
|
+
Fruit.connection.fetch_table_schema('fruits').should eql('cm_test')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "table is not unique in DMS" do
|
12
|
+
it "should return a string consisting of the schema name, a '.' and the table_name" do
|
13
|
+
Fruit.connection.fetch_table_schema('type').should eql(nil)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
3
18
|
describe '#fetch_full_table_name' do
|
4
19
|
it "should return a string consisting of the schema name, a '.' and the table_name" do
|
5
20
|
Fruit.connection.fetch_full_table_name('fruits').should eql('cm_test.fruits')
|
@@ -10,7 +25,7 @@ describe ActiveRecord::ConnectionAdapters::Mysql2Adapter do
|
|
10
25
|
it "should return true for unquoted full_names" do
|
11
26
|
Fruit.connection.table_exists?('cm_test.fruits').should be_true
|
12
27
|
end
|
13
|
-
|
28
|
+
it "should return true for table only names" do
|
14
29
|
Fruit.connection.table_exists?('fruits').should be_true
|
15
30
|
end
|
16
31
|
end
|
@@ -21,4 +36,39 @@ describe ActiveRecord::Base do
|
|
21
36
|
Fruit.arel_table.name.should eql('cm_test.fruits')
|
22
37
|
end
|
23
38
|
end
|
39
|
+
|
40
|
+
context "Cross Schema Joins" do
|
41
|
+
before :each do
|
42
|
+
@user = CmUser.new(:name => "Testing")
|
43
|
+
@user.save
|
44
|
+
@foo = Foo.new(:cm_user_id => @user.id)
|
45
|
+
@foo.save
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#joins' do
|
49
|
+
it "should work" do
|
50
|
+
@user.foos.blank?.should be_false
|
51
|
+
found = Foo.joins(:cm_user).select('cm_users.name AS user_name').where('cm_users.id = ?',@user.id).first
|
52
|
+
found.user_name.blank?.should be_false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
describe '#includes' do
|
56
|
+
before(:each) do
|
57
|
+
@user.foos.blank?.should be_false
|
58
|
+
search = Foo.includes(:cm_user).where('cm_users.id = ?',@user.id)
|
59
|
+
search = search.references(:cm_user) if search.respond_to?(:references)
|
60
|
+
@found = search.first
|
61
|
+
end
|
62
|
+
it "should return a results" do
|
63
|
+
@found.should be_a(Foo) # Make sure results are returns
|
64
|
+
end
|
65
|
+
it "should loan associations" do
|
66
|
+
if @found.respond_to?(:association)
|
67
|
+
@found.association(:cm_user).loaded?.should be_true
|
68
|
+
else
|
69
|
+
@found.cm_user.loaded?.should be_true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
24
74
|
end
|
@@ -28,7 +28,6 @@ describe ConnectionManager::Replication 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
|
-
Fruit.where(:id => f.id).fizzle.first.should_not eql(Fruit.where(:id => f.id).first)
|
32
31
|
end
|
33
32
|
|
34
33
|
it "should return an ActiveRecord::Relation" do
|
@@ -86,72 +85,4 @@ describe ConnectionManager::Replication do
|
|
86
85
|
Fruit.replicated?.should be_true
|
87
86
|
end
|
88
87
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
#ConnectionManager::Connections.build_connection_classes(:env => 'test')
|
93
|
-
class CmFooSlaveConnection < ActiveRecord::Base
|
94
|
-
establish_managed_connection(:slave_1_cm_test)
|
95
|
-
end
|
96
|
-
|
97
|
-
class CmUserConnection < ActiveRecord::Base
|
98
|
-
establish_managed_connection(:cm_user_test)
|
99
|
-
end
|
100
|
-
|
101
|
-
class SlaveCmUserConnection < ActiveRecord::Base
|
102
|
-
establish_managed_connection(:slave_1_cm_user_test)
|
103
|
-
end
|
104
|
-
|
105
|
-
class User < CmUserConnection
|
106
|
-
has_many :foos
|
107
|
-
has_many(:foo_slaves, :class_name => "Foo::Slaves")
|
108
|
-
replicated('SlaveCmUserConnection')
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
class Foo < ActiveRecord::Base
|
113
|
-
belongs_to :user
|
114
|
-
replicated("CmFooSlaveConnection")
|
115
|
-
end
|
116
|
-
|
117
|
-
context "eager loading (#includes)" do
|
118
|
-
before :each do
|
119
|
-
@user = User.new(:name => "Testing")
|
120
|
-
@user.save
|
121
|
-
@foo = Foo.new(:user_id => @user.id)
|
122
|
-
@foo.save
|
123
|
-
end
|
124
|
-
|
125
|
-
# We'd like this to happen magically some day. Possible in 3.2
|
126
|
-
it "should eager load with replication instances" #do
|
127
|
-
# user = User.slaves.includes(:foos).where(:id => @user.id).first
|
128
|
-
# user.foos.first.should_not be_kind_of(Foo)
|
129
|
-
# end
|
130
|
-
|
131
|
-
context "specifically defined replication association" do
|
132
|
-
it "should eager load with replication instances" do
|
133
|
-
user = User.slaves.includes(:foo_slaves).where(:id => @user.id).first
|
134
|
-
user.foo_slaves.first.should_not be_kind_of(Foo)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
context "cross database joins" do
|
139
|
-
before :each do
|
140
|
-
@user = User.new(:name => "Testing")
|
141
|
-
@user.save
|
142
|
-
@foo = Foo.new(:user_id => @user.id)
|
143
|
-
@foo.save
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should work" do
|
147
|
-
@user.foos.blank?.should be_false
|
148
|
-
found = Foo.select('users.name AS user_name').joins(:user).where(:id => @foo.id).first
|
149
|
-
found.user_name.blank?.should be_false
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should work with replication" do
|
153
|
-
found = Foo.slaves.select('foos.*, users.name AS user_name').joins(:user).where(:id => @foo.id).first
|
154
|
-
found.user.blank?.should be_false
|
155
|
-
end
|
156
|
-
end
|
157
88
|
end
|
data/spec/lib/using_spec.rb
CHANGED
@@ -4,11 +4,6 @@ class CmFooSlaveConnection < ActiveRecord::Base
|
|
4
4
|
end
|
5
5
|
|
6
6
|
describe ConnectionManager::Using do
|
7
|
-
|
8
|
-
it "should add sub class to current class with the name of the connection" do
|
9
|
-
Fruit.send(:fetch_duplicate_class,"CmFooSlaveConnection")
|
10
|
-
lambda { "Fruit::CmFooSlaveConnectionDup".constantize}.should_not raise_error
|
11
|
-
end
|
12
7
|
|
13
8
|
describe '#using' do
|
14
9
|
it "should return an ActiveRecord::Relation" do
|
@@ -17,22 +12,26 @@ describe ConnectionManager::Using do
|
|
17
12
|
it "should change the connection" do
|
18
13
|
Fruit.using("CmFooSlaveConnection").connection.config.should_not eql(Fruit.connection.config)
|
19
14
|
end
|
20
|
-
|
15
|
+
|
21
16
|
it "should create the exact same sql if called from model or from relation" do
|
22
17
|
class_sql = Fruit.using("CmFooSlaveConnection").where(:name => "malarky").to_sql
|
23
18
|
relation_sql = Fruit.where(:name => "malarky").using("CmFooSlaveConnection").to_sql
|
24
19
|
class_sql.should eql(relation_sql)
|
25
20
|
end
|
26
|
-
|
21
|
+
|
27
22
|
it "should have the same connection if called from model or from relation" do
|
28
23
|
Fruit.where(:name => "malarky").using("CmFooSlaveConnection").connection.config.should eql(
|
29
|
-
|
24
|
+
Fruit.using("CmFooSlaveConnection").where(:name => "malarky").connection.config)
|
30
25
|
Fruit.using("CmFooSlaveConnection").where(:name => "malarky").connection.config.should_not eql(
|
31
|
-
|
26
|
+
Fruit.where(:name => "malarky").connection.config)
|
32
27
|
Fruit.where(:name => "malarky").using("CmFooSlaveConnection").connection.config.should_not eql(
|
33
|
-
|
28
|
+
Fruit.where(:name => "malarky").connection.config)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return same record" do
|
32
|
+
fruit = FactoryGirl.create(:fruit)
|
33
|
+
Fruit.using("CmFooSlaveConnection").where(:id => fruit.id).first.should eql(fruit)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
38
37
|
|
data/spec/spec_helper.rb
CHANGED
@@ -16,24 +16,22 @@ end
|
|
16
16
|
TestMigrations.down
|
17
17
|
TestMigrations.up
|
18
18
|
FactoryGirl.find_definitions
|
19
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
19
20
|
RSpec.configure do |config|
|
20
|
-
config.mock_with :mocha
|
21
|
+
config.mock_with :mocha
|
21
22
|
# Loads database.yml and establishes primary connection
|
22
23
|
# Create tables when tests are completed
|
23
24
|
config.before(:suite) {
|
24
|
-
require 'helpers/models_spec_helper.rb'
|
25
|
-
}
|
25
|
+
require 'helpers/models_spec_helper.rb'
|
26
|
+
}
|
26
27
|
# Drops tables when tests are completed
|
27
28
|
config.after(:suite){
|
28
29
|
TestDB.clean
|
29
|
-
}
|
30
|
+
}
|
30
31
|
# Make sure every test is isolated.
|
31
32
|
config.before(:each){
|
32
|
-
ModelsHelper.models.each{|m| Object.send(:remove_const, m)}
|
33
|
+
ModelsHelper.models.each{|m| Object.send(:remove_const, m)}
|
33
34
|
load 'helpers/models_spec_helper.rb'
|
34
35
|
FactoryGirl.reload
|
35
36
|
}
|
36
|
-
|
37
37
|
end
|
38
|
-
|
39
|
-
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: connection_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Mckinney
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|