cohabit 0.0.1
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/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +11 -0
- data/cohabit.gemspec +25 -0
- data/lib/cohabit/configuration/scopes.rb +35 -0
- data/lib/cohabit/configuration/settings.rb +46 -0
- data/lib/cohabit/configuration/strategies.rb +42 -0
- data/lib/cohabit/configuration.rb +46 -0
- data/lib/cohabit/errors.rb +9 -0
- data/lib/cohabit/scope.rb +57 -0
- data/lib/cohabit/snippets.rb +4 -0
- data/lib/cohabit/strategies/basic.rb +14 -0
- data/lib/cohabit/strategies/defaults.rb +2 -0
- data/lib/cohabit/strategies/multi.rb +13 -0
- data/lib/cohabit/strategy.rb +22 -0
- data/lib/cohabit/version.rb +3 -0
- data/lib/cohabit.rb +35 -0
- data/test/all_tests.rb +5 -0
- data/test/database.yml +6 -0
- data/test/models.rb +7 -0
- data/test/schema.rb +10 -0
- data/test/scopes_test.rb +58 -0
- data/test/settings_test.rb +27 -0
- data/test/strategies_test.rb +57 -0
- data/test/strategy_test.rb +39 -0
- data/test/test_helper.rb +32 -0
- metadata +147 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mike Campbell
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Cohabit
|
2
|
+
|
3
|
+
Cohabit adds comprehensive scoped multi-tenancy functionality to any application, simply set your options up in `config/cohabit.rb` using the DSL (inspired by capistrano).
|
4
|
+
|
5
|
+
It adds:
|
6
|
+
|
7
|
+
- Model scoping (duh)
|
8
|
+
- Custom scoping strategies
|
9
|
+
- Scope validations
|
10
|
+
- Scope URL helpers
|
11
|
+
- Rake task for importing single-tenanted databases into a multi-tenant one
|
12
|
+
- Rake task for generating multi-tenanted scoped schema
|
13
|
+
|
14
|
+
## Todo
|
15
|
+
|
16
|
+
Still a WIP. Need to:
|
17
|
+
|
18
|
+
- Develop snippets to be included in strategies, i.e. scope_validations snippet (and remove that setting)
|
19
|
+
- Should snippets just be nested strategies? wah, probably.
|
20
|
+
- Work out how to integrate the url helper scopes as an option
|
21
|
+
- Write the rake tasks
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
gem 'cohabit'
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install cohabit
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
TODO: Write usage instructions here
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/cohabit.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cohabit/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cohabit"
|
8
|
+
spec.version = Cohabit::VERSION
|
9
|
+
spec.authors = ["Mike Campbell"]
|
10
|
+
spec.email = ["mike@wordofmike.net"]
|
11
|
+
spec.description = %q{Handle application scoping for multi-tenant applications with table scopes.}
|
12
|
+
spec.summary = %q{Scope multi-tenant applications.}
|
13
|
+
spec.homepage = "http://github.com/mikecmpbll/cohabit"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activerecord"
|
22
|
+
spec.add_dependency "activesupport"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Cohabit
|
2
|
+
class Configuration
|
3
|
+
module Scopes
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :alias_method, :initialize_without_scopes, :initialize
|
7
|
+
base.send :alias_method, :initialize, :initialize_with_scopes
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize_with_scopes(*args)
|
11
|
+
initialize_without_scopes(*args)
|
12
|
+
@scopes = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# deez are our scopes for our configuration instance
|
16
|
+
attr_reader :scopes
|
17
|
+
|
18
|
+
def scope(*args, &block)
|
19
|
+
args[1] &&= find_strategy_by_name(args[1])
|
20
|
+
scope = Scope.new(*args, &block)
|
21
|
+
add_scope(scope)
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_scopes!
|
25
|
+
@scopes.each{ |s| s.apply! }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def add_scope(scope)
|
30
|
+
scopes << scope
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Cohabit
|
2
|
+
class Configuration
|
3
|
+
module Settings
|
4
|
+
|
5
|
+
DEFULT_SETTINGZ = {
|
6
|
+
scope_validations: false,
|
7
|
+
scope_url_helpers: false,
|
8
|
+
association: :tenant
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.send :alias_method, :initialize_without_settings, :initialize
|
13
|
+
base.send :alias_method, :initialize, :initialize_with_settings
|
14
|
+
end
|
15
|
+
|
16
|
+
# deez are our global settings for this configuration instance
|
17
|
+
attr_reader :settings
|
18
|
+
|
19
|
+
def initialize_with_settings(*args, &block)
|
20
|
+
@settings = DEFULT_SETTINGZ.dup
|
21
|
+
initialize_without_settings(*args, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge_settings!(settings)
|
25
|
+
settings.delete_if{ |s| !DEFULT_SETTINGZ.include?(s) }
|
26
|
+
@settings.merge!(settings)
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_settings_hash!(args)
|
30
|
+
if args.last.is_a?(Hash)
|
31
|
+
args[args.length-1] = @settings.merge(args.last)
|
32
|
+
else
|
33
|
+
args << @settings.dup
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(setting, value)
|
38
|
+
if !DEFULT_SETTINGZ.include?(setting.to_sym)
|
39
|
+
raise ArgumentError, "what the fuck are you doing.. that's not a setting"
|
40
|
+
end
|
41
|
+
@settings[setting.to_sym] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Cohabit
|
2
|
+
class Configuration
|
3
|
+
module Strategies
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :alias_method, :initialize_without_strategies, :initialize
|
7
|
+
base.send :alias_method, :initialize, :initialize_with_strategies
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize_with_strategies(*args)
|
11
|
+
initialize_without_strategies(*args)
|
12
|
+
@strategies = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# deez are our strategies for our configuration instance
|
16
|
+
attr_reader :strategies
|
17
|
+
|
18
|
+
# defines a strategy. woah there batman!
|
19
|
+
def strategy(*args, &block)
|
20
|
+
strategy = Strategy.new(*args, &block)
|
21
|
+
add_strategy(strategy)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_strategy_by_name(name)
|
25
|
+
strategy = @strategies.find{|s| s.name == name}
|
26
|
+
return strategy if strategy
|
27
|
+
raise StrategyNotFoundError
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def add_strategy(strategy)
|
32
|
+
raise StrategyNameExistsError if named_strategy_exists?(strategy.name)
|
33
|
+
strategies << strategy
|
34
|
+
end
|
35
|
+
|
36
|
+
def named_strategy_exists?(strategy_name)
|
37
|
+
strategies.any?{|s| s.name == strategy_name}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "cohabit/version"
|
2
|
+
require "cohabit/configuration/settings"
|
3
|
+
require "cohabit/configuration/strategies"
|
4
|
+
require "cohabit/configuration/scopes"
|
5
|
+
require "active_record"
|
6
|
+
|
7
|
+
module Cohabit
|
8
|
+
class Configuration
|
9
|
+
|
10
|
+
attr_reader :load_paths
|
11
|
+
|
12
|
+
def initialize(config_file = nil)
|
13
|
+
@load_paths = [".", File.expand_path(File.join(File.dirname(__FILE__), "strategies"))]
|
14
|
+
end
|
15
|
+
|
16
|
+
def load(*args, &block)
|
17
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
18
|
+
|
19
|
+
if block
|
20
|
+
raise ArgumentError, "loading a block requires 0 arguments" unless options.empty? && args.empty?
|
21
|
+
instance_eval(&block)
|
22
|
+
elsif options[:file]
|
23
|
+
load string: File.read(options[:file])
|
24
|
+
elsif options[:string]
|
25
|
+
instance_eval(options[:string])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# simple require to pull in config files, eg 'basic'
|
30
|
+
def require(files)
|
31
|
+
[files].flatten.each do |file|
|
32
|
+
load_paths.each do |path|
|
33
|
+
if file.match(/\.rb\z/)
|
34
|
+
name = File.join(path, file)
|
35
|
+
else
|
36
|
+
name = File.join(path, "#{file}.rb")
|
37
|
+
end
|
38
|
+
load(file: name) if File.file?(name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
include Settings, Strategies, Scopes
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "cohabit/configuration/settings"
|
2
|
+
require "active_record"
|
3
|
+
require "active_support/inflector"
|
4
|
+
|
5
|
+
module Cohabit
|
6
|
+
class Scope
|
7
|
+
|
8
|
+
def initialize(*args, &block)
|
9
|
+
merge_settings!(args.pop) if args.last.is_a?(Hash)
|
10
|
+
apply_to(args[0])
|
11
|
+
use_strategy(args[1])
|
12
|
+
instance_eval(&block) unless block.nil?
|
13
|
+
merge_settings!(@strategy.settings)
|
14
|
+
unless valid?
|
15
|
+
raise InvalidScopeError, "provide valid model(s) and strategy"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
include Configuration::Settings
|
20
|
+
|
21
|
+
attr_reader :strategy
|
22
|
+
|
23
|
+
def use_strategy(strategy)
|
24
|
+
unless strategy.nil?
|
25
|
+
@strategy = strategy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def apply_to(models)
|
30
|
+
@models = parse_models(models)
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply!
|
34
|
+
# apply yourself, that's what my teachers always said.
|
35
|
+
@models.each do |model|
|
36
|
+
model._apply_cohabit_scope(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def valid?
|
42
|
+
return false if @models.empty?
|
43
|
+
return false if @strategy.nil?
|
44
|
+
@models.each do |model|
|
45
|
+
return false if !ActiveRecord::Base.descendants.include?(model)
|
46
|
+
end
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_models(models)
|
51
|
+
[models].flatten.compact.map do |model|
|
52
|
+
model.to_s.classify.constantize
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
strategy :basic do
|
2
|
+
model_eval do |_scope|
|
3
|
+
belongs_to _scope.settings[:association]
|
4
|
+
reflection = reflect_on_association _scope.settings[:association]
|
5
|
+
scope_validators(reflection.foreign_key) if _scope.settings[:scope_validations]
|
6
|
+
before_create Proc.new {|m|
|
7
|
+
return unless Cohabit.current_tenant
|
8
|
+
m.send "#{_scope.settings[:association]}=".to_sym, Cohabit.current_tenant
|
9
|
+
}
|
10
|
+
default_scope lambda {
|
11
|
+
where(reflection.foreign_key => Cohabit.current_tenant) if Cohabit.current_tenant
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# strategy :multi do
|
2
|
+
# model_eval do
|
3
|
+
# reflection = reflect_on_association _scope.settings[:association]
|
4
|
+
# scope_validators(reflection.foreign_key) if _scope.settings[:scope_validations]
|
5
|
+
# before_create Proc.new {|m|
|
6
|
+
# return unless _scope.current_school
|
7
|
+
# m.send "#{_scope.settings[:association]}=".to_sym, _scope.current_school
|
8
|
+
# }
|
9
|
+
# default_scope lambda {
|
10
|
+
# where(reflection.foreign_key => _scope.current_scope) if _scope.current_scope
|
11
|
+
# }
|
12
|
+
# end
|
13
|
+
# end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "cohabit/configuration/settings"
|
2
|
+
|
3
|
+
module Cohabit
|
4
|
+
class Strategy
|
5
|
+
|
6
|
+
def initialize(*args, &block)
|
7
|
+
raise ArgumentError, "you must supply a name" if args.empty?
|
8
|
+
@name = args.shift.to_sym
|
9
|
+
@settings = args.last.is_a?(Hash) ? args.last : {}
|
10
|
+
instance_eval(&block) unless block.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
include Configuration::Settings
|
14
|
+
|
15
|
+
attr_reader :name, :model_code
|
16
|
+
|
17
|
+
def model_eval(&block)
|
18
|
+
@model_code = block
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/cohabit.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cohabit/errors'
|
2
|
+
require 'cohabit/configuration'
|
3
|
+
require 'cohabit/strategy'
|
4
|
+
require 'cohabit/scope'
|
5
|
+
|
6
|
+
module Cohabit
|
7
|
+
class << self
|
8
|
+
attr_accessor :current_tenant
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActiveRecordExtensions
|
12
|
+
def _apply_cohabit_scope(_scope)
|
13
|
+
proc = _scope.strategy.model_code
|
14
|
+
instance_exec(_scope, &proc) if proc
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Base.extend Cohabit::ActiveRecordExtensions
|
20
|
+
|
21
|
+
if defined? ::Rails::Railtie
|
22
|
+
module Cohabit
|
23
|
+
class Railtie < Rails::Railtie
|
24
|
+
initializer "cohabit.configure" do |app|
|
25
|
+
config = Cohabit::Configuration.new
|
26
|
+
config.load(file: File.join(Rails.root, "config/cohabit.rb"))
|
27
|
+
config.apply_scopes!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
else
|
32
|
+
config = Cohabit::Configuration.new
|
33
|
+
config.load(file: File.join(Rails.root, "config/cohabit.rb"))
|
34
|
+
config.apply_scopes!
|
35
|
+
end
|
data/test/all_tests.rb
ADDED
data/test/database.yml
ADDED
data/test/models.rb
ADDED
data/test/schema.rb
ADDED
data/test/scopes_test.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
class ScopesTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@c = Cohabit::Configuration.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
Ybur.default_scopes.clear
|
9
|
+
Ybur._validators.clear
|
10
|
+
[:validate, :validation, :save, :create, :update, :destroy].each do |type|
|
11
|
+
Ybur.reset_callbacks(type)
|
12
|
+
end
|
13
|
+
Ybur.reflections.clear
|
14
|
+
|
15
|
+
Client.delete_all
|
16
|
+
Ybur.delete_all
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_add_valid_scope
|
20
|
+
# load basic strategy
|
21
|
+
@c.load do
|
22
|
+
require 'basic'
|
23
|
+
scope :ybur, :basic, association: :client
|
24
|
+
end
|
25
|
+
assert(@c.scopes.any?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_settings_for_scope
|
29
|
+
@c.load do
|
30
|
+
require 'basic'
|
31
|
+
scope :ybur, :basic, association: :client
|
32
|
+
end
|
33
|
+
assert_equal(:client, @c.scopes.first.settings[:association])
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_apply_scope_to_model
|
37
|
+
c = Client.create(name: "fubar")
|
38
|
+
Cohabit.current_tenant = c
|
39
|
+
@c.load do
|
40
|
+
require 'basic'
|
41
|
+
scope :ybur, :basic
|
42
|
+
end
|
43
|
+
@c.apply_scopes!
|
44
|
+
assert_not_equal(Ybur.unscoped.to_sql, Ybur.scoped.to_sql)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_setting_association_name
|
48
|
+
c = Client.create(name: "fubar")
|
49
|
+
Cohabit.current_tenant = c
|
50
|
+
@c.load do
|
51
|
+
require 'basic'
|
52
|
+
scope :ybur, :basic, association: :client
|
53
|
+
end
|
54
|
+
@c.apply_scopes!
|
55
|
+
assert_match(/client_id/, Ybur.scoped.to_sql)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class SettingsTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@c = Cohabit::Configuration.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_set_real_setting
|
8
|
+
assert_not_equal(@c.settings[:scope_validations], true)
|
9
|
+
@c.set :scope_validations, true
|
10
|
+
assert_equal(true, @c.settings[:scope_validations])
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_set_nonexitant_setting
|
14
|
+
assert_raise(ArgumentError) do
|
15
|
+
@c.set :wtf_not_a_real_setting, "dis is a value, innit"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_set_setting_with_config
|
20
|
+
assert_equal(false, @c.settings[:scope_validations])
|
21
|
+
@c.load do
|
22
|
+
set :scope_validations, true
|
23
|
+
end
|
24
|
+
assert_equal(true, @c.settings[:scope_validations])
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class StrategiesTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@c = Cohabit::Configuration.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_define_strategy
|
8
|
+
@c.strategy(:frankel) do
|
9
|
+
model_eval do
|
10
|
+
puts "do this in the model! hurah."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
assert(@c.strategies.one?)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_define_strategy_with_config
|
17
|
+
@c.load do
|
18
|
+
strategy :frankel do
|
19
|
+
model_eval do
|
20
|
+
puts "do this in the model! hurah."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
assert(@c.strategies.one?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_cant_add_same_named_strategies
|
28
|
+
assert_raise(Cohabit::StrategyNameExistsError) do
|
29
|
+
@c.load do
|
30
|
+
strategy :frankel
|
31
|
+
strategy :frankel # eh-uhhh, no can do.
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_strategy_inherits_correct_settings
|
37
|
+
# strategy block defaults > strategy arg defaults
|
38
|
+
|
39
|
+
# should take on strategy default settings when passed in
|
40
|
+
setup
|
41
|
+
@c.load do
|
42
|
+
strategy :frankel, { scope_validations: true }
|
43
|
+
end
|
44
|
+
assert_equal(true, @c.strategies.first.settings[:scope_validations])
|
45
|
+
|
46
|
+
# should take on strategy default settings when set in block
|
47
|
+
# overriding any defaults passed as arguments
|
48
|
+
setup
|
49
|
+
@c.load do
|
50
|
+
strategy :frankel, { scope_validations: "henry cecil" } do
|
51
|
+
set :scope_validations, true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
assert_equal(true, @c.strategies.first.settings[:scope_validations])
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class StrategyTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@c = Cohabit::Configuration.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
Ybur.default_scopes.clear
|
9
|
+
Ybur._validators.clear
|
10
|
+
[:validate, :validation, :save, :create, :update, :destroy].each do |type|
|
11
|
+
Ybur.reset_callbacks(type)
|
12
|
+
end
|
13
|
+
Ybur.reflections.clear
|
14
|
+
|
15
|
+
Client.delete_all
|
16
|
+
Ybur.delete_all
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_basic_strategy
|
20
|
+
@c.load do
|
21
|
+
require 'basic'
|
22
|
+
scope :ybur, :basic, association: :client
|
23
|
+
end
|
24
|
+
@c.apply_scopes!
|
25
|
+
|
26
|
+
# test before_validation to add scope association
|
27
|
+
c = Client.create(name: "fubar")
|
28
|
+
Cohabit.current_tenant = c
|
29
|
+
y = Ybur.create(joke: "I took my tomcats to get neutered today. No hard felines")
|
30
|
+
assert_same(c, y.client)
|
31
|
+
|
32
|
+
# test default_scope
|
33
|
+
c2 = Client.create(name: "rabuf")
|
34
|
+
Cohabit.current_tenant = c2
|
35
|
+
y2 = Ybur.create(joke: "I just ordered 10,000 bottles of TippEx. I made a massive mistake.")
|
36
|
+
assert_equal(1, Ybur.count)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require "active_record"
|
7
|
+
require "models"
|
8
|
+
require "cohabit"
|
9
|
+
|
10
|
+
def load_schema
|
11
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
12
|
+
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__FILE__) + "/debug.log")
|
13
|
+
db_adapter = ENV['DB']
|
14
|
+
# no db passed, try one of these fine config-free DBs before bombing.
|
15
|
+
db_adapter ||= begin
|
16
|
+
require 'rubygems'
|
17
|
+
require 'sqlite'
|
18
|
+
'sqlite'
|
19
|
+
rescue MissingSourceFile
|
20
|
+
begin
|
21
|
+
require 'sqlite3'
|
22
|
+
'sqlite3'
|
23
|
+
rescue MissingSourceFile
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if db_adapter.nil?
|
28
|
+
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
|
29
|
+
end
|
30
|
+
ActiveRecord::Base.establish_connection(config[db_adapter])
|
31
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cohabit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Campbell
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Handle application scoping for multi-tenant applications with table scopes.
|
79
|
+
email:
|
80
|
+
- mike@wordofmike.net
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- LICENSE.txt
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- cohabit.gemspec
|
91
|
+
- lib/cohabit.rb
|
92
|
+
- lib/cohabit/configuration.rb
|
93
|
+
- lib/cohabit/configuration/scopes.rb
|
94
|
+
- lib/cohabit/configuration/settings.rb
|
95
|
+
- lib/cohabit/configuration/strategies.rb
|
96
|
+
- lib/cohabit/errors.rb
|
97
|
+
- lib/cohabit/scope.rb
|
98
|
+
- lib/cohabit/snippets.rb
|
99
|
+
- lib/cohabit/strategies/basic.rb
|
100
|
+
- lib/cohabit/strategies/defaults.rb
|
101
|
+
- lib/cohabit/strategies/multi.rb
|
102
|
+
- lib/cohabit/strategy.rb
|
103
|
+
- lib/cohabit/version.rb
|
104
|
+
- test/all_tests.rb
|
105
|
+
- test/database.yml
|
106
|
+
- test/models.rb
|
107
|
+
- test/schema.rb
|
108
|
+
- test/scopes_test.rb
|
109
|
+
- test/settings_test.rb
|
110
|
+
- test/strategies_test.rb
|
111
|
+
- test/strategy_test.rb
|
112
|
+
- test/test_helper.rb
|
113
|
+
homepage: http://github.com/mikecmpbll/cohabit
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.8.25
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Scope multi-tenant applications.
|
138
|
+
test_files:
|
139
|
+
- test/all_tests.rb
|
140
|
+
- test/database.yml
|
141
|
+
- test/models.rb
|
142
|
+
- test/schema.rb
|
143
|
+
- test/scopes_test.rb
|
144
|
+
- test/settings_test.rb
|
145
|
+
- test/strategies_test.rb
|
146
|
+
- test/strategy_test.rb
|
147
|
+
- test/test_helper.rb
|