magic_multi_connections 1.0.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +0 -0
- data/History.txt +0 -0
- data/Manifest.txt +43 -32
- data/README.txt +0 -0
- data/Rakefile +21 -15
- data/lib/ext_active_record/association_extensions.rb +90 -0
- data/lib/ext_active_record/connection_specification.rb +0 -0
- data/lib/magic_multi_connections.rb +22 -21
- data/lib/magic_multi_connections/connected.rb +91 -29
- data/lib/magic_multi_connections/module.rb +27 -26
- data/lib/magic_multi_connections/version.rb +9 -9
- data/scripts/txt2html +0 -0
- data/scripts/txt2js +1 -1
- data/setup.rb +0 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_postgresql/connection.rb +0 -0
- data/test/fixtures/address.rb +3 -0
- data/test/fixtures/addresses.yml +4 -0
- data/test/fixtures/assignment.rb +7 -0
- data/test/fixtures/classified.rb +5 -0
- data/test/fixtures/contact_repository.rb +2 -2
- data/test/fixtures/db_definitions/mysql.sql +19 -0
- data/test/fixtures/db_definitions/postgresql.sql +7 -1
- data/test/fixtures/habit.rb +3 -0
- data/test/fixtures/paycheck.rb +3 -0
- data/test/fixtures/people.yml +0 -0
- data/test/fixtures/person.rb +3 -1
- data/test/fixtures/soldier.rb +8 -0
- data/test/test_helper.rb +71 -71
- data/test/test_magic_multi_connections.rb +27 -26
- data/test/test_mmc_associations.rb +46 -0
- data/test/test_parent_module.rb +0 -0
- data/test/test_preexisting_module.rb +0 -0
- data/website/index.html +354 -340
- data/website/index.txt +286 -223
- data/website/javascripts/rounded_corners_lite.inc.js +0 -0
- data/website/stylesheets/screen.css +0 -0
- data/website/template.js +0 -0
- data/website/template.rhtml +0 -0
- data/website/version-raw.js +2 -2
- data/website/version-raw.txt +1 -1
- data/website/version.js +1 -1
- data/website/version.txt +0 -0
- metadata +63 -37
@@ -1,9 +1,9 @@
|
|
1
|
-
module MagicMultiConnection #:nodoc:
|
2
|
-
module VERSION #:nodoc:
|
3
|
-
MAJOR = 1
|
4
|
-
MINOR =
|
5
|
-
TINY =
|
6
|
-
|
7
|
-
STRING = [MAJOR, MINOR, TINY].join('.')
|
8
|
-
end
|
9
|
-
end
|
1
|
+
module MagicMultiConnection #:nodoc:
|
2
|
+
module VERSION #:nodoc:
|
3
|
+
MAJOR = 1
|
4
|
+
MINOR = 2
|
5
|
+
TINY = 1
|
6
|
+
|
7
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
8
|
+
end
|
9
|
+
end
|
data/scripts/txt2html
CHANGED
File without changes
|
data/scripts/txt2js
CHANGED
@@ -8,7 +8,7 @@ require 'active_support'
|
|
8
8
|
require File.dirname(__FILE__) + '/../lib/magic_multi_connections/version.rb'
|
9
9
|
|
10
10
|
version = MagicMultiConnection::VERSION::STRING
|
11
|
-
download = 'http://rubyforge.org/projects/
|
11
|
+
download = 'http://rubyforge.org/projects/magicmodels'
|
12
12
|
|
13
13
|
class Fixnum
|
14
14
|
def ordinal
|
data/setup.rb
CHANGED
File without changes
|
@@ -0,0 +1,24 @@
|
|
1
|
+
print "Using native MySQL\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
db_connection_options = {
|
7
|
+
:adapter => "mysql",
|
8
|
+
:encoding => "utf8",
|
9
|
+
:database => 'magic_multi_connections_unittest',
|
10
|
+
:username => "ruby",
|
11
|
+
:password => "ruby"
|
12
|
+
}
|
13
|
+
|
14
|
+
db_extra_connection_options = {
|
15
|
+
:adapter => "mysql",
|
16
|
+
:encoding => "utf8",
|
17
|
+
:database => 'magic_multi_connections_extra_unittest',
|
18
|
+
:username => "ruby",
|
19
|
+
:password => "ruby"
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
ActiveRecord::Base.configurations = { 'production' => db_connection_options, 'contact_repo' => db_extra_connection_options }
|
24
|
+
ActiveRecord::Base.establish_connection(:production)
|
File without changes
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module ContactRepository
|
2
|
-
establish_connection :contact_repo
|
1
|
+
module ContactRepository
|
2
|
+
establish_connection :contact_repo, false
|
3
3
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
DROP TABLE IF EXISTS people;
|
2
|
+
CREATE TABLE people (
|
3
|
+
id INT(11) AUTO_INCREMENT PRIMARY KEY,
|
4
|
+
name VARCHAR(255) NULL,
|
5
|
+
email VARCHAR(255) NULL,
|
6
|
+
age INT(11) NULL
|
7
|
+
);
|
8
|
+
DROP TABLE IF EXISTS addresses;
|
9
|
+
CREATE TABLE addresses (
|
10
|
+
id INT(11) AUTO_INCREMENT PRIMARY KEY,
|
11
|
+
person_id INT(11) NULL,
|
12
|
+
address VARCHAR(255) NULL
|
13
|
+
);
|
14
|
+
DROP TABLE IF EXISTS habits;
|
15
|
+
CREATE TABLE habits (
|
16
|
+
id INT(11) AUTO_INCREMENT PRIMARY KEY,
|
17
|
+
person_id INT(11) NULL,
|
18
|
+
description VARCHAR(255) NULL
|
19
|
+
);
|
data/test/fixtures/people.yml
CHANGED
File without changes
|
data/test/fixtures/person.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,71 +1,71 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'rubygems'
|
3
|
-
require 'active_record'
|
4
|
-
require 'active_record/fixtures'
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'connection'
|
8
|
-
rescue MissingSourceFile => e
|
9
|
-
# required for tests not run via test_#{adapter} rake tests, e.g. autotest
|
10
|
-
adapter = '
|
11
|
-
require "#{File.dirname(__FILE__)}/connections/native_#{adapter}/connection"
|
12
|
-
end
|
13
|
-
|
14
|
-
require File.dirname(__FILE__) + '/../lib/magic_multi_connections'
|
15
|
-
|
16
|
-
|
17
|
-
models = %w[person contact_repository]
|
18
|
-
models.each { |model| require File.join(File.dirname(__FILE__), 'fixtures', model) }
|
19
|
-
|
20
|
-
class Test::Unit::TestCase #:nodoc:
|
21
|
-
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
22
|
-
self.use_instantiated_fixtures = false
|
23
|
-
self.use_transactional_fixtures = true #(ENV['AR_NO_TX_FIXTURES'] != "yes")
|
24
|
-
|
25
|
-
def create_fixtures(*table_names, &block)
|
26
|
-
Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names, {}, &block)
|
27
|
-
end
|
28
|
-
|
29
|
-
def assert_date_from_db(expected, actual, message = nil)
|
30
|
-
# SQL Server doesn't have a separate column type just for dates,
|
31
|
-
# so the time is in the string and incorrectly formatted
|
32
|
-
if current_adapter?(:SQLServerAdapter)
|
33
|
-
assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
|
34
|
-
elsif current_adapter?(:SybaseAdapter)
|
35
|
-
assert_equal expected.to_s, actual.to_date.to_s, message
|
36
|
-
else
|
37
|
-
assert_equal expected.to_s, actual.to_s, message
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def assert_queries(num = 1)
|
42
|
-
ActiveRecord::Base.connection.class.class_eval do
|
43
|
-
self.query_count = 0
|
44
|
-
alias_method :execute, :execute_with_query_counting
|
45
|
-
end
|
46
|
-
yield
|
47
|
-
ensure
|
48
|
-
ActiveRecord::Base.connection.class.class_eval do
|
49
|
-
alias_method :execute, :execute_without_query_counting
|
50
|
-
end
|
51
|
-
assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
|
52
|
-
end
|
53
|
-
|
54
|
-
def assert_no_queries(&block)
|
55
|
-
assert_queries(0, &block)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def current_adapter?(type)
|
60
|
-
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
61
|
-
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
|
62
|
-
end
|
63
|
-
|
64
|
-
ActiveRecord::Base.connection.class.class_eval do
|
65
|
-
cattr_accessor :query_count
|
66
|
-
alias_method :execute_without_query_counting, :execute
|
67
|
-
def execute_with_query_counting(sql, name = nil)
|
68
|
-
self.query_count += 1
|
69
|
-
execute_without_query_counting(sql, name)
|
70
|
-
end
|
71
|
-
end
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/fixtures'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'connection'
|
8
|
+
rescue MissingSourceFile => e
|
9
|
+
# required for tests not run via test_#{adapter} rake tests, e.g. autotest
|
10
|
+
adapter = 'mysql' #'sqlite', 'mysql'
|
11
|
+
require "#{File.dirname(__FILE__)}/connections/native_#{adapter}/connection"
|
12
|
+
end
|
13
|
+
|
14
|
+
require File.dirname(__FILE__) + '/../lib/magic_multi_connections'
|
15
|
+
|
16
|
+
|
17
|
+
models = %w[person contact_repository address soldier paycheck assignment classified habit citation]
|
18
|
+
models.each { |model| require File.join(File.dirname(__FILE__), 'fixtures', model) }
|
19
|
+
|
20
|
+
class Test::Unit::TestCase #:nodoc:
|
21
|
+
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
22
|
+
self.use_instantiated_fixtures = false
|
23
|
+
self.use_transactional_fixtures = true #(ENV['AR_NO_TX_FIXTURES'] != "yes")
|
24
|
+
|
25
|
+
def create_fixtures(*table_names, &block)
|
26
|
+
Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names, {}, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def assert_date_from_db(expected, actual, message = nil)
|
30
|
+
# SQL Server doesn't have a separate column type just for dates,
|
31
|
+
# so the time is in the string and incorrectly formatted
|
32
|
+
if current_adapter?(:SQLServerAdapter)
|
33
|
+
assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
|
34
|
+
elsif current_adapter?(:SybaseAdapter)
|
35
|
+
assert_equal expected.to_s, actual.to_date.to_s, message
|
36
|
+
else
|
37
|
+
assert_equal expected.to_s, actual.to_s, message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def assert_queries(num = 1)
|
42
|
+
ActiveRecord::Base.connection.class.class_eval do
|
43
|
+
self.query_count = 0
|
44
|
+
alias_method :execute, :execute_with_query_counting
|
45
|
+
end
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
ActiveRecord::Base.connection.class.class_eval do
|
49
|
+
alias_method :execute, :execute_without_query_counting
|
50
|
+
end
|
51
|
+
assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
|
52
|
+
end
|
53
|
+
|
54
|
+
def assert_no_queries(&block)
|
55
|
+
assert_queries(0, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def current_adapter?(type)
|
60
|
+
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
61
|
+
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
|
62
|
+
end
|
63
|
+
|
64
|
+
ActiveRecord::Base.connection.class.class_eval do
|
65
|
+
cattr_accessor :query_count
|
66
|
+
alias_method :execute_without_query_counting, :execute
|
67
|
+
def execute_with_query_counting(sql, name = nil)
|
68
|
+
self.query_count += 1
|
69
|
+
execute_without_query_counting(sql, name)
|
70
|
+
end
|
71
|
+
end
|
@@ -1,26 +1,27 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
-
|
3
|
-
module NormalModule; end
|
4
|
-
|
5
|
-
class TestMagicMultiConnection < Test::Unit::TestCase
|
6
|
-
|
7
|
-
def setup
|
8
|
-
create_fixtures :people
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_classes
|
12
|
-
assert_nothing_raised(Exception) { Person }
|
13
|
-
assert_equal('ActiveRecord::Base', Person.active_connection_name)
|
14
|
-
assert(normal_person = Person.find(1), "Cannot get Person instances")
|
15
|
-
assert_nothing_raised(Exception) { ContactRepository::Person }
|
16
|
-
assert_equal(Person, ContactRepository::Person.superclass)
|
17
|
-
assert_equal('ContactRepository::Person', ContactRepository::Person.name)
|
18
|
-
assert_equal('ContactRepository::Person', ContactRepository::Person.active_connection_name)
|
19
|
-
assert_equal(0, ContactRepository::Person.count)
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_normal_modules_shouldnt_do_anything
|
23
|
-
assert_raise(NameError) { NormalModule::Person }
|
24
|
-
|
25
|
-
end
|
26
|
-
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
module NormalModule; end
|
4
|
+
|
5
|
+
class TestMagicMultiConnection < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
create_fixtures :people, :addresses
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_classes
|
12
|
+
assert_nothing_raised(Exception) { Person }
|
13
|
+
assert_equal('ActiveRecord::Base', Person.active_connection_name)
|
14
|
+
assert(normal_person = Person.find(1), "Cannot get Person instances")
|
15
|
+
assert_nothing_raised(Exception) { ContactRepository::Person }
|
16
|
+
assert_equal(Person, ContactRepository::Person.superclass)
|
17
|
+
assert_equal('ContactRepository::Person', ContactRepository::Person.name)
|
18
|
+
assert_equal('ContactRepository::Person', ContactRepository::Person.active_connection_name)
|
19
|
+
assert_equal(0, ContactRepository::Person.count)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_normal_modules_shouldnt_do_anything
|
23
|
+
assert_raise(NameError) { NormalModule::Person }
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestMmcAssociations < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_namespace_associations
|
10
|
+
assert_nothing_raised(Exception) do
|
11
|
+
Army::Soldier
|
12
|
+
Army::Assignment
|
13
|
+
Paycheck
|
14
|
+
Justice::Citation
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_equal Army::Assignment, Army::Soldier.reflections[:assignments].klass
|
18
|
+
assert_equal Paycheck, Army::Soldier.reflections[:paychecks].klass
|
19
|
+
|
20
|
+
assert_equal true, Army::Classified.namespace_reflections_mirror_db
|
21
|
+
|
22
|
+
assert_nothing_raised(Exception) { Army::Classified::Soldier }
|
23
|
+
assert_equal Army::Classified::Assignment, Army::Classified::Soldier.reflections[:assignments].klass
|
24
|
+
assert_equal Paycheck, Army::Classified::Soldier.reflections[:paychecks].klass
|
25
|
+
assert_equal Justice::Citation, Army::Classified::Soldier.reflections[:citations].klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_explicit_associations
|
29
|
+
assert_nothing_raised(Exception) do
|
30
|
+
Person
|
31
|
+
Habit
|
32
|
+
Address
|
33
|
+
end
|
34
|
+
|
35
|
+
assert_equal Address, Person.reflections[:addresses].klass
|
36
|
+
assert_equal :default, Person.reflections[:addresses].mirror_db_connection
|
37
|
+
assert_equal Habit, Person.reflections[:habits].klass
|
38
|
+
assert_equal true, Person.reflections[:habits].mirror_db_connection
|
39
|
+
|
40
|
+
assert_equal false, ContactRepository.namespace_reflections_mirror_db
|
41
|
+
|
42
|
+
assert_nothing_raised(Exception) { ContactRepository::Person }
|
43
|
+
assert_equal Address, ContactRepository::Person.reflections[:addresses].klass
|
44
|
+
assert_equal ContactRepository::Habit, ContactRepository::Person.reflections[:habits].klass
|
45
|
+
end
|
46
|
+
end
|
data/test/test_parent_module.rb
CHANGED
File without changes
|
File without changes
|
data/website/index.html
CHANGED
@@ -1,340 +1,354 @@
|
|
1
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
-
<head>
|
5
|
-
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
|
6
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
7
|
-
<title>
|
8
|
-
Magic Multi-Connections
|
9
|
-
</title>
|
10
|
-
<script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
|
11
|
-
<style>
|
12
|
-
|
13
|
-
</style>
|
14
|
-
<script type="text/javascript">
|
15
|
-
window.onload = function() {
|
16
|
-
settings = {
|
17
|
-
tl: { radius: 10 },
|
18
|
-
tr: { radius: 10 },
|
19
|
-
bl: { radius: 10 },
|
20
|
-
br: { radius: 10 },
|
21
|
-
antiAlias: true,
|
22
|
-
autoPad: true,
|
23
|
-
validTags: ["div"]
|
24
|
-
}
|
25
|
-
var versionBox = new curvyCorners(settings, document.getElementById("version"));
|
26
|
-
versionBox.applyCornersToAll();
|
27
|
-
}
|
28
|
-
</script>
|
29
|
-
</head>
|
30
|
-
<body>
|
31
|
-
<div id="main">
|
32
|
-
<p><a href="/">↩ More Magic</a></p>
|
33
|
-
<h1 class=primary>Magic Multi-Connections</h1>
|
34
|
-
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/magicmodels"; return false'>
|
35
|
-
Get Version
|
36
|
-
<a href="http://rubyforge.org/projects/magicmodels" class="numbers">1.
|
37
|
-
</div>
|
38
|
-
<h1>→ Ruby on Rails</h1>
|
39
|
-
|
40
|
-
|
41
|
-
<h1>→ ActiveRecords</h1>
|
42
|
-
|
43
|
-
|
44
|
-
<h2>
|
45
|
-
|
46
|
-
|
47
|
-
<p>
|
48
|
-
|
49
|
-
|
50
|
-
<
|
51
|
-
|
52
|
-
|
53
|
-
<p>
|
54
|
-
|
55
|
-
|
56
|
-
<p>
|
57
|
-
|
58
|
-
|
59
|
-
<p
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
<span class="keyword">end</span>
|
66
|
-
<span class="constant">
|
67
|
-
|
68
|
-
|
69
|
-
<span class="
|
70
|
-
</
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
<p
|
80
|
-
|
81
|
-
|
82
|
-
<p>
|
83
|
-
|
84
|
-
|
85
|
-
<
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
<
|
117
|
-
|
118
|
-
|
119
|
-
<p>
|
120
|
-
|
121
|
-
|
122
|
-
<
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
<span class="
|
146
|
-
</
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
<span class="
|
160
|
-
<span class="
|
161
|
-
<span class="
|
162
|
-
|
163
|
-
<span class="
|
164
|
-
<span class="
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
<
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
<
|
177
|
-
|
178
|
-
|
179
|
-
<p><pre class="syntax">
|
180
|
-
<span class="ident">
|
181
|
-
|
182
|
-
<span class="
|
183
|
-
|
184
|
-
<span class="
|
185
|
-
</pre></p>
|
186
|
-
|
187
|
-
|
188
|
-
<p>
|
189
|
-
|
190
|
-
|
191
|
-
<h3>
|
192
|
-
|
193
|
-
|
194
|
-
<p>
|
195
|
-
|
196
|
-
|
197
|
-
<
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
<span class="ident">
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
</
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
<p>
|
274
|
-
|
275
|
-
|
276
|
-
<p>
|
277
|
-
|
278
|
-
|
279
|
-
<p
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
<
|
292
|
-
|
293
|
-
|
294
|
-
<
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
<
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
</
|
331
|
-
|
332
|
-
|
333
|
-
</
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
</
|
340
|
-
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
7
|
+
<title>
|
8
|
+
Magic Multi-Connections
|
9
|
+
</title>
|
10
|
+
<script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
|
11
|
+
<style>
|
12
|
+
|
13
|
+
</style>
|
14
|
+
<script type="text/javascript">
|
15
|
+
window.onload = function() {
|
16
|
+
settings = {
|
17
|
+
tl: { radius: 10 },
|
18
|
+
tr: { radius: 10 },
|
19
|
+
bl: { radius: 10 },
|
20
|
+
br: { radius: 10 },
|
21
|
+
antiAlias: true,
|
22
|
+
autoPad: true,
|
23
|
+
validTags: ["div"]
|
24
|
+
}
|
25
|
+
var versionBox = new curvyCorners(settings, document.getElementById("version"));
|
26
|
+
versionBox.applyCornersToAll();
|
27
|
+
}
|
28
|
+
</script>
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="main">
|
32
|
+
<p><a href="/">↩ More Magic</a></p>
|
33
|
+
<h1 class=primary>Magic Multi-Connections</h1>
|
34
|
+
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/magicmodels"; return false'>
|
35
|
+
Get Version
|
36
|
+
<a href="http://rubyforge.org/projects/magicmodels" class="numbers">1.2.0</a>
|
37
|
+
</div>
|
38
|
+
<h1>→ Ruby on Rails</h1>
|
39
|
+
|
40
|
+
|
41
|
+
<h1>→ ActiveRecords</h1>
|
42
|
+
|
43
|
+
|
44
|
+
<h2><span class="caps">WARNING</span></h2>
|
45
|
+
|
46
|
+
|
47
|
+
<p>Despite the 1.1.0 version number, this gem is not quite production ready. Various people have <a href="http://joshknowles.com/2007/6/17/migrations-pains">experienced problems</a> using the 1.0.0 version. A solution was found to deal with this issue but it has not been fully tested, so please subscribe to the <a href="http://groups.google.com/group/magicmodels">forum</a> or <a href="http://rubyforge.org/export/rss_sfnewreleases.php">RubyForge news</a> for any updates.</p>
|
48
|
+
|
49
|
+
|
50
|
+
<h2>What</h2>
|
51
|
+
|
52
|
+
|
53
|
+
<p>ActiveRecord models are allowed one connection to a database at a time, per class. Ruby on Rails sets up the default connection based on your database.yml configuration to automatically select <strong>development</strong>, <strong>test</strong> or <strong>production</strong>.</p>
|
54
|
+
|
55
|
+
|
56
|
+
<p>But, what if you want to access two or more databases – have 2+ connections open – at the same time. ActiveRecord requires that you subclass <code>ActiveRecord::Base</code>.</p>
|
57
|
+
|
58
|
+
|
59
|
+
<p>That prevents you doing migrations from one database to another. It prevents you using one set of model classes on two or more databases with the same schema.</p>
|
60
|
+
|
61
|
+
|
62
|
+
<p>Magic Multi-Connections allows you to write your models once, and use them for multiple database Rails databases at the same time. How? Using magical namespacing.</p>
|
63
|
+
|
64
|
+
|
65
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">Person</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">;</span> <span class="keyword">end</span>
|
66
|
+
<span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">.</span><span class="ident">establish_connection</span> <span class="symbol">:production</span>
|
67
|
+
<span class="constant">Person</span><span class="punct">.</span><span class="ident">connection</span> <span class="comment"># => production</span>
|
68
|
+
|
69
|
+
<span class="keyword">module </span><span class="module">ContactRepository</span>
|
70
|
+
<span class="ident">establish_connection</span> <span class="symbol">:contact_repo</span>
|
71
|
+
<span class="keyword">end</span>
|
72
|
+
<span class="constant">ContactRepository</span><span class="punct">::</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">connection</span> <span class="comment"># => contact_repo</span>
|
73
|
+
|
74
|
+
<span class="ident">old_person</span> <span class="punct">=</span> <span class="constant">ContactRepository</span><span class="punct">::</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">find_by_email</span><span class="punct">(</span><span class="ident">email</span><span class="punct">)</span>
|
75
|
+
<span class="ident">person</span> <span class="punct">=</span> <span class="ident">old_person</span><span class="punct">.</span><span class="ident">create_as</span><span class="punct">(</span><span class="constant">Person</span><span class="punct">)</span>
|
76
|
+
</pre></p>
|
77
|
+
|
78
|
+
|
79
|
+
<p>You do not have to redefine your models for the multi-connection module <code>ContactRepository</code>, they are automatically picked up for you. Magically.</p>
|
80
|
+
|
81
|
+
|
82
|
+
<p><span class="caps">TODO</span>: Example about Associations</p>
|
83
|
+
|
84
|
+
|
85
|
+
<h2>Issues</h2>
|
86
|
+
|
87
|
+
|
88
|
+
Despite the 1.1.0 version of this gem there are still a number of issues with this gem:
|
89
|
+
<ul>
|
90
|
+
<li>Single Table Inheritance is not currently supported</li>
|
91
|
+
<li>No connection pooling for alternate databases</li>
|
92
|
+
</ul>
|
93
|
+
|
94
|
+
<p>Any help would be greatly appreciated</p>
|
95
|
+
|
96
|
+
|
97
|
+
<h2>Installing</h2>
|
98
|
+
|
99
|
+
|
100
|
+
<p><pre class="syntax"><span class="ident">sudo</span> <span class="ident">gem</span> <span class="ident">install</span> <span class="ident">magic_multi_connections</span></pre></p>
|
101
|
+
|
102
|
+
|
103
|
+
<p>Rails: Add the following to the bottom of your <code>environment.rb</code> file</p>
|
104
|
+
|
105
|
+
|
106
|
+
<p><pre class="syntax"><span class="ident">require</span> <span class="punct">'</span><span class="string">magic_multi_connections</span><span class="punct">'</span></pre></p>
|
107
|
+
|
108
|
+
|
109
|
+
<p>Ruby scripts: Add the following to the top of your script</p>
|
110
|
+
|
111
|
+
|
112
|
+
<p><pre class="syntax"><span class="ident">require</span> <span class="punct">'</span><span class="string">rubygems</span><span class="punct">'</span>
|
113
|
+
<span class="ident">require</span> <span class="punct">'</span><span class="string">magic_multi_connections</span><span class="punct">'</span></pre></p>
|
114
|
+
|
115
|
+
|
116
|
+
<h2>Demonstration with Rails</h2>
|
117
|
+
|
118
|
+
|
119
|
+
<p>A quick demonstration within Rails to provide a parallel “private” database for an application.</p>
|
120
|
+
|
121
|
+
|
122
|
+
<h3>1. Create rails app</h3>
|
123
|
+
|
124
|
+
|
125
|
+
<p>Using sqlite3 here, but use your preferred db:</p>
|
126
|
+
|
127
|
+
|
128
|
+
<pre>> rails privacy -d sqlite3
|
129
|
+
> cd privacy
|
130
|
+
> ruby script/generate model Person
|
131
|
+
> cp config/environments/development.rb config/environments/private.rb
|
132
|
+
</pre>
|
133
|
+
|
134
|
+
<p>The last line allows us to play with our <strong>private</strong> database within the console and rake tasks.</p>
|
135
|
+
|
136
|
+
|
137
|
+
<h3>2. Edit <strong>config/database.yml</strong> and add our private database:</h3>
|
138
|
+
|
139
|
+
|
140
|
+
<p>Add the following to the bottom of <strong>config/database.yml</strong></p>
|
141
|
+
|
142
|
+
|
143
|
+
<p><pre class="syntax">
|
144
|
+
<span class="key">private</span><span class="punct">:</span>
|
145
|
+
<span class="key">adapter</span><span class="punct">:</span> sqlite3
|
146
|
+
<span class="key">database</span><span class="punct">:</span> db/private.sqlite3
|
147
|
+
</pre></p>
|
148
|
+
|
149
|
+
|
150
|
+
<h3>3. Create a database schema</h3>
|
151
|
+
|
152
|
+
|
153
|
+
<p>Edit <strong>db/migrate/001_create_people.rb</strong></p>
|
154
|
+
|
155
|
+
|
156
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">CreatePeople</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Migration</span>
|
157
|
+
<span class="keyword">def </span><span class="method">self.up</span>
|
158
|
+
<span class="ident">create_table</span> <span class="symbol">:people</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">t</span><span class="punct">|</span>
|
159
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:name</span><span class="punct">,</span> <span class="symbol">:string</span>
|
160
|
+
<span class="keyword">end</span>
|
161
|
+
<span class="keyword">end</span>
|
162
|
+
|
163
|
+
<span class="keyword">def </span><span class="method">self.down</span>
|
164
|
+
<span class="ident">drop_table</span> <span class="symbol">:people</span>
|
165
|
+
<span class="keyword">end</span>
|
166
|
+
<span class="keyword">end</span>
|
167
|
+
</pre></p>
|
168
|
+
|
169
|
+
|
170
|
+
<p>From the command line, migrate this to our <strong>development</strong> and <strong>private</strong> databases:</p>
|
171
|
+
|
172
|
+
|
173
|
+
<pre>> rake db:migrate
|
174
|
+
> rake db:migrate RAILS_ENV=private</pre>
|
175
|
+
|
176
|
+
<h3>4. Add some data to databases</h3>
|
177
|
+
|
178
|
+
|
179
|
+
<p><pre class="syntax"><span class="punct">></span> <span class="ident">ruby</span> <span class="ident">script</span><span class="punct">/</span><span class="ident">console</span> <span class="ident">development</span>
|
180
|
+
<span class="punct">>></span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">Nic</span><span class="punct">')</span>
|
181
|
+
<span class="punct">>></span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">Banjo</span><span class="punct">')</span>
|
182
|
+
<span class="punct">>></span> <span class="ident">exit</span>
|
183
|
+
<span class="punct">></span> <span class="ident">ruby</span> <span class="ident">script</span><span class="punct">/</span><span class="ident">console</span> <span class="ident">private</span>
|
184
|
+
<span class="punct">>></span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">Super Magical Nic</span><span class="punct">')</span>
|
185
|
+
<span class="punct">>></span> <span class="ident">exit</span></pre></p>
|
186
|
+
|
187
|
+
|
188
|
+
<p>Now it should be obvious which database our app is accessing.</p>
|
189
|
+
|
190
|
+
|
191
|
+
<h3>5. Update environment.rb</h3>
|
192
|
+
|
193
|
+
|
194
|
+
<p>Edit <strong>config/environment.rb</strong> to include the library and create the <strong>Private</strong> module.</p>
|
195
|
+
|
196
|
+
|
197
|
+
<p>Add the following to the end of the file.</p>
|
198
|
+
|
199
|
+
|
200
|
+
<p><pre class="syntax">
|
201
|
+
<span class="ident">require</span> <span class="punct">"</span><span class="string">magic_multi_connections</span><span class="punct">"</span>
|
202
|
+
|
203
|
+
<span class="keyword">module </span><span class="module">Private</span>
|
204
|
+
<span class="ident">establish_connection</span> <span class="symbol">:private</span>
|
205
|
+
<span class="keyword">end</span>
|
206
|
+
</pre></p>
|
207
|
+
|
208
|
+
|
209
|
+
<p>This tells the <strong>Private</strong> module that any model class that is requested will be assigned a connection to the <strong>private</strong> database, as defined in the <strong>config/database.yml</strong> specification.</p>
|
210
|
+
|
211
|
+
|
212
|
+
<h3>6. Setup a controller</h3>
|
213
|
+
|
214
|
+
|
215
|
+
<p>Create a <strong>people</strong> controller with a <strong>index</strong> action</p>
|
216
|
+
|
217
|
+
|
218
|
+
<pre>> ruby script/generate controller people index</pre>
|
219
|
+
|
220
|
+
<p>Edit your controller <strong>app/controllers/people_controller.rb</strong></p>
|
221
|
+
|
222
|
+
|
223
|
+
<p><pre class="syntax"><span class="keyword">class </span><span class="class">PeopleController</span> <span class="punct"><</span> <span class="constant">ApplicationController</span>
|
224
|
+
|
225
|
+
<span class="ident">before_filter</span> <span class="symbol">:check_private</span>
|
226
|
+
|
227
|
+
<span class="keyword">def </span><span class="method">index</span>
|
228
|
+
<span class="attribute">@people</span> <span class="punct">=</span> <span class="attribute">@mod</span><span class="punct">::</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:all</span><span class="punct">)</span>
|
229
|
+
<span class="keyword">end</span>
|
230
|
+
|
231
|
+
<span class="ident">private</span>
|
232
|
+
<span class="keyword">def </span><span class="method">check_private</span>
|
233
|
+
<span class="attribute">@mod</span> <span class="punct">=</span> <span class="ident">params</span><span class="punct">[</span><span class="symbol">:private</span><span class="punct">]</span> <span class="punct">?</span> <span class="constant">Private</span> <span class="punct">:</span> <span class="constant">Object</span>
|
234
|
+
<span class="keyword">end</span>
|
235
|
+
<span class="keyword">end</span>
|
236
|
+
</pre></p>
|
237
|
+
|
238
|
+
|
239
|
+
<p>The <strong>check_private</strong> action is a hack to demonstrate one method for selecting the database. In reality, a stupid one for hiding a “private” database, but you get the point.</p>
|
240
|
+
|
241
|
+
|
242
|
+
<p>After <strong>check_private</strong> is called, <strong>@mod</strong> is either the <strong>Object</strong> (default) module or the <strong>Private</strong> module. The <strong>Person</strong> class is accessible through either of them.</p>
|
243
|
+
|
244
|
+
|
245
|
+
<p>Yes, <code>@mod::Person</code> is uglier than just <code>Person</code>. Sorry.</p>
|
246
|
+
|
247
|
+
|
248
|
+
<h3>7. Setup the index.rhtml view</h3>
|
249
|
+
|
250
|
+
|
251
|
+
<p>Edit <strong>app/views/people/index.rhtml</strong></p>
|
252
|
+
|
253
|
+
|
254
|
+
<p><pre class="syntax"><h1><%= @mod::Person %></h1>
|
255
|
+
<h2><%= @mod::Person.active_connection_name %></h2>
|
256
|
+
|
257
|
+
<ol>
|
258
|
+
<% @people.each do |person| -%>
|
259
|
+
<li><%= "#{person.name} - #{person.class}" %></li>
|
260
|
+
<% end -%>
|
261
|
+
</ol>
|
262
|
+
</pre></p>
|
263
|
+
|
264
|
+
|
265
|
+
<h3>8. Test our multi-connection Rails app</h3>
|
266
|
+
|
267
|
+
|
268
|
+
<p>Launch the app</p>
|
269
|
+
|
270
|
+
|
271
|
+
<pre>> mongrel_rails start</pre>
|
272
|
+
|
273
|
+
<p>In your browser, go to <a href="http://localhost:3000/people">http://localhost:3000/people</a> and see the list of people: Nic and Banjo.</p>
|
274
|
+
|
275
|
+
|
276
|
+
<p>Now, to see the private database, go to <a href="http://localhost:3000/people?private=1">http://localhost:3000/people?private=1</a> and see the private list. Its so private, I won’t show it here.</p>
|
277
|
+
|
278
|
+
|
279
|
+
<p>Note: you may need to refresh the private url to see the results. Perhaps Rails is caching the <span class="caps">SQL</span> even though its a different connection to a different database. If you know what’s happening here, please <a href="mailto:drnicwilliams@gmail.com?subject=MMC+caching+problem">email me</a> or the <a href="mailto:magicmodels@googlegroups.com?subject=MMC+caching+problem">forum</a>. Thanks.</p>
|
280
|
+
|
281
|
+
|
282
|
+
<h3>9. End</h3>
|
283
|
+
|
284
|
+
|
285
|
+
<p>There ends our example of a Rails application using one model class to access multiple databases cleanly.</p>
|
286
|
+
|
287
|
+
|
288
|
+
<h2>Pre-existing modules</h2>
|
289
|
+
|
290
|
+
|
291
|
+
<p>In Rails, model files are placed in the <strong>app/models</strong> folder. If you place them in a subfolder, say <strong>app/models/admin</strong>, then those model classes are access via module namespaces.</p>
|
292
|
+
|
293
|
+
|
294
|
+
<p>So, <strong>app/models/admin/page.rb</strong> represents the <code>Admin::Page</code> class.</p>
|
295
|
+
|
296
|
+
|
297
|
+
<p>Magic Multi-Connections works for these model classes as well.</p>
|
298
|
+
|
299
|
+
|
300
|
+
<p><pre class="syntax"><span class="constant">Admin</span><span class="punct">.</span><span class="ident">establish_connection</span> <span class="symbol">:admin_dev</span>
|
301
|
+
<span class="constant">Admin</span><span class="punct">::</span><span class="constant">Page</span><span class="punct">.</span><span class="ident">active_connection_name</span> <span class="comment"># => "Admin::Page"</span>
|
302
|
+
</pre></p>
|
303
|
+
|
304
|
+
|
305
|
+
<h2>Related articles</h2>
|
306
|
+
|
307
|
+
|
308
|
+
<ul>
|
309
|
+
<li>Original blog article – <a href="http://drnicwilliams.com/2007/04/12/magic-multi-connections-a-facility-in-rails-to-talk-to-more-than-one-database-at-a-time/">Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time</a></li>
|
310
|
+
<li><a href="http://www.loudthinking.com/arc/000610.html">Discussed by <span class="caps">DHH</span></a></li>
|
311
|
+
</ul>
|
312
|
+
|
313
|
+
|
314
|
+
<h2>Dr Nic’s Blog</h2>
|
315
|
+
|
316
|
+
|
317
|
+
<p><a href="http://www.drnicwilliams.com">http://www.drnicwilliams.com</a> – for future announcements and
|
318
|
+
other stories and things.</p>
|
319
|
+
|
320
|
+
|
321
|
+
<h2>Forum</h2>
|
322
|
+
|
323
|
+
|
324
|
+
<p>Discussion about the Magic Multi-Connections is on the Magic Models forum:</p>
|
325
|
+
|
326
|
+
|
327
|
+
<p><a href="http://groups.google.com/group/magicmodels">http://groups.google.com/group/magicmodels</a></p>
|
328
|
+
|
329
|
+
|
330
|
+
<h2>Licence</h2>
|
331
|
+
|
332
|
+
|
333
|
+
<p>This code is free to use under the terms of the <span class="caps">MIT</span> licence.</p>
|
334
|
+
|
335
|
+
|
336
|
+
<h2>Contact</h2>
|
337
|
+
|
338
|
+
|
339
|
+
<p>Comments are welcome. Send an email to <a href="mailto:drnicwilliams@gmail.com">Dr Nic Williams</a>.</p>
|
340
|
+
<p class="coda">
|
341
|
+
<a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 2nd January 2008<br>
|
342
|
+
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
343
|
+
</p>
|
344
|
+
</div>
|
345
|
+
|
346
|
+
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
|
347
|
+
</script>
|
348
|
+
<script type="text/javascript">
|
349
|
+
_uacct = "UA-567811-2";
|
350
|
+
urchinTracker();
|
351
|
+
</script>
|
352
|
+
|
353
|
+
</body>
|
354
|
+
</html>
|