connection_manager 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +2 -2
- data/README.md +1 -1
- data/lib/connection_manager/associations.rb +0 -1
- data/lib/connection_manager/connections.rb +25 -24
- data/lib/connection_manager/method_recorder.rb +57 -0
- data/lib/connection_manager/{replication_builder.rb → secondary_connection_builder.rb} +58 -29
- data/lib/connection_manager/version.rb +1 -1
- data/lib/connection_manager.rb +7 -2
- data/spec/database.yml +7 -1
- data/spec/integration/{replication_builder_spec.rb → secondary_connection_builder_spec.rb} +3 -4
- data/spec/lib/associations_spec.rb +2 -1
- data/spec/lib/connections_spec.rb +12 -13
- data/spec/lib/method_recorder_spec.rb +39 -0
- data/spec/lib/{replication_builder_spec.rb → secondary_connection_builder_spec.rb} +8 -8
- metadata +22 -19
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
connection_manager (0.
|
4
|
+
connection_manager (0.2.0)
|
5
5
|
activerecord (~> 3.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -39,7 +39,7 @@ GEM
|
|
39
39
|
rspec-expectations (2.7.0)
|
40
40
|
diff-lcs (~> 1.1.2)
|
41
41
|
rspec-mocks (2.7.0)
|
42
|
-
sqlite3 (1.3.
|
42
|
+
sqlite3 (1.3.5)
|
43
43
|
tzinfo (0.3.31)
|
44
44
|
|
45
45
|
PLATFORMS
|
data/README.md
CHANGED
@@ -119,7 +119,7 @@ available slave connections each time it is called using a different connection
|
|
119
119
|
User.slave.where(['created_at BETWEEN ? and ?',Time.now - 5.days, Time.now]).all => returns results from slave_2_user_data_development
|
120
120
|
|
121
121
|
## TODO's
|
122
|
-
* sharding
|
122
|
+
* sharding - IN 2.0 AS BETA
|
123
123
|
|
124
124
|
## Other activerecord Connection gems
|
125
125
|
* [Octopus](https://github.com/tchandy/octopus)
|
@@ -3,7 +3,7 @@ module ConnectionManager
|
|
3
3
|
class << self
|
4
4
|
@connection_keys
|
5
5
|
@all
|
6
|
-
@
|
6
|
+
@secondary_connections
|
7
7
|
@env
|
8
8
|
|
9
9
|
def env
|
@@ -16,7 +16,7 @@ module ConnectionManager
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Get the current environment if defined
|
19
|
-
# Check for Rails, check for RACK_ENV,
|
19
|
+
# Check for Rails, check for RACK_ENV, default to 'development'
|
20
20
|
def fetch_env
|
21
21
|
return Rails.env if defined?(Rails)
|
22
22
|
return RACK_ENV if defined?(RACK_ENV)
|
@@ -36,23 +36,23 @@ module ConnectionManager
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Holds connections
|
39
|
-
def
|
40
|
-
@
|
39
|
+
def secondary_connections
|
40
|
+
@secondary_connections ||= {}
|
41
41
|
end
|
42
42
|
|
43
43
|
# Returns the database value given a connection key from the database.yml
|
44
44
|
def database_name_from_yml(name_from_yml)
|
45
|
-
ActiveRecord::Base.configurations[name_from_yml]['database']
|
45
|
+
clean_db_name(ActiveRecord::Base.configurations[name_from_yml]['database'])
|
46
46
|
end
|
47
|
-
|
48
|
-
def clean_sqlite_db_name(name)
|
47
|
+
|
48
|
+
def clean_sqlite_db_name(name,remove_env=true)
|
49
49
|
new_name = "#{name}".gsub(/(\.sqlite3$)/,'')
|
50
50
|
new_name = new_name.split("/").last
|
51
|
-
new_name.gsub!(Regexp.new("(\_#{env}$)"),'')
|
51
|
+
new_name.gsub!(Regexp.new("(\_#{env}$)"),'') if remove_env
|
52
52
|
new_name
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
55
|
+
def clean_db_name(name)
|
56
56
|
new_name = "#{name}".gsub(Regexp.new("#{env}$"),'')
|
57
57
|
if new_name.blank?
|
58
58
|
new_name = "#{database_name_from_yml(name)}"
|
@@ -64,27 +64,27 @@ module ConnectionManager
|
|
64
64
|
# Given an connection key name from the database.yml, returns the string
|
65
65
|
# equivelent of the class name for that entry.
|
66
66
|
def connection_class_name(name_from_yml)
|
67
|
-
new_class_name =
|
67
|
+
new_class_name = clean_db_name(name_from_yml)
|
68
68
|
new_class_name = new_class_name.gsub(/\_/,' ').titleize.gsub(/ /,'')
|
69
69
|
new_class_name << "Connection"
|
70
70
|
new_class_name
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
74
|
-
rep_name =
|
73
|
+
def secondary_key(name_from_yml)
|
74
|
+
rep_name = clean_db_name(name_from_yml)
|
75
75
|
rep_name.gsub!(/(\_)+(\d+)/,'')
|
76
76
|
rep_name.to_sym
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
80
|
-
key =
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
def add_secondary_connection(name_from_yml,new_connection)
|
80
|
+
key = secondary_key(name_from_yml)
|
81
|
+
secondary_connections[key] ||= []
|
82
|
+
secondary_connections[key] << new_connection
|
83
|
+
secondary_connections
|
84
84
|
end
|
85
85
|
|
86
|
-
def
|
87
|
-
|
86
|
+
def available_secondary_connections(rep_collection_key)
|
87
|
+
secondary_connections[(rep_collection_key.gsub(Regexp.new("(\_#{env}$)"),'')).to_sym]
|
88
88
|
end
|
89
89
|
|
90
90
|
# Sets class instance attributes, then builds connection classes, while populating
|
@@ -95,7 +95,7 @@ module ConnectionManager
|
|
95
95
|
end
|
96
96
|
connection_keys.each do |connection|
|
97
97
|
new_connection = connection_class_name(connection)
|
98
|
-
|
98
|
+
add_secondary_connection(connection,new_connection)
|
99
99
|
build_connection_class(new_connection,connection)
|
100
100
|
end
|
101
101
|
all
|
@@ -104,12 +104,13 @@ module ConnectionManager
|
|
104
104
|
# Addes a conneciton subclass to Connections using the supplied
|
105
105
|
# class name and connection key from database.yml
|
106
106
|
def build_connection_class(class_name,connection_key)
|
107
|
-
|
108
|
-
class #{class_name} < ActiveRecord::Base
|
107
|
+
klass = Class.new(ActiveRecord::Base) do
|
109
108
|
self.abstract_class = true
|
110
|
-
establish_connection("#{connection_key}")
|
111
109
|
end
|
112
|
-
|
110
|
+
new_connection_class = Object.const_set(class_name, klass)
|
111
|
+
new_connection_class.establish_connection(connection_key)
|
112
|
+
|
113
|
+
(const_set class_name, new_connection_class)
|
113
114
|
all << class_name
|
114
115
|
end
|
115
116
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
module ConnectionManager
|
3
|
+
class MethodRecorder
|
4
|
+
attr_accessor :classes_to_call
|
5
|
+
|
6
|
+
def initialize(classes_to_call=[])
|
7
|
+
self.classes_to_call = classes_to_call
|
8
|
+
end
|
9
|
+
|
10
|
+
# A place to store our methods and thier variables
|
11
|
+
def recordings
|
12
|
+
@recordings ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute_recordings
|
16
|
+
results = []
|
17
|
+
classes_to_call.each do |class_to_call|
|
18
|
+
called = nil
|
19
|
+
recordings.each do |name,args|
|
20
|
+
args = args[0] if [Array, Hash].include?args[0].class
|
21
|
+
if called.nil?
|
22
|
+
if args.blank?
|
23
|
+
called = class_to_call.send(name.to_sym)
|
24
|
+
else
|
25
|
+
called = class_to_call.send(name.to_sym, args)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
if args.blank?
|
29
|
+
called = called.send(name.to_sym)
|
30
|
+
else
|
31
|
+
called = called.send(name.to_sym, args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if called.is_a?(Array)
|
36
|
+
results = (results | called)
|
37
|
+
else
|
38
|
+
results << called
|
39
|
+
end
|
40
|
+
end
|
41
|
+
results
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create recorder classes for methods that might be called on a ActiveRecord
|
45
|
+
# model in the process of building a query
|
46
|
+
(ActiveRecord::FinderMethods.instance_methods | ActiveRecord::QueryMethods.instance_methods).each do |method|
|
47
|
+
define_method(method) do |*args|
|
48
|
+
recordings[method] = args
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute
|
54
|
+
execute_recordings
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module ConnectionManager
|
2
|
-
module
|
2
|
+
module SecondaryConnectionBuilder
|
3
3
|
|
4
4
|
def database_name
|
5
5
|
"#{connection.instance_variable_get(:@config)[:database].to_s}"
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def secondary_association_options(method,association,class_name,options={})
|
9
9
|
new_options = {}.merge(options)
|
10
10
|
if new_options[:class_name].blank?
|
11
11
|
new_options[:class_name] = "#{association.to_s.singularize.classify}::#{class_name}"
|
@@ -19,14 +19,14 @@ module ConnectionManager
|
|
19
19
|
new_options
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def build_secondary_associations(class_name)
|
23
23
|
str = ""
|
24
24
|
defined_associations.each do |method,defs|
|
25
25
|
unless defs.blank?
|
26
26
|
defs.each do |association,options|
|
27
27
|
options = {} if options.blank?
|
28
28
|
unless options[:no_readonly] || options[:class_name].to_s.match("::#{class_name}")
|
29
|
-
str << "#{method.to_s} :#{association}, #{
|
29
|
+
str << "#{method.to_s} :#{association}, #{secondary_association_options(method,association,class_name,options)};"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -34,28 +34,36 @@ module ConnectionManager
|
|
34
34
|
str
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
37
|
+
def secondary_connection_classes(options)
|
38
38
|
if options[:using] && options[:using].is_a?(Array)
|
39
39
|
connection_classes = options[:using].collect{|c| Connections.connection_class_name(c)}
|
40
40
|
else
|
41
41
|
rep_name = "#{options[:name].to_s}_#{Connections.clean_sqlite_db_name(database_name)}"
|
42
|
-
connection_classes = Connections.
|
42
|
+
connection_classes = Connections.available_secondary_connections(rep_name)
|
43
43
|
end
|
44
44
|
connection_classes
|
45
45
|
end
|
46
|
-
|
47
46
|
|
47
|
+
def replicated(*settings)
|
48
|
+
options = {:name => "slave", :readonly => true, :replication => true}.merge(settings.extract_options!)
|
49
|
+
build_secondary_connections(options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def sharded(*settings)
|
53
|
+
options = {:name => "shard", :readonly => false, :shards => true}.merge(settings.extract_options!)
|
54
|
+
build_secondary_connections(options)
|
55
|
+
end
|
56
|
+
|
48
57
|
# Adds subclass with the class name of the type provided in the options, which
|
49
58
|
# defaults to 'slave' if blank, that uses the connection from a connection class.
|
50
59
|
# If :database option is blank?, replicated will assume the database.yml has
|
51
60
|
# slave connections defined as: slave_database_name_test or slave_1_database_name_test,
|
52
|
-
# where slave_1 is the
|
61
|
+
# where slave_1 is the secondary instance, 'database_name' is the actual
|
53
62
|
# name of the database and '_test' is the Rails environment
|
54
|
-
def
|
55
|
-
|
56
|
-
connection_classes = replication_connection_classes(options)
|
63
|
+
def build_secondary_connections(options={})
|
64
|
+
connection_classes = secondary_connection_classes(options)
|
57
65
|
if connection_classes.blank?
|
58
|
-
raise ArgumentError, " a
|
66
|
+
raise ArgumentError, " a secondary connection was not found. Check your database.yml."
|
59
67
|
else
|
60
68
|
connection_methods = []
|
61
69
|
connection_classes.each do |c|
|
@@ -64,32 +72,34 @@ module ConnectionManager
|
|
64
72
|
method_name = method_name.insert(method_name.index(/\d/),"_")
|
65
73
|
class_name = method_name.classify
|
66
74
|
connection_methods << method_name.to_sym
|
67
|
-
|
68
|
-
|
75
|
+
build_secondary_class(class_name,c,options)
|
76
|
+
build_single_secondary_method(method_name,class_name)
|
77
|
+
set_table_name_for_joins
|
69
78
|
end
|
70
79
|
end
|
71
|
-
|
72
|
-
|
80
|
+
|
81
|
+
build_slaves_method(connection_methods) if options[:replication]
|
82
|
+
build_shards_method(connection_methods) if options[:shards]
|
73
83
|
end
|
74
84
|
|
75
85
|
# Creats a subclass that inherets from the model. The default model_name
|
76
86
|
# class method is overriden to return the super's name, which ensures rails
|
77
|
-
# helpers like link_to called on a
|
78
|
-
# master database. If options include readonly,
|
87
|
+
# helpers like link_to called on a secondary stance generate a url for the
|
88
|
+
# master database. If options include readonly, build_secondary_class also
|
79
89
|
# overrides the rails "readonly?" method to ensure saves are prevented.
|
80
|
-
#
|
90
|
+
# secondary class can be called directly for operaitons.
|
81
91
|
# Usage:
|
82
92
|
# User::Slave1.where(:id => 1).first => returns results from slave_1 database
|
83
|
-
# User::Slave2.where(:id => 2).first => returns results from
|
84
|
-
|
93
|
+
# User::Slave2.where(:id => 2).first => returns results from slave_2 database
|
94
|
+
# User::Shard1.where(:id => 2).first => returns results from slave_1 database
|
95
|
+
def build_secondary_class(class_name,connection_name,options)
|
85
96
|
class_eval <<-STR, __FILE__, __LINE__
|
86
97
|
class #{class_name} < self
|
87
|
-
#{
|
98
|
+
#{build_secondary_associations(class_name)}
|
88
99
|
class << self
|
89
100
|
def connection
|
90
101
|
Connections::#{connection_name}.connection
|
91
102
|
end
|
92
|
-
|
93
103
|
def model_name
|
94
104
|
ActiveModel::Name.new(#{name})
|
95
105
|
end
|
@@ -99,11 +109,15 @@ module ConnectionManager
|
|
99
109
|
STR
|
100
110
|
end
|
101
111
|
|
102
|
-
|
112
|
+
def set_table_name_for_joins
|
113
|
+
self.table_name_prefix = "#{database_name}." unless database_name.match(/\.sqlite3$/)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Adds as class method to call a specific secondary conneciton.
|
103
117
|
# Usage:
|
104
118
|
# User.slave_1.where(:id => 2).first => returns results from slave_1 database
|
105
119
|
# User.slave_2.where(:id => 2).first => returns results from slave_2 database
|
106
|
-
def
|
120
|
+
def build_single_secondary_method(method_name,class_name)
|
107
121
|
class_eval <<-STR, __FILE__, __LINE__
|
108
122
|
class << self
|
109
123
|
def #{method_name}
|
@@ -117,21 +131,36 @@ module ConnectionManager
|
|
117
131
|
# on each call.
|
118
132
|
# Usage:
|
119
133
|
# User.slave.where(:id => 2).first => can return results from slave_1 or slave_2
|
120
|
-
def
|
134
|
+
def build_slaves_method(connection_methods)
|
121
135
|
class_eval <<-STR, __FILE__, __LINE__
|
122
136
|
@connection_methods = #{connection_methods}
|
123
137
|
class << self
|
124
|
-
def
|
138
|
+
def slaves
|
125
139
|
current = @connection_methods.shift
|
126
140
|
@connection_methods << current
|
127
141
|
send(current)
|
128
142
|
end
|
143
|
+
alias :slave :slaves
|
129
144
|
def connection_methods
|
130
145
|
@connection_methods
|
131
146
|
end
|
132
147
|
end
|
133
148
|
STR
|
134
149
|
end
|
150
|
+
|
151
|
+
# add a class method that shifts through available connections methods
|
152
|
+
# on each call.
|
153
|
+
# Usage:
|
154
|
+
# User.shards.where(:id => 2).first => can return results from slave_1 or slave_2
|
155
|
+
def build_shards_method(connection_methods)
|
156
|
+
class_eval <<-STR, __FILE__, __LINE__
|
157
|
+
@shard_connection_methods = #{connection_methods}
|
158
|
+
class << self
|
159
|
+
def shards
|
160
|
+
ConnectionManager::MethodRecorder.new(@shard_connection_methods)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
STR
|
164
|
+
end
|
135
165
|
end
|
136
|
-
end
|
137
|
-
ActiveRecord::Base.extend(ConnectionManager::ReplicationBuilder)
|
166
|
+
end
|
data/lib/connection_manager.rb
CHANGED
@@ -4,7 +4,12 @@ module ConnectionManager
|
|
4
4
|
require 'active_record'
|
5
5
|
require 'connection_manager/connections'
|
6
6
|
require 'connection_manager/associations'
|
7
|
-
require 'connection_manager/
|
8
|
-
require 'connection_manager/
|
7
|
+
require 'connection_manager/secondary_connection_builder'
|
8
|
+
require 'connection_manager/method_recorder'
|
9
|
+
require 'connection_manager/connection_manager_railtie.rb' if defined?(Rails)
|
10
|
+
|
11
|
+
ActiveRecord::Base.extend(ConnectionManager::Associations)
|
12
|
+
ActiveRecord::Base.extend(ConnectionManager::SecondaryConnectionBuilder)
|
13
|
+
|
9
14
|
end
|
10
15
|
|
data/spec/database.yml
CHANGED
@@ -2,11 +2,11 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
# Tests for associations build form replication.
|
4
4
|
|
5
|
-
describe ConnectionManager::
|
5
|
+
describe ConnectionManager::SecondaryConnectionBuilder do
|
6
6
|
before(:all) do
|
7
7
|
# Make sure connections recreated in other tests do not presist to current
|
8
8
|
ConnectionManager::Connections.all.clear
|
9
|
-
ConnectionManager::Connections.
|
9
|
+
ConnectionManager::Connections.secondary_connections.clear
|
10
10
|
|
11
11
|
#Initialize
|
12
12
|
ConnectionManager::Connections.initialize(:env => 'test')
|
@@ -44,8 +44,7 @@ describe ConnectionManager::ReplicationBuilder do
|
|
44
44
|
context "slave" do
|
45
45
|
context('belongs_to') do
|
46
46
|
it "should return the same belongs to object as master" do
|
47
|
-
fruit = Factory.create(:fruit)
|
48
|
-
|
47
|
+
fruit = Factory.create(:fruit)
|
49
48
|
slave_fruit = Fruit.slave.where(:id => fruit.id).first
|
50
49
|
slave_fruit.region.id.should eql(fruit.region.id)
|
51
50
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
|
3
2
|
class Foo < ActiveRecord::Base
|
4
3
|
belongs_to :that
|
5
4
|
has_many :foo_bars
|
6
5
|
has_many :bars, :through => :foo_bars
|
7
6
|
has_one :noob
|
8
7
|
end
|
8
|
+
|
9
9
|
class Bar < ActiveRecord::Base
|
10
10
|
has_many :foo_bars
|
11
11
|
has_many :foos, :through => :foo_bars
|
12
12
|
end
|
13
|
+
|
13
14
|
describe ConnectionManager::Associations do
|
14
15
|
|
15
16
|
it "should add associations as keys to @defined_associations" do
|
@@ -10,20 +10,20 @@ describe ConnectionManager::Connections do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
context '#
|
13
|
+
context '#clean_db_name' do
|
14
14
|
context "when name is not just the Rails.env" do
|
15
15
|
it "should remove the Rails.env from the string" do
|
16
|
-
ConnectionManager::Connections.
|
16
|
+
ConnectionManager::Connections.clean_db_name("my_database_test").should eql("my_database")
|
17
17
|
end
|
18
18
|
end
|
19
19
|
context "when the name is only the Rails.env" do
|
20
20
|
it "should use the name of the database and remove the Rails.env" do
|
21
21
|
ConnectionManager::Connections.stubs(:database_name_from_yml).returns("my_database_test")
|
22
|
-
ConnectionManager::Connections.
|
22
|
+
ConnectionManager::Connections.clean_db_name("test").should eql("my_database")
|
23
23
|
end
|
24
24
|
it "should account for sqlite3 database name" do
|
25
25
|
ConnectionManager::Connections.stubs(:database_name_from_yml).returns("db/my_database_test.sqlite3")
|
26
|
-
ConnectionManager::Connections.
|
26
|
+
ConnectionManager::Connections.clean_db_name("test").should eql("my_database")
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -50,7 +50,7 @@ describe ConnectionManager::Connections do
|
|
50
50
|
before(:all) do
|
51
51
|
# Make sure connections recreated in other tests do not presist to tests tests
|
52
52
|
ConnectionManager::Connections.all.clear
|
53
|
-
ConnectionManager::Connections.
|
53
|
+
ConnectionManager::Connections.secondary_connections.clear
|
54
54
|
|
55
55
|
#Initialize
|
56
56
|
ConnectionManager::Connections.initialize(:env => 'test')
|
@@ -58,18 +58,18 @@ describe ConnectionManager::Connections do
|
|
58
58
|
|
59
59
|
context '#all' do
|
60
60
|
it "should return the database.yml entries for the current rails environment" do
|
61
|
-
ConnectionManager::Connections.all.should eql(["CmConnection",
|
62
|
-
"Slave1CmConnection", "Slave2CmConnection"])
|
61
|
+
ConnectionManager::Connections.all.should eql(["CmConnection",
|
62
|
+
"Slave1CmConnection", "Slave2CmConnection", "Shard1CmConnection"])
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
context '#
|
66
|
+
context '#secondary_connections' do
|
67
67
|
it "should return a hash where the keys are the generic undescored names for all connections" do
|
68
|
-
ConnectionManager::Connections.
|
69
|
-
should eql([:cm, :slave_cm])
|
68
|
+
ConnectionManager::Connections.secondary_connections.keys.
|
69
|
+
should eql([:cm, :slave_cm, :shard_cm])
|
70
70
|
end
|
71
71
|
it "should return a hash where the values are an array of connection class names as strings" do
|
72
|
-
first_value = ConnectionManager::Connections.
|
72
|
+
first_value = ConnectionManager::Connections.secondary_connections.values.first
|
73
73
|
first_value.class.should eql(Array)
|
74
74
|
defined?((ConnectionManager::Connections.class_eval(first_value[0]))).should be_true
|
75
75
|
end
|
@@ -86,8 +86,7 @@ describe ConnectionManager::Connections do
|
|
86
86
|
it "should have a super class of ActiveRecord::Base" do
|
87
87
|
ConnectionManager::Connections::MyConnectionClass.superclass.should eql(ActiveRecord::Base)
|
88
88
|
end
|
89
|
-
end
|
90
|
-
|
89
|
+
end
|
91
90
|
end
|
92
91
|
end
|
93
92
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ConnectionManager::MethodRecorder do
|
4
|
+
before(:each) do
|
5
|
+
class Fruit < ActiveRecord::Base
|
6
|
+
#include ConnectionManager::MethodRecorder
|
7
|
+
def self.shards
|
8
|
+
ConnectionManager::MethodRecorder.new([self])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
context 'shards'do
|
13
|
+
it "it should record methods" do
|
14
|
+
a = Fruit.shards.select('fruits.*').where(:id => 1).order('created_at').first
|
15
|
+
a.recordings.should eql({:select => ['fruits.*'], :where => [:id =>1], :order => ['created_at'], :first => []})
|
16
|
+
end
|
17
|
+
|
18
|
+
context '#execute' do
|
19
|
+
it "should return an array of results" do
|
20
|
+
a = Fruit.shards.first.execute
|
21
|
+
a.should be_a_kind_of(Array)
|
22
|
+
end
|
23
|
+
it "should execute the active record methods on the the provided class" do
|
24
|
+
fruit = Factory.create(:fruit)
|
25
|
+
class_to_call = Fruit
|
26
|
+
a = class_to_call.shards.where(:id => fruit.id).first.execute
|
27
|
+
a[0].should be_a_kind_of(class_to_call)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not matter how the where statement is formated" do
|
31
|
+
fruit = Factory.create(:fruit)
|
32
|
+
a = Fruit.shards.where(:id => fruit.id).first.execute
|
33
|
+
b = Fruit.shards.where(['id = ?', fruit.id]).first.execute
|
34
|
+
c = Fruit.shards.where('id = ?', fruit.id).first.execute
|
35
|
+
(a == b && b == c).should be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ConnectionManager::
|
3
|
+
describe ConnectionManager::SecondaryConnectionBuilder do
|
4
4
|
|
5
5
|
context '#database_name' do
|
6
6
|
it "should return the name of the database the model is using" do
|
@@ -11,28 +11,28 @@ describe ConnectionManager::ReplicationBuilder do
|
|
11
11
|
context '#other_association_options' do
|
12
12
|
|
13
13
|
it "should add :class_name options set to the replication subclass if :class_name is blank" do
|
14
|
-
options = Fruit.
|
14
|
+
options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
|
15
15
|
options[:class_name].should eql("Plant::Slave")
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should append :class_name with the replication subclass if :class_name is not bank" do
|
19
|
-
options = Fruit.
|
19
|
+
options = Fruit.secondary_association_options(:has_one, :plant, 'Slave', :class_name => 'Plant')
|
20
20
|
options[:class_name].should eql("Plant::Slave")
|
21
21
|
end
|
22
22
|
|
23
23
|
context "has_one or has_many" do
|
24
24
|
it "should add the :foreign_key if the :foreign_key options is not present" do
|
25
|
-
options = Fruit.
|
25
|
+
options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
|
26
26
|
options[:foreign_key].should eql('fruit_id')
|
27
|
-
options = Fruit.
|
27
|
+
options = Fruit.secondary_association_options(:has_many, :plant, 'Slave')
|
28
28
|
options[:foreign_key].should eql('fruit_id')
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
context '#
|
33
|
+
context '#secondary_connection_classes' do
|
34
34
|
it "should return the :using array with the array elements classified and append with Connection" do
|
35
|
-
Fruit.
|
35
|
+
Fruit.secondary_connection_classes({:using => ['slave_1_test_db','slave_2_test_db']}).
|
36
36
|
should eql(["Slave1TestDbConnection", "Slave2TestDbConnection"])
|
37
37
|
end
|
38
38
|
|
@@ -40,7 +40,7 @@ describe ConnectionManager::ReplicationBuilder do
|
|
40
40
|
|
41
41
|
context '#replicated' do
|
42
42
|
it "should raise an exception if no replication_connection_classes are found" do
|
43
|
-
Fruit.stubs(:
|
43
|
+
Fruit.stubs(:secondary_connection_classes).returns([])
|
44
44
|
lambda { Fruit.replicated }.should raise_error
|
45
45
|
end
|
46
46
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: connection_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-27 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70148774706040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70148774706040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: sqlite3
|
27
|
-
requirement: &
|
27
|
+
requirement: &70148774705620 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70148774705620
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70148774705160 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70148774705160
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: autotest
|
49
|
-
requirement: &
|
49
|
+
requirement: &70148774704740 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70148774704740
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: mocha
|
60
|
-
requirement: &
|
60
|
+
requirement: &70148774704320 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70148774704320
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: factory_girl
|
71
|
-
requirement: &
|
71
|
+
requirement: &70148774703900 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70148774703900
|
80
80
|
description: Simplifies connecting to Muliple and Replication databases with rails
|
81
81
|
and active_record
|
82
82
|
email:
|
@@ -97,16 +97,18 @@ files:
|
|
97
97
|
- lib/connection_manager/associations.rb
|
98
98
|
- lib/connection_manager/connection_manager_railtie.rb
|
99
99
|
- lib/connection_manager/connections.rb
|
100
|
-
- lib/connection_manager/
|
100
|
+
- lib/connection_manager/method_recorder.rb
|
101
|
+
- lib/connection_manager/secondary_connection_builder.rb
|
101
102
|
- lib/connection_manager/version.rb
|
102
103
|
- spec/database.yml
|
103
104
|
- spec/factories.rb
|
104
105
|
- spec/helpers/database_spec_helper.rb
|
105
106
|
- spec/helpers/models_spec_helper.rb
|
106
|
-
- spec/integration/
|
107
|
+
- spec/integration/secondary_connection_builder_spec.rb
|
107
108
|
- spec/lib/associations_spec.rb
|
108
109
|
- spec/lib/connections_spec.rb
|
109
|
-
- spec/lib/
|
110
|
+
- spec/lib/method_recorder_spec.rb
|
111
|
+
- spec/lib/secondary_connection_builder_spec.rb
|
110
112
|
- spec/spec.opts
|
111
113
|
- spec/spec_helper.rb
|
112
114
|
homepage: ''
|
@@ -139,9 +141,10 @@ test_files:
|
|
139
141
|
- spec/factories.rb
|
140
142
|
- spec/helpers/database_spec_helper.rb
|
141
143
|
- spec/helpers/models_spec_helper.rb
|
142
|
-
- spec/integration/
|
144
|
+
- spec/integration/secondary_connection_builder_spec.rb
|
143
145
|
- spec/lib/associations_spec.rb
|
144
146
|
- spec/lib/connections_spec.rb
|
145
|
-
- spec/lib/
|
147
|
+
- spec/lib/method_recorder_spec.rb
|
148
|
+
- spec/lib/secondary_connection_builder_spec.rb
|
146
149
|
- spec/spec.opts
|
147
150
|
- spec/spec_helper.rb
|