activerecord-multi-tenant 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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