tenancy 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 960f4ccd4c80911a5f803c6865f782a468e166e7
4
- data.tar.gz: 387cf8e4f3f74bc79ed2f28402c4c33b641788b3
3
+ metadata.gz: fe538f2809f42fdca981b88b3bbf9bb5ca19bc24
4
+ data.tar.gz: e65ce5cffe075fe7be07dd8d3525194d791e0199
5
5
  SHA512:
6
- metadata.gz: 6a41030d55da3b492c2325f5b61c8feb4cf976793d3388d10eeeace10d354d4ad7fa6267c5c7f6084e62734468d0d05981042a40843cac3dfc0b8de6a4780e13
7
- data.tar.gz: 943e3ccd96321fa385a5e0a689c4ff03b43cb5eaf445f70337e166484e5dd2ab7f6ae5a9f6fd285337f7dc0c43d86ed7b15d76de36c47fefa1d2cbad7b469404
6
+ metadata.gz: a8d0631db5f4f03b05e219d32f46e4b0f5eb57f60a5603331bff2eeba7c22590bb02967c65042bcbbcd40d819fd152f52a9e0b033ce1c6291e068f50d1156e59
7
+ data.tar.gz: d1425e735c6d9c125a749d8e41b52acd6f0b96351c7ee3bd668d5d393bb0842f05b10603d4b189dc3f2290253e0687955d127fc40b4fbdeca7898e3e02ec01d7
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-ci
2
+ repo_token: rKGLArZVmQzAMDN87mkpPahF5zlXDNbLL
data/.rspec CHANGED
@@ -1 +1,2 @@
1
- --colour
1
+ --colour
2
+ --profile
data/.rvmrc CHANGED
@@ -4,7 +4,7 @@
4
4
  # development environment upon cd'ing into the directory
5
5
 
6
6
  # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
- environment_id="ruby-2.0.0-p247@tenancy"
7
+ environment_id="ruby-2.1.0@tenancy"
8
8
 
9
9
  #
10
10
  # Uncomment the following lines if you want to verify rvm version per project
data/.travis.yml CHANGED
@@ -1,15 +1,19 @@
1
+ services: mongodb
1
2
  language: ruby
2
3
  script: "bundle exec rake spec"
3
4
  rvm:
4
5
  - 1.9.3
5
6
  - 2.0.0
7
+ - 2.1.0
6
8
  env:
7
9
  - CODECLIMATE_REPO_TOKEN=891d362268d07d6ff0f5534f92252b6195f6be8795054d3627643eb6314a8c9e
8
10
  gemfile:
9
11
  - gemfiles/active_record_32.gemfile
10
12
  - gemfiles/active_record_40.gemfile
13
+ - gemfiles/mongoid_3.gemfile
14
+ - gemfiles/mongoid_4.gemfile
11
15
  notifications:
12
16
  email: false
13
17
  addons:
14
18
  code_climate:
15
- repo_token: 891d362268d07d6ff0f5534f92252b6195f6be8795054d3627643eb6314a8c9e
19
+ repo_token: 891d362268d07d6ff0f5534f92252b6195f6be8795054d3627643eb6314a8c9e
data/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Overview
2
+
3
+ For instructions on upgrading to newer versions, visit
4
+ [mongoid.org](http://mongoid.org/en/mongoid/docs/upgrading.html).
5
+
6
+ ## 1.0.0
7
+
8
+ ### Major Changes (Backwards Incompatible)
9
+
10
+ * Rename `#with` and `#use` to `#with_tenant` and `#use_tenant` because it conflicts with mongoid.
11
+ * Replace `#without_scope` to `#tenant_scope`.
12
+ * Support Mongoid 3/4.
13
+
14
+ ## 0.2.0
15
+
16
+ * Add [request_store](https://github.com/steveklabnik/request_store) as dependency.
17
+ * Add `#without_scope`.
18
+ * Support ActiveRecord 4.
19
+
20
+ ## 0.1.0
21
+
22
+ * First Release
23
+ * Support ActiveRecord 3.
24
+ * Add `tenany/matchers`.
data/Gemfile CHANGED
@@ -1,6 +1,16 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in tenancy.gemspec
4
- gemspec
3
+ gem "codeclimate-test-reporter", group: :test, require: nil
4
+ gem "coveralls", require: false
5
+ gem "activerecord", "~> 4.0.2", :require => "active_record"
6
+ gem "mongoid", "~> 4.0.0.beta1"
7
+ gem "rspec"
8
+ gem "mongoid-rspec"
9
+ gem "database_cleaner"
10
+ gem "shoulda-matchers"
11
+ gem "pry"
12
+ gem "sqlite3"
13
+ gem "rake"
5
14
 
6
- gem "codeclimate-test-reporter", group: :test, require: nil
15
+ # Specify your gem's dependencies in tenancy.gemspec
16
+ gemspec
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
- # Tenancy [![Build Status](https://travis-ci.org/yoolk/tenancy.png?branch=master)](https://travis-ci.org/yoolk/tenancy) [![Code Climate](https://codeclimate.com/repos/527a1d45f3ea005378005fdb/badges/cfbc9ff8993d02e13b9d/gpa.png)](https://codeclimate.com/repos/527a1d45f3ea005378005fdb/feed)
1
+ # Tenancy [![Gem Version](https://badge.fury.io/rb/tenancy.png)](http://badge.fury.io/rb/tenancy) [![Build Status](https://travis-ci.org/yoolk/tenancy.png?branch=master)](https://travis-ci.org/yoolk/tenancy) [![Dependency Status](https://gemnasium.com/yoolk/tenancy.png)](https://gemnasium.com/yoolk/tenancy) [![Coverage Status](https://coveralls.io/repos/yoolk/tenancy/badge.png?branch=master)](https://coveralls.io/r/yoolk/tenancy?branch=master)
2
2
 
3
- **Tenancy** is a simple gem that provides multi-tenancy support on activerecord through scoping. I suggest you to watch an excellent [RailsCast on Multitenancy with Scopes](http://railscasts.com/episodes/388-multitenancy-with-scopes) and read this book [Multitenancy with Rails](https://leanpub.com/multi-tenancy-rails).
3
+ **Tenancy** is a simple gem that provides multi-tenancy support on activerecord/mongoid (3/4) through scoping. I suggest you to watch an excellent [RailsCast on Multitenancy with Scopes](http://railscasts.com/episodes/388-multitenancy-with-scopes) and read this book [Multitenancy with Rails](https://leanpub.com/multi-tenancy-rails).
4
+
5
+ This `README.md` file is for the latest version, v1.0.0. For the previous version, check out this [README.md](https://github.com/yoolk/tenancy/blob/v0.2.0/README.md). Please, see the [CHANGELOG.md](https://github.com/yoolk/tenancy/blob/master/CHANGELOG.md#100) to do an upgrade.
4
6
 
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
8
10
 
9
11
  ```ruby
10
- gem 'tenancy'
12
+ gem "tenancy"
11
13
  ```
12
14
 
13
15
  And then execute:
@@ -18,7 +20,7 @@ $ bundle
18
20
 
19
21
  ## Usage
20
22
 
21
- This gem provides two modules: `Tenancy::Resource` and `Tenancy::ResourceScope`.
23
+ This gem provides two modules: `Tenancy::Resource` and `Tenancy::ResourceScope`. Include them into your activerecord/mongoid models.
22
24
 
23
25
  ### Tenancy::Resource
24
26
 
@@ -43,7 +45,7 @@ Portal.current
43
45
  # => <Portal id: 1, domain_name: 'yp.com.kh'>
44
46
 
45
47
  # scope with this portal
46
- Portal.with(camyp) do
48
+ Portal.with_tenant(camyp) do
47
49
  # Do something here with this portal
48
50
  end
49
51
  ```
@@ -79,23 +81,23 @@ class ExtraCommunication < ActiveRecord::Base
79
81
  end
80
82
 
81
83
  > Portal.current = 1
82
- > Listing.find(1).to_sql
84
+ > Listing.find(1)
83
85
  # => SELECT "listings".* FROM "listings" WHERE "portal_id" = 1 AND "id" = 1
84
86
 
85
87
  > Listing.current = 1
86
- > Communication.find(1).to_sql
88
+ > Communication.find(1)
87
89
  # => SELECT "communications".* FROM "communications" WHERE "portal_id" = 1 AND "listing_id" = 1 AND "is_active" = true AND "id" = 1
88
90
 
89
- # unscoped :current_portal, :current_listing
90
- > Communication.without_scope(:portal).find(1)
91
- # => SELECT "communications".* FROM "communications" WHERE "listing_id" = 1 AND "is_active" = true AND "id" = 1
92
- > Communication.without_scope(:listing).find(1)
91
+ # include/exclude tenant_scope :current_portal, :current_listing
92
+ > Communication.tenant_scope(:portal).find(1)
93
93
  # => SELECT "communications".* FROM "communications" WHERE "portal_id" = 1 AND "is_active" = true AND "id" = 1
94
- > Communication.without_scope(:portal, :listing).find(1)
94
+ > Communication.tenant_scope(:listing).find(1)
95
+ # => SELECT "communications".* FROM "communications" WHERE "listing_id" = 1 AND "is_active" = true AND "id" = 1
96
+ > Communication.tenant_scope(nil).find(1)
95
97
  # => SELECT "communications".* FROM "communications" WHERE "is_active" = true AND "id" = 1
96
98
  ```
97
99
 
98
- `scope_to :portal` does 4 things:
100
+ `scope_to :portal` does these things:
99
101
 
100
102
  1. it adds `belongs_to :portal`.
101
103
 
@@ -105,42 +107,26 @@ end
105
107
 
106
108
  4. it overrides `#portal` so that it doesn't touch the database if `portal_id` in that record is the same as `Portal.current_id`.
107
109
 
108
- `validates :value, uniqueness: true` will validates uniqueness against the whole table. `validates_uniqueness_in_scope` validates uniqueness with the scopes you passed in `scope_to`.
109
-
110
- ## Rails
111
-
112
- Because `#current` is using thread variable, it's advisable to set to `nil` after processing controller action. This can be easily achievable by using `around_filter` and `#with` inside `application_controller.rb`. Or, you can do it manually by using `#current=`.
113
-
114
- ```ruby
115
- class ApplicationController < ActionController::Base
116
- around_filter :route_domain
117
-
118
- protected
110
+ 5. it overrides `#portal_id` so that it returns `Portal.current_id`. (mongoid 3 only)
119
111
 
120
- def route_domain(&block)
121
- Portal.with(current_portal, &block)
122
- end
112
+ 6. it overrides `#shard_key_selector` so that every update/delete query includes current tenant_id. (mongoid 3/4)
123
113
 
124
- def current_portal
125
- @current_portal ||= Portal.find_by_domain_name(request.host)
126
- end
127
- end
128
- ```
114
+ `validates :value, uniqueness: true` will validates uniqueness against the whole table. `validates_uniqueness_in_scope` validates uniqueness with the scopes you passed in `scope_to`.
129
115
 
130
- From version 0.2.0 up, you don't need to use `around_filter` because this gem add [request_store](https://github.com/steveklabnik/request_store) as dependency. It will clear threaded variables inside middleware on every request. You can just use `before_filter` to set the current tenant.
116
+ ## Rails
131
117
 
132
118
  ```ruby
133
119
  class ApplicationController < ActionController::Base
134
- before_filter :set_current_portal
120
+ before_action :set_current_portal
135
121
 
136
122
  protected
137
123
 
138
124
  def current_portal
139
- @current_portal
125
+ Portal.current
140
126
  end
141
127
 
142
128
  def set_current_portal
143
- @current_portal = Portal.find_by_domain_name(request.host)
129
+ Portal.current = Portal.find_by_domain_name(request.host)
144
130
  end
145
131
  end
146
132
  ```
@@ -175,6 +161,40 @@ describe Listing do
175
161
  end
176
162
  ```
177
163
 
164
+ ```ruby
165
+ describe Mongo::Listing do
166
+ it { should have_scope_to(:portal) }
167
+ it { should have_scope_to(:portal).of_type(Mongo::Portal) }
168
+ end
169
+ ```
170
+
171
+ I have this rspec configuration in my rails 4 apps:
172
+
173
+ ```ruby
174
+ RSpec.configure do |config|
175
+ config.before(:suite) do
176
+ DatabaseCleaner[:active_record].strategy = :transaction
177
+ DatabaseCleaner[:mongoid].strategy = :truncation
178
+
179
+ DatabaseCleaner[:active_record].clean_with(:truncation)
180
+ DatabaseCleaner[:mongoid].clean_with(:truncation)
181
+ end
182
+
183
+ config.around(:each) do |example|
184
+ DatabaseCleaner[:active_record].start
185
+ DatabaseCleaner[:mongoid].start
186
+
187
+ current_portal = FactoryGirl.create(:portal, domain_name: "yellowpages-cambodia.dev")
188
+ Yoolk::Portal.use(current_portal) do
189
+ example.run
190
+ end
191
+
192
+ DatabaseCleaner[:active_record].clean
193
+ DatabaseCleaner[:mongoid].clean if example.metadata[:mongodb]
194
+ end
195
+ end
196
+ ```
197
+
178
198
  ## Authors
179
199
 
180
- * [Chamnap Chhorn](https://github.com/chamnap)
200
+ * [Chamnap Chhorn](https://github.com/chamnap)
data/Rakefile CHANGED
@@ -10,19 +10,19 @@ end
10
10
  task :default => "spec:all"
11
11
 
12
12
  namespace :spec do
13
- %w(active_record_40 active_record_32).each do |gemfile|
13
+ %w(active_record_40 active_record_32 mongoid_4 mongoid_3).each do |gemfile|
14
14
  desc "Run Tests against #{gemfile}"
15
15
  task gemfile do
16
16
  sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle --quiet"
17
- sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake spec"
17
+ sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rspec"
18
18
  end
19
19
  end
20
20
 
21
21
  desc "Run Tests against active_record versions"
22
22
  task :all do
23
- %w(active_record_40 active_record_32).each do |gemfile|
23
+ %w(active_record_40 active_record_32 mongoid_4 mongoid_3).each do |gemfile|
24
24
  sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle --quiet"
25
- sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake spec"
25
+ sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rspec"
26
26
  end
27
27
  end
28
28
  end
@@ -1,7 +1,13 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 3.2.13', :require => 'active_record'
1
+ source "https://rubygems.org"
4
2
 
3
+ gem "activerecord", "~> 3.2.16", :require => "active_record"
4
+ gem "coveralls", require: false
5
5
  gem "codeclimate-test-reporter", group: :test, require: nil
6
+ gem "rspec"
7
+ gem "database_cleaner"
8
+ gem "shoulda-matchers"
9
+ gem "pry"
10
+ gem "sqlite3"
11
+ gem "rake"
6
12
 
7
- gemspec :path => '../'
13
+ gemspec :path => "../"
@@ -1,7 +1,13 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'activerecord', '~> 4.0.1', :require => 'active_record'
1
+ source "https://rubygems.org"
4
2
 
3
+ gem "activerecord", "~> 4.0.2", :require => "active_record"
4
+ gem "coveralls", require: false
5
5
  gem "codeclimate-test-reporter", group: :test, require: nil
6
+ gem "rspec"
7
+ gem "database_cleaner"
8
+ gem "shoulda-matchers"
9
+ gem "pry"
10
+ gem "sqlite3"
11
+ gem "rake"
6
12
 
7
- gemspec :path => '../'
13
+ gemspec :path => "../"
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", "~> 3.1.6"
4
+ gem "coveralls", require: false
5
+ gem "codeclimate-test-reporter", group: :test, require: nil
6
+ gem "rspec"
7
+ gem "database_cleaner"
8
+ gem "mongoid-rspec"
9
+ gem "pry"
10
+ gem "rake"
11
+
12
+ gemspec :path => "../"
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "mongoid", "~> 4.0.0.beta1"
4
+ gem "coveralls", require: false
5
+ gem "codeclimate-test-reporter", group: :test, require: nil
6
+ gem "rspec"
7
+ gem "database_cleaner"
8
+ gem "mongoid-rspec"
9
+ gem "pry"
10
+ gem "rake"
11
+
12
+ gemspec :path => "../"
data/lib/tenancy.rb CHANGED
@@ -3,6 +3,8 @@ require "active_support/concern"
3
3
  require "request_store"
4
4
 
5
5
  module Tenancy
6
- autoload :Resource, 'tenancy/resource'
7
- autoload :ResourceScope, 'tenancy/resource_scope'
6
+ autoload :Resource, "tenancy/resource"
7
+ autoload :ResourceScope, "tenancy/resource_scope"
8
+ autoload :Scoping, "tenancy/scoping"
9
+ autoload :Tenant, "tenancy/tenant"
8
10
  end
@@ -1,4 +1,5 @@
1
- require 'shoulda-matchers'
1
+ require "shoulda-matchers" if defined?(ActiveRecord)
2
+ require "mongoid-rspec" if defined?(Mongoid)
2
3
 
3
4
  module Tenancy
4
5
  module Shoulda
@@ -12,16 +13,29 @@ module Tenancy
12
13
  end
13
14
 
14
15
  class HaveScopeToMatcher
16
+ attr_reader :scope_name
17
+
15
18
  def initialize(scope_name)
16
- @scope_name = scope_name
17
- @presence_matcher = ::Shoulda::Matchers::ActiveModel::ValidatePresenceOfMatcher.new(@scope_name)
18
- @belong_to_matcher = ::Shoulda::Matchers::ActiveRecord::AssociationMatcher.new(:belongs_to, @scope_name)
19
+ @scope_name = scope_name
20
+ if defined?(ActiveRecord)
21
+ @ar_presence_matcher = ::Shoulda::Matchers::ActiveModel::ValidatePresenceOfMatcher.new(@scope_name)
22
+ @ar_belong_to_matcher = ::Shoulda::Matchers::ActiveRecord::AssociationMatcher.new(:belongs_to, @scope_name)
23
+ end
24
+ if defined?(Mongoid)
25
+ @mid_presence_matcher = ::Mongoid::Matchers::Validations::HaveValidationMatcher.new(@scope_name, :presence)
26
+ @mid_belong_to_matcher = ::Mongoid::Matchers::Associations::HaveAssociationMatcher.new(@scope_name, ::Mongoid::Matchers::Associations::BELONGS_TO)
27
+ end
19
28
  end
20
29
 
21
30
  def matches?(subject)
22
- @presence_matches = @presence_matcher.matches?(subject)
23
- @belong_to_matches = @belong_to_matcher.matches?(subject)
24
- @default_scope_matches = default_scope_matches?(subject)
31
+ if defined?(ActiveRecord) && subject.class <= ::ActiveRecord::Base
32
+ @ar_presence_matcher.matches?(subject) &&
33
+ @ar_belong_to_matcher.matches?(subject) &&
34
+ ar_default_scope_matches?(subject)
35
+ elsif defined?(Mongoid) && subject.class <= ::Mongoid::Document
36
+ @mid_presence_matcher.matches?(subject) &&
37
+ @mid_belong_to_matcher.matches?(subject)
38
+ end
25
39
  end
26
40
 
27
41
  def failure_message
@@ -35,7 +49,8 @@ module Tenancy
35
49
  end
36
50
 
37
51
  private
38
- def default_scope_matches?(subject)
52
+
53
+ def ar_default_scope_matches?(subject)
39
54
  actual_class = subject.class
40
55
  reflection = actual_class.reflect_on_association(@scope_name.to_sym)
41
56
  scoped_class = reflection.class_name.constantize
@@ -48,8 +63,10 @@ module Tenancy
48
63
  end
49
64
 
50
65
  def method_missing(method, *args, &block)
51
- if @belong_to_matcher.respond_to?(method)
52
- @belong_to_matcher.send(method, *args, &block)
66
+ if @ar_belong_to_matcher && @ar_belong_to_matcher.respond_to?(method)
67
+ @ar_belong_to_matcher.send(method, *args, &block)
68
+ elsif @mid_belong_to_matcher && @mid_belong_to_matcher.respond_to?(method)
69
+ @mid_belong_to_matcher.send(method, *args, &block)
53
70
  else
54
71
  super
55
72
  end
@@ -77,7 +94,7 @@ module Tenancy
77
94
  end
78
95
 
79
96
 
80
- require 'rspec/core'
97
+ require "rspec/core"
81
98
  RSpec.configure do |config|
82
99
  config.include Tenancy::Shoulda::Matchers
83
- end
100
+ end
@@ -25,7 +25,7 @@ module Tenancy
25
25
  current.try(:id)
26
26
  end
27
27
 
28
- def with(tenant, &block)
28
+ def with_tenant(tenant, &block)
29
29
  raise ArgumentError, "block required" if block.nil?
30
30
 
31
31
  begin
@@ -37,7 +37,7 @@ module Tenancy
37
37
  self.current = old
38
38
  end
39
39
  end
40
- alias_method :use, :with
40
+ alias_method :use_tenant, :with_tenant
41
41
  end
42
42
  end
43
43
  end