connection_manager 0.2.6 → 0.3.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.
- data/.gitignore +2 -1
- data/Gemfile +0 -1
- data/LICENSE.txt +1 -1
- data/README.md +115 -55
- data/Rakefile +2 -0
- data/connection_manager.gemspec +9 -3
- data/lib/connection_manager/connection_builder.rb +81 -0
- data/lib/connection_manager/connection_manager_railtie.rb +4 -3
- data/lib/connection_manager/helpers/abstract_adapter_helper.rb +40 -0
- data/lib/connection_manager/helpers/connection_helpers.rb +105 -0
- data/lib/connection_manager/{cross_schema_patch.rb → patches/cross_schema_patch.rb} +2 -3
- data/lib/connection_manager/replication.rb +165 -0
- data/lib/connection_manager/shards.rb +25 -0
- data/lib/connection_manager/using.rb +95 -0
- data/lib/connection_manager/version.rb +1 -1
- data/lib/connection_manager.rb +19 -8
- data/spec/factories.rb +7 -11
- data/spec/helpers/database_spec_helper.rb +52 -41
- data/spec/helpers/models_spec_helper.rb +23 -0
- data/spec/jdbcmysql_database.yml +55 -0
- data/spec/lib/connection_builder_spec.rb +29 -0
- data/spec/lib/connection_helpers_spec.rb +74 -0
- data/spec/lib/replication_spec.rb +134 -0
- data/spec/lib/shards_spec.rb +40 -0
- data/spec/lib/using_spec.rb +77 -0
- data/spec/mysql2_database.yml +28 -2
- data/spec/spec_helper.rb +20 -6
- metadata +42 -39
- data/Gemfile.lock +0 -56
- data/lib/connection_manager/associations.rb +0 -28
- data/lib/connection_manager/connections.rb +0 -124
- data/lib/connection_manager/method_recorder.rb +0 -57
- data/lib/connection_manager/secondary_connection_builder.rb +0 -180
- data/spec/integration/secondary_connection_builder_spec.rb +0 -115
- data/spec/lib/associations_spec.rb +0 -30
- data/spec/lib/connections_spec.rb +0 -92
- data/spec/lib/method_recorder_spec.rb +0 -43
- data/spec/lib/secondary_connection_builder_spec.rb +0 -77
@@ -1,124 +0,0 @@
|
|
1
|
-
module ConnectionManager
|
2
|
-
class Connections
|
3
|
-
class << self
|
4
|
-
@connection_keys
|
5
|
-
@all
|
6
|
-
@secondary_connections
|
7
|
-
@env
|
8
|
-
|
9
|
-
def env
|
10
|
-
@env ||= fetch_env
|
11
|
-
@env
|
12
|
-
end
|
13
|
-
|
14
|
-
def env=env
|
15
|
-
@env = env
|
16
|
-
end
|
17
|
-
|
18
|
-
def env_regex(with_underscore=true)
|
19
|
-
s = "#{env}$"
|
20
|
-
s.insert(0,"\_") if with_underscore
|
21
|
-
Regexp.new("(#{s})")
|
22
|
-
end
|
23
|
-
|
24
|
-
# Get the current environment if defined
|
25
|
-
# Check for Rails, check for RACK_ENV, default to 'development'
|
26
|
-
def fetch_env
|
27
|
-
return Rails.env if defined?(Rails)
|
28
|
-
return RACK_ENV if defined?(RACK_ENV)
|
29
|
-
"development"
|
30
|
-
end
|
31
|
-
|
32
|
-
# Grab only thoses connections that correspond to the current env. If env
|
33
|
-
# is blank it grabs all the connection keys
|
34
|
-
def connection_keys
|
35
|
-
@connection_keys ||= ActiveRecord::Base.configurations.keys.
|
36
|
-
select{|n| n.match(env_regex(false))}
|
37
|
-
end
|
38
|
-
|
39
|
-
# Contains all the connection classes built
|
40
|
-
def all
|
41
|
-
@all ||= []
|
42
|
-
end
|
43
|
-
|
44
|
-
# Holds connections
|
45
|
-
def secondary_connections
|
46
|
-
@secondary_connections ||= {}
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns the database value given a connection key from the database.yml
|
50
|
-
def database_name_from_yml(name_from_yml)
|
51
|
-
clean_db_name(ActiveRecord::Base.configurations[name_from_yml]['database'])
|
52
|
-
end
|
53
|
-
|
54
|
-
def clean_sqlite_db_name(name,remove_env=true)
|
55
|
-
new_name = "#{name}".gsub(/(\.sqlite3$)/,'')
|
56
|
-
new_name = new_name.split("/").last
|
57
|
-
new_name.gsub!(env_regex,'') if remove_env
|
58
|
-
new_name
|
59
|
-
end
|
60
|
-
|
61
|
-
def clean_db_name(name)
|
62
|
-
new_name = "#{name}".gsub(env_regex(false),'')
|
63
|
-
if new_name.blank?
|
64
|
-
new_name = "#{database_name_from_yml(name)}"
|
65
|
-
end
|
66
|
-
new_name = clean_sqlite_db_name(new_name)
|
67
|
-
new_name.gsub(/\_$/,'')
|
68
|
-
end
|
69
|
-
|
70
|
-
# Given an connection key name from the database.yml, returns the string
|
71
|
-
# equivelent of the class name for that entry.
|
72
|
-
def connection_class_name(name_from_yml)
|
73
|
-
new_class_name = clean_db_name(name_from_yml)
|
74
|
-
new_class_name = new_class_name.gsub(/\_/,' ').titleize.gsub(/ /,'')
|
75
|
-
new_class_name << "Connection"
|
76
|
-
new_class_name
|
77
|
-
end
|
78
|
-
|
79
|
-
def secondary_key(name_from_yml)
|
80
|
-
rep_name = clean_db_name(name_from_yml)
|
81
|
-
rep_name.gsub!(/(\_)+(\d+)/,'')
|
82
|
-
rep_name.to_sym
|
83
|
-
end
|
84
|
-
|
85
|
-
def add_secondary_connection(name_from_yml,new_connection)
|
86
|
-
key = secondary_key(name_from_yml)
|
87
|
-
secondary_connections[key] ||= []
|
88
|
-
secondary_connections[key] << new_connection
|
89
|
-
secondary_connections
|
90
|
-
end
|
91
|
-
|
92
|
-
def available_secondary_connections(rep_collection_key)
|
93
|
-
secondary_connections[(rep_collection_key.gsub(env_regex,'')).to_sym]
|
94
|
-
end
|
95
|
-
|
96
|
-
# Sets class instance attributes, then builds connection classes, while populating
|
97
|
-
# available_connctions and replication_connection
|
98
|
-
def initialize(options={})
|
99
|
-
options.each do |k,v|
|
100
|
-
send("#{k.to_s}=",v)
|
101
|
-
end
|
102
|
-
connection_keys.each do |connection|
|
103
|
-
new_connection = connection_class_name(connection)
|
104
|
-
add_secondary_connection(connection,new_connection)
|
105
|
-
build_connection_class(new_connection,connection)
|
106
|
-
end
|
107
|
-
all
|
108
|
-
end
|
109
|
-
|
110
|
-
# Addes a conneciton subclass to Connections using the supplied
|
111
|
-
# class name and connection key from database.yml
|
112
|
-
def build_connection_class(class_name,connection_key)
|
113
|
-
klass = Class.new(ActiveRecord::Base) do
|
114
|
-
self.abstract_class = true
|
115
|
-
end
|
116
|
-
new_connection_class = Object.const_set(class_name, klass)
|
117
|
-
new_connection_class.table_name_prefix=("#{ActiveRecord::Base.configurations[connection_key]['database']}.")
|
118
|
-
new_connection_class.establish_connection(connection_key)
|
119
|
-
(const_set class_name, new_connection_class)
|
120
|
-
all << class_name
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
@@ -1,57 +0,0 @@
|
|
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,180 +0,0 @@
|
|
1
|
-
module ConnectionManager
|
2
|
-
module SecondaryConnectionBuilder
|
3
|
-
|
4
|
-
def database_name
|
5
|
-
"#{connection.instance_variable_get(:@config)[:database].to_s}"
|
6
|
-
end
|
7
|
-
|
8
|
-
def secondary_association_options(method,association,class_name,options={})
|
9
|
-
new_options = {}.merge(options)
|
10
|
-
if new_options[:class_name].blank?
|
11
|
-
new_options[:class_name] = "#{association.to_s.singularize.classify}::#{class_name}"
|
12
|
-
else
|
13
|
-
new_options[:class_name] = "#{new_options[:class_name]}::#{class_name}"
|
14
|
-
end
|
15
|
-
|
16
|
-
if [:has_one,:has_many].include?(method) && new_options[:foreign_key].blank?
|
17
|
-
new_options[:foreign_key] = "#{table_name.singularize}_id"
|
18
|
-
end
|
19
|
-
new_options
|
20
|
-
end
|
21
|
-
|
22
|
-
def build_secondary_associations(class_name)
|
23
|
-
str = ""
|
24
|
-
defined_associations.each do |method,defs|
|
25
|
-
unless defs.blank?
|
26
|
-
defs.each do |association,options|
|
27
|
-
options = {} if options.blank?
|
28
|
-
unless options[:no_readonly] || options[:class_name].to_s.match("::#{class_name}")
|
29
|
-
str << "#{method.to_s} :#{association}, #{secondary_association_options(method,association,class_name,options)};"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
str
|
35
|
-
end
|
36
|
-
|
37
|
-
def secondary_connection_classes(options)
|
38
|
-
if options[:using] && options[:using].is_a?(Array)
|
39
|
-
connection_classes = options[:using].collect{|c| Connections.connection_class_name(c)}
|
40
|
-
else
|
41
|
-
rep_name = "#{options[:name].to_s}_#{Connections.clean_sqlite_db_name(database_name)}"
|
42
|
-
connection_classes = Connections.available_secondary_connections(rep_name)
|
43
|
-
end
|
44
|
-
connection_classes
|
45
|
-
end
|
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 shard(*settings)
|
53
|
-
options = {:name => "shard", :readonly => false, :shards => true}.merge(settings.extract_options!)
|
54
|
-
build_secondary_connections(options)
|
55
|
-
end
|
56
|
-
|
57
|
-
def child_connection_class?
|
58
|
-
false
|
59
|
-
end
|
60
|
-
# Adds subclass with the class name of the type provided in the options, which
|
61
|
-
# defaults to 'slave' if blank, that uses the connection from a connection class.
|
62
|
-
# If :database option is blank?, replicated will assume the database.yml has
|
63
|
-
# slave connections defined as: slave_database_name_test or slave_1_database_name_test,
|
64
|
-
# where slave_1 is the secondary instance, 'database_name' is the actual
|
65
|
-
# name of the database and '_test' is the Rails environment
|
66
|
-
def build_secondary_connections(options={})
|
67
|
-
unless name.match(/\:\:/)
|
68
|
-
connection_classes = secondary_connection_classes(options)
|
69
|
-
sub_classes = []
|
70
|
-
if connection_classes.blank?
|
71
|
-
raise ArgumentError, " a secondary connection was not found. Check your database.yml."
|
72
|
-
else
|
73
|
-
connection_methods = []
|
74
|
-
connection_classes.each do |c|
|
75
|
-
under_scored = c.underscore
|
76
|
-
method_name = under_scored.split("_")[0]
|
77
|
-
method_name = method_name.insert(method_name.index(/\d/),"_")
|
78
|
-
class_name = method_name.classify
|
79
|
-
connection_methods << method_name.to_sym
|
80
|
-
# set_table_name_for_joins
|
81
|
-
build_secondary_class(class_name,c,options)
|
82
|
-
build_single_secondary_method(method_name,class_name)
|
83
|
-
sub_classes << "#{self.name}::#{class_name}".constantize if options[:shards]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
build_slaves_method(connection_methods) if options[:replication]
|
87
|
-
build_shards_method(sub_classes) if options[:shards]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Creats a subclass that inherets from the model. The default model_name
|
92
|
-
# class method is overriden to return the super's name, which ensures rails
|
93
|
-
# helpers like link_to called on a secondary stance generate a url for the
|
94
|
-
# master database. If options include readonly, build_secondary_class also
|
95
|
-
# overrides the rails "readonly?" method to ensure saves are prevented.
|
96
|
-
# secondary class can be called directly for operaitons.
|
97
|
-
# Usage:
|
98
|
-
# User::Slave1.where(:id => 1).first => returns results from slave_1 database
|
99
|
-
# User::Slave2.where(:id => 2).first => returns results from slave_2 database
|
100
|
-
# User::Shard1.where(:id => 2).first => returns results from slave_1 database
|
101
|
-
def build_secondary_class(class_name,connection_name,options)
|
102
|
-
klass = Class.new(self) do
|
103
|
-
class << self
|
104
|
-
def model_name
|
105
|
-
ActiveModel::Name.new(superclass)
|
106
|
-
end
|
107
|
-
def child_connection_class?
|
108
|
-
true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
if (options[:name] == "readonly" || options[:readonly])
|
113
|
-
def readonly?
|
114
|
-
true
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
sub_class = const_set(class_name, klass)
|
120
|
-
sub_class.build_secondary_associations(class_name)
|
121
|
-
sub_class.class_eval <<-STR, __FILE__, __LINE__
|
122
|
-
class << self
|
123
|
-
def connection
|
124
|
-
Connections::#{connection_name}.connection
|
125
|
-
end
|
126
|
-
end
|
127
|
-
STR
|
128
|
-
connection_sub_classes << sub_class
|
129
|
-
sub_class
|
130
|
-
end
|
131
|
-
|
132
|
-
def connection_sub_classes
|
133
|
-
@connection_sub_classes ||= []
|
134
|
-
end
|
135
|
-
|
136
|
-
def set_table_name_for_joins
|
137
|
-
self.table_name_prefix = "#{database_name}." unless database_name.match(/\.sqlite3$/)
|
138
|
-
end
|
139
|
-
|
140
|
-
# Adds as class method to call a specific secondary conneciton.
|
141
|
-
# Usage:
|
142
|
-
# User.slave_1.where(:id => 2).first => returns results from slave_1 database
|
143
|
-
# User.slave_2.where(:id => 2).first => returns results from slave_2 database
|
144
|
-
def build_single_secondary_method(method_name,class_name)
|
145
|
-
self.class.instance_eval do
|
146
|
-
define_method method_name.to_s do
|
147
|
-
"#{name}::#{class_name}".constantize
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# add a class method that shifts through available connections methods
|
153
|
-
# on each call.
|
154
|
-
# Usage:
|
155
|
-
# User.slave.where(:id => 2).first => can return results from slave_1 or slave_2
|
156
|
-
def build_slaves_method(connection_methods)
|
157
|
-
@connection_methods = connection_methods
|
158
|
-
self.class.instance_eval do
|
159
|
-
define_method 'slaves' do
|
160
|
-
current = @connection_methods.shift
|
161
|
-
@connection_methods << current
|
162
|
-
send(current)
|
163
|
-
end
|
164
|
-
alias_method :slave, :slaves
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# add a class method that shifts through available connections methods
|
169
|
-
# on each call.
|
170
|
-
# Usage:
|
171
|
-
# User.shards.where(:id => 2).first => can return results from slave_1 or slave_2
|
172
|
-
def build_shards_method(connection_classes)
|
173
|
-
self.class.instance_eval do
|
174
|
-
define_method 'shards' do
|
175
|
-
ConnectionManager::MethodRecorder.new(connection_classes)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
# Tests for associations build form replication.
|
4
|
-
|
5
|
-
describe ConnectionManager::SecondaryConnectionBuilder do
|
6
|
-
before(:all) do
|
7
|
-
# Make sure connections recreated in other tests do not presist to current
|
8
|
-
ConnectionManager::Connections.all.clear
|
9
|
-
ConnectionManager::Connections.secondary_connections.clear
|
10
|
-
|
11
|
-
#Initialize
|
12
|
-
ConnectionManager::Connections.initialize(:env => 'test')
|
13
|
-
|
14
|
-
# Add Replication to models
|
15
|
-
Fruit.replicated
|
16
|
-
Basket.replicated
|
17
|
-
FruitBasket.replicated
|
18
|
-
Region.replicated :readonly => false
|
19
|
-
end
|
20
|
-
|
21
|
-
context "models that have been replicated" do
|
22
|
-
it "should respond to slave_1" do
|
23
|
-
Fruit.respond_to?(:slave_1).should be_true
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should respond to slave" do
|
27
|
-
Fruit.respond_to?(:slave).should be_true
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should have a subclass of Slave1" do
|
31
|
-
defined?(Fruit::Slave1).should be_true
|
32
|
-
end
|
33
|
-
|
34
|
-
context "subclasses" do
|
35
|
-
it "should have a superclass of the parent class" do
|
36
|
-
|
37
|
-
Fruit::Slave1.superclass.should eql(Fruit)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "readonly" do
|
43
|
-
end
|
44
|
-
|
45
|
-
context "slave" do
|
46
|
-
context('belongs_to') do
|
47
|
-
it "should return the same belongs to object as master" do
|
48
|
-
fruit = Factory.create(:fruit)
|
49
|
-
slave_fruit = Fruit.slave.where(:id => fruit.id).first
|
50
|
-
slave_fruit.region.id.should eql(fruit.region.id)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context('has_one') do
|
55
|
-
it "should return the same has_on object as master" do
|
56
|
-
region = Factory.create(:fruit).region
|
57
|
-
slave_region = Region.slave.where(:id => region.id).first
|
58
|
-
slave_region.fruit.id.should eql(region.fruit.id)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
context('has_many') do
|
63
|
-
it "should return the same has_many objects as master" do
|
64
|
-
fruit = Factory.create(:fruit)
|
65
|
-
3.times do
|
66
|
-
Factory.create(:fruit_basket, :fruit_id => fruit.id)
|
67
|
-
end
|
68
|
-
fruit.fruit_baskets.length.should eql(3)
|
69
|
-
slave_fruit = Fruit.slave.where(:id => fruit.id).first
|
70
|
-
slave_fruit.fruit_baskets.length.should eql(fruit.fruit_baskets.length)
|
71
|
-
slave_fruit.fruit_baskets.collect(&:id).should eql(fruit.fruit_baskets.collect(&:id))
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context('has_many :through') do
|
76
|
-
it "should return the same fruit as master" do
|
77
|
-
basket = Factory.create(:fruit_basket)
|
78
|
-
slave_fruit = Fruit.slave.where(:id => basket.fruit_id).first
|
79
|
-
master_fruit = Fruit.where(:id => basket.fruit_id).first
|
80
|
-
slave_fruit.basket_ids.should eql(master_fruit.basket_ids)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
context "readonly" do
|
84
|
-
it "should return a readonly object by default" do
|
85
|
-
Factory.create(:fruit)
|
86
|
-
readonly_fruit = Fruit.slave.first
|
87
|
-
readonly_fruit.readonly?.should be_true
|
88
|
-
lambda { readonly_fruit.save }.should raise_error
|
89
|
-
end
|
90
|
-
it "should not return readonly if replicated readonly is to false" do
|
91
|
-
Factory.create(:region)
|
92
|
-
Region.slave.first.readonly?.should be_false
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
context "model_name for slave" do
|
97
|
-
it "should return the supers model_name" do
|
98
|
-
Fruit.slave_1.model_name.should eql(Fruit.model_name)
|
99
|
-
end
|
100
|
-
|
101
|
-
it "should not interfear with inheritance" do
|
102
|
-
Factory.create(:fruit)
|
103
|
-
class MyFruit < Fruit
|
104
|
-
replicated
|
105
|
-
end
|
106
|
-
Fruit.model_name.should_not eql(MyFruit.model_name)
|
107
|
-
MyFruit.model_name.should eql("MyFruit")
|
108
|
-
MyFruit.model_name.respond_to?(:plural).should be_true
|
109
|
-
MyFruit.slave_1.model_name.should eql("MyFruit")
|
110
|
-
MyFruit.slave_1.first.class.should eql(MyFruit::Slave1)
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
class Foo < ActiveRecord::Base
|
3
|
-
belongs_to :that
|
4
|
-
has_many :foo_bars
|
5
|
-
has_many :bars, :through => :foo_bars
|
6
|
-
has_one :noob
|
7
|
-
end
|
8
|
-
|
9
|
-
class Bar < ActiveRecord::Base
|
10
|
-
has_many :foo_bars
|
11
|
-
has_many :foos, :through => :foo_bars
|
12
|
-
end
|
13
|
-
|
14
|
-
describe ConnectionManager::Associations do
|
15
|
-
|
16
|
-
it "should add associations as keys to @defined_associations" do
|
17
|
-
Foo.defined_associations.keys.should eql([:belongs_to,:has_many,:has_one])
|
18
|
-
Bar.defined_associations.keys.should eql([:has_many])
|
19
|
-
end
|
20
|
-
|
21
|
-
context "defined_association values" do
|
22
|
-
it "should be an array of association options (which are Arrays as well)" do
|
23
|
-
Foo.defined_associations[:belongs_to].should eql([[:that]])
|
24
|
-
Foo.defined_associations[:has_many].should eql([[:foo_bars],[:bars, {:through=>:foo_bars, :extend=>[]}]]) # when options are present active_record addes the :extend option defaulted to []
|
25
|
-
Foo.defined_associations[:has_one].should eql([[:noob]])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
@@ -1,92 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
describe ConnectionManager::Connections do
|
3
|
-
# For all tests set the env to "test"
|
4
|
-
before(:all) do
|
5
|
-
ConnectionManager::Connections.env = "test"
|
6
|
-
end
|
7
|
-
context '#clean_sqlite_db_name' do
|
8
|
-
it "should remove the directory .sqlite3, Rails.env from the string" do
|
9
|
-
ConnectionManager::Connections.clean_sqlite_db_name("db/my_database_test.sqlite3").should eql("my_database")
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
context '#clean_db_name' do
|
14
|
-
context "when name is not just the Rails.env" do
|
15
|
-
it "should remove the Rails.env from the string" do
|
16
|
-
ConnectionManager::Connections.clean_db_name("my_database_test").should eql("my_database")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
context "when the name is only the Rails.env" do
|
20
|
-
it "should use the name of the database and remove the Rails.env" do
|
21
|
-
ConnectionManager::Connections.stubs(:database_name_from_yml).returns("my_database_test")
|
22
|
-
ConnectionManager::Connections.clean_db_name("test").should eql("my_database")
|
23
|
-
end
|
24
|
-
it "should account for sqlite3 database name" do
|
25
|
-
ConnectionManager::Connections.stubs(:database_name_from_yml).returns("db/my_database_test.sqlite3")
|
26
|
-
ConnectionManager::Connections.clean_db_name("test").should eql("my_database")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context '#connection_class_name' do
|
32
|
-
|
33
|
-
it "should return a string for a class name appended with 'Connection' " do
|
34
|
-
ConnectionManager::Connections.connection_class_name("my_database").should eql("MyDatabaseConnection")
|
35
|
-
end
|
36
|
-
it "should return remove the appended rails env" do
|
37
|
-
ConnectionManager::Connections.connection_class_name("my_database_test").should eql("MyDatabaseConnection")
|
38
|
-
end
|
39
|
-
it "should handle sqlite database names correctly " do
|
40
|
-
ConnectionManager::Connections.connection_class_name("db/my_database_test.sqlite3").should eql("MyDatabaseConnection")
|
41
|
-
end
|
42
|
-
it "should use the database name from the database.yml if supplied string is only is only the Rails.env" do
|
43
|
-
ConnectionManager::Connections.stubs(:database_name_from_yml).returns("my_test_test")
|
44
|
-
ConnectionManager::Connections.connection_class_name("test").should eql("MyTestConnection")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
context 'after #initialize' do
|
50
|
-
before(:all) do
|
51
|
-
# Make sure connections recreated in other tests do not presist to tests tests
|
52
|
-
ConnectionManager::Connections.all.clear
|
53
|
-
ConnectionManager::Connections.secondary_connections.clear
|
54
|
-
|
55
|
-
#Initialize
|
56
|
-
ConnectionManager::Connections.initialize(:env => 'test')
|
57
|
-
end
|
58
|
-
|
59
|
-
context '#all' do
|
60
|
-
it "should return the database.yml entries for the current rails environment" do
|
61
|
-
ConnectionManager::Connections.all.should eql(["CmConnection",
|
62
|
-
"Slave1CmConnection", "Slave2CmConnection", "Shard1CmConnection"])
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context '#secondary_connections' do
|
67
|
-
it "should return a hash where the keys are the generic undescored names for all connections" do
|
68
|
-
ConnectionManager::Connections.secondary_connections.keys.
|
69
|
-
should eql([:cm, :slave_cm, :shard_cm])
|
70
|
-
end
|
71
|
-
it "should return a hash where the values are an array of connection class names as strings" do
|
72
|
-
first_value = ConnectionManager::Connections.secondary_connections.values.first
|
73
|
-
first_value.class.should eql(Array)
|
74
|
-
defined?((ConnectionManager::Connections.class_eval(first_value[0]))).should be_true
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
context '#build_connection_class' do
|
79
|
-
before(:all) do
|
80
|
-
ConnectionManager::Connections.build_connection_class("MyConnectionClass", 'test')
|
81
|
-
end
|
82
|
-
it "should add a class with supplied class name to ConnectionManager::Connections" do
|
83
|
-
defined?(ConnectionManager::Connections::MyConnectionClass).should be_true
|
84
|
-
ConnectionManager::Connections::MyConnectionClass.is_a?(Class).should be_true
|
85
|
-
end
|
86
|
-
it "should have a super class of ActiveRecord::Base" do
|
87
|
-
ConnectionManager::Connections::MyConnectionClass.superclass.should eql(ActiveRecord::Base)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ConnectionManager::MethodRecorder do
|
4
|
-
before(:all) do
|
5
|
-
TestMigrations.up('shard_1_cm_test')
|
6
|
-
ConnectionManager::Connections.initialize(:env => 'test')
|
7
|
-
class Fruit < ActiveRecord::Base
|
8
|
-
shard(:using => ["shard_1_cm_test"])
|
9
|
-
end
|
10
|
-
|
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
|
-
after(:all) do
|
40
|
-
TestMigrations.down('shard_1_cm_test')
|
41
|
-
ActiveRecord::Base.establish_connection('test')
|
42
|
-
end
|
43
|
-
end
|