rails_multisite 0.0.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.

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: