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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e45ef95202698ebf603b6442abf02eba8f6a25e2
4
- data.tar.gz: c47de25e43b5a8c01d8a3643bcff11592075dc4c
3
+ metadata.gz: 0fd46e714ed81aade7bf750d0229ec1a96cb3f9f
4
+ data.tar.gz: 9ea4c386f993da1f6648acdf33ff854a51d3af79
5
5
  SHA512:
6
- metadata.gz: 87c49b6fdd43d33155168227cf94949d8e114a6b440bf0c53e3233e19c1cd78f42e7057c17d2752920225b050b2effc4b7cf29e2715f452283392bcbf1aca0cd
7
- data.tar.gz: 4ef167a5687c24657d69a1744a1d9c18d07593f7fc1546bca98bce24996056fa2b92abe58c843b4070b6e6af95464ea0a2cb0574ed6cff17eeda914632d236e6
6
+ metadata.gz: 7e3c52ff6e9120af35e0b2e5677296d85d453d2d68520682846dcf0c1ff1ee24afe2039b91420ddb8907dc9480ab69a7c66fbcaed3086df909bf8d672b60f053
7
+ data.tar.gz: cb516155225e1db227abfb0f36b67d86f8767d47e331e693a25c98d73612964ed2c64ec0e3d19a242f49920e261370e2e365f2903e09d57a7a44f3792f5f8f9f
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
- # RailsMultisite
1
+ # Rails Multisite
2
2
 
3
- TODO: Write a gem description
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
- TODO: Write usage instructions here
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] == "default" && (!defined?(@@default_spec) || !@@default_spec)
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
- if rails4?
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 rails4? && !@@established_default
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
- unless rails4?
35
- handler.establish_connection("ActiveRecord::Base", spec)
36
- end
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.each_connection
64
+ def self.with_connection(db = "default")
41
65
  old = current_db
42
66
  connected = ActiveRecord::Base.connection_pool.connected?
43
- all_dbs.each do |db|
44
- establish_connection(:db => db)
45
- yield db
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
- db = ActiveRecord::Base.connection_pool.spec.config[:db_key] || "default"
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 = rails4? ? ActiveRecord::ConnectionAdapters::ConnectionSpecification : ActiveRecord::Base::ConnectionSpecification
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(k, configs).spec]
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(Rails.env, ActiveRecord::Base.configurations).spec
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 call(env)
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[request.host]
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 => request['__ws'] || request.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)
@@ -1,3 +1,3 @@
1
1
  module RailsMultisite
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.1"
3
3
  end
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: 0.0.1
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: 2013-12-29 00:00:00.000000000 Z
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.2.0
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: