acts_as_tenant 2.2.1 → 2.2.2
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c0fbfe96e632768ed0f7dfae94d955c791c6ce1b724ef84e0ce6c2ca0fe0d88
|
|
4
|
+
data.tar.gz: 4294ce8d41e28d09c730f28ef22d2a28aa59ac1a4d4aba8e0b12ea3dea410719
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c41db14ddabafb233b66c0ac39d2fddd4fb03742ed874ca8a8027e3b46927f70e7eef1d2e65b157073e02b79de95f492859449cc6cd45c828cd4590463191b5
|
|
7
|
+
data.tar.gz: 3e9f92da5f007404e79e0381a8a065f04515b5d96f451ce1d91636a8d2c37bac71a0944d2404d53fde07f01e98b26f194317e5be9fbaf66aac9fc155c4ef4ae6
|
data/README.md
CHANGED
|
@@ -128,6 +128,8 @@ end
|
|
|
128
128
|
This approach is useful when running background processes for a specified tenant. For example, by putting this in your worker's run method,
|
|
129
129
|
any code in this block will be scoped to the current tenant. All methods that set the current tenant are thread safe.
|
|
130
130
|
|
|
131
|
+
The tenant is stored in `ActiveSupport::CurrentAttributes`, which is isolated per thread. On servers that run requests in fibers, such as Falcon, set `config.active_support.isolation_level = :fiber` so each request keeps its own tenant.
|
|
132
|
+
|
|
131
133
|
**Note:** If the current tenant is not set by one of these methods, Acts_as_tenant will be unable to apply the proper scope to your models. So make sure you use one of the two methods to tell acts_as_tenant about the current tenant.
|
|
132
134
|
|
|
133
135
|
### Disabling tenant checking for a block
|
|
@@ -197,11 +199,12 @@ Project.tasks.all # => all tasks with account_id => 3
|
|
|
197
199
|
|
|
198
200
|
Acts_as_tenant uses Rails' `default_scope` method to scope models. Rails 3.1 changed the way `default_scope` works in a good way. A user defined `default_scope` should integrate seamlessly with the one added by `acts_as_tenant`.
|
|
199
201
|
|
|
200
|
-
Because the scoping comes from `default_scope`, queries built from the model are scoped when a tenant is set. This includes `update_all`, `delete_all` and `destroy_all
|
|
202
|
+
Because the scoping comes from `default_scope`, queries built from the model are scoped when a tenant is set. This includes `update_all`, `delete_all` and `destroy_all`. `insert_all` and `upsert_all` set the current tenant on the rows they write, but they skip validations. `upsert_all` also matches existing rows by primary key across all tenants, so passing another tenant's id overwrites that record and moves it to the current tenant. Never pass ids from user input to `upsert_all`. These are **not** scoped or checked:
|
|
201
203
|
|
|
202
204
|
* Any query when no tenant is set, unless `require_tenant` is enabled
|
|
203
205
|
* Queries that remove the default scope, such as `unscoped`
|
|
204
206
|
* Raw SQL, such as `find_by_sql`, `connection.execute` and `exec_query`
|
|
207
|
+
* `update_counters`, `increment_counter` and `decrement_counter`, which Rails runs without default scopes
|
|
205
208
|
* `update_column` and `update_columns`, which skip the check that prevents changing a record's tenant
|
|
206
209
|
|
|
207
210
|
New records are assigned the current tenant unless they already have one. A record assigned to a different tenant fails validation, so to create records for another tenant, wrap them in `ActsAsTenant.with_tenant(other_tenant) { ... }`.
|
|
@@ -12,8 +12,16 @@ module ActsAsTenant
|
|
|
12
12
|
|
|
13
13
|
def find_tenant_by_subdomain_or_domain
|
|
14
14
|
subdomain = request.subdomains.public_send(subdomain_lookup)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
domain = request.domain
|
|
16
|
+
|
|
17
|
+
# request.domain is nil for IP addresses, so there is no tenant to look up
|
|
18
|
+
query = if subdomain.present?
|
|
19
|
+
{tenant_primary_column => subdomain.downcase}
|
|
20
|
+
elsif domain.present?
|
|
21
|
+
{tenant_second_column => domain.downcase}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ActsAsTenant.current_tenant = query && tenant_class.find_by(query)
|
|
17
25
|
end
|
|
18
26
|
end
|
|
19
27
|
end
|
|
@@ -16,6 +16,7 @@ module ActsAsTenant
|
|
|
16
16
|
tenant_reflection = reflect_on_association(tenant)
|
|
17
17
|
fkey = tenant_reflection.foreign_key.to_sym
|
|
18
18
|
polymorphic_type = tenant_reflection.foreign_type&.to_sym
|
|
19
|
+
tenant_attributes = [fkey, polymorphic_type].compact
|
|
19
20
|
|
|
20
21
|
# Polymorphic tenants are stored with polymorphic_name, like Rails does. Records saved before
|
|
21
22
|
# that used the class name, which differs for STI tenants, so match both when scoping.
|
|
@@ -65,6 +66,13 @@ module ActsAsTenant
|
|
|
65
66
|
end
|
|
66
67
|
}, on: :create
|
|
67
68
|
|
|
69
|
+
# Returns the model class named by a polymorphic type column, or nil when the name
|
|
70
|
+
# is not a model, such as "Kernel" or "String" submitted in form params
|
|
71
|
+
polymorphic_class = lambda do |name|
|
|
72
|
+
klass = name&.safe_constantize
|
|
73
|
+
klass if klass.is_a?(Class) && klass < ActiveRecord::Base
|
|
74
|
+
end
|
|
75
|
+
|
|
68
76
|
# Returns [tenant id, tenant class] for a record, or nil if it has no tenant.
|
|
69
77
|
# Classes are normalized with polymorphic_name so STI tenants compare equal.
|
|
70
78
|
tenant_identity = lambda do |model|
|
|
@@ -74,7 +82,7 @@ module ActsAsTenant
|
|
|
74
82
|
if id
|
|
75
83
|
type = if reflection.polymorphic?
|
|
76
84
|
stored_type = model.read_attribute(reflection.foreign_type)
|
|
77
|
-
stored_type&.
|
|
85
|
+
polymorphic_class.call(stored_type)&.polymorphic_name || stored_type
|
|
78
86
|
else
|
|
79
87
|
reflection.klass.polymorphic_name
|
|
80
88
|
end
|
|
@@ -104,7 +112,6 @@ module ActsAsTenant
|
|
|
104
112
|
current_id, current_type = current_tenant_identity.call
|
|
105
113
|
next unless current_id
|
|
106
114
|
|
|
107
|
-
tenant_attributes = [fkey, polymorphic_type].compact
|
|
108
115
|
next unless record.new_record? || tenant_attributes.any? { |attr| record.will_save_change_to_attribute?(attr) }
|
|
109
116
|
|
|
110
117
|
record_tenant = tenant_identity.call(record)
|
|
@@ -143,8 +150,7 @@ module ActsAsTenant
|
|
|
143
150
|
next unless record.validate_tenant_association?(association)
|
|
144
151
|
|
|
145
152
|
klass = if association.polymorphic?
|
|
146
|
-
|
|
147
|
-
type if type.is_a?(Class) && type < ActiveRecord::Base
|
|
153
|
+
polymorphic_class.call(record.read_attribute(association.foreign_type))
|
|
148
154
|
else
|
|
149
155
|
# Raises for composite keys whose column counts don't match, like loading the association would
|
|
150
156
|
association.check_validity! if attrs.size > 1
|
|
@@ -166,12 +172,15 @@ module ActsAsTenant
|
|
|
166
172
|
end
|
|
167
173
|
end
|
|
168
174
|
|
|
169
|
-
# Tenant writers raise if the tenant changes on a persisted record
|
|
175
|
+
# Tenant writers raise if the tenant changes on a persisted record. The change is
|
|
176
|
+
# undone before raising so it isn't saved later, for example by update_attribute.
|
|
170
177
|
to_include = Module.new {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
tenant_attributes.each do |attr|
|
|
179
|
+
define_method :"#{attr}=" do |value|
|
|
180
|
+
write_attribute(attr, value)
|
|
181
|
+
raise_if_tenant_changed
|
|
182
|
+
value
|
|
183
|
+
end
|
|
175
184
|
end
|
|
176
185
|
|
|
177
186
|
define_method :"#{tenant}=" do |model|
|
|
@@ -181,12 +190,15 @@ module ActsAsTenant
|
|
|
181
190
|
end
|
|
182
191
|
|
|
183
192
|
define_method :raise_if_tenant_changed do
|
|
184
|
-
|
|
193
|
+
if tenant_modified? && !ActsAsTenant.mutable_tenant?
|
|
194
|
+
restore_attributes(tenant_attributes)
|
|
195
|
+
raise ActsAsTenant::Errors::TenantIsImmutable
|
|
196
|
+
end
|
|
185
197
|
end
|
|
186
198
|
private :raise_if_tenant_changed
|
|
187
199
|
|
|
188
200
|
define_method :tenant_modified? do
|
|
189
|
-
|
|
201
|
+
persisted? && attribute_in_database(fkey).present? && tenant_attributes.any? { |attr| will_save_change_to_attribute?(attr) }
|
|
190
202
|
end
|
|
191
203
|
}
|
|
192
204
|
include to_include
|