multitenant-mysql 1.0.2 → 1.1.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.
- 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
|