rails_multisite 0.0.1 → 1.0.1
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.
Potentially problematic release.
This version of rails_multisite might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +28 -3
- data/lib/rails_multisite/connection_management.rb +111 -43
- data/lib/rails_multisite/railtie.rb +2 -0
- data/lib/rails_multisite/version.rb +1 -1
- data/lib/tasks/db.rake +31 -0
- data/lib/tasks/generators.rake +26 -0
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fd46e714ed81aade7bf750d0229ec1a96cb3f9f
|
4
|
+
data.tar.gz: 9ea4c386f993da1f6648acdf33ff854a51d3af79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e3c52ff6e9120af35e0b2e5677296d85d453d2d68520682846dcf0c1ff1ee24afe2039b91420ddb8907dc9480ab69a7c66fbcaed3086df909bf8d672b60f053
|
7
|
+
data.tar.gz: cb516155225e1db227abfb0f36b67d86f8767d47e331e693a25c98d73612964ed2c64ec0e3d19a242f49920e261370e2e365f2903e09d57a7a44f3792f5f8f9f
|
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# Rails Multisite
|
2
2
|
|
3
|
-
|
3
|
+
This gem provides multi-db support for Rails applications.
|
4
|
+
|
5
|
+
Using its middleware you can partition your app so each hostname has its own db.
|
6
|
+
|
7
|
+
It provides a series of helper for working with multiple database, and some additional rails tasks for working with them.
|
8
|
+
|
9
|
+
It was extracted from Discourse. http://discourse.org
|
4
10
|
|
5
11
|
## Installation
|
6
12
|
|
@@ -18,7 +24,26 @@ Or install it yourself as:
|
|
18
24
|
|
19
25
|
## Usage
|
20
26
|
|
21
|
-
|
27
|
+
Configuration requires a file called: `config/multisite.yml` that specifies connection specs for all dbs.
|
28
|
+
|
29
|
+
|
30
|
+
### Exectue a query on each connection
|
31
|
+
|
32
|
+
```
|
33
|
+
RailsMultisite::ConnectionManagement.each_connection do |db|
|
34
|
+
# run query in context of db
|
35
|
+
# eg: User.find(1)
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
```
|
40
|
+
RailsMultisite::ConnectionManagement.each_connection(threads: 5) do |db|
|
41
|
+
# run query in context of db, will do so in a thread pool of 5 threads
|
42
|
+
# if any query fails an exception will be raised
|
43
|
+
# eg: User.find(1)
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
22
47
|
|
23
48
|
## Contributing
|
24
49
|
|
@@ -1,13 +1,19 @@
|
|
1
1
|
module RailsMultisite
|
2
2
|
class ConnectionManagement
|
3
3
|
CONFIG_FILE = 'config/multisite.yml'
|
4
|
+
DEFAULT = 'default'.freeze
|
5
|
+
|
6
|
+
def self.has_db?(db)
|
7
|
+
return true if db == DEFAULT
|
8
|
+
(defined? @@db_spec_cache) && @@db_spec_cache && @@db_spec_cache[db]
|
9
|
+
end
|
4
10
|
|
5
11
|
def self.rails4?
|
6
12
|
!!(Rails.version =~ /^4/)
|
7
13
|
end
|
8
14
|
|
9
15
|
def self.establish_connection(opts)
|
10
|
-
if opts[:db] ==
|
16
|
+
if opts[:db] == DEFAULT && (!defined?(@@default_spec) || !@@default_spec)
|
11
17
|
# don't do anything .. handled implicitly
|
12
18
|
else
|
13
19
|
spec = connection_spec(opts) || @@default_spec
|
@@ -16,35 +22,116 @@ module RailsMultisite
|
|
16
22
|
handler = @@connection_handlers[spec]
|
17
23
|
unless handler
|
18
24
|
handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
19
|
-
|
20
|
-
handler.establish_connection(ActiveRecord::Base, spec)
|
21
|
-
end
|
25
|
+
handler.establish_connection(ActiveRecord::Base, spec)
|
22
26
|
@@connection_handlers[spec] = handler
|
23
27
|
end
|
24
28
|
else
|
25
29
|
handler = @@default_connection_handler
|
26
|
-
if
|
30
|
+
if !@@established_default
|
27
31
|
handler.establish_connection(ActiveRecord::Base, spec)
|
28
32
|
@@established_default = true
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
36
|
ActiveRecord::Base.connection_handler = handler
|
37
|
+
end
|
38
|
+
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
40
|
+
def self.with_hostname(hostname)
|
41
|
+
|
42
|
+
unless defined? @@db_spec_cache
|
43
|
+
# just fake it for non multisite
|
44
|
+
yield hostname
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
old = current_hostname
|
49
|
+
connected = ActiveRecord::Base.connection_pool.connected?
|
50
|
+
|
51
|
+
establish_connection(:host => hostname) unless connected && hostname == old
|
52
|
+
rval = yield hostname
|
53
|
+
|
54
|
+
unless connected && hostname == old
|
55
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
56
|
+
|
57
|
+
establish_connection(:host => old)
|
58
|
+
ActiveRecord::Base.connection_handler.clear_active_connections! unless connected
|
37
59
|
end
|
60
|
+
|
61
|
+
rval
|
38
62
|
end
|
39
63
|
|
40
|
-
def self.
|
64
|
+
def self.with_connection(db = "default")
|
41
65
|
old = current_db
|
42
66
|
connected = ActiveRecord::Base.connection_pool.connected?
|
43
|
-
|
44
|
-
|
45
|
-
|
67
|
+
|
68
|
+
establish_connection(:db => db) unless connected && db == old
|
69
|
+
rval = yield db
|
70
|
+
|
71
|
+
unless connected && db == old
|
46
72
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
73
|
+
|
74
|
+
establish_connection(:db => old)
|
75
|
+
ActiveRecord::Base.connection_handler.clear_active_connections! unless connected
|
76
|
+
end
|
77
|
+
|
78
|
+
rval
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.each_connection(opts=nil, &blk)
|
82
|
+
|
83
|
+
old = current_db
|
84
|
+
connected = ActiveRecord::Base.connection_pool.connected?
|
85
|
+
|
86
|
+
queue = nil
|
87
|
+
threads = nil
|
88
|
+
|
89
|
+
if (opts && (threads = opts[:threads]))
|
90
|
+
queue = Queue.new
|
91
|
+
all_dbs.each{|db| queue << db}
|
47
92
|
end
|
93
|
+
|
94
|
+
errors = nil
|
95
|
+
|
96
|
+
if queue
|
97
|
+
threads.times.map do
|
98
|
+
Thread.new do
|
99
|
+
|
100
|
+
while true
|
101
|
+
begin
|
102
|
+
db = queue.deq(true)
|
103
|
+
rescue ThreadError
|
104
|
+
db = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
break unless db
|
108
|
+
|
109
|
+
establish_connection(:db => db)
|
110
|
+
# no choice but to rescue, should probably log
|
111
|
+
|
112
|
+
begin
|
113
|
+
blk.call(db)
|
114
|
+
rescue => e
|
115
|
+
(errors ||= []) << e
|
116
|
+
end
|
117
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end.map(&:join)
|
121
|
+
else
|
122
|
+
all_dbs.each do |db|
|
123
|
+
establish_connection(:db => db)
|
124
|
+
blk.call(db)
|
125
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
if errors && errors.length > 0
|
130
|
+
raise StandardError, "Failed to run queries #{errors.inspect}"
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
ensure
|
48
135
|
establish_connection(:db => old)
|
49
136
|
ActiveRecord::Base.connection_handler.clear_active_connections! unless connected
|
50
137
|
end
|
@@ -59,7 +146,7 @@ module RailsMultisite
|
|
59
146
|
end
|
60
147
|
|
61
148
|
def self.current_db
|
62
|
-
|
149
|
+
ActiveRecord::Base.connection_pool.spec.config[:db_key] || "default"
|
63
150
|
end
|
64
151
|
|
65
152
|
def self.config_filename=(config_filename)
|
@@ -75,7 +162,6 @@ module RailsMultisite
|
|
75
162
|
config[:host_names].nil? ? config[:host] : config[:host_names].first
|
76
163
|
end
|
77
164
|
|
78
|
-
|
79
165
|
def self.clear_settings!
|
80
166
|
@@db_spec_cache = nil
|
81
167
|
@@host_spec_cache = nil
|
@@ -83,7 +169,7 @@ module RailsMultisite
|
|
83
169
|
end
|
84
170
|
|
85
171
|
def self.load_settings!
|
86
|
-
spec_klass =
|
172
|
+
spec_klass = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
87
173
|
configs = YAML::load(File.open(self.config_filename))
|
88
174
|
configs.each do |k,v|
|
89
175
|
raise ArgumentError.new("Please do not name any db default!") if k == "default"
|
@@ -91,7 +177,7 @@ module RailsMultisite
|
|
91
177
|
end
|
92
178
|
|
93
179
|
@@db_spec_cache = Hash[*configs.map do |k, data|
|
94
|
-
[k, spec_klass::Resolver.new(
|
180
|
+
[k, spec_klass::Resolver.new(configs).spec(k)]
|
95
181
|
end.flatten]
|
96
182
|
|
97
183
|
@@host_spec_cache = {}
|
@@ -102,54 +188,36 @@ module RailsMultisite
|
|
102
188
|
end
|
103
189
|
end
|
104
190
|
|
105
|
-
@@default_spec = spec_klass::Resolver.new(
|
191
|
+
@@default_spec = spec_klass::Resolver.new(ActiveRecord::Base.configurations).spec(Rails.env)
|
106
192
|
ActiveRecord::Base.configurations[Rails.env]["host_names"].each do |host|
|
107
193
|
@@host_spec_cache[host] = @@default_spec
|
108
194
|
end
|
109
195
|
|
110
196
|
@@default_connection_handler = ActiveRecord::Base.connection_handler
|
111
197
|
|
112
|
-
# inject our connection_handler pool
|
113
|
-
# WARNING MONKEY PATCH
|
114
|
-
#
|
115
|
-
# see: https://github.com/rails/rails/issues/8344#issuecomment-10800848
|
116
|
-
if ActiveRecord::VERSION::MAJOR == 3
|
117
|
-
ActiveRecord::Base.send :include, NewConnectionHandler
|
118
|
-
ActiveRecord::Base.connection_handler = @@default_connection_handler
|
119
|
-
end
|
120
|
-
|
121
198
|
@@connection_handlers = {}
|
122
199
|
@@established_default = false
|
123
200
|
end
|
124
201
|
|
125
|
-
module NewConnectionHandler
|
126
|
-
def self.included(klass)
|
127
|
-
klass.class_eval do
|
128
|
-
define_singleton_method :connection_handler do
|
129
|
-
Thread.current[:connection_handler] || @connection_handler
|
130
|
-
end
|
131
|
-
define_singleton_method :connection_handler= do |handler|
|
132
|
-
@connection_handler ||= handler
|
133
|
-
Thread.current[:connection_handler] = handler
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
202
|
|
140
203
|
def initialize(app, config = nil)
|
141
204
|
@app = app
|
142
205
|
end
|
143
206
|
|
144
|
-
def
|
207
|
+
def self.host(env)
|
145
208
|
request = Rack::Request.new(env)
|
209
|
+
request['__ws'] || request.host
|
210
|
+
end
|
211
|
+
|
212
|
+
def call(env)
|
213
|
+
host = self.class.host(env)
|
146
214
|
begin
|
147
215
|
|
148
216
|
#TODO: add a callback so users can simply go to a domain to register it, or something
|
149
|
-
return [404, {}, ["not found"]] unless @@host_spec_cache[
|
217
|
+
return [404, {}, ["not found"]] unless @@host_spec_cache[host]
|
150
218
|
|
151
219
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
152
|
-
self.class.establish_connection(:host =>
|
220
|
+
self.class.establish_connection(:host => host)
|
153
221
|
@app.call(env)
|
154
222
|
ensure
|
155
223
|
ActiveRecord::Base.connection_handler.clear_active_connections!
|
@@ -5,7 +5,9 @@ module RailsMultisite
|
|
5
5
|
end
|
6
6
|
|
7
7
|
initializer "RailsMultisite.init" do |app|
|
8
|
+
Rails.configuration.multisite = false
|
8
9
|
if File.exists?(ConnectionManagement.config_filename)
|
10
|
+
Rails.configuration.multisite = true
|
9
11
|
ConnectionManagement.load_settings!
|
10
12
|
app.middleware.insert_after(ActiveRecord::ConnectionAdapters::ConnectionManagement, RailsMultisite::ConnectionManagement)
|
11
13
|
app.middleware.delete(ActiveRecord::ConnectionAdapters::ConnectionManagement)
|
data/lib/tasks/db.rake
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
desc "migrate all sites in tier"
|
2
|
+
task "multisite:migrate" => :environment do
|
3
|
+
RailsMultisite::ConnectionManagement.each_connection do |db|
|
4
|
+
puts "Migrating #{db}"
|
5
|
+
puts "---------------------------------\n"
|
6
|
+
t = Rake::Task["db:migrate"]
|
7
|
+
t.reenable
|
8
|
+
t.invoke
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
task "multisite:seed_fu" => :environment do
|
13
|
+
RailsMultisite::ConnectionManagement.each_connection do |db|
|
14
|
+
puts "Seeding #{db}"
|
15
|
+
puts "---------------------------------\n"
|
16
|
+
t = Rake::Task["db:seed_fu"]
|
17
|
+
t.reenable
|
18
|
+
t.invoke
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "rollback migrations for all sites in tier"
|
23
|
+
task "multisite:rollback" => :environment do
|
24
|
+
RailsMultisite::ConnectionManagement.each_connection do |db|
|
25
|
+
puts "Rollback #{db}"
|
26
|
+
puts "---------------------------------\n"
|
27
|
+
t = Rake::Task["db:rollback"]
|
28
|
+
t.reenable
|
29
|
+
t.invoke
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
desc "generate multisite config file (if missing)"
|
2
|
+
task "multisite:generate:config" => :environment do
|
3
|
+
filename = RailsMultisite::ConnectionManagement.config_filename
|
4
|
+
|
5
|
+
if File.exists?(filename)
|
6
|
+
puts "Config is already generated at #{RailsMultisite::ConnectionManagement::CONFIG_FILE}"
|
7
|
+
else
|
8
|
+
puts "Generated config file at #{RailsMultisite::ConnectionManagement::CONFIG_FILE}"
|
9
|
+
File.open(filename, 'w') do |f|
|
10
|
+
f.write <<-CONFIG
|
11
|
+
# site_name:
|
12
|
+
# adapter: postgresql
|
13
|
+
# database: db_name
|
14
|
+
# host: localhost
|
15
|
+
# pool: 5
|
16
|
+
# timeout: 5000
|
17
|
+
# db_id: 1 # optionally include other settings you need
|
18
|
+
# host_names:
|
19
|
+
# - www.mysite.com
|
20
|
+
# - www.anothersite.com
|
21
|
+
CONFIG
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_multisite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Multi tenancy support for Rails
|
14
14
|
email:
|
@@ -23,6 +23,8 @@ files:
|
|
23
23
|
- lib/rails_multisite/connection_management.rb
|
24
24
|
- lib/rails_multisite/railtie.rb
|
25
25
|
- lib/rails_multisite/version.rb
|
26
|
+
- lib/tasks/db.rake
|
27
|
+
- lib/tasks/generators.rake
|
26
28
|
homepage: ''
|
27
29
|
licenses: []
|
28
30
|
metadata: {}
|
@@ -32,19 +34,18 @@ require_paths:
|
|
32
34
|
- lib
|
33
35
|
required_ruby_version: !ruby/object:Gem::Requirement
|
34
36
|
requirements:
|
35
|
-
- -
|
37
|
+
- - ">="
|
36
38
|
- !ruby/object:Gem::Version
|
37
39
|
version: '0'
|
38
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
41
|
requirements:
|
40
|
-
- -
|
42
|
+
- - ">="
|
41
43
|
- !ruby/object:Gem::Version
|
42
44
|
version: '0'
|
43
45
|
requirements: []
|
44
46
|
rubyforge_project:
|
45
|
-
rubygems_version: 2.
|
47
|
+
rubygems_version: 2.4.5.1
|
46
48
|
signing_key:
|
47
49
|
specification_version: 4
|
48
50
|
summary: Multi tenancy support for Rails
|
49
51
|
test_files: []
|
50
|
-
has_rdoc:
|