multitenant-mysql 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG.rdoc +4 -0
  2. data/Gemfile +1 -0
  3. data/README.rdoc +9 -8
  4. data/lib/generators/multitenant/install/install_generator.rb +1 -1
  5. data/lib/generators/multitenant/install/templates/multitenant_mysql_conf.rb +14 -13
  6. data/lib/generators/multitenant/migrations/migration_builder.rb +1 -1
  7. data/lib/generators/multitenant/migrations/migrations_generator.rb +0 -1
  8. data/lib/generators/multitenant/triggers/create_generator.rb +0 -1
  9. data/lib/generators/multitenant/triggers/refresh_generator.rb +0 -1
  10. data/lib/generators/multitenant/triggers/sql/create.rb +1 -1
  11. data/lib/generators/multitenant/views/create_generator.rb +0 -1
  12. data/lib/generators/multitenant/views/refresh_generator.rb +0 -1
  13. data/lib/generators/multitenant/views/sql/create.rb +1 -1
  14. data/lib/generators/multitenant/views_and_triggers/create_generator.rb +0 -2
  15. data/lib/generators/multitenant/views_and_triggers/drop_generator.rb +20 -0
  16. data/lib/generators/multitenant/views_and_triggers/refresh_generator.rb +0 -2
  17. data/lib/multitenant-mysql.rb +2 -3
  18. data/lib/multitenant-mysql/action_controller_extension.rb +1 -8
  19. data/lib/multitenant-mysql/active_record_extension.rb +22 -22
  20. data/lib/multitenant-mysql/configs.rb +23 -0
  21. data/lib/multitenant-mysql/configs/base.rb +20 -0
  22. data/lib/multitenant-mysql/configs/bucket.rb +43 -0
  23. data/lib/multitenant-mysql/connection_switcher.rb +4 -4
  24. data/lib/multitenant-mysql/db.rb +0 -1
  25. data/lib/multitenant-mysql/errors.rb +9 -0
  26. data/lib/multitenant-mysql/version.rb +1 -1
  27. data/spec/configs_spec.rb +39 -0
  28. data/spec/connection_switcher_spec.rb +36 -28
  29. data/spec/db_spec.rb +35 -0
  30. data/spec/rails/active_record_base_spec.rb +6 -6
  31. data/spec/spec_helper.rb +10 -3
  32. metadata +12 -11
  33. data/lib/multitenant-mysql/arc.rb +0 -52
  34. data/lib/multitenant-mysql/conf_file.rb +0 -22
  35. data/spec/arc_spec.rb +0 -81
  36. data/spec/conf_file_spec.rb +0 -48
  37. data/spec/support/multitenant_mysql_conf.rb +0 -4
@@ -1,2 +1,6 @@
1
1
  1.1.1 (March 18, 2013)
2
2
  * fixed migration generation (https://github.com/eugenekorpan/multitenant-mysql/issues/20)
3
+
4
+ 1.2.0 (March 20, 2013)
5
+ * refactored some code
6
+ * changed config file structure little bit, for older versions just update the config file
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
  gem 'rails'
6
6
  gem 'rspec'
7
7
  gem 'mysql2'
8
+ gem 'coveralls', require: false
@@ -16,7 +16,7 @@ The advantages of such approach:
16
16
  * {<img src="https://travis-ci.org/eugenekorpan/multitenant-mysql.png?branch=master"/>}[http://travis-ci.org/eugenekorpan/multitenant-mysql]
17
17
  * {<img src="https://gemnasium.com/eugenekorpan/multitenant-mysql.png" alt="Dependency Status" />}[https://gemnasium.com/eugenekorpan/multitenant-mysql]
18
18
  * {<img src="https://codeclimate.com/github/eugenekorpan/multitenant-mysql.png" />}[https://codeclimate.com/github/eugenekorpan/multitenant-mysql]
19
-
19
+ * {<img src="https://coveralls.io/repos/eugenekorpan/multitenant-mysql/badge.png?branch=master" alt="Coverage Status" />}[https://coveralls.io/r/eugenekorpan/multitenant-mysql]
20
20
 
21
21
  == Installation
22
22
 
@@ -33,14 +33,14 @@ The advantages of such approach:
33
33
  1 run
34
34
  rails g multitenant:install
35
35
 
36
- this will create a sample of config file in "rails_root/config/multitenant_mysql_conf.rb"
37
- This is the place where you list all your tenant dependent models and the model which contains all the tenants.
38
- E.g:
36
+ This will create a sample of config file in "rails_root/config/initializers/multitenant_mysql_conf.rb". Update it according to your needs. E.g:
39
37
 
40
- Multitenant::Mysql.arc = {
41
- models: ['Book', 'Task'],
42
- tenant_model: { name: 'Subdomain' }
43
- }
38
+ Multitenant::Mysql.configure do |conf|
39
+ conf.models = ['Book', 'Task']
40
+ conf.tenants_bucket 'Subdomain' do |tb|
41
+ tb.field = 'name'
42
+ end
43
+ end
44
44
 
45
45
  Important: Before moving on you have to update this file as all further steps use those configs.
46
46
 
@@ -88,6 +88,7 @@ if method used by `set_current_tenant` returns blank name then `root` account is
88
88
  multitenant:views:drop
89
89
  multitenant:views:refresh
90
90
  multitenant:views_and_triggers:create
91
+ multitenant:views_and_triggers:drop
91
92
  multitenant:views_and_triggers:refresh
92
93
 
93
94
  == How It Works
@@ -7,7 +7,7 @@ module Multitenant
7
7
  CONFIG_FILE_NAME = 'multitenant_mysql_conf.rb'
8
8
 
9
9
  def copy_conf_file_into_app
10
- dest = "config/#{CONFIG_FILE_NAME}"
10
+ dest = "config/initializers/#{CONFIG_FILE_NAME}"
11
11
  return if FileTest.exist?(dest) # to prevent overwritting of existing file
12
12
  src = File.expand_path(File.dirname(__FILE__)) + "/templates/#{CONFIG_FILE_NAME}"
13
13
  FileUtils.copy_file src, dest
@@ -1,17 +1,18 @@
1
- # simple example
1
+ # Usage example:
2
2
  #
3
- # Multitenant::Mysql.arc = {
4
- # models: ['Book', 'Task'],
5
- # tenant_model: { name: 'Subdomain', tenant_name_attr: name }
6
- # }
3
+ # Multitenant::Mysql.configure do |conf|
4
+ # conf.models = ['Book', 'Task', 'Post']
5
+ # conf.tenants_bucket 'Subdomain' do |tb|
6
+ # tb.field = 'name'
7
+ # end
8
+ # end
7
9
  #
8
10
  # where:
9
- # models - list of tenant related models
10
- # tenant_model - model where tenant info is stored
11
- # name - model name
12
- # tenant_name_attr - attribute used to fetch tenant name
11
+ # models - list of tenant dependent models
12
+ # tenants_bucket - model which stores all the tenants, as an argument recives the name of the model
13
+ # field - attribute used to fetch tenant name (not required, default values are: name, title)
13
14
 
14
- Multitenant::Mysql.arc = {
15
- models: [],
16
- tenant_model: { name: '' }
17
- }
15
+ Multitenant::Mysql.configure do |conf|
16
+ conf.models = []
17
+ conf.tenants_bucket ''
18
+ end
@@ -8,7 +8,7 @@ module Multitenant
8
8
  def run
9
9
  return if migration_exists?
10
10
 
11
- actions = Multitenant::Mysql.models.map { |model_name|
11
+ actions = Multitenant::Mysql.configs.models.map { |model_name|
12
12
  model = model_name.constantize
13
13
  "add_column :#{model.original_table_name}, :tenant, :string"
14
14
  }
@@ -1,7 +1,6 @@
1
1
  require 'rails/generators'
2
2
 
3
3
  require_relative './migration_builder'
4
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
5
4
 
6
5
  module Multitenant
7
6
  class MigrationsGenerator < Rails::Generators::Base
@@ -1,6 +1,5 @@
1
1
  require 'rails/generators'
2
2
  require_relative './sql/create'
3
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
4
3
 
5
4
  module Multitenant
6
5
  module Triggers
@@ -1,7 +1,6 @@
1
1
  require 'rails/generators'
2
2
  require_relative './sql/drop'
3
3
  require_relative './sql/create'
4
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
5
4
 
6
5
  module Multitenant
7
6
  module Triggers
@@ -7,7 +7,7 @@ module Multitenant
7
7
 
8
8
  class Create
9
9
  def self.run
10
- Multitenant::Mysql.models.each do |model_name|
10
+ Multitenant::Mysql.configs.models.each do |model_name|
11
11
  model = model_name.constantize
12
12
  trigger_name = model.original_table_name + '_tenant_trigger'
13
13
 
@@ -1,6 +1,5 @@
1
1
  require 'rails/generators'
2
2
  require_relative './sql/create'
3
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
4
3
 
5
4
  module Multitenant
6
5
  module Views
@@ -1,7 +1,6 @@
1
1
  require 'rails/generators'
2
2
  require_relative './sql/drop'
3
3
  require_relative './sql/create'
4
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
5
4
 
6
5
  module Multitenant
7
6
  module Views
@@ -7,7 +7,7 @@ module Multitenant
7
7
  class Create
8
8
 
9
9
  def self.run
10
- Multitenant::Mysql.models.each do |model_name|
10
+ Multitenant::Mysql.configs.models.each do |model_name|
11
11
  model = model_name.constantize
12
12
  columns = model.column_names.join(', ')
13
13
  view_name = model_name.to_s.downcase.pluralize + "_view"
@@ -3,8 +3,6 @@ require 'rails/generators'
3
3
  require_relative '../views/sql/create'
4
4
  require_relative '../triggers/sql/create'
5
5
 
6
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
7
-
8
6
  module Multitenant
9
7
  module ViewsAndTriggers
10
8
  class CreateGenerator < Rails::Generators::Base
@@ -0,0 +1,20 @@
1
+ require 'rails/generators'
2
+
3
+ require_relative '../views/sql/drop'
4
+ require_relative '../triggers/sql/drop'
5
+
6
+ module Multitenant
7
+ module ViewsAndTriggers
8
+ class DropGenerator < Rails::Generators::Base
9
+ desc "drops all views and triggers"
10
+
11
+ def generate_mysql_views
12
+ Multitenant::Views::SQL::Drop.run
13
+ end
14
+
15
+ def generate_mysql_triggers
16
+ Multitenant::Triggers::SQL::Drop.run
17
+ end
18
+ end
19
+ end
20
+ end
@@ -5,8 +5,6 @@ require_relative '../views/sql/drop'
5
5
  require_relative '../triggers/sql/create'
6
6
  require_relative '../triggers/sql/drop'
7
7
 
8
- require Rails.root.to_s + '/config/multitenant_mysql_conf'
9
-
10
8
  module Multitenant
11
9
  module ViewsAndTriggers
12
10
  class RefreshGenerator < Rails::Generators::Base
@@ -1,6 +1,5 @@
1
1
  require 'multitenant-mysql/version'
2
+ require 'multitenant-mysql/errors'
3
+ require 'multitenant-mysql/configs'
2
4
  require 'multitenant-mysql/action_controller_extension'
3
- require 'multitenant-mysql/conf_file'
4
- require 'multitenant-mysql/arc'
5
-
6
5
  require 'multitenant-mysql/active_record_extension'
@@ -2,24 +2,17 @@ require_relative './connection_switcher'
2
2
 
3
3
  class ActionController::Base
4
4
  def self.set_current_tenant(tenant_method)
5
- require Multitenant::Mysql::ConfFile.path
6
-
7
- raise "you should provide tenant method" unless tenant_method
8
-
5
+ raise InvalidTenantError.new('Multitenant::Mysql: you should provide tenant method') unless tenant_method
9
6
  @@tenant_method = tenant_method
10
7
 
11
8
  before_filter :establish_tenant_connection
12
-
13
9
  def establish_tenant_connection
14
10
  Multitenant::Mysql::ConnectionSwitcher.new(self, @@tenant_method).execute
15
11
  end
16
12
  end
17
13
 
18
14
  def self.set_current_tenant_by_subdomain
19
- require Multitenant::Mysql::ConfFile.path
20
-
21
15
  before_filter :establish_tenant_connection_by_subdomain
22
-
23
16
  def establish_tenant_connection_by_subdomain
24
17
  tenant_name = request.subdomain
25
18
  Multitenant::Mysql::ConnectionSwitcher.set_tenant(tenant_name)
@@ -1,5 +1,5 @@
1
1
  class ActiveRecord::Base
2
- def self.acts_as_tenant
2
+ def self.acts_as_tenants_bucket
3
3
  after_create do
4
4
  config = Rails.configuration.database_configuration
5
5
  password = config[Rails.env]["password"]
@@ -8,34 +8,34 @@ class ActiveRecord::Base
8
8
  end
9
9
  end
10
10
 
11
- def self.inherited(child)
12
- return unless FileTest.exist?(Multitenant::Mysql::ConfFile.full_path) # do nothing if no config file provided
13
- require Multitenant::Mysql::ConfFile.path
14
-
15
- model_name = child.to_s
16
- if Multitenant::Mysql.models.include? model_name
17
- view_name = model_name.to_s.downcase.pluralize + "_view"
18
-
19
- # check whether the view exists in db
20
- if ActiveRecord::Base.connection.table_exists? view_name
21
- child.class_eval do
22
- cattr_accessor :original_table_name
11
+ def self.acts_as_tenant
12
+ view_name = model_name.to_s.downcase.pluralize + "_view"
13
+ # check whether the view exists in db
14
+ if ActiveRecord::Base.connection.table_exists? view_name
15
+ self.class_eval do
16
+ cattr_accessor :original_table_name
23
17
 
24
- self.original_table_name = self.table_name
25
- self.table_name = view_name
26
- self.primary_key = :id
18
+ self.original_table_name = self.table_name
19
+ self.table_name = view_name
20
+ self.primary_key = :id
27
21
 
28
- def self.new(*args)
29
- object = super(*args)
30
- object.id = nil # workaround for https://github.com/rails/rails/issues/5982
31
- object
32
- end
22
+ def self.new(*args)
23
+ object = super(*args)
24
+ object.id = nil # workaround for https://github.com/rails/rails/issues/5982
25
+ object
33
26
  end
34
27
  end
35
28
  end
29
+ end
36
30
 
37
- if Multitenant::Mysql.tenant == child
31
+ def self.inherited(child)
32
+ model_name = child.to_s
33
+ if Multitenant::Mysql.configs.models.include? model_name
38
34
  child.send :acts_as_tenant
39
35
  end
36
+
37
+ if Multitenant::Mysql.configs.tenant == child
38
+ child.send :acts_as_tenants_bucket
39
+ end
40
40
  end
41
41
  end
@@ -0,0 +1,23 @@
1
+ require_relative './configs/bucket'
2
+ require_relative './configs/base'
3
+
4
+ module Multitenant
5
+ module Mysql
6
+ class << self
7
+ def configure &block
8
+ raise InvalidConfigsError.new('Multitenant::Mysql: No configs provided') unless block_given?
9
+ configs = Configs::Base.new
10
+ block.call(configs)
11
+ self.configs = configs
12
+ end
13
+
14
+ def configs=configs
15
+ @configs = configs
16
+ end
17
+
18
+ def configs
19
+ @configs
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Multitenant
2
+ module Mysql
3
+ module Configs
4
+ class Base
5
+ attr_accessor :models, :bucket
6
+
7
+ def tenants_bucket(name)
8
+ raise InvalidBucketError.new('Multitenant::Mysql: invalid bucket') if name.blank?
9
+
10
+ @bucket = Bucket.new(name)
11
+ yield(@bucket) if block_given?
12
+ end
13
+
14
+ def tenant
15
+ @bucket.model
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ module Multitenant
2
+ module Mysql
3
+ module Configs
4
+ class Bucket
5
+ DEFAULT_TENANT_NAME_ATTR = [:name, :title]
6
+ attr_accessor :name, :field
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def model
13
+ @name.constantize
14
+ rescue
15
+ if @name.blank?
16
+ raise InvalidBucketError.new('Multitenant::Mysql: You should specify model which stores info about tenants.')
17
+ else
18
+ raise InvalidBucketError.new("Please check your multitenant-mysql configs. Seems like you are trying to use model which doesn't exist: #{@name}")
19
+ end
20
+ end
21
+
22
+ def field
23
+ @field ||= default_field
24
+ @field || raise(InvalidBucketFieldError.new('Multitenant::Mysql: You should specify tenants bucket field or use one default: name, title'))
25
+ raise InvalidBucketFieldError.new("Multitenant::Mysql: #{model} doesn't have '#{@field}' attribute") unless validate_field?
26
+ @field
27
+ end
28
+
29
+ private
30
+
31
+ def default_field
32
+ DEFAULT_TENANT_NAME_ATTR.each do |n|
33
+ return n if model.column_names.include?(n.to_s)
34
+ end
35
+ end
36
+
37
+ def validate_field?
38
+ @valid ||= model.column_names.include?(@field.to_s)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -2,13 +2,13 @@ require_relative './db'
2
2
 
3
3
  module Multitenant
4
4
  module Mysql
5
- class NoTenantRegistratedError < StandardError; end;
6
-
7
5
  class Tenant
8
6
  def self.exists? tenant_name
9
7
  return true if tenant_name.blank?
10
- if Multitenant::Mysql.tenant.where(Multitenant::Mysql.tenant_name_attr => tenant_name).blank?
11
- raise Multitenant::Mysql::NoTenantRegistratedError.new("No tenant registered: #{tenant_name}")
8
+ tenant = Multitenant::Mysql.configs.tenant
9
+ field = Multitenant::Mysql.configs.bucket.field
10
+ if tenant.where(field => tenant_name).blank?
11
+ raise NoTenantRegistratedError.new("No tenant registered: #{tenant_name}")
12
12
  end
13
13
  true
14
14
  end
@@ -14,7 +14,6 @@ module Multitenant
14
14
  config = configs
15
15
  config['username'] = tenant_name.blank? ? 'root' : tenant_name
16
16
  ActiveRecord::Base.establish_connection(config)
17
- true
18
17
  end
19
18
  end
20
19
  end
@@ -0,0 +1,9 @@
1
+ module Multitenant
2
+ module Mysql
3
+ class InvalidBucketError < StandardError; end;
4
+ class NoTenantRegistratedError < StandardError; end;
5
+ class InvalidConfigsError < StandardError; end;
6
+ class InvalidBucketFieldError < StandardError; end;
7
+ class InvalidTenantError < StandardError; end;
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module Multitenant
2
2
  module Mysql
3
- VERSION = "1.1.1"
3
+ VERSION = "1.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Multitenant::Mysql do
4
+ subject { Multitenant::Mysql }
5
+
6
+ context '#configure' do
7
+ it 'should raise error with no block given' do
8
+ expect { subject.configure }.to raise_error(Multitenant::Mysql::InvalidConfigsError)
9
+ end
10
+
11
+ context 'with valid params' do
12
+ before do
13
+ class Subdomain; end;
14
+ subject.configure do |conf|
15
+ conf.models = ['Book']
16
+ conf.tenants_bucket 'Subdomain'
17
+ end
18
+ end
19
+
20
+ it 'should be of valid type' do
21
+ expect(subject.configs).to be_kind_of Multitenant::Mysql::Configs::Base
22
+ end
23
+
24
+ it 'should have valid values' do
25
+ expect(subject.configs.models).to eql(['Book'])
26
+ expect(subject.configs.tenant).to eql(Subdomain)
27
+ end
28
+ end
29
+ end
30
+
31
+ context '#configs=' do
32
+ let(:configs) { Multitenant::Mysql::Configs::Base.new }
33
+
34
+ it 'should set configs' do
35
+ subject.configs = configs
36
+ expect(subject.configs).to eql(configs)
37
+ end
38
+ end
39
+ end
@@ -1,49 +1,57 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Multitenant::Mysql::ConnectionSwitcher do
3
+ describe Multitenant::Mysql::Tenant do
4
+ subject { Multitenant::Mysql::Tenant }
5
+
4
6
  before do
5
- Multitenant::Mysql.stub(:tenant_name_attr).and_return('name')
7
+ create_table('subdomains')
8
+ class Subdomain < ActiveRecord::Base; end;
6
9
  end
7
10
 
8
- context '#execute' do
9
- it 'should raise error if there is no tenant account in db' do
10
- ar_mock = double('ActiveRecord::Relation')
11
- ar_mock.stub(:where).and_return(nil)
12
- Multitenant::Mysql.stub(:tenant).and_return(ar_mock)
11
+ context '#exists?' do
12
+ it 'should return true for existing tenant' do
13
+ mock = double('Subdomain')
14
+ Subdomain.stub(:where).and_return([mock])
15
+ expect(subject.exists?('blade')).to be_true
16
+ end
13
17
 
14
- ac_mock = double('ActionController::Base')
15
- ac_mock.should_receive(:send).and_return('unexisting tenant')
16
- switcher = Multitenant::Mysql::ConnectionSwitcher.new(ac_mock, :tenant_method)
17
- Multitenant::Mysql::DB.stub(:configs).and_return({ 'username' => 'root' })
18
+ it 'should return true for blank name' do
19
+ expect(subject.exists?('')).to be_true
20
+ end
18
21
 
19
- expect { switcher.execute }.to raise_error(Multitenant::Mysql::NoTenantRegistratedError)
22
+ it 'should raise an error for unexisting tenant' do
23
+ ActiveRecord::Base.stub(:where).and_return(nil)
24
+ expect { subject.exists?('invalid tenant') }.to raise_error(Multitenant::Mysql::NoTenantRegistratedError)
20
25
  end
26
+ end
21
27
 
22
- it 'should change db connection' do
23
- ar_mock = double('ActiveRecord::Relation')
24
- ar_mock.stub(:where).and_return(:some_result)
25
- Multitenant::Mysql.stub(:tenant).and_return(ar_mock)
28
+ end
26
29
 
27
- ac_mock = double('ActionController::Base')
28
- ac_mock.should_receive(:tenant_method)
29
- switcher = Multitenant::Mysql::ConnectionSwitcher.new(ac_mock, :tenant_method)
30
- Multitenant::Mysql::DB.stub(:configs).and_return({ 'username' => 'root' })
30
+ describe Multitenant::Mysql::ConnectionSwitcher do
31
+ context '#set_tenant' do
32
+ subject { Multitenant::Mysql::ConnectionSwitcher }
31
33
 
32
- ActiveRecord::Base.should_receive(:establish_connection)
34
+ before do
35
+ Multitenant::Mysql::Tenant.stub(:exists?).and_return(true)
36
+ end
33
37
 
34
- expect( switcher.execute ).to be
38
+ it 'should change db connection' do
39
+ Multitenant::Mysql::DB.should respond_to(:establish_connection_for)
40
+ Multitenant::Mysql::DB.should_receive(:establish_connection_for).with('tenant')
41
+ expect{ subject.set_tenant('tenant') }.to_not raise_error
35
42
  end
36
43
  end
37
44
 
38
- context '.set_tenant' do
39
- subject { Multitenant::Mysql::ConnectionSwitcher }
45
+ context '.execute' do
46
+ let(:mock_ac) { double('ActionController::Base') }
47
+
48
+ subject { Multitenant::Mysql::ConnectionSwitcher.new(mock_ac, :tenant) }
40
49
 
41
50
  it 'should change db connection' do
42
51
  Multitenant::Mysql::Tenant.stub(:exists?).and_return(true)
43
- Multitenant::Mysql::DB.stub(:configs).and_return({ 'username' => 'root' })
44
-
45
- ActiveRecord::Base.should_receive(:establish_connection).with({ 'username' => 'google'})
46
- expect( subject.set_tenant('google') ).to be
52
+ mock_ac.should_receive(:send).with(:tenant).and_return('wallmart')
53
+ Multitenant::Mysql::DB.should_receive(:establish_connection_for).with('wallmart')
54
+ expect { subject.execute }.to_not raise_error
47
55
  end
48
56
  end
49
57
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Multitenant::Mysql::DB do
4
+ subject { Multitenant::Mysql::DB }
5
+ let(:configs) { { 'username' => 'root', 'password' => 'password' } }
6
+
7
+ context '#configs' do
8
+ it 'should set configs' do
9
+ subject.configs = configs
10
+ expect(subject.configs).to eql(configs)
11
+ end
12
+
13
+ it 'should get configs' do
14
+ class Rails; end;
15
+ Rails.stub_chain(:configuration, :database_configuration, :[]).and_return(configs)
16
+
17
+ expect(subject.configs['username']).to eql('root')
18
+ expect(subject.configs['password']).to eql('password')
19
+ end
20
+ end
21
+
22
+ context '#establish_connection_for' do
23
+ it 'should change db connection with root account' do
24
+ subject.configs = configs
25
+ ActiveRecord::Base.should_receive(:establish_connection).with({ 'username' => 'root', 'password' => 'password' })
26
+ expect { subject.establish_connection_for(nil) }.to_not raise_error
27
+ end
28
+
29
+ it 'should change db connection for particular tenant' do
30
+ subject.configs = configs
31
+ ActiveRecord::Base.should_receive(:establish_connection).with({ 'username' => 'wallmart', 'password' => 'password' })
32
+ expect { subject.establish_connection_for('wallmart') }.to_not raise_error
33
+ end
34
+ end
35
+ end
@@ -2,19 +2,19 @@ require 'spec_helper'
2
2
 
3
3
  describe ActiveRecord::Base do
4
4
  subject { ActiveRecord::Base }
5
-
6
- before do
7
- Multitenant::Mysql.arc = { models: ['Book', 'Task'] }
5
+ it 'should respond to acts_as_tenant' do
6
+ subject.should respond_to(:acts_as_tenant)
8
7
  end
9
8
 
10
- it 'should respond to acts_as_tenant' do
11
- subject.should respond_to(:acts_as_tenant)
9
+ it 'should respond to acts_as_tenants_bucket' do
10
+ subject.should respond_to(:acts_as_tenants_bucket)
12
11
  end
13
12
 
14
13
  it 'should redefine table name and primary key and keep original table name' do
15
14
  ActiveRecord::Base.stub_chain(:connection, :table_exists?).and_return(true)
16
15
 
17
- Multitenant::Mysql.stub(:tenant).and_return(true)
16
+ Multitenant::Mysql.stub_chain(:configs, :models).and_return(['Book'])
17
+ Multitenant::Mysql.stub_chain(:configs, :tenant).and_return(false)
18
18
 
19
19
  class Book < ActiveRecord::Base
20
20
  end
@@ -5,14 +5,13 @@ require 'active_record'
5
5
  require 'action_controller'
6
6
  require 'multitenant-mysql'
7
7
 
8
+ require 'coveralls'
9
+ Coveralls.wear!
8
10
 
9
11
  GEM_ROOT_PATH = File.expand_path('../../', __FILE__)
10
- CONF_FILE_PATH = GEM_ROOT_PATH + '/spec/support/multitenant_mysql_conf'
11
12
 
12
13
  RSpec.configure do |config|
13
14
  config.before do
14
- Multitenant::Mysql::ConfFile.path = CONF_FILE_PATH
15
-
16
15
  ActiveRecord::Base.establish_connection({
17
16
  adapter: 'mysql2',
18
17
  username: 'root',
@@ -21,6 +20,14 @@ RSpec.configure do |config|
21
20
  ActiveRecord::Base.connection.execute('drop database if exists `tenant_test`;')
22
21
  ActiveRecord::Base.connection.execute('create database `tenant_test`;')
23
22
  ActiveRecord::Base.connection.execute('use `tenant_test`;')
23
+
24
+
25
+ Multitenant::Mysql.configure do |conf|
26
+ conf.models = ['Book']
27
+ conf.tenants_bucket 'Subdomain' do |tb|
28
+ tb.field = 'name'
29
+ end
30
+ end
24
31
  end
25
32
 
26
33
  config.after do
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.1.1
4
+ version: 1.2.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-18 00:00:00.000000000 Z
12
+ date: 2013-03-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -57,22 +57,25 @@ files:
57
57
  - lib/generators/multitenant/views/sql/create.rb
58
58
  - lib/generators/multitenant/views/sql/drop.rb
59
59
  - lib/generators/multitenant/views_and_triggers/create_generator.rb
60
+ - lib/generators/multitenant/views_and_triggers/drop_generator.rb
60
61
  - lib/generators/multitenant/views_and_triggers/list/list.rb
61
62
  - lib/generators/multitenant/views_and_triggers/list/sql.rb
62
63
  - lib/generators/multitenant/views_and_triggers/refresh_generator.rb
63
64
  - lib/multitenant-mysql.rb
64
65
  - lib/multitenant-mysql/action_controller_extension.rb
65
66
  - lib/multitenant-mysql/active_record_extension.rb
66
- - lib/multitenant-mysql/arc.rb
67
- - lib/multitenant-mysql/conf_file.rb
67
+ - lib/multitenant-mysql/configs.rb
68
+ - lib/multitenant-mysql/configs/base.rb
69
+ - lib/multitenant-mysql/configs/bucket.rb
68
70
  - lib/multitenant-mysql/connection_switcher.rb
69
71
  - lib/multitenant-mysql/db.rb
72
+ - lib/multitenant-mysql/errors.rb
70
73
  - lib/multitenant-mysql/version.rb
71
74
  - multitenant-mysql.gemspec
72
75
  - rails/init.rb
73
- - spec/arc_spec.rb
74
- - spec/conf_file_spec.rb
76
+ - spec/configs_spec.rb
75
77
  - spec/connection_switcher_spec.rb
78
+ - spec/db_spec.rb
76
79
  - spec/generators/list_spec.rb
77
80
  - spec/generators/triggers/sql/create_spec.rb
78
81
  - spec/generators/triggers/sql/drop_spec.rb
@@ -81,7 +84,6 @@ files:
81
84
  - spec/rails/action_controller_extension_spec.rb
82
85
  - spec/rails/active_record_base_spec.rb
83
86
  - spec/spec_helper.rb
84
- - spec/support/multitenant_mysql_conf.rb
85
87
  homepage: https://github.com/eugenekorpan/multitenant-mysql
86
88
  licenses: []
87
89
  post_install_message:
@@ -102,14 +104,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
104
  version: '0'
103
105
  requirements: []
104
106
  rubyforge_project:
105
- rubygems_version: 1.8.21
107
+ rubygems_version: 1.8.24
106
108
  signing_key:
107
109
  specification_version: 3
108
110
  summary: Add multi-tenancy to Rails application using MySql views
109
111
  test_files:
110
- - spec/arc_spec.rb
111
- - spec/conf_file_spec.rb
112
+ - spec/configs_spec.rb
112
113
  - spec/connection_switcher_spec.rb
114
+ - spec/db_spec.rb
113
115
  - spec/generators/list_spec.rb
114
116
  - spec/generators/triggers/sql/create_spec.rb
115
117
  - spec/generators/triggers/sql/drop_spec.rb
@@ -118,4 +120,3 @@ test_files:
118
120
  - spec/rails/action_controller_extension_spec.rb
119
121
  - spec/rails/active_record_base_spec.rb
120
122
  - spec/spec_helper.rb
121
- - spec/support/multitenant_mysql_conf.rb
@@ -1,52 +0,0 @@
1
- module Multitenant
2
- module Mysql
3
-
4
- class << self
5
-
6
- DEFAULT_TENANT_NAME_ATTR = [:name, :title]
7
-
8
- def active_record_configs=(configs)
9
- @configs = configs
10
- end
11
-
12
- def active_record_configs
13
- @configs
14
- end
15
-
16
- alias_method :arc, :active_record_configs
17
- alias_method :arc=, :active_record_configs=
18
-
19
- def tenant
20
- arc[:tenant_model][:name].constantize
21
- rescue
22
- if arc.blank? || arc[:tenant_model].blank? || arc[:tenant_model][:name].blank?
23
- raise "
24
- Multitenant::Mysql: You should specify model which stores info about tenants.
25
- E.g. in initializer:
26
- Multitenant::Mysql.arc = {
27
- tenant_model: { name: 'Subdomain' }
28
- }
29
- "
30
- else
31
- raise "Please check your multitenant-mysql configs. Seems like you are trying to use model which doesn't exist: #{arc[:tenant_model][:name]}"
32
- end
33
- end
34
-
35
- def models
36
- active_record_configs[:models]
37
- end
38
-
39
- def tenant_name_attr
40
- return active_record_configs[:tenant_model][:tenant_name_attr] if active_record_configs[:tenant_model][:tenant_name_attr]
41
-
42
- DEFAULT_TENANT_NAME_ATTR.each do |name|
43
- return name if tenant.column_names.include?(name.to_s)
44
- end
45
-
46
- raise 'You should specify tenant name attribute or use one default: name, title'
47
- end
48
-
49
- end
50
-
51
- end
52
- end
@@ -1,22 +0,0 @@
1
- module Multitenant
2
- module Mysql
3
- class ConfFile
4
- def self.path=(path)
5
- @path = path
6
- end
7
-
8
- def self.path
9
- # workaround to reqire conf file in rails app
10
- @path ||= default_path
11
- end
12
-
13
- def self.full_path
14
- "#{path}.rb"
15
- end
16
-
17
- def self.default_path
18
- Rails.root.to_s + '/config/multitenant_mysql_conf'
19
- end
20
- end
21
- end
22
- end
@@ -1,81 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Multitenant::Mysql do
4
- subject { Multitenant::Mysql }
5
-
6
- context 'active record configs' do
7
-
8
- it 'should raise error if no tenant name provided' do
9
- subject.active_record_configs = nil
10
- expect {
11
- subject.tenant_name_attr
12
- }.to raise_error
13
- end
14
-
15
- context 'tenant name attribute' do
16
- it 'should use name or title by default' do
17
- mock = double()
18
- mock.stub(:column_names).and_return(['name'])
19
- subject.stub(:tenant).and_return(mock)
20
- subject.stub(:active_record_configs).and_return({tenant_model: {}})
21
- expect(subject.tenant_name_attr).to eql(:name)
22
-
23
- mock.stub(:column_names).and_return(['title'])
24
- expect(subject.tenant_name_attr).to eql(:title)
25
- end
26
-
27
- it 'should use attribute from configs' do
28
- subject.stub(:active_record_configs).and_return({tenant_model: {tenant_name_attr: 'subdomain'}})
29
- expect(subject.tenant_name_attr).to eql('subdomain')
30
- end
31
- end
32
-
33
- context '.tenant' do
34
- before do
35
- Subdomain = :constant
36
- end
37
-
38
- it 'should find and return appropriate model' do
39
- subject.active_record_configs = { tenant_model: { name: 'Subdomain' } }
40
- expect(subject.tenant).to eq(Subdomain)
41
- end
42
-
43
- context 'invalid data' do
44
- it 'should raise error if no data provided' do
45
- subject.active_record_configs = {}
46
- expect { subject.tenant }.to raise_error(RuntimeError)
47
- end
48
-
49
- it 'should raise error if invalid data provided' do
50
- subject.active_record_configs = { tenant_model: { name: nil } }
51
- expect { subject.tenant }.to raise_error(RuntimeError)
52
- end
53
-
54
- it 'should raise error if invalid model provided' do
55
- subject.active_record_configs = { tenant_model: { name: 'UnexistingModelLa' } }
56
- expect { subject.tenant }.to raise_error(RuntimeError)
57
- end
58
- end
59
- end
60
-
61
- context '.models' do
62
- it 'should return listed models' do
63
- subject.active_record_configs = { models: ['Book', 'Task'] }
64
- expect(subject.models).to have(2).items
65
- end
66
- end
67
-
68
- context 'alias methods' do
69
- it 'should use aliased setter' do
70
- subject.arc = 'ARC'
71
- expect(subject.active_record_configs).to eql('ARC')
72
- end
73
-
74
- it 'should use aliased getter' do
75
- subject.active_record_configs = 'ABC'
76
- expect(subject.arc).to eql('ABC')
77
- end
78
- end
79
-
80
- end
81
- end
@@ -1,48 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Multitenant::Mysql::ConfFile do
4
- subject { Multitenant::Mysql::ConfFile }
5
-
6
- context '.path' do
7
- it 'should return path' do
8
- subject.path = '/conf/file'
9
- expect(subject.path).to eql('/conf/file')
10
- end
11
-
12
- it 'should return default path to conf file in rails app' do
13
- path = 'Rails.root/config/multitenant_mysql_conf'
14
-
15
- subject.stub(:default_path).and_return(path)
16
- subject.path = nil
17
-
18
- expect(subject.path).to eql(path)
19
- end
20
- end
21
-
22
- context '.full_path' do
23
- it 'should return path with extension of the file' do
24
- subject.path = '/conf/file'
25
- expect(subject.full_path).to eql('/conf/file.rb')
26
- end
27
-
28
- it 'should return correct path no metter how many times we call it' do
29
- subject.path = '/conf/file'
30
- 2.times do
31
- expect(subject.full_path).to eql('/conf/file.rb')
32
- end
33
- end
34
- end
35
-
36
- context '.default_path' do
37
- it 'should return path to conf file in rails app' do
38
- class Rails
39
- def self.root
40
- 'Rails.root'
41
- end
42
- end
43
-
44
- expect(subject.default_path).to eql('Rails.root/config/multitenant_mysql_conf')
45
- end
46
- end
47
-
48
- end
@@ -1,4 +0,0 @@
1
- Multitenant::Mysql.active_record_configs = {
2
- models: ['Book'],
3
- tenant_model: { name: 'Subdomain', tenant_name_attr: 'title' }
4
- }