connection_manager 1.1.5 → 2.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +14 -0
- data/README.md +9 -10
- data/connection_manager.gemspec +5 -4
- data/gemfiles/4.0.gemfile +6 -0
- data/gemfiles/4.1.gemfile +7 -0
- data/gemfiles/4.2.gemfile +5 -0
- data/lib/connection_manager.rb +14 -15
- data/lib/connection_manager/builder.rb +9 -5
- data/lib/connection_manager/connection_adapters/abstract_adapter.rb +4 -4
- data/lib/connection_manager/connection_handling.rb +6 -11
- data/lib/connection_manager/core.rb +1 -5
- data/lib/connection_manager/querying.rb +4 -8
- data/lib/connection_manager/railtie.rb +0 -1
- data/lib/connection_manager/replication.rb +24 -21
- data/lib/connection_manager/shards.rb +6 -6
- data/lib/connection_manager/using.rb +4 -55
- data/lib/connection_manager/version.rb +1 -1
- data/spec/helpers/database_spec_helper.rb +0 -6
- data/spec/lib/builder_spec.rb +2 -2
- data/spec/lib/connection_adapters/abstract_adapter_spec.rb +3 -3
- data/spec/lib/connection_adapters/mysql_adapter_spec.rb +2 -2
- data/spec/lib/connection_handling_spec.rb +3 -3
- data/spec/lib/integration/cross_schema_spec.rb +2 -2
- data/spec/lib/replication_spec.rb +1 -1
- data/spec/lib/shards_spec.rb +2 -2
- data/spec/lib/using_spec.rb +1 -1
- metadata +23 -6
- data/lib/connection_manager/connection_adapters/mysql_adapter.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 147e706061b9694c3582c5d7afe29134b7b1fdbe
|
4
|
+
data.tar.gz: 93e2744959ed2c6fdb8b65fdd5c7a04c2535ee55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5ca838d7a5b23faa9fdd3fac02484338b8f5a778e47427482827d4f2bddc8c2a30b09504ec8bd48ee66dc2b038592af025232751c40921c35f9d5fe464fa281
|
7
|
+
data.tar.gz: 9999b5b6365cf69d59ec15d34850453746493ceb8b926b3026b082c1a810a9371d6ee4c9e872b643ea2084a899d9acddff1a9776eae0ccca290adb563e41218d
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# ConnectionManager
|
1
|
+
# ConnectionManager [](https://travis-ci.org/JoshMcKin/connection_manager)
|
2
2
|
Improved cross-schema, replication and mutli-DMS gem for ActiveRecord.
|
3
3
|
|
4
4
|
## Features
|
@@ -12,7 +12,7 @@ ConnectionManager is available through [Rubygems](https://rubygems.org/gems/conn
|
|
12
12
|
|
13
13
|
$ gem install connection_manager
|
14
14
|
|
15
|
-
## Rails
|
15
|
+
## Rails 4 setup
|
16
16
|
|
17
17
|
Add connection_manager to you gemfile:
|
18
18
|
|
@@ -136,16 +136,15 @@ If there are multiple replication connections the system will pick a connection
|
|
136
136
|
User.slaves.where('id BETWEEN ? and ?',1,100]).all => returns results from slave_2_user_data_development
|
137
137
|
|
138
138
|
### Repliation with cross-schema queries
|
139
|
-
Setup replication as you would normally
|
139
|
+
Setup replication as you would normally. Then build connection classes that inherit from you base connection
|
140
|
+
classes for each of your schemas.
|
140
141
|
|
141
|
-
Next build connection classes that inherit from you base connection classes for each of your schemas
|
142
|
-
EX
|
143
142
|
class UserSchema < ActiveRecord::Base
|
144
143
|
self.abstract_class = true
|
145
|
-
self.
|
144
|
+
self.schema_name = 'user_schema'
|
146
145
|
|
147
146
|
def self.inherited(base)
|
148
|
-
base.
|
147
|
+
base.schema_name = self.schema_name
|
149
148
|
end
|
150
149
|
end
|
151
150
|
|
@@ -155,10 +154,10 @@ EX
|
|
155
154
|
|
156
155
|
class FooSchema < ActiveRecord::Base
|
157
156
|
self.abstract_class = true
|
158
|
-
self.
|
157
|
+
self.schema_name = 'foo'
|
159
158
|
|
160
159
|
def self.inherited(base)
|
161
|
-
base.
|
160
|
+
base.schema_name = self.schema_name
|
162
161
|
end
|
163
162
|
end
|
164
163
|
|
@@ -168,7 +167,7 @@ EX
|
|
168
167
|
|
169
168
|
User.joins(:bars).limit(1).to_sql # => SELECT * FROM `user_schema`.`users` INNER JOIN `foo.bars` ON `foo.bars`.`user_id` = `user_schema`.`users` LIMIT 1"
|
170
169
|
|
171
|
-
##
|
170
|
+
## Shards
|
172
171
|
|
173
172
|
After tinkering with some solutions for shards, I've come to a similar conclusion as [DataFabric] (https://github.com/mperham/data_fabric):
|
174
173
|
"Sharding should be implemented at the application level". The `shards` method is very basic and
|
data/connection_manager.gemspec
CHANGED
@@ -17,10 +17,11 @@ 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','>= 3.0', '< 5.0'
|
21
|
-
s.add_runtime_dependency 'activesupport','>= 3.0', '< 5.0'
|
22
|
-
s.add_runtime_dependency '
|
23
|
-
s.add_development_dependency 'rspec', '~>
|
20
|
+
s.add_runtime_dependency 'activerecord', '>= 3.0', '< 5.0'
|
21
|
+
s.add_runtime_dependency 'activesupport', '>= 3.0', '< 5.0'
|
22
|
+
s.add_runtime_dependency 'concurrent-ruby'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
24
|
+
s.add_development_dependency 'rspec-autotest'
|
24
25
|
s.add_development_dependency 'autotest'
|
25
26
|
s.add_development_dependency 'mocha'
|
26
27
|
s.add_development_dependency 'factory_girl'
|
data/lib/connection_manager.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
require "connection_manager/version"
|
2
|
+
require 'concurrent'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support'
|
5
|
+
require 'connection_manager/connection_adapters/abstract_adapter'
|
6
|
+
require 'connection_manager/core'
|
7
|
+
require 'connection_manager/connection_handling'
|
8
|
+
require 'connection_manager/relation'
|
9
|
+
require 'connection_manager/querying'
|
10
|
+
require 'connection_manager/builder'
|
11
|
+
require 'connection_manager/using'
|
12
|
+
require 'connection_manager/replication'
|
13
|
+
require 'connection_manager/shards'
|
14
|
+
require 'connection_manager/railtie' if defined?(Rails)
|
2
15
|
|
3
16
|
module ConnectionManager
|
4
|
-
require 'thread_safe'
|
5
|
-
require 'active_record'
|
6
|
-
require 'active_support'
|
7
|
-
require 'connection_manager/connection_adapters/abstract_adapter'
|
8
|
-
require 'connection_manager/core'
|
9
|
-
require 'connection_manager/connection_handling'
|
10
|
-
require 'connection_manager/relation'
|
11
|
-
require 'connection_manager/querying'
|
12
|
-
require 'connection_manager/builder'
|
13
|
-
require 'connection_manager/using'
|
14
|
-
require 'connection_manager/replication'
|
15
|
-
require 'connection_manager/shards'
|
16
|
-
require 'connection_manager/railtie' if defined?(Rails)
|
17
|
-
|
18
17
|
# Get the current environment if defined
|
19
18
|
# Check for Rails, check for RACK_ENV, default to 'development'
|
20
19
|
def self.env
|
@@ -32,7 +31,7 @@ module ConnectionManager
|
|
32
31
|
def self.logger
|
33
32
|
@logger ||= ActiveRecord::Base.logger
|
34
33
|
end
|
35
|
-
|
34
|
+
|
36
35
|
def self.logger=logger
|
37
36
|
@logger = logger
|
38
37
|
end
|
@@ -31,23 +31,27 @@ module ConnectionManager
|
|
31
31
|
|
32
32
|
def env_regex
|
33
33
|
return @env_regex if @env_regex
|
34
|
-
|
35
|
-
@env_regex = Regexp.new("(#{
|
34
|
+
env_str = "#{ConnectionManager.env}$"
|
35
|
+
@env_regex = Regexp.new("(#{env_str})")
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
39
|
# Creates a string to be used for the class name. Removes the current env.
|
40
40
|
def clean_yml_key(name)
|
41
|
-
new_name = "#{name}"
|
41
|
+
new_name = "#{name}"
|
42
|
+
new_name.gsub!(env_regex,'')
|
42
43
|
new_name = "Base" if new_name.blank?
|
43
|
-
new_name.gsub(/\_$/,'')
|
44
|
+
new_name.gsub!(/\_$/,'')
|
45
|
+
new_name
|
44
46
|
end
|
45
47
|
|
46
48
|
# Given an connection key name from the database.yml, returns the string
|
47
49
|
# equivalent of the class name for that entry.
|
48
50
|
def connection_class_name(name_from_yml)
|
49
51
|
new_class_name = clean_yml_key(name_from_yml)
|
50
|
-
new_class_name
|
52
|
+
new_class_name.gsub!(/\_/,' ')
|
53
|
+
new_class_name.gsub!(/\b(?<!['â`])[a-z]/) { $&.capitalize! }
|
54
|
+
new_class_name.gsub!(/ /,'')
|
51
55
|
new_class_name << "Connection"
|
52
56
|
new_class_name
|
53
57
|
end
|
@@ -4,20 +4,20 @@ module ConnectionManager
|
|
4
4
|
|
5
5
|
# Determines if connection supports cross database queries
|
6
6
|
def cross_schema_support?
|
7
|
-
@cross_schema_support ||= (config[:adapter]
|
7
|
+
@cross_schema_support ||= (config[:adapter] =~ /(mysql)|(postgres)|(sqlserver)/i)
|
8
8
|
end
|
9
9
|
alias :cross_database_support? :cross_schema_support?
|
10
10
|
|
11
11
|
def mysql?
|
12
|
-
@is_mysql ||= (config[:adapter]
|
12
|
+
@is_mysql ||= (config[:adapter] =~ /(mysql)/i)
|
13
13
|
end
|
14
14
|
|
15
15
|
def postgresql?
|
16
|
-
@is_postgresql ||= (config[:adapter]
|
16
|
+
@is_postgresql ||= (config[:adapter] =~ /(postgres)/i)
|
17
17
|
end
|
18
18
|
|
19
19
|
def sqlserver?
|
20
|
-
@is_sqlserver ||= (config[:adapter]
|
20
|
+
@is_sqlserver ||= (config[:adapter] =~ /(sqlserver)/i)
|
21
21
|
end
|
22
22
|
|
23
23
|
def replicated?
|
@@ -1,13 +1,12 @@
|
|
1
|
-
require 'thread'
|
2
1
|
require 'active_record/relation'
|
3
2
|
module ConnectionManager
|
4
3
|
module ConnectionHandling
|
5
|
-
@@managed_connections =
|
4
|
+
@@managed_connections = Concurrent::Map.new
|
6
5
|
|
7
6
|
# Attempts to return the schema from table_name and table_name_prefix
|
8
7
|
def schema_name
|
9
|
-
return self.table_name.split('.')[0] if self.table_name && self.table_name
|
10
|
-
return self.table_name_prefix.to_s.gsub(/\./,'') if self.table_name_prefix && self.table_name_prefix
|
8
|
+
return self.table_name.split('.')[0] if self.table_name && self.table_name =~ /\./
|
9
|
+
return self.table_name_prefix.to_s.gsub(/\./,'') if self.table_name_prefix && self.table_name_prefix =~ /\./
|
11
10
|
return self.connection.config[:database] if self.connection.mysql?
|
12
11
|
end
|
13
12
|
alias :database_name :schema_name
|
@@ -21,7 +20,7 @@ module ConnectionManager
|
|
21
20
|
# User.table_name_prefix # => 'users_db.'
|
22
21
|
# User.table_name # => 'users_db.users'
|
23
22
|
def schema_name=schema_name
|
24
|
-
self.table_name_prefix = "#{schema_name}." if schema_name && schema_name.
|
23
|
+
self.table_name_prefix = "#{schema_name}." if schema_name && !schema_name.blank?
|
25
24
|
self.table_name = "#{self.table_name_prefix}#{self.table_name}" unless self.abstract_class?
|
26
25
|
end
|
27
26
|
alias :database_name= :schema_name=
|
@@ -83,9 +82,5 @@ module ConnectionManager
|
|
83
82
|
end
|
84
83
|
end
|
85
84
|
end
|
86
|
-
|
87
|
-
|
88
|
-
else
|
89
|
-
require 'active_record/base'
|
90
|
-
ActiveRecord::Base.extend(ConnectionManager::ConnectionHandling)
|
91
|
-
end
|
85
|
+
ActiveRecord::ConnectionHandling.send(:include, ConnectionManager::ConnectionHandling)
|
86
|
+
|
@@ -17,8 +17,4 @@ module ConnectionManager
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
21
|
-
ActiveRecord::Core::ClassMethods.send(:include,ConnectionManager::Core)
|
22
|
-
else
|
23
|
-
ActiveRecord::Base.extend ConnectionManager::Core
|
24
|
-
end
|
20
|
+
ActiveRecord::Core::ClassMethods.send(:include,ConnectionManager::Core)
|
@@ -1,13 +1,9 @@
|
|
1
1
|
require 'active_support/core_ext/module/delegation'
|
2
2
|
module ConnectionManager
|
3
3
|
module Querying
|
4
|
-
delegate :using, :to =>
|
5
|
-
delegate :slaves, :to =>
|
6
|
-
delegate :masters, :to =>
|
4
|
+
delegate :using, :to => :all
|
5
|
+
delegate :slaves, :to => :all
|
6
|
+
delegate :masters, :to => :all
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
10
|
-
ActiveRecord::Querying.send(:include, ConnectionManager::Querying)
|
11
|
-
else
|
12
|
-
ActiveRecord::Base.send(:extend, ConnectionManager::Querying)
|
13
|
-
end
|
9
|
+
ActiveRecord::Querying.send(:include, ConnectionManager::Querying)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module ConnectionManager
|
2
2
|
class Railtie < ::Rails::Railtie
|
3
3
|
initializer "connection_manager.build_connection_classes" do
|
4
|
-
require 'connection_manager/connection_adapters/mysql_adapter' if (ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1 && (defined?(Mysql2::VERSION) || defined?(Mysql2::VERSION)))
|
5
4
|
ConnectionManager.env = Rails.env
|
6
5
|
ConnectionManager.logger = Rails.logger
|
7
6
|
ConnectionManager::Builder.build_connection_classes(Rails.application.config.database_configuration.select{ |k,v| v['build_connection_class'] && k.match(ConnectionManager::Builder.env_regex)}.keys)
|
@@ -1,20 +1,17 @@
|
|
1
1
|
module ConnectionManager
|
2
2
|
module Replication
|
3
|
-
|
3
|
+
|
4
4
|
# Replication methods (replication_method_name, which is the option[:name] for the
|
5
5
|
# #replication method) and all their associated connections. The key is the
|
6
6
|
# replication_method_name and the value is an array of all the replication_classes
|
7
7
|
# the replication_method has access to.
|
8
8
|
#
|
9
9
|
# EX: replication_methods[:slaves] => ['Slave1Connection',Slave2Connection]
|
10
|
-
|
11
|
-
@replication_connections ||= {:slaves => [], :masters => []}
|
12
|
-
end
|
13
|
-
|
10
|
+
attr_accessor :replication_connections
|
14
11
|
|
15
12
|
# Is this class replicated
|
16
13
|
def replicated?
|
17
|
-
(@replication_connections
|
14
|
+
defined?(@replication_connections)
|
18
15
|
end
|
19
16
|
|
20
17
|
# Builds a class method that returns an ActiveRecord::Relation for use with
|
@@ -30,9 +27,13 @@ module ConnectionManager
|
|
30
27
|
# * :name - name of class method to call to access replication, default to slaves
|
31
28
|
# * :type - the type of replication; :slaves or :masters, defaults to :slaves
|
32
29
|
def replicated(*connections)
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
opts = connections.extract_options!
|
31
|
+
opts.symbolize_keys!
|
32
|
+
opts[:slaves] ||= []
|
33
|
+
opts[:masters] ||= []
|
34
|
+
opts[:type] ||= :slaves
|
35
|
+
opts[opts[:type]] = connections unless connections.empty?
|
36
|
+
set_replications_connections(opts)
|
36
37
|
end
|
37
38
|
|
38
39
|
def fetch_slave_connection
|
@@ -50,10 +51,11 @@ module ConnectionManager
|
|
50
51
|
# connections we use sample to get a random connection instead of blocking
|
51
52
|
# to rotate the pool on every fetch.
|
52
53
|
def fetch_replication_connection(method_name)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
if @replication_connections && available_connections = @replication_connections[method_name]
|
55
|
+
available_connections.sample
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Replication connections could not be found for #{method_name}."
|
58
|
+
end
|
57
59
|
end
|
58
60
|
|
59
61
|
# Builds replication connection classes and methods
|
@@ -61,21 +63,22 @@ module ConnectionManager
|
|
61
63
|
[:masters,:slaves].each do |type|
|
62
64
|
cons = (options[type].empty? ? connection.replications[type] : options[type])
|
63
65
|
unless cons.empty?
|
64
|
-
|
66
|
+
@replication_connections ||= {}
|
65
67
|
cons.each do |to_use|
|
66
|
-
|
68
|
+
@replication_connections[type] ||= []
|
69
|
+
@replication_connections[type] << fetch_connection_class_name(to_use)
|
67
70
|
end
|
68
71
|
end
|
69
72
|
end
|
70
|
-
raise ArgumentError, "
|
71
|
-
|
73
|
+
raise ArgumentError, "Connection class could not be found for #{self.name}." unless (replicated? && (@replication_connections[:masters] || @replication_connections[:slaves]))
|
74
|
+
@replication_connections
|
72
75
|
end
|
73
76
|
|
74
77
|
def fetch_connection_class_name(to_use)
|
75
|
-
|
76
|
-
|
77
|
-
raise ArgumentError, "For #{self.name}, the class #{
|
78
|
-
|
78
|
+
conn_class_name = to_use
|
79
|
+
conn_class_name = fetch_connection_class_name_from_yml_key(conn_class_name) if conn_class_name.to_s =~ /_/
|
80
|
+
raise ArgumentError, "For #{self.name}, the class #{conn_class_name} could not be found." if conn_class_name.blank?
|
81
|
+
conn_class_name
|
79
82
|
end
|
80
83
|
|
81
84
|
def fetch_connection_class_name_from_yml_key(yml_key)
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module ConnectionManager
|
2
|
-
module Shards
|
2
|
+
module Shards
|
3
3
|
@shard_class_names = []
|
4
|
-
|
4
|
+
|
5
5
|
def shard_class_names(*shard_class_names)
|
6
6
|
@shard_class_names = shard_class_names
|
7
|
-
end
|
8
|
-
|
7
|
+
end
|
8
|
+
|
9
9
|
# Takes a block that is call on all available shards.
|
10
10
|
def shards(*opts,&shards_block)
|
11
11
|
opts = {:include_self => true}.merge(opts.extract_options!)
|
12
12
|
raise ArgumentError, "shard_class_names have not been defined for #{self.class.name}" if @shard_class_names.length == 0
|
13
|
-
if block_given?
|
13
|
+
if block_given?
|
14
14
|
results = []
|
15
15
|
@shard_class_names.each do |s|
|
16
16
|
results << shards_block.call(s.constantize)
|
@@ -23,4 +23,4 @@ module ConnectionManager
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
ActiveRecord::Base.extend(ConnectionManager::Shards)
|
26
|
+
ActiveRecord::Base.extend(ConnectionManager::Shards)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_record/querying.rb'
|
1
2
|
module ConnectionManager
|
2
3
|
module Using
|
3
4
|
module ClassMethods
|
@@ -16,7 +17,10 @@ module ConnectionManager
|
|
16
17
|
super(compare)
|
17
18
|
end
|
18
19
|
end
|
20
|
+
|
19
21
|
class Proxy
|
22
|
+
include ActiveRecord::Querying
|
23
|
+
|
20
24
|
attr_accessor :klass, :connection_class
|
21
25
|
|
22
26
|
def initialize(klass,connection_class)
|
@@ -63,61 +67,6 @@ module ConnectionManager
|
|
63
67
|
@klass.parent
|
64
68
|
end
|
65
69
|
|
66
|
-
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
67
|
-
# Rails 3.0
|
68
|
-
def find_by_sql(sql)
|
69
|
-
connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
|
70
|
-
end
|
71
|
-
|
72
|
-
elsif ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
|
73
|
-
# Rails 3.1
|
74
|
-
def find_by_sql(sql, binds = [])
|
75
|
-
connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
|
76
|
-
end
|
77
|
-
|
78
|
-
elsif ActiveRecord::VERSION::MAJOR == 3
|
79
|
-
# Rails 3.2
|
80
|
-
def find_by_sql(sql, binds = [])
|
81
|
-
logging_query_plan do
|
82
|
-
connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
elsif ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR <= 1
|
87
|
-
#Rails 4.0 & 4.1
|
88
|
-
def find_by_sql(sql, binds = [])
|
89
|
-
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
|
90
|
-
column_types = {}
|
91
|
-
if result_set.respond_to? :column_types
|
92
|
-
column_types = result_set.column_types
|
93
|
-
else
|
94
|
-
ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
|
95
|
-
end
|
96
|
-
result_set.map { |record| instantiate(record, column_types) }
|
97
|
-
end
|
98
|
-
|
99
|
-
else
|
100
|
-
# Edge Rails
|
101
|
-
def find_by_sql(sql, binds = [])
|
102
|
-
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
|
103
|
-
column_types = result_set.column_types.dup
|
104
|
-
columns_hash.each_key { |k| column_types.delete k }
|
105
|
-
message_bus = ActiveSupport::Notifications.instrumenter
|
106
|
-
payload = {
|
107
|
-
record_count: result_set.length,
|
108
|
-
class_name: name
|
109
|
-
}
|
110
|
-
message_bus.instrument('instantiation.active_record', payload) do
|
111
|
-
result_set.map { |record| instantiate(record, column_types) }
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def count_by_sql(sql)
|
117
|
-
sql = sanitize_conditions(sql)
|
118
|
-
connection.select_value(sql, "#{name} Count").to_i
|
119
|
-
end
|
120
|
-
|
121
70
|
# Pass all methods to @klass, this ensures objects
|
122
71
|
# build from the query are the correct class and
|
123
72
|
# any settings in the model like table_name_prefix
|
@@ -64,7 +64,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
64
64
|
create_table "cm_test.fruits" do |t|
|
65
65
|
t.string :name
|
66
66
|
t.integer :region_id
|
67
|
-
t.timestamps
|
68
67
|
end
|
69
68
|
rescue => e
|
70
69
|
puts "tables failed to create: #{e}"
|
@@ -72,7 +71,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
72
71
|
begin
|
73
72
|
create_table "cm_test.baskets" do |t|
|
74
73
|
t.string :name
|
75
|
-
t.timestamps
|
76
74
|
end
|
77
75
|
rescue => e
|
78
76
|
puts "tables failed to create: #{e}"
|
@@ -81,7 +79,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
81
79
|
create_table "cm_test.fruit_baskets" do |t|
|
82
80
|
t.integer :fruit_id
|
83
81
|
t.integer :basket_id
|
84
|
-
t.timestamps
|
85
82
|
end
|
86
83
|
rescue => e
|
87
84
|
puts "tables failed to create: #{e}"
|
@@ -90,7 +87,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
90
87
|
create_table "cm_test.regions" do |t|
|
91
88
|
t.string :name
|
92
89
|
t.integer :type_id
|
93
|
-
t.timestamps
|
94
90
|
end
|
95
91
|
rescue => e
|
96
92
|
puts "tables failed to create: #{e}"
|
@@ -98,7 +94,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
98
94
|
begin
|
99
95
|
create_table "cm_test.types" do |t|
|
100
96
|
t.string :name
|
101
|
-
t.timestamps
|
102
97
|
end
|
103
98
|
rescue => e
|
104
99
|
puts "tables failed to create: #{e}"
|
@@ -117,7 +112,6 @@ class TestMigrations < ActiveRecord::Migration
|
|
117
112
|
begin
|
118
113
|
create_table "cm_user_test.types" do |t|
|
119
114
|
t.string :name
|
120
|
-
t.timestamps
|
121
115
|
end
|
122
116
|
rescue => e
|
123
117
|
puts "tables failed to create: #{e}"
|
data/spec/lib/builder_spec.rb
CHANGED
@@ -18,14 +18,14 @@ describe ConnectionManager::Builder do
|
|
18
18
|
ConnectionManager::Builder.build_connection_class("MyConnectionClass", :test)
|
19
19
|
end
|
20
20
|
it "should add a class with supplied class name to ConnectionManager::Builder" do
|
21
|
-
expect(defined?(MyConnectionClass)).to
|
21
|
+
expect(defined?(MyConnectionClass)).to eql("constant")
|
22
22
|
expect(MyConnectionClass).to be_a(Class)
|
23
23
|
end
|
24
24
|
it "should have a super class of ActiveRecord::Base" do
|
25
25
|
expect(MyConnectionClass.superclass).to eql(ActiveRecord::Base)
|
26
26
|
end
|
27
27
|
it "should be an abstract class" do
|
28
|
-
expect(MyConnectionClass.abstract_class?).to
|
28
|
+
expect(MyConnectionClass.abstract_class?).to eql(true)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -11,15 +11,15 @@ describe ConnectionManager::AbstractAdapter do
|
|
11
11
|
describe '#cross_schema_support?' do
|
12
12
|
it "should be true for Mysql" do
|
13
13
|
@con.stubs(:config).returns({:adapter => 'mysql'})
|
14
|
-
expect(@con
|
14
|
+
expect(@con).to be_cross_schema_support
|
15
15
|
end
|
16
16
|
it "should be true for Postgres" do
|
17
17
|
@con.stubs(:config).returns({:adapter => 'postgresql'})
|
18
|
-
expect(@con
|
18
|
+
expect(@con).to be_cross_schema_support
|
19
19
|
end
|
20
20
|
it "should be true for SQL server" do
|
21
21
|
@con.stubs(:config).returns({:adapter => 'sqlserver'})
|
22
|
-
expect(@con
|
22
|
+
expect(@con).to be_cross_schema_support
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -4,10 +4,10 @@ require 'spec_helper'
|
|
4
4
|
describe ActiveRecord::Base do
|
5
5
|
describe '#table_exists?' do
|
6
6
|
it "should return true for unquoted full_names" do
|
7
|
-
expect(Fruit.connection.table_exists?('cm_test.fruits')).to
|
7
|
+
expect(Fruit.connection.table_exists?('cm_test.fruits')).to eql(true)
|
8
8
|
end
|
9
9
|
it "should return true for table only names" do
|
10
|
-
expect(Fruit.connection.table_exists?('fruits')).to
|
10
|
+
expect(Fruit.connection.table_exists?('fruits')).to eql(true)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -47,18 +47,18 @@ describe ConnectionManager::ConnectionHandling do
|
|
47
47
|
|
48
48
|
describe '#establish_connection' do
|
49
49
|
it "should register class as connection class" do
|
50
|
-
expect(ActiveRecord::Base.managed_connection_classes.include?("MyConnectionClass")).to
|
50
|
+
expect(ActiveRecord::Base.managed_connection_classes.include?("MyConnectionClass")).to eql(true)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
describe '#establish_managed_connection' do
|
55
55
|
context 'the connection class' do
|
56
56
|
it "should create abstract class" do
|
57
|
-
expect(MyManagedConnectionClass.abstract_class).to
|
57
|
+
expect(MyManagedConnectionClass.abstract_class).to eql(true)
|
58
58
|
end
|
59
59
|
|
60
60
|
it "should check in the connection" do
|
61
|
-
expect(ActiveRecord::Base.managed_connection_classes.include?("MyManagedConnectionClass")).to
|
61
|
+
expect(ActiveRecord::Base.managed_connection_classes.include?("MyManagedConnectionClass")).to eql(true)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -9,14 +9,14 @@ describe ActiveRecord::Base do
|
|
9
9
|
|
10
10
|
describe '#joins' do
|
11
11
|
it "should work" do
|
12
|
-
@user.foos.blank?.should
|
12
|
+
@user.foos.blank?.should eql(false)
|
13
13
|
found = Foo.joins(:cm_user).select('cm_users.name AS user_name').where('cm_users.id = ?',@user.id).first
|
14
14
|
expect(found.user_name).to_not be_blank
|
15
15
|
end
|
16
16
|
end
|
17
17
|
describe '#includes' do
|
18
18
|
before(:each) do
|
19
|
-
@user.foos.blank?.should
|
19
|
+
@user.foos.blank?.should eql(false)
|
20
20
|
search = Foo.includes(:cm_user).where('cm_users.id = ?',@user.id)
|
21
21
|
search = search.references(:cm_user) if search.respond_to?(:references)
|
22
22
|
@found = search.first
|
@@ -12,7 +12,7 @@ describe ConnectionManager::Replication do
|
|
12
12
|
ActiveRecord::Base.stubs(:replication_connections).returns({:masters => [], :slaves => []})
|
13
13
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.any_instance.stubs(:slave_keys).returns([])
|
14
14
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.any_instance.stubs(:master_keys).returns([])
|
15
|
-
expect(lambda { Fruit.replicated }).to raise_error
|
15
|
+
expect(lambda { Fruit.replicated }).to raise_error(ArgumentError)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should not raise an exception if no connections are empty, but connection.replication_keys are not blank" do
|
data/spec/lib/shards_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe ConnectionManager::Shards do
|
|
17
17
|
a = Fruit.shards do |shard|
|
18
18
|
shard.where(:id => fruit.id).first
|
19
19
|
end
|
20
|
-
expect(klasses.include?(a[0].class.name)).to
|
20
|
+
expect(klasses.include?(a[0].class.name)).to eql(true)
|
21
21
|
end
|
22
22
|
|
23
23
|
|
@@ -34,7 +34,7 @@ describe ConnectionManager::Shards do
|
|
34
34
|
cfruit = Fruit.shards do |shard|
|
35
35
|
shard.where('id = ?', fruit.id).first
|
36
36
|
end
|
37
|
-
expect((afruit == bfruit && bfruit == cfruit)).to
|
37
|
+
expect((afruit == bfruit && bfruit == cfruit)).to eql(true)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/spec/lib/using_spec.rb
CHANGED
@@ -43,7 +43,7 @@ describe ConnectionManager::Using do
|
|
43
43
|
context 'A shard like connection' do
|
44
44
|
it "should use other connection" do
|
45
45
|
fruit = FactoryGirl.create(:fruit)
|
46
|
-
expect(Fruit.using("OtherConnection").where(:name => fruit.name).exists?).to
|
46
|
+
expect(Fruit.using("OtherConnection").where(:name => fruit.name).exists?).to eql(false)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Mckinney
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -51,7 +51,7 @@ dependencies:
|
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '5.0'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: concurrent-ruby
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
@@ -70,14 +70,28 @@ dependencies:
|
|
70
70
|
requirements:
|
71
71
|
- - "~>"
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: '
|
73
|
+
version: '3.0'
|
74
74
|
type: :development
|
75
75
|
prerelease: false
|
76
76
|
version_requirements: !ruby/object:Gem::Requirement
|
77
77
|
requirements:
|
78
78
|
- - "~>"
|
79
79
|
- !ruby/object:Gem::Version
|
80
|
-
version: '
|
80
|
+
version: '3.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rspec-autotest
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
81
95
|
- !ruby/object:Gem::Dependency
|
82
96
|
name: autotest
|
83
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,16 +172,19 @@ extra_rdoc_files: []
|
|
158
172
|
files:
|
159
173
|
- ".gitignore"
|
160
174
|
- ".rspec"
|
175
|
+
- ".travis.yml"
|
161
176
|
- CHANGE.md
|
162
177
|
- Gemfile
|
163
178
|
- LICENSE.txt
|
164
179
|
- README.md
|
165
180
|
- Rakefile
|
166
181
|
- connection_manager.gemspec
|
182
|
+
- gemfiles/4.0.gemfile
|
183
|
+
- gemfiles/4.1.gemfile
|
184
|
+
- gemfiles/4.2.gemfile
|
167
185
|
- lib/connection_manager.rb
|
168
186
|
- lib/connection_manager/builder.rb
|
169
187
|
- lib/connection_manager/connection_adapters/abstract_adapter.rb
|
170
|
-
- lib/connection_manager/connection_adapters/mysql_adapter.rb
|
171
188
|
- lib/connection_manager/connection_handling.rb
|
172
189
|
- lib/connection_manager/core.rb
|
173
190
|
- lib/connection_manager/querying.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module ConnectionManager
|
2
|
-
module MysqlAdapter
|
3
|
-
|
4
|
-
# Force all tables to be cached for the life connection
|
5
|
-
def cached_tables
|
6
|
-
@cached_tables ||= {}
|
7
|
-
end
|
8
|
-
|
9
|
-
def new_tables(name = nil, database = nil, like =nil)
|
10
|
-
return cached_tables[database] if cached_tables[database] && like.nil?
|
11
|
-
cached_tables[database] ||= []
|
12
|
-
return [like] if like && cached_tables[database].include?(like)
|
13
|
-
sql = "SHOW TABLES "
|
14
|
-
sql << "IN #{database} " if database
|
15
|
-
sql << "LIKE #{quote(like)}" if like
|
16
|
-
result = execute(sql, 'SCHEMA')
|
17
|
-
cached_tables[database] = (cached_tables[database] | result.collect { |field| field[0] }).compact
|
18
|
-
end
|
19
|
-
alias :tables :new_tables
|
20
|
-
|
21
|
-
|
22
|
-
# We have to clean the name of '`' and fetch table name with schema
|
23
|
-
def table_exists?(name)
|
24
|
-
return false unless name
|
25
|
-
name = name.to_s
|
26
|
-
schema, table = name.split('.', 2)
|
27
|
-
unless table # A table was provided without a schema
|
28
|
-
table = schema
|
29
|
-
schema = nil
|
30
|
-
end
|
31
|
-
new_tables(nil, schema, table).include?(table)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1
|
36
|
-
require 'active_record/connection_adapters/mysql2_adapter'
|
37
|
-
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include,(ConnectionManager::MysqlAdapter)) if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
|
38
|
-
ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:include,(ConnectionManager::MysqlAdapter))
|
39
|
-
end
|