multitenant-mysql 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/Gemfile +1 -0
- data/README.rdoc +19 -3
- data/lib/generators/multitenant/triggers/create_generator.rb +15 -0
- data/lib/generators/multitenant/triggers/drop_generator.rb +14 -0
- data/lib/generators/multitenant/triggers/refresh_generator.rb +17 -0
- data/lib/generators/multitenant/triggers/sql/create.rb +33 -0
- data/lib/generators/multitenant/triggers/sql/drop.rb +23 -0
- data/lib/generators/multitenant/views/create_generator.rb +15 -0
- data/lib/generators/multitenant/views/drop_generator.rb +14 -0
- data/lib/generators/multitenant/views/refresh_generator.rb +17 -0
- data/lib/generators/multitenant/views/sql/create.rb +33 -0
- data/lib/generators/multitenant/views/sql/drop.rb +24 -0
- data/lib/generators/multitenant/views_and_triggers/create_generator.rb +22 -0
- data/lib/generators/multitenant/views_and_triggers/list/list.rb +17 -0
- data/lib/generators/multitenant/views_and_triggers/list/sql.rb +6 -0
- data/lib/generators/multitenant/views_and_triggers/refresh_generator.rb +26 -0
- data/lib/multitenant-mysql/connection_switcher.rb +2 -15
- data/lib/multitenant-mysql/db.rb +22 -0
- data/lib/multitenant-mysql/version.rb +1 -1
- data/spec/arc_spec.rb +3 -1
- data/spec/generators/list_spec.rb +33 -0
- data/spec/generators/triggers/sql/create_spec.rb +14 -0
- data/spec/generators/triggers/sql/drop_spec.rb +15 -0
- data/spec/generators/views/sql/create_spec.rb +14 -0
- data/spec/generators/views/sql/drop_spec.rb +15 -0
- data/spec/{action_controller_extension_spec.rb → rails/action_controller_extension_spec.rb} +0 -0
- data/spec/rails/active_record_base_spec.rb +0 -1
- data/spec/spec_helper.rb +54 -2
- metadata +29 -7
- data/lib/generators/multitenant/views_and_triggers/trigger_generator.rb +0 -38
- data/lib/generators/multitenant/views_and_triggers/view_generator.rb +0 -28
- data/lib/generators/multitenant/views_and_triggers/views_and_triggers_generator.rb +0 -20
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -37,7 +37,7 @@ this will create a sample of config file in "rails_root/config/multitenant_mysql
|
|
37
37
|
This is the place where you list all your tenant dependent models and the model which contains all the tenants.
|
38
38
|
E.g:
|
39
39
|
|
40
|
-
Multitenant::Mysql.
|
40
|
+
Multitenant::Mysql.arc = {
|
41
41
|
models: ['Book', 'Task'],
|
42
42
|
tenant_model: { name: 'Subdomain' }
|
43
43
|
}
|
@@ -51,7 +51,7 @@ Important: Before moving on you have to update this file as all further steps us
|
|
51
51
|
rake db:migrate
|
52
52
|
|
53
53
|
4 generate mysql views and triggers
|
54
|
-
rails g multitenant:views_and_triggers
|
54
|
+
rails g multitenant:views_and_triggers:create
|
55
55
|
|
56
56
|
5 in ApplicationController
|
57
57
|
set_current_tenant :tenant_method
|
@@ -71,7 +71,23 @@ E.g.
|
|
71
71
|
|
72
72
|
if method used by `set_current_tenant` returns blank name then `root` account is used
|
73
73
|
|
74
|
-
|
74
|
+
== Options and Notes
|
75
|
+
|
76
|
+
- if you have changed columns for tenant dependend models then you need to regenerate views, you could use generator
|
77
|
+
multitenant:views_and_triggers:refresh
|
78
|
+
|
79
|
+
- if you want to use subdomain as a tenant name then you can use metho
|
80
|
+
set_current_tenant_by_subdomain
|
81
|
+
|
82
|
+
- list of available generators to manage views and triggers
|
83
|
+
multitenant:triggers:create
|
84
|
+
multitenant:triggers:drop
|
85
|
+
multitenant:triggers:refresh
|
86
|
+
multitenant:views:create
|
87
|
+
multitenant:views:drop
|
88
|
+
multitenant:views:refresh
|
89
|
+
multitenant:views_and_triggers:create
|
90
|
+
multitenant:views_and_triggers:refresh
|
75
91
|
|
76
92
|
== How It Works
|
77
93
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/create'
|
3
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
4
|
+
|
5
|
+
module Multitenant
|
6
|
+
module Triggers
|
7
|
+
class Create < Rails::Generators::Base
|
8
|
+
desc 'create triggers for all tenant depended models'
|
9
|
+
|
10
|
+
def generate_mysql_triggers
|
11
|
+
Multitenant::Triggers::SQL::Create.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/drop'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Triggers
|
6
|
+
class Drop < Rails::Generators::Base
|
7
|
+
desc 'drops all triggers in db'
|
8
|
+
|
9
|
+
def drop_all_triggers
|
10
|
+
Multitenant::Triggers::SQL::Drop.run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/drop'
|
3
|
+
require_relative './sql/create'
|
4
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
5
|
+
|
6
|
+
module Multitenant
|
7
|
+
module Triggers
|
8
|
+
class Refresh < Rails::Generators::Base
|
9
|
+
desc 'drops all triggers and creates new ones based on configs'
|
10
|
+
|
11
|
+
def refresh_all_triggers
|
12
|
+
Multitenant::Triggers::SQL::Drop.run
|
13
|
+
Multitenant::Triggers::SQL::Create.run
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../../views_and_triggers/list/list'
|
2
|
+
require_relative '../../views_and_triggers/list/sql'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Triggers
|
6
|
+
module SQL
|
7
|
+
|
8
|
+
class Create
|
9
|
+
def self.run
|
10
|
+
Multitenant::Mysql.models.each do |model_name|
|
11
|
+
model = model_name.constantize
|
12
|
+
trigger_name = model.original_table_name + '_tenant_trigger'
|
13
|
+
|
14
|
+
return if Multitenant::List.new(Multitenant::SQL::TRIGGERS).exists?(trigger_name)
|
15
|
+
|
16
|
+
trigger_sql = %Q(
|
17
|
+
CREATE TRIGGER #{trigger_name}
|
18
|
+
BEFORE INSERT ON #{model.original_table_name}
|
19
|
+
FOR EACH ROW
|
20
|
+
SET new.tenant = SUBSTRING_INDEX(USER(), '@', 1);
|
21
|
+
)
|
22
|
+
|
23
|
+
p trigger_sql
|
24
|
+
ActiveRecord::Base.connection.execute(trigger_sql)
|
25
|
+
p "==================== Generated Trigger: #{trigger_name} =================="
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../../views_and_triggers/list/list'
|
2
|
+
require_relative '../../views_and_triggers/list/sql'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Triggers
|
6
|
+
module SQL
|
7
|
+
|
8
|
+
class Drop
|
9
|
+
|
10
|
+
def self.run
|
11
|
+
list = Multitenant::List.new
|
12
|
+
list.sql = Multitenant::SQL::TRIGGERS
|
13
|
+
list.to_a.each do |trigger|
|
14
|
+
ActiveRecord::Base.connection.execute("DROP TRIGGER #{trigger};")
|
15
|
+
p "==================== Dropped Trigger: #{trigger} =================="
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/create'
|
3
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
4
|
+
|
5
|
+
module Multitenant
|
6
|
+
module Views
|
7
|
+
class Create < Rails::Generators::Base
|
8
|
+
desc 'create views for all tenant depended models'
|
9
|
+
|
10
|
+
def generate_mysql_views
|
11
|
+
Multitenant::Views::SQL::Create.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/drop'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Views
|
6
|
+
class Drop < Rails::Generators::Base
|
7
|
+
desc 'drop all views in db'
|
8
|
+
|
9
|
+
def drop_all_views
|
10
|
+
Multitenant::Views::SQL::Drop.run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require_relative './sql/drop'
|
3
|
+
require_relative './sql/create'
|
4
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
5
|
+
|
6
|
+
module Multitenant
|
7
|
+
module Views
|
8
|
+
class Refresh < Rails::Generators::Base
|
9
|
+
desc 'drops all views and creates new ones based on configs'
|
10
|
+
|
11
|
+
def refresh_all_views
|
12
|
+
Multitenant::Views::SQL::Drop.run
|
13
|
+
Multitenant::Views::SQL::Create.run
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../../views_and_triggers/list/list'
|
2
|
+
require_relative '../../views_and_triggers/list/sql'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Views
|
6
|
+
module SQL
|
7
|
+
class Create
|
8
|
+
|
9
|
+
def self.run
|
10
|
+
Multitenant::Mysql.models.each do |model_name|
|
11
|
+
model = model_name.constantize
|
12
|
+
columns = model.column_names.join(', ')
|
13
|
+
view_name = model_name.to_s.downcase.pluralize + "_view"
|
14
|
+
|
15
|
+
# stop if view already exists
|
16
|
+
return if Multitenant::List.new(Multitenant::SQL::VIEWS).exists?(view_name)
|
17
|
+
|
18
|
+
view_sql = %Q(
|
19
|
+
CREATE VIEW #{view_name} AS
|
20
|
+
SELECT #{columns}
|
21
|
+
FROM #{model.table_name}
|
22
|
+
WHERE tenant = SUBSTRING_INDEX(USER(), '@', 1);
|
23
|
+
)
|
24
|
+
|
25
|
+
ActiveRecord::Base.connection.execute(view_sql)
|
26
|
+
p "==================== Generated View: #{view_name} =================="
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../../views_and_triggers/list/list'
|
2
|
+
require_relative '../../views_and_triggers/list/sql'
|
3
|
+
|
4
|
+
module Multitenant
|
5
|
+
module Views
|
6
|
+
module SQL
|
7
|
+
|
8
|
+
class Drop
|
9
|
+
|
10
|
+
def self.run
|
11
|
+
list = Multitenant::List.new
|
12
|
+
list.sql = Multitenant::SQL::VIEWS
|
13
|
+
|
14
|
+
list.to_a.each do |view|
|
15
|
+
ActiveRecord::Base.connection.execute("DROP VIEW #{view};")
|
16
|
+
p "==================== Dropped View: #{view} =================="
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
require_relative '../views/sql/create'
|
4
|
+
require_relative '../triggers/sql/create'
|
5
|
+
|
6
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
7
|
+
|
8
|
+
module Multitenant
|
9
|
+
module ViewsAndTriggers
|
10
|
+
class CreateGenerator < Rails::Generators::Base
|
11
|
+
desc "based on specified models creates appropriate mysql views and triggers"
|
12
|
+
|
13
|
+
def generate_mysql_views
|
14
|
+
Multitenant::Views::SQL::Create.run
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_mysql_triggers
|
18
|
+
Multitenant::Triggers::SQL::Create.run
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Multitenant
|
2
|
+
class List
|
3
|
+
attr_accessor :sql
|
4
|
+
|
5
|
+
def initialize(sql = nil)
|
6
|
+
@sql = sql
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_a
|
10
|
+
ActiveRecord::Base.connection.execute(sql).to_a.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def exists?(name)
|
14
|
+
to_a.include?(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
module Multitenant
|
2
|
+
module SQL
|
3
|
+
VIEWS = "SELECT TABLE_NAME FROM information_schema.`TABLES` WHERE TABLE_TYPE LIKE 'VIEW' AND TABLE_SCHEMA LIKE '#{Multitenant::Mysql::DB.configs['database']}';"
|
4
|
+
TRIGGERS = "SELECT trigger_name FROM information_schema.TRIGGERS where trigger_schema = '#{Multitenant::Mysql::DB.configs['database']}';"
|
5
|
+
end
|
6
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
require_relative '../views/sql/create'
|
4
|
+
require_relative '../views/sql/drop'
|
5
|
+
require_relative '../triggers/sql/create'
|
6
|
+
require_relative '../triggers/sql/drop'
|
7
|
+
|
8
|
+
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
9
|
+
|
10
|
+
module Multitenant
|
11
|
+
module ViewsAndTriggers
|
12
|
+
class RefreshGenerator < Rails::Generators::Base
|
13
|
+
desc "drops all views and triggers and creates new ones based on configs"
|
14
|
+
|
15
|
+
def generate_mysql_views
|
16
|
+
Multitenant::Views::SQL::Drop.run
|
17
|
+
Multitenant::Views::SQL::Create.run
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate_mysql_triggers
|
21
|
+
Multitenant::Triggers::SQL::Drop.run
|
22
|
+
Multitenant::Triggers::SQL::Create.run
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,22 +1,9 @@
|
|
1
|
+
require_relative './db'
|
2
|
+
|
1
3
|
module Multitenant
|
2
4
|
module Mysql
|
3
5
|
class NoTenantRegistratedError < StandardError; end;
|
4
6
|
|
5
|
-
class DB
|
6
|
-
class << self
|
7
|
-
def configs
|
8
|
-
Rails.configuration.database_configuration[Rails.env]
|
9
|
-
end
|
10
|
-
|
11
|
-
def establish_connection_for tenant_name
|
12
|
-
config = configs
|
13
|
-
config['username'] = tenant_name.blank? ? 'root' : tenant_name
|
14
|
-
ActiveRecord::Base.establish_connection(config)
|
15
|
-
true
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
7
|
class Tenant
|
21
8
|
def self.exists? tenant_name
|
22
9
|
return true if tenant_name.blank?
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Multitenant
|
2
|
+
module Mysql
|
3
|
+
class DB
|
4
|
+
class << self
|
5
|
+
def configs
|
6
|
+
@configs ||= Rails.configuration.database_configuration[Rails.env]
|
7
|
+
end
|
8
|
+
|
9
|
+
def configs=(configs)
|
10
|
+
@configs = configs
|
11
|
+
end
|
12
|
+
|
13
|
+
def establish_connection_for tenant_name
|
14
|
+
config = configs
|
15
|
+
config['username'] = tenant_name.blank? ? 'root' : tenant_name
|
16
|
+
ActiveRecord::Base.establish_connection(config)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/arc_spec.rb
CHANGED
@@ -31,7 +31,9 @@ describe Multitenant::Mysql do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
context '.tenant' do
|
34
|
-
|
34
|
+
before do
|
35
|
+
Subdomain = :constant
|
36
|
+
end
|
35
37
|
|
36
38
|
it 'should find and return appropriate model' do
|
37
39
|
subject.active_record_configs = { tenant_model: { name: 'Subdomain' } }
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Multitenant::List do
|
4
|
+
|
5
|
+
context 'views' do
|
6
|
+
subject { Multitenant::List.new(Multitenant::SQL::VIEWS) }
|
7
|
+
|
8
|
+
it 'should find all views' do
|
9
|
+
create_view_for_table('books')
|
10
|
+
expect(subject.to_a).to eq(['books_view'])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should exist' do
|
14
|
+
create_view_for_table('books')
|
15
|
+
expect(subject.exists?('books_view')).to be
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'triggers' do
|
20
|
+
subject { Multitenant::List.new(Multitenant::SQL::TRIGGERS) }
|
21
|
+
|
22
|
+
it 'should find all triggers' do
|
23
|
+
create_trigger_for_table('books')
|
24
|
+
expect(subject.to_a).to eq(['books_tenant_trigger'])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should exist' do
|
28
|
+
create_trigger_for_table('books')
|
29
|
+
expect(subject.exists?('books_tenant_trigger')).to be
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Multitenant::Triggers::SQL::Create do
|
4
|
+
subject { Multitenant::Triggers::SQL::Create }
|
5
|
+
|
6
|
+
it 'should generate trigger' do
|
7
|
+
create_table('books')
|
8
|
+
class Subdomain < ActiveRecord::Base; end;
|
9
|
+
class Book < ActiveRecord::Base; end;
|
10
|
+
subject.run
|
11
|
+
expect( Multitenant::List.new(Multitenant::SQL::TRIGGERS).to_a ).to eq(['books_tenant_trigger'])
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Multitenant::Triggers::SQL::Drop do
|
4
|
+
subject { Multitenant::Triggers::SQL::Drop }
|
5
|
+
|
6
|
+
it 'should drop trigger' do
|
7
|
+
create_trigger_for_table('books')
|
8
|
+
class Subdomain < ActiveRecord::Base; end;
|
9
|
+
class Book < ActiveRecord::Base; end;
|
10
|
+
expect( Multitenant::List.new(Multitenant::SQL::TRIGGERS).to_a ).to eq(['books_tenant_trigger'])
|
11
|
+
subject.run
|
12
|
+
expect( Multitenant::List.new(Multitenant::SQL::TRIGGERS).to_a ).to be_empty
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Multitenant::Views::SQL::Create do
|
4
|
+
subject { Multitenant::Views::SQL::Create }
|
5
|
+
|
6
|
+
it 'should generate view' do
|
7
|
+
create_table('books')
|
8
|
+
class Subdomain < ActiveRecord::Base; end;
|
9
|
+
class Book < ActiveRecord::Base; end;
|
10
|
+
subject.run
|
11
|
+
expect( Multitenant::List.new(Multitenant::SQL::VIEWS).to_a ).to eq(['books_view'])
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Multitenant::Views::SQL::Drop do
|
4
|
+
subject { Multitenant::Views::SQL::Drop }
|
5
|
+
|
6
|
+
it 'should drop view' do
|
7
|
+
create_view_for_table('books')
|
8
|
+
class Subdomain < ActiveRecord::Base; end;
|
9
|
+
class Book < ActiveRecord::Base; end;
|
10
|
+
expect( Multitenant::List.new(Multitenant::SQL::VIEWS).to_a ).to eq(['books_view'])
|
11
|
+
subject.run
|
12
|
+
expect( Multitenant::List.new(Multitenant::SQL::VIEWS).to_a ).to be_empty
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,59 @@ require 'active_record'
|
|
5
5
|
require 'action_controller'
|
6
6
|
require 'multitenant-mysql'
|
7
7
|
|
8
|
+
|
9
|
+
GEM_ROOT_PATH = File.expand_path('../../', __FILE__)
|
10
|
+
CONF_FILE_PATH = GEM_ROOT_PATH + '/spec/support/multitenant_mysql_conf'
|
11
|
+
|
8
12
|
RSpec.configure do |config|
|
9
|
-
|
10
|
-
|
13
|
+
config.before do
|
14
|
+
Multitenant::Mysql::ConfFile.path = CONF_FILE_PATH
|
15
|
+
|
16
|
+
ActiveRecord::Base.establish_connection({
|
17
|
+
adapter: 'mysql2',
|
18
|
+
username: 'root',
|
19
|
+
password: ''
|
20
|
+
})
|
21
|
+
ActiveRecord::Base.connection.execute('drop database if exists `tenant_test`;')
|
22
|
+
ActiveRecord::Base.connection.execute('create database `tenant_test`;')
|
23
|
+
ActiveRecord::Base.connection.execute('use `tenant_test`;')
|
24
|
+
end
|
25
|
+
|
26
|
+
config.after do
|
27
|
+
Object.send(:remove_const, :Subdomain) if defined?(Subdomain)
|
28
|
+
Object.send(:remove_const, :Book) if defined?(Book)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_table(name)
|
33
|
+
ActiveRecord::Base.connection.execute("drop table if exists `#{name}`;")
|
34
|
+
ActiveRecord::Base.connection.execute("create table `#{name}`
|
35
|
+
(`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
36
|
+
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
|
37
|
+
`tenant` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL);")
|
11
38
|
end
|
39
|
+
|
40
|
+
def create_view_for_table(name)
|
41
|
+
create_table(name)
|
42
|
+
ActiveRecord::Base.connection.execute("
|
43
|
+
CREATE VIEW #{name}_view AS
|
44
|
+
SELECT id, name, tenant FROM #{name}
|
45
|
+
")
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_trigger_for_table(name)
|
49
|
+
create_table(name)
|
50
|
+
ActiveRecord::Base.connection.execute("
|
51
|
+
CREATE TRIGGER #{name}_tenant_trigger
|
52
|
+
BEFORE INSERT ON #{name}
|
53
|
+
FOR EACH ROW
|
54
|
+
SET new.tenant = SUBSTRING_INDEX(USER(), '@', 1);
|
55
|
+
")
|
56
|
+
end
|
57
|
+
|
58
|
+
Multitenant::Mysql::DB.configs = { 'username' => 'root', 'database' => 'tenant_test' }
|
59
|
+
|
60
|
+
require GEM_ROOT_PATH + '/lib/generators/multitenant/views/sql/create'
|
61
|
+
require GEM_ROOT_PATH + '/lib/generators/multitenant/views/sql/drop'
|
62
|
+
require GEM_ROOT_PATH + '/lib/generators/multitenant/triggers/sql/create'
|
63
|
+
require GEM_ROOT_PATH + '/lib/generators/multitenant/triggers/sql/drop'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multitenant-mysql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -45,22 +45,39 @@ files:
|
|
45
45
|
- lib/generators/multitenant/install/templates/multitenant_mysql_conf.rb
|
46
46
|
- lib/generators/multitenant/migrations/migration_builder.rb
|
47
47
|
- lib/generators/multitenant/migrations/migrations_generator.rb
|
48
|
-
- lib/generators/multitenant/
|
49
|
-
- lib/generators/multitenant/
|
50
|
-
- lib/generators/multitenant/
|
48
|
+
- lib/generators/multitenant/triggers/create_generator.rb
|
49
|
+
- lib/generators/multitenant/triggers/drop_generator.rb
|
50
|
+
- lib/generators/multitenant/triggers/refresh_generator.rb
|
51
|
+
- lib/generators/multitenant/triggers/sql/create.rb
|
52
|
+
- lib/generators/multitenant/triggers/sql/drop.rb
|
53
|
+
- lib/generators/multitenant/views/create_generator.rb
|
54
|
+
- lib/generators/multitenant/views/drop_generator.rb
|
55
|
+
- lib/generators/multitenant/views/refresh_generator.rb
|
56
|
+
- lib/generators/multitenant/views/sql/create.rb
|
57
|
+
- lib/generators/multitenant/views/sql/drop.rb
|
58
|
+
- lib/generators/multitenant/views_and_triggers/create_generator.rb
|
59
|
+
- lib/generators/multitenant/views_and_triggers/list/list.rb
|
60
|
+
- lib/generators/multitenant/views_and_triggers/list/sql.rb
|
61
|
+
- lib/generators/multitenant/views_and_triggers/refresh_generator.rb
|
51
62
|
- lib/multitenant-mysql.rb
|
52
63
|
- lib/multitenant-mysql/action_controller_extension.rb
|
53
64
|
- lib/multitenant-mysql/active_record_extension.rb
|
54
65
|
- lib/multitenant-mysql/arc.rb
|
55
66
|
- lib/multitenant-mysql/conf_file.rb
|
56
67
|
- lib/multitenant-mysql/connection_switcher.rb
|
68
|
+
- lib/multitenant-mysql/db.rb
|
57
69
|
- lib/multitenant-mysql/version.rb
|
58
70
|
- multitenant-mysql.gemspec
|
59
71
|
- rails/init.rb
|
60
|
-
- spec/action_controller_extension_spec.rb
|
61
72
|
- spec/arc_spec.rb
|
62
73
|
- spec/conf_file_spec.rb
|
63
74
|
- spec/connection_switcher_spec.rb
|
75
|
+
- spec/generators/list_spec.rb
|
76
|
+
- spec/generators/triggers/sql/create_spec.rb
|
77
|
+
- spec/generators/triggers/sql/drop_spec.rb
|
78
|
+
- spec/generators/views/sql/create_spec.rb
|
79
|
+
- spec/generators/views/sql/drop_spec.rb
|
80
|
+
- spec/rails/action_controller_extension_spec.rb
|
64
81
|
- spec/rails/active_record_base_spec.rb
|
65
82
|
- spec/spec_helper.rb
|
66
83
|
- spec/support/multitenant_mysql_conf.rb
|
@@ -89,10 +106,15 @@ signing_key:
|
|
89
106
|
specification_version: 3
|
90
107
|
summary: Add multi-tenancy to Rails application using MySql views
|
91
108
|
test_files:
|
92
|
-
- spec/action_controller_extension_spec.rb
|
93
109
|
- spec/arc_spec.rb
|
94
110
|
- spec/conf_file_spec.rb
|
95
111
|
- spec/connection_switcher_spec.rb
|
112
|
+
- spec/generators/list_spec.rb
|
113
|
+
- spec/generators/triggers/sql/create_spec.rb
|
114
|
+
- spec/generators/triggers/sql/drop_spec.rb
|
115
|
+
- spec/generators/views/sql/create_spec.rb
|
116
|
+
- spec/generators/views/sql/drop_spec.rb
|
117
|
+
- spec/rails/action_controller_extension_spec.rb
|
96
118
|
- spec/rails/active_record_base_spec.rb
|
97
119
|
- spec/spec_helper.rb
|
98
120
|
- spec/support/multitenant_mysql_conf.rb
|
@@ -1,38 +0,0 @@
|
|
1
|
-
module Multitenant
|
2
|
-
class TriggerGenerator
|
3
|
-
|
4
|
-
class << self
|
5
|
-
def run
|
6
|
-
Multitenant::Mysql.models.each do |model_name|
|
7
|
-
model = model_name.constantize
|
8
|
-
trigger_name = model.original_table_name + '_tenant_trigger'
|
9
|
-
|
10
|
-
return if trigger_exists?(trigger_name)
|
11
|
-
|
12
|
-
trigger_sql = %Q(
|
13
|
-
CREATE TRIGGER #{trigger_name}
|
14
|
-
BEFORE INSERT ON #{model.original_table_name}
|
15
|
-
FOR EACH ROW
|
16
|
-
SET new.tenant = SUBSTRING_INDEX(USER(), '@', 1);
|
17
|
-
)
|
18
|
-
|
19
|
-
p trigger_sql
|
20
|
-
ActiveRecord::Base.connection.execute(trigger_sql)
|
21
|
-
p "==================== Generated Trigger: #{trigger_name} =================="
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def trigger_exists?(trigger_name)
|
27
|
-
config = Rails.configuration.database_configuration[Rails.env]
|
28
|
-
db_name = config['database']
|
29
|
-
|
30
|
-
find_trigger_sql = "SELECT trigger_name FROM information_schema.TRIGGERS where trigger_schema = '#{db_name}';"
|
31
|
-
result = ActiveRecord::Base.connection.execute(find_trigger_sql).to_a.flatten
|
32
|
-
|
33
|
-
result.include?(trigger_name)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Multitenant
|
2
|
-
class ViewGenerator
|
3
|
-
|
4
|
-
def self.run
|
5
|
-
Multitenant::Mysql.models.each do |model_name|
|
6
|
-
model = model_name.constantize
|
7
|
-
columns = model.column_names.join(', ')
|
8
|
-
view_name = model_name.to_s.downcase.pluralize + "_view"
|
9
|
-
|
10
|
-
# stop if view already exists
|
11
|
-
return if ActiveRecord::Base.connection.table_exists?(view_name)
|
12
|
-
|
13
|
-
view_sql = %Q(
|
14
|
-
CREATE VIEW #{view_name} AS
|
15
|
-
SELECT #{columns}
|
16
|
-
FROM #{model.table_name}
|
17
|
-
WHERE tenant = SUBSTRING_INDEX(USER(), '@', 1);
|
18
|
-
)
|
19
|
-
|
20
|
-
p view_sql
|
21
|
-
|
22
|
-
ActiveRecord::Base.connection.execute(view_sql)
|
23
|
-
p "==================== Generated View: #{view_name} =================="
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'rails/generators'
|
2
|
-
|
3
|
-
require_relative './trigger_generator'
|
4
|
-
require_relative './view_generator'
|
5
|
-
|
6
|
-
require Rails.root.to_s + '/config/multitenant_mysql_conf'
|
7
|
-
|
8
|
-
module Multitenant
|
9
|
-
class ViewsAndTriggersGenerator < Rails::Generators::Base
|
10
|
-
desc "based on specified models will create appropriate mysql views, triggers and migrations"
|
11
|
-
|
12
|
-
def generate_mysql_views
|
13
|
-
Multitenant::ViewGenerator.run
|
14
|
-
end
|
15
|
-
|
16
|
-
def generate_mysql_triggers
|
17
|
-
Multitenant::TriggerGenerator.run
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|