connection_manager 1.1.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/JoshMcKin/connection_manager.svg)](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
|