acts_as_multi_tenant 1.1.0 → 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.
- checksums.yaml +4 -4
- data/README.md +23 -2
- data/lib/acts_as_multi_tenant.rb +8 -0
- data/lib/multi_tenant/acts_as_tenant.rb +13 -10
- data/lib/multi_tenant/belongs_to_tenant.rb +3 -26
- data/lib/multi_tenant/belongs_to_tenant_through.rb +2 -11
- data/lib/multi_tenant/impl/multiple_current.rb +104 -0
- data/lib/multi_tenant/impl/single_current.rb +124 -0
- data/lib/multi_tenant/middleware.rb +9 -5
- data/lib/multi_tenant/proxies_to_tenant.rb +10 -26
- data/lib/multi_tenant/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 839e8371c9bef7b4192ed3ed8a724358185dadefd207455495c74417a582eb46
|
4
|
+
data.tar.gz: fb6107209c01fef24a0e7ab46ca5f627916f76fcd04263e197f465a6d2962c87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 781cce611831503923b1a4351c5bdf7f70f26e5f280b8d99489d4296f73d36b9511add58df9348fe06c8e1526b9b90a2b1f63b81810a5c3ac23e3e3f628038a7
|
7
|
+
data.tar.gz: d6c1d709cc05cf8de12eea5eb2cea0aa1703cd5210b06c06485f0d3e272f764c56fb56462146fc3545545dd9fab613de014a4e7aaa27f18a785f5bad3a0c3967
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# acts_as_multi_tenant
|
2
2
|
|
3
|
-
Keep multiple tenants in a single ActiveRecord database, and keep their data separate. Let's say the `Client` AR model represents your "tenants".
|
3
|
+
Keep multiple tenants in a single ActiveRecord database, and keep their data separate. Let's say the `Client` AR model represents your "tenants". Rack middleware will keep track of the "current" client in the request cycle which will automatically filter *all ActiveRecord queries* by that client. New records will automatically be associated to that client as well.
|
4
4
|
|
5
5
|
There are 3 main components:
|
6
6
|
|
@@ -10,7 +10,7 @@ There are 3 main components:
|
|
10
10
|
|
11
11
|
## MultiTenant::Middleware
|
12
12
|
|
13
|
-
Add the middleware in **config.ru
|
13
|
+
Add the middleware in **config.ru** or wherever you add middleware.
|
14
14
|
|
15
15
|
```ruby
|
16
16
|
use MultiTenant::Middleware,
|
@@ -115,6 +115,27 @@ end
|
|
115
115
|
License.current == Client.current.license
|
116
116
|
```
|
117
117
|
|
118
|
+
## Multiple current tenants
|
119
|
+
|
120
|
+
Some applications may need to allow multiple current tenants at once. For example, a single user account may have access to multiple clients. `acts_as_multi_tenant` supports this with the `current: :multiple` option. When this is set, `Client.current` will be an array of clients. Queries will be filtered to ANY of those clients.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
class Client < ActiveRecord::Base
|
124
|
+
acts_as_tenant using: :code, current: :multiple
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
When you add your middleware, your `identifier` option must also return an array:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
use MultiTenant::Middleware,
|
132
|
+
model: -> { Client.active },
|
133
|
+
|
134
|
+
identifier: ->(req) {
|
135
|
+
req.params["clients"] || []
|
136
|
+
}
|
137
|
+
```
|
138
|
+
|
118
139
|
## Testing
|
119
140
|
|
120
141
|
bundle install
|
data/lib/acts_as_multi_tenant.rb
CHANGED
@@ -5,3 +5,11 @@ require_relative 'multi_tenant/proxies_to_tenant'
|
|
5
5
|
require_relative 'multi_tenant/belongs_to_tenant'
|
6
6
|
require_relative 'multi_tenant/belongs_to_tenant_through'
|
7
7
|
require_relative 'multi_tenant/middleware'
|
8
|
+
|
9
|
+
module MultiTenant
|
10
|
+
module Impl
|
11
|
+
NotImplemented = Class.new(StandardError)
|
12
|
+
autoload :SingleCurrent, 'multi_tenant/impl/single_current'
|
13
|
+
autoload :MultipleCurrent, 'multi_tenant/impl/multiple_current'
|
14
|
+
end
|
15
|
+
end
|
@@ -9,13 +9,22 @@ module MultiTenant
|
|
9
9
|
#
|
10
10
|
# Use this ActiveRecord model as the tenant source.
|
11
11
|
#
|
12
|
+
# The "current" option allows you to specify whether Client.current is a single record (the default) or an array of records.
|
13
|
+
#
|
12
14
|
# @param using [String] (optional) column that contains the unique lookup identifier. Defaults to :code.
|
15
|
+
# @param current [Symbol] :single | :multiple
|
13
16
|
#
|
14
|
-
def acts_as_tenant(using: :code)
|
15
|
-
cattr_accessor :tenant_identifier, :tenant_thread_var
|
17
|
+
def acts_as_tenant(using: :code, current: :single)
|
18
|
+
cattr_accessor :tenant_identifier, :tenant_thread_var, :multi_tenant_impl
|
16
19
|
self.tenant_identifier = using
|
17
20
|
self.tenant_thread_var = "current_tenant_#{object_id}".freeze # allows there to be multiple tenant classes
|
21
|
+
self.multi_tenant_impl = case current
|
22
|
+
when :single then MultiTenant::Impl::SingleCurrent.new(self)
|
23
|
+
when :multiple then MultiTenant::Impl::MultipleCurrent.new(self)
|
24
|
+
else raise ArgumentError, "Unknown current option '#{current}'"
|
25
|
+
end
|
18
26
|
self.extend MultiTenant::ActsAsTenant::ClassMethods
|
27
|
+
self.extend self.multi_tenant_impl.acts_as_tenant_class_methods
|
19
28
|
end
|
20
29
|
|
21
30
|
#
|
@@ -60,13 +69,7 @@ module MultiTenant
|
|
60
69
|
# @param record_or_identifier the record or the identifier in the 'tenant_identifier' column.
|
61
70
|
#
|
62
71
|
def current=(record_or_identifier)
|
63
|
-
obj =
|
64
|
-
record_or_identifier
|
65
|
-
elsif record_or_identifier
|
66
|
-
where({tenant_identifier => record_or_identifier}).first
|
67
|
-
else
|
68
|
-
nil
|
69
|
-
end
|
72
|
+
obj = resolve_tenant record_or_identifier
|
70
73
|
Thread.current.thread_variable_set tenant_thread_var, obj
|
71
74
|
end
|
72
75
|
|
@@ -102,7 +105,7 @@ module MultiTenant
|
|
102
105
|
#
|
103
106
|
def without_tenant
|
104
107
|
old_current = self.current
|
105
|
-
self.current = nil
|
108
|
+
self.current = resolve_tenant nil
|
106
109
|
yield if block_given?
|
107
110
|
ensure
|
108
111
|
self.current = old_current
|
@@ -27,17 +27,10 @@ module MultiTenant
|
|
27
27
|
self.tenant_foreign_key = reflection.foreign_key.to_sym
|
28
28
|
self.tenant_primary_key = reflection.association_primary_key.to_sym
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
self.class_eval do
|
34
|
-
include MultiTenant::BelongsToTenant::InstanceMethods
|
30
|
+
include tenant_class.multi_tenant_impl.belongs_to_tenant_instance_methods
|
31
|
+
default_scope(&tenant_class.multi_tenant_impl.belongs_to_tenant_default_scope(self))
|
35
32
|
|
36
|
-
|
37
|
-
current = tenant_class.current
|
38
|
-
current ? where({tenant_foreign_key => current.send(tenant_primary_key)}) : where('1=1')
|
39
|
-
}
|
40
|
-
end
|
33
|
+
validates_presence_of tenant_foreign_key
|
41
34
|
end
|
42
35
|
|
43
36
|
#
|
@@ -48,22 +41,6 @@ module MultiTenant
|
|
48
41
|
def belongs_to_tenant?
|
49
42
|
respond_to? :tenant_class
|
50
43
|
end
|
51
|
-
|
52
|
-
#
|
53
|
-
# Instance methods given to tenant-owned models.
|
54
|
-
#
|
55
|
-
module InstanceMethods
|
56
|
-
private
|
57
|
-
|
58
|
-
#
|
59
|
-
# Assign this model to the current tenant (if any)
|
60
|
-
#
|
61
|
-
def assign_to_tenant
|
62
|
-
if self.class.tenant_class.current and send(self.class.tenant_foreign_key).blank?
|
63
|
-
send "#{self.class.tenant_foreign_key}=", self.class.tenant_class.current.send(self.class.tenant_primary_key)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
44
|
end
|
68
45
|
end
|
69
46
|
|
@@ -22,17 +22,8 @@ module MultiTenant
|
|
22
22
|
cattr_accessor :delegate_class
|
23
23
|
self.delegate_class = ref.klass
|
24
24
|
|
25
|
-
self.
|
26
|
-
|
27
|
-
tenant = delegate_class.tenant_class.current
|
28
|
-
next where('1=1') if tenant.nil?
|
29
|
-
|
30
|
-
# Using straight sql so we can JOIN against two columns. Otherwise one must go into "WHERE", and Arel would mistakenly apply it to UPDATEs and DELETEs.
|
31
|
-
quoted_tenant_id = connection.quote tenant.send delegate_class.tenant_primary_key
|
32
|
-
joins("INNER JOIN #{ref.klass.table_name} ON #{ref.klass.table_name}.#{ref.foreign_key}=#{table_name}.#{ref.association_primary_key} AND #{ref.klass.table_name}.#{ref.klass.tenant_foreign_key}=#{quoted_tenant_id}").
|
33
|
-
readonly(false) # using "joins" makes records readonly, which we don't want
|
34
|
-
}
|
35
|
-
end
|
25
|
+
impl = self.delegate_class.tenant_class.multi_tenant_impl
|
26
|
+
default_scope(&impl.belongs_to_tenant_through_default_scope(self, ref))
|
36
27
|
end
|
37
28
|
|
38
29
|
#
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
module Impl
|
3
|
+
#
|
4
|
+
# An implementation where Tenant.current is an array of tenants. All queries will be scoped to ANY of these
|
5
|
+
# tenants.
|
6
|
+
#
|
7
|
+
class MultipleCurrent
|
8
|
+
attr_reader :tenant_class
|
9
|
+
|
10
|
+
def initialize(tenant_class)
|
11
|
+
@tenant_class = tenant_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def acts_as_tenant_class_methods
|
15
|
+
ActsAsTenantClassMethods
|
16
|
+
end
|
17
|
+
|
18
|
+
def belongs_to_tenant_instance_methods
|
19
|
+
BelongsToTenantInstanceMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
def belongs_to_tenant_default_scope(model)
|
23
|
+
-> {
|
24
|
+
current = tenant_class.current.map(&model.tenant_primary_key)
|
25
|
+
current.any? ? model.where({model.tenant_foreign_key => current}) : model.where('1=1')
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def belongs_to_tenant_through_default_scope(model, ref)
|
30
|
+
-> {
|
31
|
+
tenants = model.delegate_class.tenant_class.current
|
32
|
+
next model.where('1=1') if tenants.empty?
|
33
|
+
|
34
|
+
# Using straight sql so we can JOIN against two columns. Otherwise one must go into "WHERE", and Arel would apply it to UPDATEs and DELETEs.
|
35
|
+
quoted_tenant_ids = tenants.map { |t| model.connection.quote t.send model.delegate_class.tenant_primary_key }
|
36
|
+
model.joins("INNER JOIN #{ref.klass.table_name} ON #{ref.klass.table_name}.#{ref.foreign_key}=#{model.table_name}.#{ref.association_primary_key} AND #{ref.klass.table_name}.#{ref.klass.tenant_foreign_key} IN (#{quoted_tenant_ids.join(',')})").
|
37
|
+
distinct.
|
38
|
+
readonly(false) # using "joins" makes records readonly, which we don't want
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def proxies_to_tenant_class_methods(_ref)
|
43
|
+
raise MultiTenant::Impl::NotImplemented, "`proxies_to_tenant` is not currently supported for impl `:multiple`."
|
44
|
+
end
|
45
|
+
|
46
|
+
def matching_globals(records_or_identifiers, globals)
|
47
|
+
records_or_identifiers.reduce([]) { |a, rec_or_id|
|
48
|
+
id = rec_or_id.is_a?(tenant_class) ? rec_or_id.send(tenant_class.tenant_identifier) : rec_or_id
|
49
|
+
a << globals[id] if globals.has_key? id
|
50
|
+
a
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Class methods given to the tenant model.
|
56
|
+
#
|
57
|
+
module ActsAsTenantClassMethods
|
58
|
+
def self.extended(model)
|
59
|
+
model.current = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def current?
|
63
|
+
!current.nil? && current.any?
|
64
|
+
end
|
65
|
+
|
66
|
+
def resolve_tenant(records_or_identifiers)
|
67
|
+
if records_or_identifiers.nil?
|
68
|
+
[]
|
69
|
+
elsif records_or_identifiers.any? { |x| x.is_a? self }
|
70
|
+
records_or_identifiers
|
71
|
+
elsif records_or_identifiers.any?
|
72
|
+
where({tenant_identifier => records_or_identifiers}).to_a
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Instance methods given to tenant-owned models.
|
81
|
+
#
|
82
|
+
module BelongsToTenantInstanceMethods
|
83
|
+
def self.included(model)
|
84
|
+
model.class_eval do
|
85
|
+
validate :ensure_assigned_to_current_tenants
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
#
|
92
|
+
# If the tenant_id is set, make sure it's one of the current ones.
|
93
|
+
#
|
94
|
+
def ensure_assigned_to_current_tenants
|
95
|
+
_tenants = self.class.tenant_class.current.map(&:id)
|
96
|
+
_tenant_id = send self.class.tenant_foreign_key
|
97
|
+
if _tenants.any? and _tenant_id.present? and !_tenants.include?(_tenant_id.to_s)
|
98
|
+
errors.add(self.class.tenant_foreign_key, "is incorrect")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
module Impl
|
3
|
+
#
|
4
|
+
# An implementation where Tenant.current is set to the current tenant, or nil.
|
5
|
+
#
|
6
|
+
class SingleCurrent
|
7
|
+
attr_reader :tenant_class
|
8
|
+
|
9
|
+
def initialize(tenant_class)
|
10
|
+
@tenant_class = tenant_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def acts_as_tenant_class_methods
|
14
|
+
ActsAsTenantClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
def belongs_to_tenant_instance_methods
|
18
|
+
BelongsToTenantInstanceMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
def belongs_to_tenant_default_scope(model)
|
22
|
+
-> {
|
23
|
+
current = model.tenant_class.current
|
24
|
+
current ? model.where({model.tenant_foreign_key => current.send(model.tenant_primary_key)}) : model.where('1=1')
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def belongs_to_tenant_through_default_scope(model, ref)
|
29
|
+
-> {
|
30
|
+
tenant = model.delegate_class.tenant_class.current
|
31
|
+
next model.where('1=1') if tenant.nil?
|
32
|
+
|
33
|
+
# Using straight sql so we can JOIN against two columns. Otherwise one must go into "WHERE", and Arel would apply it to UPDATEs and DELETEs.
|
34
|
+
quoted_tenant_id = model.connection.quote tenant.send model.delegate_class.tenant_primary_key
|
35
|
+
model.joins("INNER JOIN #{ref.klass.table_name} ON #{ref.klass.table_name}.#{ref.foreign_key}=#{model.table_name}.#{ref.association_primary_key} AND #{ref.klass.table_name}.#{ref.klass.tenant_foreign_key}=#{quoted_tenant_id}").
|
36
|
+
readonly(false) # using "joins" makes records readonly, which we don't want
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def proxies_to_tenant_class_methods(ref)
|
41
|
+
case [ref.macro, ref.inverse_of.macro]
|
42
|
+
when [:has_many, :belongs_to], [:has_one, :belongs_to], [:belongs_to, :has_one]
|
43
|
+
ProxiesToTenantSingularInverseAssociation
|
44
|
+
else
|
45
|
+
raise MultiTenant::Impl::NotImplemented, "`proxies_to_tenant` does not currently support `#{ref.macro}` associations with `#{ref.inverse_of.macro} inverses."
|
46
|
+
ProxiesToTenantPluralInverseAssociation
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def matching_globals(record_or_identifier, globals)
|
51
|
+
id = record_or_identifier.is_a?(tenant_class) ? record_or_identifier.send(tenant_class.tenant_identifier) : record_or_identifier
|
52
|
+
globals.has_key?(id) ? [globals[id]] : []
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Class methods given to the tenant model.
|
57
|
+
#
|
58
|
+
module ActsAsTenantClassMethods
|
59
|
+
def current?
|
60
|
+
!current.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
def resolve_tenant(record_or_identifier)
|
64
|
+
if record_or_identifier.is_a? self
|
65
|
+
record_or_identifier
|
66
|
+
elsif record_or_identifier
|
67
|
+
where({tenant_identifier => record_or_identifier}).first
|
68
|
+
else
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Instance methods given to tenant-owned models.
|
76
|
+
#
|
77
|
+
module BelongsToTenantInstanceMethods
|
78
|
+
def self.included(model)
|
79
|
+
model.class_eval do
|
80
|
+
before_validation :assign_to_tenant
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
#
|
87
|
+
# Assign this model to the current tenant (if any)
|
88
|
+
#
|
89
|
+
def assign_to_tenant
|
90
|
+
if self.class.tenant_class.current
|
91
|
+
current_tenant_id = self.class.tenant_class.current.send(self.class.tenant_primary_key)
|
92
|
+
send "#{self.class.tenant_foreign_key}=", current_tenant_id
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Class methods for tenant proxies that have a singular inverse association (i.e. belongs_to or has_one).
|
99
|
+
#
|
100
|
+
module ProxiesToTenantSingularInverseAssociation
|
101
|
+
# Returns the current record of the proxy model
|
102
|
+
def current
|
103
|
+
if (tenant = proxied_tenant_class.current)
|
104
|
+
tenant.send proxied_tenant_inverse_assoc
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Class methods for tenant proxies that have a plural inverse association (i.e. has_many).
|
111
|
+
# NOTE These are just some thoughts on *maybe* how to support this if we ever need it.
|
112
|
+
#
|
113
|
+
module ProxiesToTenantPluralInverseAssociation
|
114
|
+
# Returns the current record of the proxy model
|
115
|
+
def current
|
116
|
+
raise MultiTenant::Impl::NotImplemented, "needs confirmed"
|
117
|
+
if (tenant = proxied_tenant_class.current)
|
118
|
+
tenant.send(proxied_tenant_inverse_assoc).instance_eval(&proxied_tenant_inverse_scope).first
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -47,7 +47,8 @@ module MultiTenant
|
|
47
47
|
|
48
48
|
# Default Proc for the not_found option
|
49
49
|
DEFAULT_NOT_FOUND = ->(x) {
|
50
|
-
|
50
|
+
body = "<h1>Invalid tenant: #{Array(x).map(&:to_s).join ', '}</h1>"
|
51
|
+
[404, {'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s}, [body]]
|
51
52
|
}
|
52
53
|
|
53
54
|
#
|
@@ -73,15 +74,18 @@ module MultiTenant
|
|
73
74
|
# Rack request call
|
74
75
|
def call(env)
|
75
76
|
tenant_class.current = nil
|
77
|
+
impl = tenant_class.multi_tenant_impl
|
78
|
+
|
76
79
|
request = Rack::Request.new env
|
77
80
|
tenant_identifier = identifier.(request)
|
78
81
|
|
79
|
-
if (
|
80
|
-
allowed =
|
82
|
+
if (matching_globals = impl.matching_globals(tenant_identifier, globals)).any?
|
83
|
+
allowed = matching_globals.any? { |allowed_paths|
|
84
|
+
path_matches?(request, allowed_paths)
|
85
|
+
}
|
81
86
|
return allowed ? @app.call(env) : not_found.(tenant_identifier)
|
82
87
|
|
83
|
-
elsif (
|
84
|
-
tenant_class.current = tenant
|
88
|
+
elsif (tenant_class.current = tenant_identifier) and tenant_class.current?
|
85
89
|
return @app.call env
|
86
90
|
|
87
91
|
else
|
@@ -52,17 +52,14 @@ module MultiTenant
|
|
52
52
|
raise "`proxies_to_tenant :#{association_name}`: #{reflection.klass.name} must use `acts_as_tenant`" if !reflection.klass.acts_as_tenant?
|
53
53
|
raise "`proxies_to_tenant :#{association_name}`: the `:#{association_name}` association must use the `:inverse_of` option." if reflection.inverse_of.nil?
|
54
54
|
|
55
|
-
case [reflection.macro, reflection.inverse_of.macro]
|
56
|
-
when [:has_many, :belongs_to], [:has_one, :belongs_to], [:belongs_to, :has_one]
|
57
|
-
self.extend SingularInverseAssociation
|
58
|
-
else
|
59
|
-
raise "`proxies_to_tenant` does not currently support `#{reflection.macro}` associations with `#{reflection.inverse_of.macro} inverses."
|
60
|
-
end
|
61
|
-
|
62
55
|
cattr_accessor :proxied_tenant_class, :proxied_tenant_inverse_assoc, :proxied_tenant_inverse_scope
|
63
56
|
self.proxied_tenant_class = reflection.klass
|
64
57
|
self.proxied_tenant_inverse_assoc = reflection.inverse_of.name
|
65
58
|
self.proxied_tenant_inverse_scope = scope
|
59
|
+
|
60
|
+
impl = self.proxied_tenant_class.multi_tenant_impl
|
61
|
+
self.extend impl.proxies_to_tenant_class_methods(reflection)
|
62
|
+
self.extend ClassMethods
|
66
63
|
end
|
67
64
|
|
68
65
|
#
|
@@ -74,25 +71,12 @@ module MultiTenant
|
|
74
71
|
respond_to? :proxied_tenant_class
|
75
72
|
end
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
#
|
80
|
-
module
|
81
|
-
|
82
|
-
|
83
|
-
if (tenant = proxied_tenant_class.current)
|
84
|
-
tenant.send proxied_tenant_inverse_assoc
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# NOTE just some thoughts on *maybe* how to support this if we ever need it.
|
90
|
-
module PluralInverseAssociation
|
91
|
-
# Returns the "current" record of the proxy model
|
92
|
-
def current
|
93
|
-
if (tenant = proxied_tenant_class.current)
|
94
|
-
tenant.send(proxied_tenant_inverse_assoc).instance_eval(&proxied_tenant_inverse_scope).first
|
95
|
-
end
|
74
|
+
#
|
75
|
+
# Class methods given to proxies.
|
76
|
+
#
|
77
|
+
module ClassMethods
|
78
|
+
def multi_tenant_impl
|
79
|
+
proxied_tenant_class.multi_tenant_impl
|
96
80
|
end
|
97
81
|
end
|
98
82
|
end
|
data/lib/multi_tenant/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_multi_tenant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Hollinger
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-11-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -58,6 +58,8 @@ files:
|
|
58
58
|
- lib/multi_tenant/acts_as_tenant.rb
|
59
59
|
- lib/multi_tenant/belongs_to_tenant.rb
|
60
60
|
- lib/multi_tenant/belongs_to_tenant_through.rb
|
61
|
+
- lib/multi_tenant/impl/multiple_current.rb
|
62
|
+
- lib/multi_tenant/impl/single_current.rb
|
61
63
|
- lib/multi_tenant/middleware.rb
|
62
64
|
- lib/multi_tenant/proxies_to_tenant.rb
|
63
65
|
- lib/multi_tenant/version.rb
|