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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 366debe159c5693b73fbca961fae7889febd5e00
4
- data.tar.gz: 3670f0bd43e9074463c44e397ccbc1f25246bd4d
2
+ SHA256:
3
+ metadata.gz: 63718531a69f576dfff1966daa127d22a90501d8d367c59cfa14ed60f0e11d11
4
+ data.tar.gz: d19ff33cd3ba08237015186e87a5c92910292561962e3fde49e128b341067eea
5
5
  SHA512:
6
- metadata.gz: 9274691cbc15fcba204a5b9fcd01e550aefddcf3b0b37ec1aa329a66637dffb8e22d08b6fd9e55f63d42b2582146d544552921f8558722b047f31fb8f66fb9f1
7
- data.tar.gz: 8e162537b956585c28734850d8f189433d8337854df10c9a1bf16637000c9c448eed6739763c8e20a488edf7d43743ea84254a137f0d77c6b7c84318a10d3c4b
6
+ metadata.gz: 959702bc65ece2ca74c92e7a2e07f2429c2aba56c5c64517b5625aab2ccd2efe1eb7639a7070c08ad617e63a3fc218a07f0ff4d4383598304108467dc31434cb
7
+ data.tar.gz: 2eefe3dd780f6bd2ad1ead39e23ed185db777ec3cf84257b29c370e4f774cd80650435cdb3a86a9d25561d53dfaab3e5e28ee1147a5dabe6119ef7acca118a17
@@ -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)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-multi-tenant (0.10.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.1.0)
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
- 2.0.1
183
+ 1.16.4
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.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.1)
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.0)
65
+ mail (2.7.1)
66
66
  mini_mime (>= 0.1.1)
67
67
  method_source (0.8.2)
68
- mini_mime (1.0.0)
68
+ mini_mime (1.0.1)
69
69
  mini_portile2 (2.1.0)
70
70
  minitest (5.10.1)
71
- nio4r (2.3.0)
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.1)
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.3)
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.16.1
173
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.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.1)
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.0)
72
+ mail (2.7.1)
73
73
  mini_mime (>= 0.1.1)
74
- marcel (0.3.2)
74
+ marcel (0.3.3)
75
75
  mimemagic (~> 0.3.2)
76
76
  method_source (0.9.0)
77
- mimemagic (0.3.2)
78
- mini_mime (1.0.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.0)
161
+ websocket-driver (0.7.1)
162
162
  websocket-extensions (>= 0.1.0)
163
- websocket-extensions (0.1.3)
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.16.1
182
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -138,4 +138,4 @@ DEPENDENCIES
138
138
  thor
139
139
 
140
140
  BUNDLED WITH
141
- 1.16.1
141
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -143,4 +143,4 @@ DEPENDENCIES
143
143
  thor
144
144
 
145
145
  BUNDLED WITH
146
- 1.16.1
146
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -166,4 +166,4 @@ DEPENDENCIES
166
166
  thor
167
167
 
168
168
  BUNDLED WITH
169
- 1.16.1
169
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -172,4 +172,4 @@ DEPENDENCIES
172
172
  thor
173
173
 
174
174
  BUNDLED WITH
175
- 1.16.1
175
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -172,4 +172,4 @@ DEPENDENCIES
172
172
  thor
173
173
 
174
174
  BUNDLED WITH
175
- 1.16.1
175
+ 1.17.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.9.0)
4
+ activerecord-multi-tenant (0.11.0)
5
5
  rails (>= 4.0)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -179,4 +179,4 @@ DEPENDENCIES
179
179
  thor
180
180
 
181
181
  BUNDLED WITH
182
- 1.16.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 MultiTenant.current_tenant_id && !MultiTenant.with_write_only_mode_enabled?
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
- enforcement_clause = MultiTenant::TenantEnforcementClause.new(relation.arel_table[model.partition_key])
242
-
243
- case node
244
- when Arel::Nodes::Join #Arel::Nodes::OuterJoin, Arel::Nodes::RightOuterJoin, Arel::Nodes::FullOuterJoin
245
- node.right.expr = node.right.expr.and(enforcement_clause)
246
- when Arel::Nodes::SelectCore
247
- if node.wheres.empty?
248
- node.wheres = [enforcement_clause]
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
- node.wheres[0] = enforcement_clause.and(node.wheres[0])
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
@@ -1,3 +1,3 @@
1
1
  module MultiTenant
2
- VERSION = '0.10.0'
2
+ VERSION = '0.11.0'
3
3
  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)
@@ -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.10.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-05-31 00:00:00.000000000 Z
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
- rubyforge_project:
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