tenancy 0.1.0 → 0.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.
- checksums.yaml +6 -14
- data/.gitignore +1 -0
- data/.travis.yml +15 -0
- data/Gemfile +2 -0
- data/README.md +47 -13
- data/Rakefile +27 -0
- data/gemfiles/active_record_32.gemfile +7 -0
- data/gemfiles/active_record_40.gemfile +7 -0
- data/lib/tenancy/matchers.rb +23 -2
- data/lib/tenancy/resource.rb +3 -2
- data/lib/tenancy/resource_scope.rb +20 -5
- data/lib/tenancy/version.rb +1 -1
- data/lib/tenancy.rb +1 -0
- data/spec/lib/resource_scope_spec.rb +36 -18
- data/spec/lib/resource_spec.rb +4 -4
- data/spec/lib/shoulda_matchers_spec.rb +9 -1
- data/spec/spec_helper.rb +3 -0
- data/spec/support/models.rb +3 -1
- data/spec/support/schema.rb +4 -2
- data/tenancy.gemspec +6 -5
- metadata +45 -28
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
YjM4NWFhMjEwZTAxMGZhNTRmZTcyMWYwYzQ2Njk2ZjNkZTZlMDg3YjRjYTRi
|
10
|
-
ZWY2OTg3ODliYzQ5YzQwMjNkYmRlNzc4MTZmMTdlZTI0OTAzNjI2ZGQ3MmIz
|
11
|
-
NzUxNjFjYzc3NDc1MmE0NzY4Mzg0YTUxZDYyNWE3ZDU5MDkyNDY=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MGI2MWY0ZjI2NjM1ZDcyMWRhMTAwNDhkMzIxOTIwNGQ2YmYzYmM4NmNlZjk5
|
14
|
-
YzE1MWJhNDUwZTAyNjY4M2M3YzViNjEyYzI0MzU4YTlmMzRiYmExZGJkMzRh
|
15
|
-
MTQ4MWI3NWQ3MzhmNDQwZjBmMGY0YjE1MTc2Y2QyNTQwNDQ2YTc=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 960f4ccd4c80911a5f803c6865f782a468e166e7
|
4
|
+
data.tar.gz: 387cf8e4f3f74bc79ed2f28402c4c33b641788b3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a41030d55da3b492c2325f5b61c8feb4cf976793d3388d10eeeace10d354d4ad7fa6267c5c7f6084e62734468d0d05981042a40843cac3dfc0b8de6a4780e13
|
7
|
+
data.tar.gz: 943e3ccd96321fa385a5e0a689c4ff03b43cb5eaf445f70337e166484e5dd2ab7f6ae5a9f6fd285337f7dc0c43d86ed7b15d76de36c47fefa1d2cbad7b469404
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
script: "bundle exec rake spec"
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
6
|
+
env:
|
7
|
+
- CODECLIMATE_REPO_TOKEN=891d362268d07d6ff0f5534f92252b6195f6be8795054d3627643eb6314a8c9e
|
8
|
+
gemfile:
|
9
|
+
- gemfiles/active_record_32.gemfile
|
10
|
+
- gemfiles/active_record_40.gemfile
|
11
|
+
notifications:
|
12
|
+
email: false
|
13
|
+
addons:
|
14
|
+
code_climate:
|
15
|
+
repo_token: 891d362268d07d6ff0f5534f92252b6195f6be8795054d3627643eb6314a8c9e
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# Tenancy
|
1
|
+
# Tenancy [](https://travis-ci.org/yoolk/tenancy) [](https://codeclimate.com/repos/527a1d45f3ea005378005fdb/feed)
|
2
2
|
|
3
|
-
|
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).
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -65,6 +65,7 @@ class Communication < ActiveRecord::Base
|
|
65
65
|
include Tenancy::ResourceScope
|
66
66
|
|
67
67
|
scope_to :portal, :listing
|
68
|
+
default_scope -> { where(is_active: true) }
|
68
69
|
validates_uniqueness_in_scope :value
|
69
70
|
end
|
70
71
|
|
@@ -77,13 +78,21 @@ class ExtraCommunication < ActiveRecord::Base
|
|
77
78
|
validates_uniqueness_in_scope :value
|
78
79
|
end
|
79
80
|
|
80
|
-
Portal.current = 1
|
81
|
-
Listing.find(1).to_sql
|
81
|
+
> Portal.current = 1
|
82
|
+
> Listing.find(1).to_sql
|
82
83
|
# => SELECT "listings".* FROM "listings" WHERE "portal_id" = 1 AND "id" = 1
|
83
84
|
|
84
|
-
Listing.current = 1
|
85
|
-
Communication.find(1).to_sql
|
86
|
-
# => SELECT "communications".* FROM "communications" WHERE "portal_id" = 1 AND "listing_id" = 1 AND "id" = 1
|
85
|
+
> Listing.current = 1
|
86
|
+
> Communication.find(1).to_sql
|
87
|
+
# => SELECT "communications".* FROM "communications" WHERE "portal_id" = 1 AND "listing_id" = 1 AND "is_active" = true AND "id" = 1
|
88
|
+
|
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)
|
93
|
+
# => SELECT "communications".* FROM "communications" WHERE "portal_id" = 1 AND "is_active" = true AND "id" = 1
|
94
|
+
> Communication.without_scope(:portal, :listing).find(1)
|
95
|
+
# => SELECT "communications".* FROM "communications" WHERE "is_active" = true AND "id" = 1
|
87
96
|
```
|
88
97
|
|
89
98
|
`scope_to :portal` does 4 things:
|
@@ -107,13 +116,32 @@ class ApplicationController < ActionController::Base
|
|
107
116
|
around_filter :route_domain
|
108
117
|
|
109
118
|
protected
|
110
|
-
def route_domain(&block)
|
111
|
-
Portal.with(current_portal, &block)
|
112
|
-
end
|
113
119
|
|
114
|
-
|
115
|
-
|
116
|
-
|
120
|
+
def route_domain(&block)
|
121
|
+
Portal.with(current_portal, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
def current_portal
|
125
|
+
@current_portal ||= Portal.find_by_domain_name(request.host)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
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.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class ApplicationController < ActionController::Base
|
134
|
+
before_filter :set_current_portal
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
def current_portal
|
139
|
+
@current_portal
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_current_portal
|
143
|
+
@current_portal = Portal.find_by_domain_name(request.host)
|
144
|
+
end
|
117
145
|
end
|
118
146
|
```
|
119
147
|
|
@@ -134,6 +162,12 @@ require "tenancy/matchers"
|
|
134
162
|
|
135
163
|
Example:
|
136
164
|
|
165
|
+
```ruby
|
166
|
+
describe Portal do
|
167
|
+
it { should be_a_tenant }
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
137
171
|
```ruby
|
138
172
|
describe Listing do
|
139
173
|
it { should have_scope_to(:portal) }
|
data/Rakefile
CHANGED
@@ -1 +1,28 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => "spec:all"
|
11
|
+
|
12
|
+
namespace :spec do
|
13
|
+
%w(active_record_40 active_record_32).each do |gemfile|
|
14
|
+
desc "Run Tests against #{gemfile}"
|
15
|
+
task gemfile do
|
16
|
+
sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle --quiet"
|
17
|
+
sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake spec"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Run Tests against active_record versions"
|
22
|
+
task :all do
|
23
|
+
%w(active_record_40 active_record_32).each do |gemfile|
|
24
|
+
sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle --quiet"
|
25
|
+
sh "BUNDLE_GEMFILE='gemfiles/#{gemfile}.gemfile' bundle exec rake spec"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/tenancy/matchers.rb
CHANGED
@@ -7,6 +7,10 @@ module Tenancy
|
|
7
7
|
HaveScopeToMatcher.new(name)
|
8
8
|
end
|
9
9
|
|
10
|
+
def be_a_tenant
|
11
|
+
BeATenant.new
|
12
|
+
end
|
13
|
+
|
10
14
|
class HaveScopeToMatcher
|
11
15
|
def initialize(scope_name)
|
12
16
|
@scope_name = scope_name
|
@@ -37,7 +41,7 @@ module Tenancy
|
|
37
41
|
scoped_class = reflection.class_name.constantize
|
38
42
|
|
39
43
|
if scoped_class.current_id
|
40
|
-
actual_class.
|
44
|
+
actual_class.where(nil).to_sql.include? %Q{#{actual_class.quoted_table_name}.#{scoped_class.connection.quote_column_name(reflection.foreign_key)} = #{scoped_class.connection.quote(scoped_class.current_id)}}
|
41
45
|
else
|
42
46
|
true
|
43
47
|
end
|
@@ -51,6 +55,23 @@ module Tenancy
|
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
58
|
+
|
59
|
+
class BeATenant
|
60
|
+
attr_accessor :klass
|
61
|
+
|
62
|
+
def matches?(instance)
|
63
|
+
self.klass = instance.class
|
64
|
+
klass.included_modules.include? Tenancy::Resource
|
65
|
+
end
|
66
|
+
|
67
|
+
def failure_message
|
68
|
+
"Expected to call `include Tenancy::Resource` inside #{klass}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def description
|
72
|
+
"require to call `include Tenancy::Resource`"
|
73
|
+
end
|
74
|
+
end
|
54
75
|
end
|
55
76
|
end
|
56
77
|
end
|
@@ -59,4 +80,4 @@ end
|
|
59
80
|
require 'rspec/core'
|
60
81
|
RSpec.configure do |config|
|
61
82
|
config.include Tenancy::Shoulda::Matchers
|
62
|
-
end
|
83
|
+
end
|
data/lib/tenancy/resource.rb
CHANGED
@@ -14,11 +14,11 @@ module Tenancy
|
|
14
14
|
find(value)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
RequestStore.store[:"#{name}.current"] = tenant
|
18
18
|
end
|
19
19
|
|
20
20
|
def current
|
21
|
-
|
21
|
+
RequestStore.store[:"#{name}.current"]
|
22
22
|
end
|
23
23
|
|
24
24
|
def current_id
|
@@ -37,6 +37,7 @@ module Tenancy
|
|
37
37
|
self.current = old
|
38
38
|
end
|
39
39
|
end
|
40
|
+
alias_method :use, :with
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
@@ -12,13 +12,12 @@ module Tenancy
|
|
12
12
|
def scope_to(*resources)
|
13
13
|
options = resources.extract_options!.dup
|
14
14
|
raise ArgumentError, 'options should be blank if there are multiple resources' if resources.count > 1 and options.present?
|
15
|
-
|
15
|
+
|
16
16
|
resources.each do |resource|
|
17
17
|
resource = resource.to_sym
|
18
18
|
resource_class_name ||= (options[:class_name].to_s.presence || resource.to_s).classify
|
19
19
|
resource_class = resource_class_name.constantize
|
20
|
-
|
21
|
-
|
20
|
+
|
22
21
|
# validates and belongs_to
|
23
22
|
validates resource, presence: true
|
24
23
|
belongs_to resource, options
|
@@ -38,15 +37,31 @@ module Tenancy
|
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
40
|
+
# inspired by: https://github.com/goncalossilva/acts_as_paranoid/blob/rails3.2/lib/acts_as_paranoid/core.rb#L76
|
41
|
+
def without_scope(*resources)
|
42
|
+
scope = where(nil).with_default_scope
|
43
|
+
resources.each do |resource|
|
44
|
+
resource = resource.to_sym
|
45
|
+
reflection = reflect_on_association(resource)
|
46
|
+
next if reflection.nil?
|
47
|
+
|
48
|
+
resource_scope_sql = where(nil).table[reflection.foreign_key].eq(reflection.klass.current_id).to_sql
|
49
|
+
|
50
|
+
scope.where_values.delete_if { |query| query.to_sql == resource_scope_sql }
|
51
|
+
end
|
52
|
+
|
53
|
+
scope
|
54
|
+
end
|
55
|
+
|
41
56
|
def validates_uniqueness_in_scope(fields, args={})
|
42
57
|
if args[:scope]
|
43
58
|
args[:scope] = Array.wrap(args[:scope]) << scope_fields
|
44
59
|
else
|
45
60
|
args[:scope] = scope_fields
|
46
61
|
end
|
47
|
-
|
62
|
+
|
48
63
|
validates_uniqueness_of(fields, args)
|
49
64
|
end
|
50
65
|
end
|
51
66
|
end
|
52
|
-
end
|
67
|
+
end
|
data/lib/tenancy/version.rb
CHANGED
data/lib/tenancy.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Tenancy::ResourceScope" do
|
4
|
-
let(:camyp)
|
5
|
-
let(:panpages)
|
6
|
-
let(:listing)
|
4
|
+
let(:camyp) { Portal.create(domain_name: 'yp.com.kh') }
|
5
|
+
let(:panpages) { Portal.create(domain_name: 'panpages.com') }
|
6
|
+
let(:listing) { Listing.create(name: 'Listing 1', portal_id: camyp.id) }
|
7
7
|
|
8
8
|
after(:all) do
|
9
9
|
Portal.delete_all
|
@@ -11,31 +11,31 @@ describe "Tenancy::ResourceScope" do
|
|
11
11
|
|
12
12
|
describe Listing do
|
13
13
|
it { should belong_to(:portal) }
|
14
|
-
|
14
|
+
|
15
15
|
it { should validate_presence_of(:portal) }
|
16
16
|
|
17
17
|
it { should validate_uniqueness_of(:name).scoped_to(:portal_id).case_insensitive }
|
18
|
-
|
18
|
+
|
19
19
|
it "have default_scope with :portal_id field" do
|
20
20
|
Portal.current = camyp
|
21
21
|
|
22
|
-
Listing.
|
22
|
+
expect(Listing.where(nil).to_sql).to eq(Listing.where(portal_id: Portal.current_id).to_sql)
|
23
23
|
end
|
24
24
|
|
25
25
|
it "doesn't have default_scope when it doesn't have current portal" do
|
26
26
|
Portal.current = nil
|
27
27
|
|
28
|
-
Listing.
|
28
|
+
expect(Listing.where(nil).to_sql).not_to include(%{"listings"."portal_id" = #{Portal.current_id}})
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
describe Communication do
|
33
33
|
it { should belong_to(:portal) }
|
34
|
-
|
34
|
+
|
35
35
|
it { should validate_presence_of(:portal) }
|
36
36
|
|
37
37
|
it { should belong_to(:listing) }
|
38
|
-
|
38
|
+
|
39
39
|
it { should validate_presence_of(:listing) }
|
40
40
|
|
41
41
|
it { should validate_uniqueness_of(:value).scoped_to(:portal_id, :listing_id) }
|
@@ -44,14 +44,15 @@ describe "Tenancy::ResourceScope" do
|
|
44
44
|
Portal.current = camyp
|
45
45
|
Listing.current = listing
|
46
46
|
|
47
|
-
Communication.
|
47
|
+
Communication.where(nil).to_sql.should == Communication.where(portal_id: Portal.current_id, listing_id: Listing.current_id).to_sql
|
48
48
|
end
|
49
49
|
|
50
50
|
it "doesn't have default_scope when it doesn't have current portal and listing" do
|
51
51
|
Portal.current = nil
|
52
52
|
Listing.current = nil
|
53
53
|
|
54
|
-
Communication.
|
54
|
+
expect(Communication.where(nil).to_sql).not_to include(%{"communications"."portal_id" = #{Portal.current_id}})
|
55
|
+
expect(Communication.where(nil).to_sql).not_to include(%{"communications"."listing_id" = #{Listing.current_id}})
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -69,10 +70,10 @@ describe "Tenancy::ResourceScope" do
|
|
69
70
|
|
70
71
|
Portal.current = camyp
|
71
72
|
Listing.current = listing2
|
72
|
-
|
73
|
+
|
73
74
|
extra_communication = ExtraCommunication.new
|
74
|
-
extra_communication.listing_id.
|
75
|
-
extra_communication.portal_id.
|
75
|
+
expect(extra_communication.listing_id).to eq(listing2.id)
|
76
|
+
expect(extra_communication.portal_id).to eq(camyp.id)
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
@@ -82,26 +83,43 @@ describe "Tenancy::ResourceScope" do
|
|
82
83
|
|
83
84
|
it "reload belongs_to when passes true" do
|
84
85
|
listing.portal.domain_name = 'abc.com'
|
85
|
-
listing.portal(true).object_id.
|
86
|
+
expect(listing.portal(true).object_id).not_to eq(Portal.current.object_id)
|
86
87
|
end
|
87
88
|
|
88
89
|
it "doesn't reload belongs_to" do
|
89
90
|
listing.portal.domain_name = 'abc.com'
|
90
|
-
listing.portal.object_id.
|
91
|
+
expect(listing.portal.object_id).to eq(Portal.current.object_id)
|
91
92
|
end
|
92
93
|
|
93
94
|
it "returns different object" do
|
94
95
|
listing.portal_id = panpages.id
|
95
|
-
listing.portal.object_id.
|
96
|
+
expect(listing.portal.object_id).not_to eq(Portal.current.object_id)
|
96
97
|
end
|
97
98
|
|
98
99
|
it "doesn't touch db" do
|
99
100
|
current_listing = listing
|
100
101
|
|
101
102
|
Portal.establish_connection(adapter: "sqlite3", database: "spec/invalid.sqlite3")
|
102
|
-
current_listing.portal.object_id.
|
103
|
+
expect(current_listing.portal.object_id).to eq(Portal.current.object_id)
|
103
104
|
|
104
105
|
Portal.establish_connection(ActiveRecord::Base.connection_config)
|
105
106
|
end
|
106
107
|
end
|
108
|
+
|
109
|
+
describe "#without_scope" do
|
110
|
+
before(:each) { Portal.current = camyp }
|
111
|
+
after(:each) { Portal.current = nil and Listing.current = nil }
|
112
|
+
|
113
|
+
it "unscopes :current_portal" do
|
114
|
+
expect(Listing.without_scope(:portal).to_sql).not_to include(%{"listings"."portal_id" = #{Portal.current_id}})
|
115
|
+
end
|
116
|
+
|
117
|
+
it "unscopes :current_portal and :current_listing" do
|
118
|
+
Listing.current = listing
|
119
|
+
|
120
|
+
expect(Communication.without_scope(:portal).to_sql).not_to include(%{"communications"."portal_id" = #{Portal.current_id}})
|
121
|
+
expect(Communication.without_scope(:listing).to_sql).not_to include(%{"communications"."listing_id" = #{Listing.current_id}})
|
122
|
+
expect(Communication.without_scope(:portal, :listing).to_sql).to eq(%{SELECT "communications".* FROM "communications" WHERE "communications"."is_active" = 't'})
|
123
|
+
end
|
124
|
+
end
|
107
125
|
end
|
data/spec/lib/resource_spec.rb
CHANGED
@@ -11,20 +11,20 @@ describe "Tenancy::Resource" do
|
|
11
11
|
Portal.delete_all
|
12
12
|
end
|
13
13
|
|
14
|
-
before(:each) {
|
14
|
+
before(:each) { RequestStore.store[:'Portal.current'] = nil }
|
15
15
|
|
16
16
|
it "set current with instance" do
|
17
17
|
Portal.current = @camyp
|
18
18
|
|
19
19
|
Portal.current.should == @camyp
|
20
|
-
|
20
|
+
RequestStore.store[:'Portal.current'].should == @camyp
|
21
21
|
end
|
22
22
|
|
23
23
|
it "set current with id" do
|
24
24
|
Portal.current = @panpage.id
|
25
25
|
|
26
26
|
Portal.current.should == @panpage
|
27
|
-
|
27
|
+
RequestStore.store[:'Portal.current'].should == @panpage
|
28
28
|
end
|
29
29
|
|
30
30
|
it "set current with nil" do
|
@@ -32,7 +32,7 @@ describe "Tenancy::Resource" do
|
|
32
32
|
Portal.current = nil
|
33
33
|
|
34
34
|
Portal.current.should == nil
|
35
|
-
|
35
|
+
RequestStore.store[:'Portal.current'].should == nil
|
36
36
|
end
|
37
37
|
|
38
38
|
it "#current_id" do
|
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'tenancy/matchers'
|
3
3
|
|
4
|
+
describe Portal do
|
5
|
+
it { should be_a_tenant }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Listing do
|
9
|
+
it { should be_a_tenant }
|
10
|
+
end
|
11
|
+
|
4
12
|
describe ExtraCommunication do
|
5
13
|
let(:camyp) { Portal.create(domain_name: 'yp.com.kh') }
|
6
14
|
before { Portal.current = camyp }
|
@@ -9,4 +17,4 @@ describe ExtraCommunication do
|
|
9
17
|
it { should have_scope_to(:portal).class_name('Portal') }
|
10
18
|
it { should have_scope_to(:listing) }
|
11
19
|
it { should have_scope_to(:listing).class_name('Listing') }
|
12
|
-
end
|
20
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/models.rb
CHANGED
@@ -6,6 +6,7 @@ class Listing < ActiveRecord::Base
|
|
6
6
|
include Tenancy::Resource
|
7
7
|
include Tenancy::ResourceScope
|
8
8
|
|
9
|
+
default_scope -> { where(is_active: true) }
|
9
10
|
scope_to :portal
|
10
11
|
validates_uniqueness_in_scope :name, case_sensitive: false
|
11
12
|
end
|
@@ -13,6 +14,7 @@ end
|
|
13
14
|
class Communication < ActiveRecord::Base
|
14
15
|
include Tenancy::ResourceScope
|
15
16
|
|
17
|
+
default_scope -> { where(is_active: true) }
|
16
18
|
scope_to :portal, :listing
|
17
19
|
validates_uniqueness_in_scope :value
|
18
20
|
end
|
@@ -22,4 +24,4 @@ class ExtraCommunication < ActiveRecord::Base
|
|
22
24
|
|
23
25
|
scope_to :portal, class_name: 'Portal'
|
24
26
|
scope_to :listing, class_name: 'Listing'
|
25
|
-
end
|
27
|
+
end
|
data/spec/support/schema.rb
CHANGED
@@ -15,14 +15,16 @@ ActiveRecord::Schema.define do
|
|
15
15
|
create_table :listings, :force => true do |t|
|
16
16
|
t.string :name
|
17
17
|
t.references :portal
|
18
|
+
t.boolean :is_active
|
18
19
|
t.timestamps
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
create_table :communications, :force => true do |t|
|
22
23
|
t.string :label
|
23
24
|
t.string :value
|
24
25
|
t.references :listing
|
25
26
|
t.references :portal
|
27
|
+
t.boolean :is_active
|
26
28
|
t.timestamps
|
27
29
|
end
|
28
30
|
|
@@ -33,4 +35,4 @@ ActiveRecord::Schema.define do
|
|
33
35
|
t.references :portal
|
34
36
|
t.timestamps
|
35
37
|
end
|
36
|
-
end
|
38
|
+
end
|
data/tenancy.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Tenancy::VERSION
|
9
9
|
spec.authors = ["chamnap"]
|
10
10
|
spec.email = ["chamnapchhorn@gmail.com"]
|
11
|
-
spec.description = %q{A simple
|
12
|
-
spec.summary = %q{A simple
|
13
|
-
spec.homepage = ""
|
11
|
+
spec.description = %q{A simple multitenancy with activerecord through scoping}
|
12
|
+
spec.summary = %q{A simple multitenancy with activerecord through scoping}
|
13
|
+
spec.homepage = "https://github.com/yoolk/tenancy"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "sqlite3", "~> 1.3.7"
|
25
25
|
spec.add_development_dependency "rake"
|
26
26
|
|
27
|
-
spec.add_dependency "activerecord", "
|
28
|
-
|
27
|
+
spec.add_dependency "activerecord", ">= 3.2.13"
|
28
|
+
spec.add_dependency "request_store", "~> 1.0.5"
|
29
|
+
end
|
metadata
CHANGED
@@ -1,100 +1,114 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tenancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- chamnap
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ~>
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: 2.12.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
19
22
|
version_requirements: !ruby/object:Gem::Requirement
|
20
23
|
requirements:
|
21
24
|
- - ~>
|
22
25
|
- !ruby/object:Gem::Version
|
23
26
|
version: 2.12.0
|
24
|
-
type: :development
|
25
|
-
prerelease: false
|
26
|
-
name: rspec
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
+
name: shoulda
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - ~>
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: 3.5.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
33
36
|
version_requirements: !ruby/object:Gem::Requirement
|
34
37
|
requirements:
|
35
38
|
- - ~>
|
36
39
|
- !ruby/object:Gem::Version
|
37
40
|
version: 3.5.0
|
38
|
-
type: :development
|
39
|
-
prerelease: false
|
40
|
-
name: shoulda
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
43
44
|
requirements:
|
44
45
|
- - ~>
|
45
46
|
- !ruby/object:Gem::Version
|
46
47
|
version: 0.9.12
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
47
50
|
version_requirements: !ruby/object:Gem::Requirement
|
48
51
|
requirements:
|
49
52
|
- - ~>
|
50
53
|
- !ruby/object:Gem::Version
|
51
54
|
version: 0.9.12
|
52
|
-
type: :development
|
53
|
-
prerelease: false
|
54
|
-
name: pry
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
56
57
|
requirement: !ruby/object:Gem::Requirement
|
57
58
|
requirements:
|
58
59
|
- - ~>
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: 1.3.7
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
61
64
|
version_requirements: !ruby/object:Gem::Requirement
|
62
65
|
requirements:
|
63
66
|
- - ~>
|
64
67
|
- !ruby/object:Gem::Version
|
65
68
|
version: 1.3.7
|
66
|
-
type: :development
|
67
|
-
prerelease: false
|
68
|
-
name: sqlite3
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
70
71
|
requirement: !ruby/object:Gem::Requirement
|
71
72
|
requirements:
|
72
|
-
- -
|
73
|
+
- - '>='
|
73
74
|
- !ruby/object:Gem::Version
|
74
75
|
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
75
78
|
version_requirements: !ruby/object:Gem::Requirement
|
76
79
|
requirements:
|
77
|
-
- -
|
80
|
+
- - '>='
|
78
81
|
- !ruby/object:Gem::Version
|
79
82
|
version: '0'
|
80
|
-
type: :development
|
81
|
-
prerelease: false
|
82
|
-
name: rake
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord
|
84
85
|
requirement: !ruby/object:Gem::Requirement
|
85
86
|
requirements:
|
86
|
-
- -
|
87
|
+
- - '>='
|
87
88
|
- !ruby/object:Gem::Version
|
88
89
|
version: 3.2.13
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
89
92
|
version_requirements: !ruby/object:Gem::Requirement
|
90
93
|
requirements:
|
91
|
-
- -
|
94
|
+
- - '>='
|
92
95
|
- !ruby/object:Gem::Version
|
93
96
|
version: 3.2.13
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: request_store
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.0.5
|
94
104
|
type: :runtime
|
95
105
|
prerelease: false
|
96
|
-
|
97
|
-
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.0.5
|
111
|
+
description: A simple multitenancy with activerecord through scoping
|
98
112
|
email:
|
99
113
|
- chamnapchhorn@gmail.com
|
100
114
|
executables: []
|
@@ -104,10 +118,13 @@ files:
|
|
104
118
|
- .gitignore
|
105
119
|
- .rspec
|
106
120
|
- .rvmrc
|
121
|
+
- .travis.yml
|
107
122
|
- Gemfile
|
108
123
|
- LICENSE.txt
|
109
124
|
- README.md
|
110
125
|
- Rakefile
|
126
|
+
- gemfiles/active_record_32.gemfile
|
127
|
+
- gemfiles/active_record_40.gemfile
|
111
128
|
- lib/tenancy.rb
|
112
129
|
- lib/tenancy/matchers.rb
|
113
130
|
- lib/tenancy/resource.rb
|
@@ -120,7 +137,7 @@ files:
|
|
120
137
|
- spec/support/models.rb
|
121
138
|
- spec/support/schema.rb
|
122
139
|
- tenancy.gemspec
|
123
|
-
homepage:
|
140
|
+
homepage: https://github.com/yoolk/tenancy
|
124
141
|
licenses:
|
125
142
|
- MIT
|
126
143
|
metadata: {}
|
@@ -130,20 +147,20 @@ require_paths:
|
|
130
147
|
- lib
|
131
148
|
required_ruby_version: !ruby/object:Gem::Requirement
|
132
149
|
requirements:
|
133
|
-
- -
|
150
|
+
- - '>='
|
134
151
|
- !ruby/object:Gem::Version
|
135
152
|
version: '0'
|
136
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
154
|
requirements:
|
138
|
-
- -
|
155
|
+
- - '>='
|
139
156
|
- !ruby/object:Gem::Version
|
140
157
|
version: '0'
|
141
158
|
requirements: []
|
142
159
|
rubyforge_project:
|
143
|
-
rubygems_version: 2.
|
160
|
+
rubygems_version: 2.1.10
|
144
161
|
signing_key:
|
145
162
|
specification_version: 4
|
146
|
-
summary: A simple
|
163
|
+
summary: A simple multitenancy with activerecord through scoping
|
147
164
|
test_files:
|
148
165
|
- spec/lib/resource_scope_spec.rb
|
149
166
|
- spec/lib/resource_spec.rb
|