rails_multisite_discourse 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 992f8e0cc674d6bdc7f6e252940596f006de0c64
4
+ data.tar.gz: 0246b02a3da70e424823b77a5c5c69ff2b166885
5
+ SHA512:
6
+ metadata.gz: f06bfa63fd47d83ebb99a4a0f5e111c3a238c926386b060101a0990255368a9e8983e70dbbf83096965ad77ee016d56941600f7dd49574219cb69c91eb95a556
7
+ data.tar.gz: 9f91b582b0adc7ec741119ae33158bcfc267737670cf947ed0c41cda68f3a709b3e4d33c6f8457ce6ce4347a9b89107e4dfd77eb9845c3025363c0b821f77da1
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Sam Saffron
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Rails Multisite
2
+
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
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'rails_multisite'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install rails_multisite
24
+
25
+ ## Usage
26
+
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
+
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
@@ -0,0 +1,3 @@
1
+ require "rails_multisite/version"
2
+ require "rails_multisite/railtie"
3
+ require "rails_multisite/connection_management"
@@ -0,0 +1,236 @@
1
+ module RailsMultisite
2
+ class ConnectionManagement
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
10
+
11
+ def self.rails4?
12
+ !!(Rails.version =~ /^4/)
13
+ end
14
+
15
+ def self.establish_connection(opts)
16
+ if opts[:db] == DEFAULT && (!defined?(@@default_spec) || !@@default_spec)
17
+ # don't do anything .. handled implicitly
18
+ else
19
+ spec = connection_spec(opts) || @@default_spec
20
+ handler = nil
21
+ if spec != @@default_spec
22
+ handler = @@connection_handlers[spec]
23
+ unless handler
24
+ handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
25
+ handler.establish_connection(ActiveRecord::Base, spec)
26
+ @@connection_handlers[spec] = handler
27
+ end
28
+ else
29
+ handler = @@default_connection_handler
30
+ if !@@established_default
31
+ handler.establish_connection(ActiveRecord::Base, spec)
32
+ @@established_default = true
33
+ end
34
+ end
35
+
36
+ ActiveRecord::Base.connection_handler = handler
37
+ end
38
+ end
39
+
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
59
+ end
60
+
61
+ rval
62
+ end
63
+
64
+ def self.with_connection(db = "default")
65
+ old = current_db
66
+ connected = ActiveRecord::Base.connection_pool.connected?
67
+
68
+ establish_connection(:db => db) unless connected && db == old
69
+ rval = yield db
70
+
71
+ unless connected && db == old
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}
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
135
+ establish_connection(:db => old)
136
+ ActiveRecord::Base.connection_handler.clear_active_connections! unless connected
137
+ end
138
+
139
+ def self.all_dbs
140
+ ["default"] +
141
+ if defined?(@@db_spec_cache) && @@db_spec_cache
142
+ @@db_spec_cache.keys.to_a
143
+ else
144
+ []
145
+ end
146
+ end
147
+
148
+ def self.current_db
149
+ ActiveRecord::Base.connection_pool.spec.config[:db_key] || "default"
150
+ end
151
+
152
+ def self.config_filename=(config_filename)
153
+ @@config_filename = config_filename
154
+ end
155
+
156
+ def self.config_filename
157
+ @@config_filename ||= File.absolute_path(Rails.root.to_s + "/" + RailsMultisite::ConnectionManagement::CONFIG_FILE)
158
+ end
159
+
160
+ def self.current_hostname
161
+ config = ActiveRecord::Base.connection_pool.spec.config
162
+ config[:host_names].nil? ? config[:host] : config[:host_names].first
163
+ end
164
+
165
+ def self.clear_settings!
166
+ @@db_spec_cache = nil
167
+ @@host_spec_cache = nil
168
+ @@default_spec = nil
169
+ end
170
+
171
+ def self.load_settings!
172
+ spec_klass = ActiveRecord::ConnectionAdapters::ConnectionSpecification
173
+ configs = YAML::load(File.open(self.config_filename))
174
+ configs.each do |k,v|
175
+ raise ArgumentError.new("Please do not name any db default!") if k == "default"
176
+ v[:db_key] = k
177
+ end
178
+
179
+ @@db_spec_cache = Hash[*configs.map do |k, data|
180
+ [k, spec_klass::Resolver.new(configs).spec(k)]
181
+ end.flatten]
182
+
183
+ @@host_spec_cache = {}
184
+ configs.each do |k,v|
185
+ next unless v["host_names"]
186
+ v["host_names"].each do |host|
187
+ @@host_spec_cache[host] = @@db_spec_cache[k]
188
+ end
189
+ end
190
+
191
+ @@default_spec = spec_klass::Resolver.new(ActiveRecord::Base.configurations).spec(Rails.env)
192
+ ActiveRecord::Base.configurations[Rails.env]["host_names"].each do |host|
193
+ @@host_spec_cache[host] = @@default_spec
194
+ end
195
+
196
+ @@default_connection_handler = ActiveRecord::Base.connection_handler
197
+
198
+ @@connection_handlers = {}
199
+ @@established_default = false
200
+ end
201
+
202
+
203
+ def initialize(app, config = nil)
204
+ @app = app
205
+ end
206
+
207
+ def self.host(env)
208
+ request = Rack::Request.new(env)
209
+ request['__ws'] || request.host
210
+ end
211
+
212
+ def call(env)
213
+ host = self.class.host(env)
214
+ begin
215
+
216
+ #TODO: add a callback so users can simply go to a domain to register it, or something
217
+ return [404, {}, ["not found"]] unless @@host_spec_cache[host]
218
+
219
+ ActiveRecord::Base.connection_handler.clear_active_connections!
220
+ self.class.establish_connection(:host => host)
221
+ @app.call(env)
222
+ ensure
223
+ ActiveRecord::Base.connection_handler.clear_active_connections!
224
+ end
225
+ end
226
+
227
+ def self.connection_spec(opts)
228
+ if opts[:host]
229
+ @@host_spec_cache[opts[:host]]
230
+ else
231
+ @@db_spec_cache[opts[:db]]
232
+ end
233
+ end
234
+
235
+ end
236
+ end
@@ -0,0 +1,23 @@
1
+ module RailsMultisite
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
5
+ end
6
+
7
+ initializer "RailsMultisite.init" do |app|
8
+ Rails.configuration.multisite = false
9
+ if File.exists?(ConnectionManagement.config_filename)
10
+ Rails.configuration.multisite = true
11
+ ConnectionManagement.load_settings!
12
+ app.middleware.insert_after(ActiveRecord::ConnectionAdapters::ConnectionManagement, RailsMultisite::ConnectionManagement)
13
+ app.middleware.delete(ActiveRecord::ConnectionAdapters::ConnectionManagement)
14
+ if ENV['RAILS_DB']
15
+ ConnectionManagement.establish_connection(:db => ENV['RAILS_DB'])
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,3 @@
1
+ module RailsMultisite
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_multisite_discourse
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Saffron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Multi tenancy support for Rails
14
+ email:
15
+ - sam.saffron@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - lib/rails_multisite.rb
23
+ - lib/rails_multisite/connection_management.rb
24
+ - lib/rails_multisite/railtie.rb
25
+ - lib/rails_multisite/version.rb
26
+ homepage: ''
27
+ licenses: []
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.4.5.1
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Multi tenancy support for Rails
49
+ test_files: []