activerecord-multi-tenant 0.10.0 → 0.11.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 +5 -5
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +3 -3
- data/gemfiles/active_record_5.1.gemfile.lock +8 -8
- data/gemfiles/active_record_5.2.gemfile.lock +9 -9
- data/gemfiles/rails_4.0.gemfile.lock +2 -2
- data/gemfiles/rails_4.1.gemfile.lock +2 -2
- data/gemfiles/rails_4.2.gemfile.lock +2 -2
- data/gemfiles/rails_5.0.gemfile.lock +2 -2
- data/gemfiles/rails_5.1.gemfile.lock +2 -2
- data/gemfiles/rails_5.2.gemfile.lock +2 -2
- data/lib/activerecord-multi-tenant/query_rewriter.rb +78 -12
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +150 -2
- data/spec/schema.rb +28 -0
- metadata +3 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 63718531a69f576dfff1966daa127d22a90501d8d367c59cfa14ed60f0e11d11
|
|
4
|
+
data.tar.gz: d19ff33cd3ba08237015186e87a5c92910292561962e3fde49e128b341067eea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 959702bc65ece2ca74c92e7a2e07f2429c2aba56c5c64517b5625aab2ccd2efe1eb7639a7070c08ad617e63a3fc218a07f0ff4d4383598304108467dc31434cb
|
|
7
|
+
data.tar.gz: 2eefe3dd780f6bd2ad1ead39e23ed185db777ec3cf84257b29c370e4f774cd80650435cdb3a86a9d25561d53dfaab3e5e28ee1147a5dabe6119ef7acca118a17
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## 0.11.0 2019-06-12
|
|
5
|
+
|
|
6
|
+
* Fix queries with joins by including the tenant column when current tenant isn't set
|
|
7
|
+
- A common use case is having a filter on the tenant, but `MultiTenant.with` isn't used like `Project.where(account_id: 1).eager_load(:categories)`. This version fixes the ORM call to include in the `join`: `"project_categories"."account_id" = "projects"."account_id"`
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
## 0.10.0 2019-05-31
|
|
4
11
|
|
|
5
12
|
* Add `MultiTenant.without` to remove already set tenant context in a block [#45](https://github.com/citusdata/activerecord-multi-tenant/pull/45) [Jackson Miller](https://github.com/jaxn)
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
activerecord-multi-tenant (0.
|
|
4
|
+
activerecord-multi-tenant (0.11.0)
|
|
5
5
|
rails (>= 4.0)
|
|
6
6
|
request_store (>= 1.0.5)
|
|
7
7
|
|
|
@@ -49,7 +49,7 @@ GEM
|
|
|
49
49
|
i18n (>= 0.7, < 2)
|
|
50
50
|
minitest (~> 5.1)
|
|
51
51
|
tzinfo (~> 1.1)
|
|
52
|
-
appraisal (2.
|
|
52
|
+
appraisal (2.2.0)
|
|
53
53
|
bundler
|
|
54
54
|
rake
|
|
55
55
|
thor (>= 0.14.0)
|
|
@@ -180,4 +180,4 @@ DEPENDENCIES
|
|
|
180
180
|
thor
|
|
181
181
|
|
|
182
182
|
BUNDLED WITH
|
|
183
|
-
|
|
183
|
+
1.16.4
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
activerecord-multi-tenant (0.
|
|
4
|
+
activerecord-multi-tenant (0.11.0)
|
|
5
5
|
rails (>= 4.0)
|
|
6
6
|
request_store (>= 1.0.5)
|
|
7
7
|
|
|
@@ -57,18 +57,18 @@ GEM
|
|
|
57
57
|
connection_pool (2.2.1)
|
|
58
58
|
diff-lcs (1.3)
|
|
59
59
|
erubi (1.6.0)
|
|
60
|
-
globalid (0.4.
|
|
60
|
+
globalid (0.4.2)
|
|
61
61
|
activesupport (>= 4.2.0)
|
|
62
62
|
i18n (0.8.1)
|
|
63
63
|
loofah (2.0.3)
|
|
64
64
|
nokogiri (>= 1.5.9)
|
|
65
|
-
mail (2.7.
|
|
65
|
+
mail (2.7.1)
|
|
66
66
|
mini_mime (>= 0.1.1)
|
|
67
67
|
method_source (0.8.2)
|
|
68
|
-
mini_mime (1.0.
|
|
68
|
+
mini_mime (1.0.1)
|
|
69
69
|
mini_portile2 (2.1.0)
|
|
70
70
|
minitest (5.10.1)
|
|
71
|
-
nio4r (2.3.
|
|
71
|
+
nio4r (2.3.1)
|
|
72
72
|
nokogiri (1.7.1)
|
|
73
73
|
mini_portile2 (~> 2.1.0)
|
|
74
74
|
pg (0.20.0)
|
|
@@ -138,7 +138,7 @@ GEM
|
|
|
138
138
|
rack-protection (>= 1.5.0)
|
|
139
139
|
redis (~> 3.2, >= 3.2.1)
|
|
140
140
|
slop (3.6.0)
|
|
141
|
-
sprockets (3.7.
|
|
141
|
+
sprockets (3.7.2)
|
|
142
142
|
concurrent-ruby (~> 1.0)
|
|
143
143
|
rack (> 1, < 3)
|
|
144
144
|
sprockets-rails (3.2.1)
|
|
@@ -151,7 +151,7 @@ GEM
|
|
|
151
151
|
thread_safe (~> 0.1)
|
|
152
152
|
websocket-driver (0.6.5)
|
|
153
153
|
websocket-extensions (>= 0.1.0)
|
|
154
|
-
websocket-extensions (0.1.
|
|
154
|
+
websocket-extensions (0.1.4)
|
|
155
155
|
|
|
156
156
|
PLATFORMS
|
|
157
157
|
ruby
|
|
@@ -170,4 +170,4 @@ DEPENDENCIES
|
|
|
170
170
|
thor
|
|
171
171
|
|
|
172
172
|
BUNDLED WITH
|
|
173
|
-
1.
|
|
173
|
+
1.17.2
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
activerecord-multi-tenant (0.
|
|
4
|
+
activerecord-multi-tenant (0.11.0)
|
|
5
5
|
rails (>= 4.0)
|
|
6
6
|
request_store (>= 1.0.5)
|
|
7
7
|
|
|
@@ -62,20 +62,20 @@ GEM
|
|
|
62
62
|
crass (1.0.4)
|
|
63
63
|
diff-lcs (1.3)
|
|
64
64
|
erubi (1.7.1)
|
|
65
|
-
globalid (0.4.
|
|
65
|
+
globalid (0.4.2)
|
|
66
66
|
activesupport (>= 4.2.0)
|
|
67
67
|
i18n (1.0.1)
|
|
68
68
|
concurrent-ruby (~> 1.0)
|
|
69
69
|
loofah (2.2.2)
|
|
70
70
|
crass (~> 1.0.2)
|
|
71
71
|
nokogiri (>= 1.5.9)
|
|
72
|
-
mail (2.7.
|
|
72
|
+
mail (2.7.1)
|
|
73
73
|
mini_mime (>= 0.1.1)
|
|
74
|
-
marcel (0.3.
|
|
74
|
+
marcel (0.3.3)
|
|
75
75
|
mimemagic (~> 0.3.2)
|
|
76
76
|
method_source (0.9.0)
|
|
77
|
-
mimemagic (0.3.
|
|
78
|
-
mini_mime (1.0.
|
|
77
|
+
mimemagic (0.3.3)
|
|
78
|
+
mini_mime (1.0.1)
|
|
79
79
|
mini_portile2 (2.3.0)
|
|
80
80
|
minitest (5.11.3)
|
|
81
81
|
nio4r (2.3.1)
|
|
@@ -158,9 +158,9 @@ GEM
|
|
|
158
158
|
thread_safe (0.3.6)
|
|
159
159
|
tzinfo (1.2.5)
|
|
160
160
|
thread_safe (~> 0.1)
|
|
161
|
-
websocket-driver (0.7.
|
|
161
|
+
websocket-driver (0.7.1)
|
|
162
162
|
websocket-extensions (>= 0.1.0)
|
|
163
|
-
websocket-extensions (0.1.
|
|
163
|
+
websocket-extensions (0.1.4)
|
|
164
164
|
|
|
165
165
|
PLATFORMS
|
|
166
166
|
ruby
|
|
@@ -179,4 +179,4 @@ DEPENDENCIES
|
|
|
179
179
|
thor
|
|
180
180
|
|
|
181
181
|
BUNDLED WITH
|
|
182
|
-
1.
|
|
182
|
+
1.17.2
|
|
@@ -84,6 +84,10 @@ module MultiTenant
|
|
|
84
84
|
@current_context.visited_handled_relation(o.tenant_attribute.relation)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def visit_MultiTenant_TenantJoinEnforcementClause(o, *)
|
|
88
|
+
@current_context.visited_handled_relation(o.tenant_attribute.relation)
|
|
89
|
+
end
|
|
90
|
+
|
|
87
91
|
def visit_Arel_Table(o, _collector = nil)
|
|
88
92
|
@current_context.visited_relation(o) if tenant_relation?(o.table_name)
|
|
89
93
|
end
|
|
@@ -169,15 +173,49 @@ module MultiTenant
|
|
|
169
173
|
end
|
|
170
174
|
end
|
|
171
175
|
|
|
176
|
+
|
|
177
|
+
class TenantJoinEnforcementClause < Arel::Nodes::Node
|
|
178
|
+
attr_reader :tenant_attribute
|
|
179
|
+
attr_reader :table_left
|
|
180
|
+
def initialize(tenant_attribute, table_left)
|
|
181
|
+
@table_left = table_left
|
|
182
|
+
@model_left = MultiTenant.multi_tenant_model_for_table(table_left.table_name)
|
|
183
|
+
@tenant_attribute = tenant_attribute
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def to_s; to_sql; end
|
|
187
|
+
def to_str; to_sql; end
|
|
188
|
+
|
|
189
|
+
def to_sql(*)
|
|
190
|
+
tenant_arel.to_sql
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
def tenant_arel
|
|
196
|
+
@tenant_attribute.eq(@table_left[@model_left.partition_key])
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
|
|
172
201
|
module TenantValueVisitor
|
|
173
202
|
if ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2)
|
|
174
203
|
def visit_MultiTenant_TenantEnforcementClause(o, collector)
|
|
175
204
|
collector << o
|
|
176
205
|
end
|
|
206
|
+
|
|
207
|
+
def visit_MultiTenant_TenantJoinEnforcementClause(o, collector)
|
|
208
|
+
collector << o
|
|
209
|
+
end
|
|
210
|
+
|
|
177
211
|
else
|
|
178
212
|
def visit_MultiTenant_TenantEnforcementClause(o, a = nil)
|
|
179
213
|
o
|
|
180
214
|
end
|
|
215
|
+
|
|
216
|
+
def visit_MultiTenant_TenantJoinEnforcementClause(o, a = nil)
|
|
217
|
+
o
|
|
218
|
+
end
|
|
181
219
|
end
|
|
182
220
|
end
|
|
183
221
|
|
|
@@ -232,25 +270,53 @@ module ActiveRecord
|
|
|
232
270
|
def build_arel(*args)
|
|
233
271
|
arel = build_arel_orig(*args)
|
|
234
272
|
|
|
235
|
-
if
|
|
273
|
+
if !MultiTenant.with_write_only_mode_enabled?
|
|
236
274
|
visitor = MultiTenant::ArelTenantVisitor.new(arel)
|
|
275
|
+
|
|
237
276
|
visitor.contexts.each do |context|
|
|
238
277
|
node = context.arel_node
|
|
278
|
+
|
|
239
279
|
context.unhandled_relations.each do |relation|
|
|
240
280
|
model = MultiTenant.multi_tenant_model_for_table(relation.arel_table.table_name)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
node.wheres
|
|
281
|
+
|
|
282
|
+
if MultiTenant.current_tenant_id
|
|
283
|
+
enforcement_clause = MultiTenant::TenantEnforcementClause.new(relation.arel_table[model.partition_key])
|
|
284
|
+
case node
|
|
285
|
+
when Arel::Nodes::Join #Arel::Nodes::OuterJoin, Arel::Nodes::RightOuterJoin, Arel::Nodes::FullOuterJoin
|
|
286
|
+
node.right.expr = node.right.expr.and(enforcement_clause)
|
|
287
|
+
when Arel::Nodes::SelectCore
|
|
288
|
+
if node.wheres.empty?
|
|
289
|
+
node.wheres = [enforcement_clause]
|
|
290
|
+
else
|
|
291
|
+
node.wheres[0] = enforcement_clause.and(node.wheres[0])
|
|
292
|
+
end
|
|
249
293
|
else
|
|
250
|
-
|
|
294
|
+
raise "UnknownContext"
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
if node.is_a?(Arel::Nodes::SelectCore) || node.is_a?(Arel::Nodes::Join)
|
|
299
|
+
|
|
300
|
+
if node.is_a?Arel::Nodes::Join
|
|
301
|
+
node_list = [node]
|
|
302
|
+
else
|
|
303
|
+
node_list = node.source.right
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
node_list.select{ |n| n.is_a? Arel::Nodes::Join }.each do |node_join|
|
|
307
|
+
if !node_join.right || !node_join.right.expr.right.is_a?(Arel::Attributes::Attribute)
|
|
308
|
+
next
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
relation_right = node_join.right.expr.right.relation
|
|
312
|
+
relation_left = node_join.right.expr.left.relation
|
|
313
|
+
model_left = MultiTenant.multi_tenant_model_for_table(relation_right.table_name)
|
|
314
|
+
model_right = MultiTenant.multi_tenant_model_for_table(relation_left.table_name)
|
|
315
|
+
if model_right && model_left
|
|
316
|
+
join_enforcement_clause = MultiTenant::TenantJoinEnforcementClause.new(relation_left[model_left.partition_key], relation_right)
|
|
317
|
+
node_join.right.expr = node_join.right.expr.and(join_enforcement_clause)
|
|
318
|
+
end
|
|
251
319
|
end
|
|
252
|
-
else
|
|
253
|
-
raise "UnknownContext"
|
|
254
320
|
end
|
|
255
321
|
end
|
|
256
322
|
end
|
|
@@ -345,11 +345,15 @@ describe MultiTenant do
|
|
|
345
345
|
it "applies the team_id conditions in the where clause" do
|
|
346
346
|
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR < 2
|
|
347
347
|
<<-sql
|
|
348
|
-
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" WHERE "tasks"."account_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = $1
|
|
348
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."account_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = $1
|
|
349
|
+
sql
|
|
350
|
+
elsif uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 0
|
|
351
|
+
<<-sql
|
|
352
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."account_id" = 1 AND "tasks"."project_id" = 1
|
|
349
353
|
sql
|
|
350
354
|
else
|
|
351
355
|
<<-sql
|
|
352
|
-
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" WHERE "tasks"."account_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1
|
|
356
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."account_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1
|
|
353
357
|
sql
|
|
354
358
|
end
|
|
355
359
|
account1 = Account.create! name: 'Account 1'
|
|
@@ -362,8 +366,152 @@ describe MultiTenant do
|
|
|
362
366
|
expect(project1.sub_tasks.to_sql).to eq(expected_sql.strip)
|
|
363
367
|
expect(project1.sub_tasks).to include(subtask1)
|
|
364
368
|
end
|
|
369
|
+
|
|
370
|
+
MultiTenant.without do
|
|
371
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR < 2
|
|
372
|
+
<<-sql
|
|
373
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."project_id" = $1
|
|
374
|
+
sql
|
|
375
|
+
else
|
|
376
|
+
<<-sql
|
|
377
|
+
SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."project_id" = 1
|
|
378
|
+
sql
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
project = Project.first
|
|
382
|
+
expect(project.sub_tasks.to_sql).to eq(expected_sql.strip)
|
|
383
|
+
end
|
|
365
384
|
end
|
|
366
385
|
|
|
386
|
+
it "tests joins between distributed and reference table" do
|
|
387
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR < 2
|
|
388
|
+
<<-sql
|
|
389
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."account_id" = 1 AND "project_categories"."project_id" = $1
|
|
390
|
+
sql
|
|
391
|
+
else
|
|
392
|
+
<<-sql
|
|
393
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."account_id" = 1 AND "project_categories"."project_id" = 1
|
|
394
|
+
sql
|
|
395
|
+
end
|
|
396
|
+
account1 = Account.create! name: 'Account 1'
|
|
397
|
+
category1 = Category.create! name: 'Category 1'
|
|
398
|
+
|
|
399
|
+
MultiTenant.with(account1) do
|
|
400
|
+
project1 = Project.create! name: 'Project 1'
|
|
401
|
+
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
|
402
|
+
expect(project1.categories.to_sql).to eq(expected_sql.strip)
|
|
403
|
+
expect(project1.categories).to include(category1)
|
|
404
|
+
expect(project1.project_categories).to include(projectcategory)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
MultiTenant.without do
|
|
408
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR < 2
|
|
409
|
+
<<-sql
|
|
410
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = $1
|
|
411
|
+
sql
|
|
412
|
+
else
|
|
413
|
+
<<-sql
|
|
414
|
+
SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = 1
|
|
415
|
+
sql
|
|
416
|
+
end
|
|
417
|
+
project = Project.first
|
|
418
|
+
expect(project.categories.to_sql).to eq(expected_sql.strip)
|
|
419
|
+
expect(project.categories).to include(category1)
|
|
420
|
+
|
|
421
|
+
expected_sql = <<-sql
|
|
422
|
+
SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = "projects"."account_id" INNER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
|
|
423
|
+
sql
|
|
424
|
+
|
|
425
|
+
expect(Project.where(account_id: 1).joins(:categories).to_sql).to eq(expected_sql.strip)
|
|
426
|
+
project = Project.where(account_id: 1).joins(:categories).first
|
|
427
|
+
expect(project.categories).to include(category1)
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
it "test eager_load" do
|
|
433
|
+
account1 = Account.create! name: 'Account 1'
|
|
434
|
+
category1 = Category.create! name: 'Category 1'
|
|
435
|
+
|
|
436
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR > 1
|
|
437
|
+
<<-sql
|
|
438
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = 1 AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
|
439
|
+
sql
|
|
440
|
+
elsif uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0
|
|
441
|
+
<<-sql
|
|
442
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."account_id" = 1
|
|
443
|
+
sql
|
|
444
|
+
else
|
|
445
|
+
<<-sql
|
|
446
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = "projects"."account_id" AND "project_categories"."account_id" = 1 AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
|
447
|
+
sql
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
MultiTenant.with(account1) do
|
|
451
|
+
project1 = Project.create! name: 'Project 1'
|
|
452
|
+
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
|
453
|
+
|
|
454
|
+
expect(Project.eager_load(:categories).to_sql).to eq(expected_sql.strip)
|
|
455
|
+
|
|
456
|
+
project = Project.eager_load(:categories).first
|
|
457
|
+
expect(project.categories).to include(category1)
|
|
458
|
+
expect(project.project_categories).to include(projectcategory)
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
MultiTenant.without do
|
|
462
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0
|
|
463
|
+
<<-sql
|
|
464
|
+
SELECT "projects".* FROM "projects" WHERE "projects"."account_id" = 1
|
|
465
|
+
sql
|
|
466
|
+
else
|
|
467
|
+
<<-sql
|
|
468
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = "projects"."account_id" LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
|
|
469
|
+
sql
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
expect(Project.where(account_id: 1).eager_load(:categories).to_sql).to eq(expected_sql.strip)
|
|
473
|
+
|
|
474
|
+
project = Project.where(account_id: 1).eager_load(:categories).first
|
|
475
|
+
expect(project.categories).to include(category1)
|
|
476
|
+
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
it "test raw SQL joins" do
|
|
481
|
+
account1 = Account.create! name: 'Account 1'
|
|
482
|
+
category1 = Category.create! name: 'Category 1'
|
|
483
|
+
|
|
484
|
+
MultiTenant.with(account1) do
|
|
485
|
+
expected_sql = if uses_prepared_statements? && ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR > 1
|
|
486
|
+
<<-sql
|
|
487
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = 1 LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
|
488
|
+
sql
|
|
489
|
+
else
|
|
490
|
+
<<-sql
|
|
491
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = "tasks"."account_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "projects"."account_id" = 1 AND "tasks"."account_id" = 1
|
|
492
|
+
sql
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
project1 = Project.create! name: 'Project 1'
|
|
496
|
+
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
|
497
|
+
|
|
498
|
+
project1.tasks.create! name: 'baz'
|
|
499
|
+
|
|
500
|
+
expect(Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(expected_sql.strip)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
MultiTenant.without do
|
|
504
|
+
expected_sql = <<-sql
|
|
505
|
+
SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = "tasks"."account_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
|
|
506
|
+
sql
|
|
507
|
+
expect(Task.where(account_id: 1).joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(expected_sql.strip)
|
|
508
|
+
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
|
|
367
515
|
# Versions earlier than 4.2 pass an arel object to find_by_sql(...) and it would make
|
|
368
516
|
# this test unnecesssarily complicated to support that
|
|
369
517
|
if ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2)
|
data/spec/schema.rb
CHANGED
|
@@ -78,6 +78,17 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
|
78
78
|
t.column :description, :string
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
create_table :categories, force: true do |t|
|
|
82
|
+
t.column :name, :string
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
create_table :project_categories, force: true, partition_key: :account_id do |t|
|
|
86
|
+
t.column :name, :string
|
|
87
|
+
t.column :account_id, :integer
|
|
88
|
+
t.column :project_id, :integer
|
|
89
|
+
t.column :category_id, :integer
|
|
90
|
+
end
|
|
91
|
+
|
|
81
92
|
create_distributed_table :accounts, :id
|
|
82
93
|
create_distributed_table :projects, :account_id
|
|
83
94
|
create_distributed_table :managers, :account_id
|
|
@@ -89,6 +100,8 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
|
89
100
|
create_distributed_table :partition_key_not_model_tasks, :non_model_id
|
|
90
101
|
create_distributed_table :subclass_tasks, :non_model_id
|
|
91
102
|
create_distributed_table :uuid_records, :organization_id
|
|
103
|
+
create_distributed_table :project_categories, :account_id
|
|
104
|
+
create_reference_table :categories
|
|
92
105
|
end
|
|
93
106
|
|
|
94
107
|
class Account < ActiveRecord::Base
|
|
@@ -103,6 +116,9 @@ class Project < ActiveRecord::Base
|
|
|
103
116
|
has_many :tasks
|
|
104
117
|
has_many :sub_tasks, through: :tasks
|
|
105
118
|
|
|
119
|
+
has_many :project_categories
|
|
120
|
+
has_many :categories, through: :project_categories
|
|
121
|
+
|
|
106
122
|
validates_uniqueness_of :name, scope: [:account]
|
|
107
123
|
end
|
|
108
124
|
|
|
@@ -171,3 +187,15 @@ end
|
|
|
171
187
|
class UuidRecord < ActiveRecord::Base
|
|
172
188
|
multi_tenant :organization
|
|
173
189
|
end
|
|
190
|
+
|
|
191
|
+
class Category < ActiveRecord::Base
|
|
192
|
+
has_many :project_categories
|
|
193
|
+
has_many :projects, through: :project_categories
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class ProjectCategory < ActiveRecord::Base
|
|
197
|
+
multi_tenant :account
|
|
198
|
+
belongs_to :project
|
|
199
|
+
belongs_to :category
|
|
200
|
+
belongs_to :account
|
|
201
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-multi-tenant
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Citus Data
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-06-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: request_store
|
|
@@ -226,8 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
226
226
|
- !ruby/object:Gem::Version
|
|
227
227
|
version: '0'
|
|
228
228
|
requirements: []
|
|
229
|
-
|
|
230
|
-
rubygems_version: 2.5.1
|
|
229
|
+
rubygems_version: 3.0.3
|
|
231
230
|
signing_key:
|
|
232
231
|
specification_version: 4
|
|
233
232
|
summary: ActiveRecord/Rails integration for multi-tenant databases, in particular
|