multitenant-mysql 1.1.1 → 1.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.
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
- }