connection_manager 1.0.3 → 1.0.4
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.
- 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
|