multidb 3.2.0
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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.md +21 -0
- data/README.md +65 -0
- data/lib/multi_db/action_controller_patches.rb +53 -0
- data/lib/multi_db/active_record_patches.rb +94 -0
- data/lib/multi_db/after_initialize_patches.rb +3 -0
- data/lib/multi_db/engine.rb +28 -0
- data/lib/multi_db/multidb.rake +360 -0
- data/lib/multi_db/organization.rb +60 -0
- data/lib/multi_db/organization_host.rb +7 -0
- data/lib/multidb.rb +10 -0
- data/multidb.gemspec +26 -0
- data/testapp_mysql2/.simplecov +3 -0
- data/testapp_mysql2/Gemfile +25 -0
- data/testapp_mysql2/Gemfile.lock +148 -0
- data/testapp_mysql2/Rakefile +7 -0
- data/testapp_mysql2/app/assets/javascripts/application.js +15 -0
- data/testapp_mysql2/app/assets/stylesheets/application.css +13 -0
- data/testapp_mysql2/app/controllers/application_controller.rb +3 -0
- data/testapp_mysql2/app/models/organization.rb +14 -0
- data/testapp_mysql2/app/views/layouts/application.html.erb +14 -0
- data/testapp_mysql2/config/application.rb +62 -0
- data/testapp_mysql2/config/boot.rb +6 -0
- data/testapp_mysql2/config/database.yml +17 -0
- data/testapp_mysql2/config/environment.rb +5 -0
- data/testapp_mysql2/config/environments/development.rb +37 -0
- data/testapp_mysql2/config/environments/production.rb +67 -0
- data/testapp_mysql2/config/environments/test.rb +37 -0
- data/testapp_mysql2/config/initializers/backtrace_silencers.rb +7 -0
- data/testapp_mysql2/config/initializers/inflections.rb +15 -0
- data/testapp_mysql2/config/initializers/mime_types.rb +5 -0
- data/testapp_mysql2/config/initializers/secret_token.rb +7 -0
- data/testapp_mysql2/config/initializers/session_store.rb +8 -0
- data/testapp_mysql2/config/initializers/wrap_parameters.rb +14 -0
- data/testapp_mysql2/config/routes.rb +58 -0
- data/testapp_mysql2/config.ru +4 -0
- data/testapp_mysql2/db/migrate/master/20140110031857_add_organizations_tables.rb +22 -0
- data/testapp_mysql2/db/migrate/sessions/20140110031856_add_sessions_table.rb +14 -0
- data/testapp_mysql2/db/schema_master.rb +35 -0
- data/testapp_mysql2/db/schema_organization.rb +16 -0
- data/testapp_mysql2/db/schema_sessions.rb +26 -0
- data/testapp_mysql2/log/.gitkeep +0 -0
- data/testapp_mysql2/script/rails +6 -0
- data/testapp_mysql2/spec/models/organization_spec.rb +61 -0
- data/testapp_mysql2/spec/spec_helper.rb +24 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 871069e0b89ed728780ae50f13950dc1e8e1c6db
|
4
|
+
data.tar.gz: 56645bb4e9fd29f8f696e6e2534f963ce414dc62
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67773f48625923f72efa87033d8e988ef36679c58d7ad928c1b79abd13722c54b14590c390c6e1d0c4542cb420199d8a709776ebdfbe2f0ef564086157909b74
|
7
|
+
data.tar.gz: 8ca58cc43cc994807fb72979866defbaf325f861f77b5ab2809c55a08c10b17673b1bead7a26e4482e0aaf85bff395cb5dd64093e9241e7800144670bda76dd2
|
data/.gitignore
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
|
3
|
+
# Ignore .lock file as this is a gem
|
4
|
+
Gemfile.lock
|
5
|
+
|
6
|
+
# Ignore gem
|
7
|
+
multidb-*.gem
|
8
|
+
|
9
|
+
# Ignore all logfiles and tempfiles.
|
10
|
+
testapp_*/coverage
|
11
|
+
testapp_*/log/*.log
|
12
|
+
testapp_*/tmp
|
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.0
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Aaron Namba
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#About MultiDB
|
2
|
+
|
3
|
+
MultiDB is a multitenant extension for Rails (or just ActiveRecord) that allows you to isolate each tenant into its own individual database without requiring major changes to your application code.
|
4
|
+
|
5
|
+
MultiDB is _not_ meant for systems that may have large numbers of tenants; you wouldn't want to have that many databases. It was designed for a system with 100-200 tenants, with the option to go to 1,000, and an absolute upper limit of 10,000. If you anticipate having more, MultiDB may not be an appropriate solution. (MySQL itself does not specify an upper limit on the number of databases, but it will be constrained by open file limits and, in some cases, directory entry limits.)
|
6
|
+
|
7
|
+
To minimize API changes for you, MultiDB patches ActiveRecord, ActionController and their associated rake tasks as needed to enable database switching at appropriate times and add support for three sets of schemas and migrations. ActiveRecord::SessionStore::Session has also been patched.
|
8
|
+
|
9
|
+
Internally, MultiDB refers to tenants as organizations. In addition to the organization databases, two additional databases are created: one is just for sessions, and the other is referred to as the master database, which houses the organizations table (tenants), and all other tables not associated with a particular organization. (Yes, it seems like these two additional databases could be combined, but it is a design decision intended to keep all essential data out of the default database. More on this later.)
|
10
|
+
|
11
|
+
MultiDB, when used with Rails (ActionController), determines which database to connect to at the beginning of each request by checking for `request.host`, `params[:org_code]`, then `session[:org_code]`. In a test environment, it can will also check the environment variable `RAILS_ORG`. If no organization code is found in any of those places, the sessions database is used (which is one reason it is important that no actual data be stored there).
|
12
|
+
|
13
|
+
|
14
|
+
## Compatibility
|
15
|
+
|
16
|
+
### Rails & ActiveRecord
|
17
|
+
MultiDB 3.2 works with Rails 3.2. A new branch will be created to work with Rails 4.
|
18
|
+
|
19
|
+
The concept should work with just ActiveRecord (no Rails), but this use case has not been tested. (Pull requests welcome.)
|
20
|
+
|
21
|
+
### Database Adapters
|
22
|
+
Warning: MultiDB works only with mysql2 at present. If you are handy with Ruby, please help to add support for more adapters. See the "Contributing" section below.
|
23
|
+
|
24
|
+
MultiDB is known to work with [Makara](https://github.com/taskrabbit/makara) in a production environment.
|
25
|
+
|
26
|
+
|
27
|
+
##Get Started With MultiDB
|
28
|
+
|
29
|
+
Install the gem:
|
30
|
+
|
31
|
+
gem install multidb
|
32
|
+
|
33
|
+
Or add it to your Gemfile:
|
34
|
+
|
35
|
+
gem 'multidb'
|
36
|
+
|
37
|
+
Then have your Organization class inherit from MultiDB::Organization. This gets you:
|
38
|
+
|
39
|
+
Organization#connect(set_env = false)
|
40
|
+
Organization#create_database
|
41
|
+
Organization#drop_database!
|
42
|
+
|
43
|
+
Your table will need to have columns for `code` (string) and `active` (boolean), and to use the request.host feature, you will need organization_hosts as well. Recommended migration:
|
44
|
+
|
45
|
+
create_table "organization_hosts", :force => true do |t|
|
46
|
+
t.integer "organization_id", :null => false
|
47
|
+
t.string "host", :null => false
|
48
|
+
t.datetime "created_at", :null => false
|
49
|
+
t.datetime "updated_at", :null => false
|
50
|
+
end
|
51
|
+
add_index "organization_hosts", ["host"], :name => "index_organization_hosts_on_host", :unique => true
|
52
|
+
|
53
|
+
create_table "organizations", :force => true do |t|
|
54
|
+
t.string "name"
|
55
|
+
t.string "code", :null => false
|
56
|
+
t.boolean "active", :default => true, :null => false
|
57
|
+
t.datetime "created_at", :null => false
|
58
|
+
t.datetime "updated_at", :null => false
|
59
|
+
end
|
60
|
+
add_index "organizations", ["code"], :name => "index_organizations_on_code", :unique => true
|
61
|
+
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
Pull requests welcome. Don't forget tests! When adding support for a new adapter, create a new Rails app for your adapter (e.g. `rails _3.2.16_ new testapp_postgresql`), customize database.yml and other files as needed, then add specs to that app.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class ActionController::Base
|
2
|
+
prepend_before_filter :connect_to_organization_database
|
3
|
+
|
4
|
+
# manually establish a connection to the proper database
|
5
|
+
def connect_to_organization_database
|
6
|
+
@org = nil
|
7
|
+
|
8
|
+
# request is first priority
|
9
|
+
if params[:org_code]
|
10
|
+
if session[:org_code] && session[:org_code] != params[:org_code]
|
11
|
+
reset_session
|
12
|
+
end
|
13
|
+
@org = MultiDB::Organization.active.where(:code => params[:org_code]).first
|
14
|
+
end
|
15
|
+
|
16
|
+
# try hostname if we don't already have a code in the session
|
17
|
+
if !@org && !session[:org_code] && request && request.host
|
18
|
+
@org ||= MultiDB::Organization.active.where(:code => $1.gsub('-', '_')).first if request.host =~ /^([-\w\d]+)/
|
19
|
+
@org ||= MultiDB::Organization.active.includes(:hosts).where('organization_hosts.host = ?', request.host).first
|
20
|
+
end
|
21
|
+
|
22
|
+
if @org
|
23
|
+
if session[:org_code] != @org.code
|
24
|
+
session[:org_code] = @org.code
|
25
|
+
session[:org_name] = @org.name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if session[:org_code]
|
30
|
+
@org ||= MultiDB::Organization.active.where(:code => session[:org_code]).first
|
31
|
+
if @org
|
32
|
+
ActiveRecord::Base.connect_to_organization(session[:org_code], true)
|
33
|
+
return @org
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if Rails.env.test? && ENV['RAILS_ORG']
|
38
|
+
@org ||= MultiDB::Organization.active.where(:code => ENV['RAILS_ORG']).first
|
39
|
+
if @org
|
40
|
+
ActiveRecord::Base.connect_to_organization(session[:org_code], true)
|
41
|
+
return @org
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# if we don't issue an establish_connection by now, connect to default db (sessions)
|
46
|
+
session[:org_code] = session[:org_name] = nil
|
47
|
+
ActiveRecord::Base.connect_to_sessions
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect_to_master_database
|
51
|
+
ActiveRecord::Base.connect_to_master
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def master_configuration(env = nil)
|
6
|
+
env ||= Rails.env
|
7
|
+
|
8
|
+
# use master db configuration in config/database.yml if present
|
9
|
+
configurations["master_#{env}"] or Proc.new {
|
10
|
+
c = configurations[env].dup
|
11
|
+
c['database'] += '_master'
|
12
|
+
c
|
13
|
+
}.call
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect_to_sessions
|
17
|
+
config = configurations[Rails.env || 'development']
|
18
|
+
self.establish_connection(config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect_to_master
|
22
|
+
self.establish_connection(master_configuration)
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect_to_organization(org = nil, set_env = false)
|
26
|
+
org_code = org.is_a?(MultiDB::Organization) ? org.code : org
|
27
|
+
org_code ||= ENV['RAILS_ORG'] || 'org1'
|
28
|
+
|
29
|
+
config = ActiveRecord::Base.configurations[Rails.env]
|
30
|
+
self.establish_connection(config.merge('database' => "#{config['database']}_#{org_code}"))
|
31
|
+
|
32
|
+
begin
|
33
|
+
ActiveRecord::Base.connection.tables
|
34
|
+
rescue Mysql2::Error
|
35
|
+
Rails.logger.error "Couldn't connect to org db for #{org_code}"
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
ENV['RAILS_ORG'] = org_code if set_env
|
40
|
+
org_code
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Migration
|
46
|
+
|
47
|
+
alias_method :migrate_without_multidb, :migrate
|
48
|
+
def migrate(direction)
|
49
|
+
if ENV['RAILS_ORG'] == 'master'
|
50
|
+
Base.connect_to_master
|
51
|
+
elsif ENV['RAILS_ORG'] == 'sessions'
|
52
|
+
Base.connect_to_sessions
|
53
|
+
else
|
54
|
+
Base.connect_to_organization
|
55
|
+
end
|
56
|
+
|
57
|
+
migrate_without_multidb(direction)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class Migrator
|
63
|
+
|
64
|
+
alias_method :initialize_without_multidb, :initialize
|
65
|
+
def initialize(direction, migrations_paths, target_version = nil)
|
66
|
+
if ENV['RAILS_ORG'] == 'master'
|
67
|
+
Base.connect_to_master
|
68
|
+
elsif ENV['RAILS_ORG'] == 'sessions'
|
69
|
+
Base.connect_to_sessions
|
70
|
+
else
|
71
|
+
Base.connect_to_organization
|
72
|
+
end
|
73
|
+
|
74
|
+
initialize_without_multidb(direction, migrations_paths, target_version)
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
|
79
|
+
def migrations_paths
|
80
|
+
@migrations_paths ||= ['db/migrate']
|
81
|
+
# just to not break things if someone uses: migration_path = some_string
|
82
|
+
paths = Array.wrap(@migrations_paths)
|
83
|
+
|
84
|
+
case ENV['RAILS_ORG']
|
85
|
+
when 'sessions', 'master'
|
86
|
+
paths.map { |path| "#{path}/#{ENV['RAILS_ORG']}" }
|
87
|
+
else
|
88
|
+
paths.map { |path| "#{path}/org" }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MultiDB
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
engine_name "multidb"
|
4
|
+
|
5
|
+
config.app_root = root
|
6
|
+
config.autoload_paths += Dir["#{config.root}/lib/**/"]
|
7
|
+
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
9
|
+
require 'multi_db/active_record_patches'
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveSupport.on_load(:action_controller) do
|
13
|
+
require 'multi_db/action_controller_patches'
|
14
|
+
end
|
15
|
+
|
16
|
+
ActiveSupport.on_load(:after_initialize) do
|
17
|
+
require 'multi_db/after_initialize_patches'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module MultiDB
|
23
|
+
class Railtie < Rails::Railtie
|
24
|
+
rake_tasks do
|
25
|
+
load "multi_db/multidb.rake"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,360 @@
|
|
1
|
+
#
|
2
|
+
# Based on activerecord-3.2.15/lib/active_record/railties/databases.rake
|
3
|
+
# Last updated 2013-11-19 by Aaron Namba
|
4
|
+
#
|
5
|
+
# AKN: Warning! All code to support db engines other than mysql has been deleted,
|
6
|
+
# since I know it will never receive proper testing.
|
7
|
+
#
|
8
|
+
|
9
|
+
tasks = Rake.application.instance_variable_get '@tasks'
|
10
|
+
tasks.delete 'db:migrate'
|
11
|
+
tasks.delete 'db:migrate:up'
|
12
|
+
tasks.delete 'db:migrate:down'
|
13
|
+
tasks.delete 'db:rollback'
|
14
|
+
tasks.delete 'db:forward'
|
15
|
+
tasks.delete 'db:schema:dump'
|
16
|
+
tasks.delete 'db:schema:load'
|
17
|
+
tasks.delete 'db:test:load_schema'
|
18
|
+
tasks.delete 'db:test:purge'
|
19
|
+
|
20
|
+
|
21
|
+
db_namespace = namespace :db do
|
22
|
+
namespace :create do
|
23
|
+
desc 'Runs create for sessions and master databases.'
|
24
|
+
task :multi => [:load_config] do
|
25
|
+
[ 'sessions', 'master' ].each do |org|
|
26
|
+
ENV['RAILS_ORG'] = org
|
27
|
+
db_namespace[:create].execute
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
namespace :drop do
|
33
|
+
desc 'Runs drop for sessions and master databases.'
|
34
|
+
task :multi => [:load_config] do
|
35
|
+
[ 'sessions', 'master' ].each do |org|
|
36
|
+
ENV['RAILS_ORG'] = org
|
37
|
+
db_namespace[:drop].execute
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
namespace :setup do
|
43
|
+
desc 'Runs setup for sessions and master databases.'
|
44
|
+
task :multi => [:load_config] do
|
45
|
+
[ 'sessions', 'master' ].each do |org|
|
46
|
+
ENV['RAILS_ORG'] = org
|
47
|
+
db_namespace[:create].execute
|
48
|
+
db_namespace['schema:load'].execute
|
49
|
+
db_namespace[:seed].execute
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :migrate do
|
55
|
+
desc 'Runs migrate for sessions, master and org databases.'
|
56
|
+
task :multi => [:environment, :load_config] do
|
57
|
+
puts "=========================================================="
|
58
|
+
puts "===== SESSIONS"
|
59
|
+
puts "=========================================================="
|
60
|
+
puts
|
61
|
+
ENV['RAILS_ORG'] = 'sessions'
|
62
|
+
db_namespace[:migrate].execute
|
63
|
+
puts
|
64
|
+
|
65
|
+
puts "=========================================================="
|
66
|
+
puts "===== MASTER"
|
67
|
+
puts "=========================================================="
|
68
|
+
puts
|
69
|
+
ENV['RAILS_ORG'] = 'master'
|
70
|
+
db_namespace[:migrate].execute
|
71
|
+
puts
|
72
|
+
|
73
|
+
puts "=========================================================="
|
74
|
+
puts "===== ORGANIZATIONS"
|
75
|
+
puts "=========================================================="
|
76
|
+
puts
|
77
|
+
ENV['RAILS_ORG'] = nil
|
78
|
+
db_namespace[:migrate].execute
|
79
|
+
puts
|
80
|
+
end
|
81
|
+
|
82
|
+
# desc 'Runs the "up" for a given migration VERSION.'
|
83
|
+
task :up => [:environment, :load_config] do
|
84
|
+
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
85
|
+
raise 'VERSION is required' unless version
|
86
|
+
|
87
|
+
if ENV['RAILS_ORG'] == 'sessions' || ENV['RAILS_ORG'] == 'all'
|
88
|
+
ActiveRecord::Base.connect_to_sessions
|
89
|
+
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
90
|
+
db_namespace['_dump'].invoke
|
91
|
+
end
|
92
|
+
|
93
|
+
if ENV['RAILS_ORG'] == 'master' || ENV['RAILS_ORG'] == 'all'
|
94
|
+
ActiveRecord::Base.connect_to_master
|
95
|
+
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
96
|
+
db_namespace['_dump'].invoke
|
97
|
+
end
|
98
|
+
|
99
|
+
if ENV['RAILS_ORG'] == 'all' || ![ 'sessions', 'master' ].include?(ENV['RAILS_ORG'])
|
100
|
+
schema_dumped = false
|
101
|
+
MultiDB::Organization.active.each do |org|
|
102
|
+
ActiveRecord::Base.connect_to_organization(org, true)
|
103
|
+
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
104
|
+
db_namespace['_dump'].invoke unless schema_dumped
|
105
|
+
schema_dumped = true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# desc 'Runs the "down" for a given migration VERSION.'
|
111
|
+
task :down => [:environment, :load_config] do
|
112
|
+
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
113
|
+
raise 'VERSION is required' unless version
|
114
|
+
|
115
|
+
if ENV['RAILS_ORG'] == 'sessions'
|
116
|
+
ActiveRecord::Base.connect_to_sessions
|
117
|
+
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
118
|
+
db_namespace['_dump'].invoke
|
119
|
+
end
|
120
|
+
|
121
|
+
if ENV['RAILS_ORG'] == 'master'
|
122
|
+
ActiveRecord::Base.connect_to_master
|
123
|
+
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
124
|
+
db_namespace['_dump'].invoke
|
125
|
+
end
|
126
|
+
|
127
|
+
if ![ 'sessions', 'master' ].include?(ENV['RAILS_ORG'])
|
128
|
+
schema_dumped = false
|
129
|
+
MultiDB::Organization.active.each do |org|
|
130
|
+
ActiveRecord::Base.connect_to_organization(org, true)
|
131
|
+
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
132
|
+
db_namespace['_dump'].invoke unless schema_dumped
|
133
|
+
schema_dumped = true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
|
140
|
+
task :migrate => [:environment, :load_config] do
|
141
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
142
|
+
|
143
|
+
if ENV['RAILS_ORG'] == 'sessions'
|
144
|
+
ActiveRecord::Base.connect_to_sessions
|
145
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
146
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
147
|
+
end
|
148
|
+
db_namespace['_dump'].invoke
|
149
|
+
end
|
150
|
+
|
151
|
+
if ENV['RAILS_ORG'] == 'master'
|
152
|
+
ActiveRecord::Base.connect_to_master
|
153
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
154
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
155
|
+
end
|
156
|
+
db_namespace['_dump'].invoke
|
157
|
+
end
|
158
|
+
|
159
|
+
if ![ 'sessions', 'master' ].include?(ENV['RAILS_ORG'])
|
160
|
+
schema_dumped = false
|
161
|
+
MultiDB::Organization.active.each do |org|
|
162
|
+
puts "== Migrating: #{org.code}"
|
163
|
+
ActiveRecord::Base.connect_to_organization(org, true)
|
164
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
|
165
|
+
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
166
|
+
end
|
167
|
+
db_namespace['_dump'].invoke unless schema_dumped
|
168
|
+
schema_dumped = true
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
174
|
+
task :rollback => [:environment, :load_config] do
|
175
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
176
|
+
|
177
|
+
if ENV['RAILS_ORG'] == 'sessions'
|
178
|
+
ActiveRecord::Base.connect_to_sessions
|
179
|
+
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
180
|
+
db_namespace['_dump'].invoke
|
181
|
+
end
|
182
|
+
|
183
|
+
if ENV['RAILS_ORG'] == 'master'
|
184
|
+
ActiveRecord::Base.connect_to_master
|
185
|
+
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
186
|
+
db_namespace['_dump'].invoke
|
187
|
+
end
|
188
|
+
|
189
|
+
if ![ 'sessions', 'master' ].include?(ENV['RAILS_ORG'])
|
190
|
+
schema_dumped = false
|
191
|
+
MultiDB::Organization.active.each do |org|
|
192
|
+
puts "== Rollback: #{org.code}"
|
193
|
+
ActiveRecord::Base.connect_to_organization(org, true)
|
194
|
+
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
|
195
|
+
db_namespace['_dump'].invoke unless schema_dumped
|
196
|
+
schema_dumped = true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
202
|
+
task :forward => [:environment, :load_config] do
|
203
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
204
|
+
|
205
|
+
if ENV['RAILS_ORG'] == 'sessions'
|
206
|
+
ActiveRecord::Base.connect_to_sessions
|
207
|
+
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
|
208
|
+
db_namespace['_dump'].invoke
|
209
|
+
end
|
210
|
+
|
211
|
+
if ENV['RAILS_ORG'] == 'master'
|
212
|
+
ActiveRecord::Base.connect_to_master
|
213
|
+
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
|
214
|
+
db_namespace['_dump'].invoke
|
215
|
+
end
|
216
|
+
|
217
|
+
if ![ 'sessions', 'master' ].include?(ENV['RAILS_ORG'])
|
218
|
+
schema_dumped = false
|
219
|
+
MultiDB::Organization.active.each do |org|
|
220
|
+
ActiveRecord::Base.connect_to_organization(org, true)
|
221
|
+
ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
|
222
|
+
db_namespace['_dump'].invoke unless schema_dumped
|
223
|
+
schema_dumped = true
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
namespace :schema do
|
229
|
+
desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
|
230
|
+
task :dump => [:environment, :load_config] do
|
231
|
+
require 'active_record/schema_dumper'
|
232
|
+
|
233
|
+
databases_to_dump = case ENV['RAILS_ORG']
|
234
|
+
when nil then [ :sessions, :master, :organization ]
|
235
|
+
when 'sessions' then [ :sessions ]
|
236
|
+
when 'master' then [ :master ]
|
237
|
+
else [ :organization ]
|
238
|
+
end
|
239
|
+
|
240
|
+
databases_to_dump.each do |db|
|
241
|
+
ActiveRecord::Base.send("connect_to_#{db}")
|
242
|
+
filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema_#{db}.rb"
|
243
|
+
File.open(filename, "w:utf-8") do |file|
|
244
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
db_namespace['schema:dump'].reenable
|
248
|
+
end
|
249
|
+
|
250
|
+
desc 'Load a schema.rb file into the database'
|
251
|
+
task :load => [:environment, :load_config] do
|
252
|
+
databases_to_load = case ENV['RAILS_ORG']
|
253
|
+
when nil then [ :sessions, :master, :organization ]
|
254
|
+
when 'sessions' then [ :sessions ]
|
255
|
+
when 'master' then [ :master ]
|
256
|
+
else [ :organization ]
|
257
|
+
end
|
258
|
+
|
259
|
+
databases_to_load.each do |db|
|
260
|
+
ActiveRecord::Base.send("connect_to_#{db}")
|
261
|
+
file = ENV['SCHEMA'] || "#{Rails.root}/db/schema_#{db}.rb"
|
262
|
+
if File.exists?(file)
|
263
|
+
load(file)
|
264
|
+
else
|
265
|
+
abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded}
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
namespace :test do
|
272
|
+
# desc "Recreate the test database from an existent schema.rb file"
|
273
|
+
task :load_schema => 'db:test:purge' do
|
274
|
+
old_env = Rails.env
|
275
|
+
Rails.env = 'test'
|
276
|
+
ActiveRecord::Schema.verbose = false
|
277
|
+
db_namespace["schema:load"].invoke
|
278
|
+
Rails.env = old_env
|
279
|
+
end
|
280
|
+
|
281
|
+
# desc "Empty the test databases"
|
282
|
+
task :purge => [:environment, :load_config] do
|
283
|
+
abcs = ActiveRecord::Base.configurations
|
284
|
+
|
285
|
+
# sessions
|
286
|
+
ActiveRecord::Base.connect_to_sessions
|
287
|
+
ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], mysql_creation_options(abcs['test']))
|
288
|
+
|
289
|
+
# master
|
290
|
+
ActiveRecord::Base.connect_to_master
|
291
|
+
ActiveRecord::Base.connection.recreate_database(ActiveRecord::Base.master_configuration('test')['database'], mysql_creation_options(abcs['test']))
|
292
|
+
|
293
|
+
# org
|
294
|
+
ActiveRecord::Base.connect_to_organization
|
295
|
+
ActiveRecord::Base.connection.recreate_database(abcs['test']['database'] + '_org1', mysql_creation_options(abcs['test']))
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
alias :create_database_without_multidb :create_database
|
300
|
+
def create_database(_config)
|
301
|
+
is_test = ActiveRecord::Base.configurations.invert[_config] == 'test'
|
302
|
+
config = _config.dup
|
303
|
+
|
304
|
+
case ENV['RAILS_ORG']
|
305
|
+
when nil
|
306
|
+
# set default org database
|
307
|
+
config['database'] += '_org1'
|
308
|
+
when 'sessions'
|
309
|
+
# do nothing
|
310
|
+
when 'master'
|
311
|
+
config = ActiveRecord::Base.master_configuration(is_test ? 'test' : nil)
|
312
|
+
else
|
313
|
+
config['database'] += '_' + ENV['RAILS_ORG']
|
314
|
+
end
|
315
|
+
|
316
|
+
create_database_without_multidb(config)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
|
321
|
+
alias :drop_database_without_multidb :drop_database
|
322
|
+
def drop_database(_config)
|
323
|
+
is_test = ActiveRecord::Base.configurations.invert[_config] == 'test'
|
324
|
+
config = _config.dup
|
325
|
+
|
326
|
+
case ENV['RAILS_ORG']
|
327
|
+
when nil
|
328
|
+
# set default org database
|
329
|
+
config['database'] += '_org1'
|
330
|
+
when 'sessions'
|
331
|
+
# do nothing
|
332
|
+
when 'master'
|
333
|
+
config = ActiveRecord::Base.master_configuration(is_test ? 'test' : nil)
|
334
|
+
else
|
335
|
+
config['database'] += '_' + ENV['RAILS_ORG']
|
336
|
+
end
|
337
|
+
|
338
|
+
drop_database_without_multidb(config)
|
339
|
+
end
|
340
|
+
|
341
|
+
# not sure yet why environment is not loaded before running create_database/drop_database
|
342
|
+
# for now, copied this in from lib/multi_db/active_record_patches.rb
|
343
|
+
module ActiveRecord
|
344
|
+
class Base
|
345
|
+
class << self
|
346
|
+
|
347
|
+
def master_configuration(env = nil)
|
348
|
+
env ||= Rails.env
|
349
|
+
|
350
|
+
# use master db configuration in config/database.yml if present
|
351
|
+
configurations["master_#{env}"] or Proc.new {
|
352
|
+
c = configurations[env].dup
|
353
|
+
c['database'] += '_master'
|
354
|
+
c
|
355
|
+
}.call
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|