activerecord-multi-tenant 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +6 -14
  4. data/Appraisals +24 -24
  5. data/CHANGELOG.md +22 -0
  6. data/Gemfile.lock +79 -63
  7. data/README.md +1 -1
  8. data/activerecord-multi-tenant.gemspec +1 -1
  9. data/gemfiles/.bundle/config +2 -0
  10. data/gemfiles/active_record_5.2.gemfile +10 -2
  11. data/gemfiles/active_record_5.2.gemfile.lock +102 -96
  12. data/gemfiles/{active_record_5.1.gemfile → active_record_6.0.gemfile} +2 -2
  13. data/gemfiles/active_record_6.0.gemfile.lock +198 -0
  14. data/gemfiles/rails_5.2.gemfile +10 -2
  15. data/gemfiles/rails_5.2.gemfile.lock +109 -103
  16. data/gemfiles/{rails_5.0.gemfile → rails_6.0.gemfile} +2 -2
  17. data/gemfiles/rails_6.0.gemfile.lock +198 -0
  18. data/lib/activerecord-multi-tenant.rb +1 -0
  19. data/lib/activerecord-multi-tenant/controller_extensions.rb +2 -6
  20. data/lib/activerecord-multi-tenant/copy_from_client.rb +2 -2
  21. data/lib/activerecord-multi-tenant/migrations.rb +2 -2
  22. data/lib/activerecord-multi-tenant/model_extensions.rb +8 -15
  23. data/lib/activerecord-multi-tenant/multi_tenant.rb +9 -0
  24. data/lib/activerecord-multi-tenant/persistence_extension.rb +13 -0
  25. data/lib/activerecord-multi-tenant/query_rewriter.rb +59 -105
  26. data/lib/activerecord-multi-tenant/sidekiq.rb +2 -1
  27. data/lib/activerecord-multi-tenant/version.rb +1 -1
  28. data/spec/activerecord-multi-tenant/controller_extensions_spec.rb +19 -24
  29. data/spec/activerecord-multi-tenant/model_extensions_spec.rb +54 -104
  30. data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +40 -0
  31. data/spec/activerecord-multi-tenant/record_modifications_spec.rb +60 -3
  32. data/spec/activerecord-multi-tenant/schema_dumper_tester.rb +0 -0
  33. data/spec/activerecord-multi-tenant/sidekiq_spec.rb +4 -4
  34. data/spec/schema.rb +1 -4
  35. data/spec/spec_helper.rb +1 -6
  36. metadata +15 -20
  37. data/gemfiles/active_record_5.1.gemfile.lock +0 -173
  38. data/gemfiles/rails_4.0.gemfile +0 -8
  39. data/gemfiles/rails_4.0.gemfile.lock +0 -141
  40. data/gemfiles/rails_4.1.gemfile +0 -8
  41. data/gemfiles/rails_4.1.gemfile.lock +0 -146
  42. data/gemfiles/rails_4.2.gemfile +0 -8
  43. data/gemfiles/rails_4.2.gemfile.lock +0 -169
  44. data/gemfiles/rails_5.0.gemfile.lock +0 -175
  45. data/gemfiles/rails_5.1.gemfile +0 -8
  46. data/gemfiles/rails_5.1.gemfile.lock +0 -175
@@ -3,6 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
- gem "rails", "5.0.1"
6
+ gem "rails", "~> 6.0.3"
7
7
 
8
- gemspec :path => "../"
8
+ gemspec path: "../"
@@ -0,0 +1,198 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ activerecord-multi-tenant (1.1.0)
5
+ rails (>= 4.2)
6
+ request_store (>= 1.0.5)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.0.3.1)
12
+ actionpack (= 6.0.3.1)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (6.0.3.1)
16
+ actionpack (= 6.0.3.1)
17
+ activejob (= 6.0.3.1)
18
+ activerecord (= 6.0.3.1)
19
+ activestorage (= 6.0.3.1)
20
+ activesupport (= 6.0.3.1)
21
+ mail (>= 2.7.1)
22
+ actionmailer (6.0.3.1)
23
+ actionpack (= 6.0.3.1)
24
+ actionview (= 6.0.3.1)
25
+ activejob (= 6.0.3.1)
26
+ mail (~> 2.5, >= 2.5.4)
27
+ rails-dom-testing (~> 2.0)
28
+ actionpack (6.0.3.1)
29
+ actionview (= 6.0.3.1)
30
+ activesupport (= 6.0.3.1)
31
+ rack (~> 2.0, >= 2.0.8)
32
+ rack-test (>= 0.6.3)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
+ actiontext (6.0.3.1)
36
+ actionpack (= 6.0.3.1)
37
+ activerecord (= 6.0.3.1)
38
+ activestorage (= 6.0.3.1)
39
+ activesupport (= 6.0.3.1)
40
+ nokogiri (>= 1.8.5)
41
+ actionview (6.0.3.1)
42
+ activesupport (= 6.0.3.1)
43
+ builder (~> 3.1)
44
+ erubi (~> 1.4)
45
+ rails-dom-testing (~> 2.0)
46
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
+ activejob (6.0.3.1)
48
+ activesupport (= 6.0.3.1)
49
+ globalid (>= 0.3.6)
50
+ activemodel (6.0.3.1)
51
+ activesupport (= 6.0.3.1)
52
+ activerecord (6.0.3.1)
53
+ activemodel (= 6.0.3.1)
54
+ activesupport (= 6.0.3.1)
55
+ activestorage (6.0.3.1)
56
+ actionpack (= 6.0.3.1)
57
+ activejob (= 6.0.3.1)
58
+ activerecord (= 6.0.3.1)
59
+ marcel (~> 0.3.1)
60
+ activesupport (6.0.3.1)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ zeitwerk (~> 2.2, >= 2.2.2)
66
+ appraisal (2.2.0)
67
+ bundler
68
+ rake
69
+ thor (>= 0.14.0)
70
+ builder (3.2.4)
71
+ byebug (11.1.3)
72
+ coderay (1.1.2)
73
+ concurrent-ruby (1.1.6)
74
+ connection_pool (2.2.2)
75
+ crass (1.0.6)
76
+ diff-lcs (1.3)
77
+ erubi (1.9.0)
78
+ globalid (0.4.2)
79
+ activesupport (>= 4.2.0)
80
+ i18n (1.8.2)
81
+ concurrent-ruby (~> 1.0)
82
+ loofah (2.5.0)
83
+ crass (~> 1.0.2)
84
+ nokogiri (>= 1.5.9)
85
+ mail (2.7.1)
86
+ mini_mime (>= 0.1.1)
87
+ marcel (0.3.3)
88
+ mimemagic (~> 0.3.2)
89
+ method_source (1.0.0)
90
+ mimemagic (0.3.5)
91
+ mini_mime (1.0.2)
92
+ mini_portile2 (2.4.0)
93
+ minitest (5.14.1)
94
+ nio4r (2.5.2)
95
+ nokogiri (1.10.9)
96
+ mini_portile2 (~> 2.4.0)
97
+ pg (1.2.3)
98
+ pry (0.13.1)
99
+ coderay (~> 1.1)
100
+ method_source (~> 1.0)
101
+ pry-byebug (3.9.0)
102
+ byebug (~> 11.0)
103
+ pry (~> 0.13.0)
104
+ rack (2.2.2)
105
+ rack-protection (2.0.8.1)
106
+ rack
107
+ rack-test (1.1.0)
108
+ rack (>= 1.0, < 3)
109
+ rails (6.0.3.1)
110
+ actioncable (= 6.0.3.1)
111
+ actionmailbox (= 6.0.3.1)
112
+ actionmailer (= 6.0.3.1)
113
+ actionpack (= 6.0.3.1)
114
+ actiontext (= 6.0.3.1)
115
+ actionview (= 6.0.3.1)
116
+ activejob (= 6.0.3.1)
117
+ activemodel (= 6.0.3.1)
118
+ activerecord (= 6.0.3.1)
119
+ activestorage (= 6.0.3.1)
120
+ activesupport (= 6.0.3.1)
121
+ bundler (>= 1.3.0)
122
+ railties (= 6.0.3.1)
123
+ sprockets-rails (>= 2.0.0)
124
+ rails-dom-testing (2.0.3)
125
+ activesupport (>= 4.2.0)
126
+ nokogiri (>= 1.6)
127
+ rails-html-sanitizer (1.3.0)
128
+ loofah (~> 2.3)
129
+ railties (6.0.3.1)
130
+ actionpack (= 6.0.3.1)
131
+ activesupport (= 6.0.3.1)
132
+ method_source
133
+ rake (>= 0.8.7)
134
+ thor (>= 0.20.3, < 2.0)
135
+ rake (13.0.1)
136
+ redis (4.1.4)
137
+ request_store (1.5.0)
138
+ rack (>= 1.4)
139
+ rspec (3.9.0)
140
+ rspec-core (~> 3.9.0)
141
+ rspec-expectations (~> 3.9.0)
142
+ rspec-mocks (~> 3.9.0)
143
+ rspec-core (3.9.2)
144
+ rspec-support (~> 3.9.3)
145
+ rspec-expectations (3.9.2)
146
+ diff-lcs (>= 1.2.0, < 2.0)
147
+ rspec-support (~> 3.9.0)
148
+ rspec-mocks (3.9.1)
149
+ diff-lcs (>= 1.2.0, < 2.0)
150
+ rspec-support (~> 3.9.0)
151
+ rspec-rails (4.0.1)
152
+ actionpack (>= 4.2)
153
+ activesupport (>= 4.2)
154
+ railties (>= 4.2)
155
+ rspec-core (~> 3.9)
156
+ rspec-expectations (~> 3.9)
157
+ rspec-mocks (~> 3.9)
158
+ rspec-support (~> 3.9)
159
+ rspec-support (3.9.3)
160
+ sidekiq (6.0.7)
161
+ connection_pool (>= 2.2.2)
162
+ rack (~> 2.0)
163
+ rack-protection (>= 2.0.0)
164
+ redis (>= 4.1.0)
165
+ sprockets (4.0.0)
166
+ concurrent-ruby (~> 1.0)
167
+ rack (> 1, < 3)
168
+ sprockets-rails (3.2.1)
169
+ actionpack (>= 4.0)
170
+ activesupport (>= 4.0)
171
+ sprockets (>= 3.0.0)
172
+ thor (1.0.1)
173
+ thread_safe (0.3.6)
174
+ tzinfo (1.2.7)
175
+ thread_safe (~> 0.1)
176
+ websocket-driver (0.7.2)
177
+ websocket-extensions (>= 0.1.0)
178
+ websocket-extensions (0.1.4)
179
+ zeitwerk (2.3.0)
180
+
181
+ PLATFORMS
182
+ ruby
183
+
184
+ DEPENDENCIES
185
+ activerecord-multi-tenant!
186
+ appraisal
187
+ pg
188
+ pry
189
+ pry-byebug
190
+ rails (~> 6.0.3)
191
+ rake
192
+ rspec (>= 3.0)
193
+ rspec-rails
194
+ sidekiq
195
+ thor
196
+
197
+ BUNDLED WITH
198
+ 2.1.4
@@ -10,3 +10,4 @@ require_relative 'activerecord-multi-tenant/query_rewriter'
10
10
  require_relative 'activerecord-multi-tenant/query_monitor'
11
11
  require_relative 'activerecord-multi-tenant/version'
12
12
  require_relative 'activerecord-multi-tenant/with_lock'
13
+ require_relative 'activerecord-multi-tenant/persistence_extension'
@@ -20,10 +20,6 @@ module MultiTenant
20
20
  end
21
21
  end
22
22
 
23
- if defined?(ActionController::Base)
24
- ActionController::Base.extend MultiTenant::ControllerExtensions
25
- end
26
-
27
- if defined?(ActionController::API)
28
- ActionController::API.extend MultiTenant::ControllerExtensions
23
+ ActiveSupport.on_load(:action_controller) do |base|
24
+ base.extend MultiTenant::ControllerExtensions
29
25
  end
@@ -28,6 +28,6 @@ module MultiTenant
28
28
  end
29
29
  end
30
30
 
31
- if defined?(ActiveRecord::Base)
32
- ActiveRecord::Base.extend(MultiTenant::CopyFromClient)
31
+ ActiveSupport.on_load(:active_record) do |base|
32
+ base.extend(MultiTenant::CopyFromClient)
33
33
  end
@@ -44,10 +44,10 @@ module ActiveRecord
44
44
  module SchemaStatements
45
45
  alias :orig_create_table :create_table
46
46
  def create_table(table_name, options = {}, &block)
47
- ret = orig_create_table(table_name, options.except(:partition_key), &block)
47
+ ret = orig_create_table(table_name, **options.except(:partition_key), &block)
48
48
  if options[:partition_key] && options[:partition_key].to_s != 'id'
49
49
  execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{table_name}_pkey"
50
- execute "ALTER TABLE #{table_name} ADD PRIMARY KEY(id, \"#{options[:partition_key]}\")"
50
+ execute "ALTER TABLE #{table_name} ADD PRIMARY KEY(\"#{options[:partition_key]}\", id)"
51
51
  end
52
52
  ret
53
53
  end
@@ -24,11 +24,6 @@ module MultiTenant
24
24
  def primary_key
25
25
  return @primary_key if @primary_key
26
26
 
27
- if ::ActiveRecord::VERSION::MAJOR < 5
28
- @primary_key = super || DEFAULT_ID_FIELD
29
- return @primary_key if connection.schema_cache.columns_hash(table_name).include? @primary_key
30
- end
31
-
32
27
  primary_object_keys = Array.wrap(connection.schema_cache.primary_keys(table_name)) - [partition_key]
33
28
 
34
29
  if primary_object_keys.size == 1
@@ -54,7 +49,7 @@ module MultiTenant
54
49
 
55
50
  # Create an implicit belongs_to association only if tenant class exists
56
51
  if MultiTenant.tenant_klass_defined?(tenant_name)
57
- belongs_to tenant_name, options.slice(:class_name, :inverse_of).merge(foreign_key: options[:partition_key])
52
+ belongs_to tenant_name, **options.slice(:class_name, :inverse_of).merge(foreign_key: options[:partition_key])
58
53
  end
59
54
 
60
55
  # New instances should have the tenant set
@@ -126,16 +121,14 @@ module MultiTenant
126
121
  end
127
122
  end
128
123
 
129
- if defined?(ActiveRecord::Base)
130
- ActiveRecord::Base.extend(MultiTenant::ModelExtensionsClassMethods)
124
+ ActiveSupport.on_load(:active_record) do |base|
125
+ base.extend MultiTenant::ModelExtensionsClassMethods
131
126
  end
132
127
 
133
- if ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2)
134
- class ActiveRecord::Associations::Association
135
- alias skip_statement_cache_orig skip_statement_cache?
136
- def skip_statement_cache?(*scope)
137
- return true if klass.respond_to?(:scoped_by_tenant?) && klass.scoped_by_tenant?
138
- skip_statement_cache_orig(*scope)
139
- end
128
+ class ActiveRecord::Associations::Association
129
+ alias skip_statement_cache_orig skip_statement_cache?
130
+ def skip_statement_cache?(*scope)
131
+ return true if klass.respond_to?(:scoped_by_tenant?) && klass.scoped_by_tenant?
132
+ skip_statement_cache_orig(*scope)
140
133
  end
141
134
  end
@@ -33,6 +33,15 @@ module MultiTenant
33
33
  @@multi_tenant_models[table_name.to_s]
34
34
  end
35
35
 
36
+ def self.multi_tenant_model_for_arel(arel)
37
+ return nil unless arel.respond_to?(:ast)
38
+ if arel.ast.relation.is_a? Arel::Nodes::JoinSource
39
+ MultiTenant.multi_tenant_model_for_table(arel.ast.relation.left.table_name)
40
+ else
41
+ MultiTenant.multi_tenant_model_for_table(arel.ast.relation.table_name)
42
+ end
43
+ end
44
+
36
45
  def self.current_tenant=(tenant)
37
46
  RequestStore.store[:current_tenant] = tenant
38
47
  end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module Persistence
3
+ alias :delete_orig :delete
4
+
5
+ def delete
6
+ if MultiTenant.multi_tenant_model_for_table(self.class.table_name).present? && persisted? && MultiTenant.current_tenant_id.nil?
7
+ MultiTenant.with(self.public_send(self.class.partition_key)) { delete_orig }
8
+ else
9
+ delete_orig
10
+ end
11
+ end
12
+ end
13
+ end
@@ -149,21 +149,27 @@ module MultiTenant
149
149
  end
150
150
  end
151
151
 
152
- class TenantEnforcementClause < Arel::Nodes::Node
152
+ class BaseTenantEnforcementClause < Arel::Nodes::Node
153
153
  attr_reader :tenant_attribute
154
154
  def initialize(tenant_attribute)
155
155
  @tenant_attribute = tenant_attribute
156
+ @tenant_model = MultiTenant.multi_tenant_model_for_table(tenant_attribute.relation.table_name)
156
157
  end
157
158
 
158
159
  def to_s; to_sql; end
159
160
  def to_str; to_sql; end
160
161
 
161
162
  def to_sql(*)
162
- tenant_arel.to_sql
163
+ collector = Arel::Collectors::SQLString.new
164
+ collector = @tenant_model.connection.visitor.accept tenant_arel, collector
165
+ collector.value
163
166
  end
164
167
 
165
- private
166
168
 
169
+ end
170
+
171
+ class TenantEnforcementClause < BaseTenantEnforcementClause
172
+ private
167
173
  def tenant_arel
168
174
  if defined?(Arel::Nodes::Quoted)
169
175
  @tenant_attribute.eq(Arel::Nodes::Quoted.new(MultiTenant.current_tenant_id))
@@ -174,24 +180,15 @@ module MultiTenant
174
180
  end
175
181
 
176
182
 
177
- class TenantJoinEnforcementClause < Arel::Nodes::Node
178
- attr_reader :tenant_attribute
183
+ class TenantJoinEnforcementClause < BaseTenantEnforcementClause
179
184
  attr_reader :table_left
180
185
  def initialize(tenant_attribute, table_left)
186
+ super(tenant_attribute)
181
187
  @table_left = table_left
182
188
  @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
189
  end
192
190
 
193
191
  private
194
-
195
192
  def tenant_arel
196
193
  @tenant_attribute.eq(@table_left[@model_left.partition_key])
197
194
  end
@@ -199,23 +196,12 @@ module MultiTenant
199
196
 
200
197
 
201
198
  module TenantValueVisitor
202
- if ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2)
203
- def visit_MultiTenant_TenantEnforcementClause(o, collector)
204
- collector << o
205
- end
206
-
207
- def visit_MultiTenant_TenantJoinEnforcementClause(o, collector)
208
- collector << o
209
- end
210
-
211
- else
212
- def visit_MultiTenant_TenantEnforcementClause(o, a = nil)
213
- o
214
- end
199
+ def visit_MultiTenant_TenantEnforcementClause(o, collector)
200
+ collector << o
201
+ end
215
202
 
216
- def visit_MultiTenant_TenantJoinEnforcementClause(o, a = nil)
217
- o
218
- end
203
+ def visit_MultiTenant_TenantJoinEnforcementClause(o, collector)
204
+ collector << o
219
205
  end
220
206
  end
221
207
 
@@ -223,7 +209,7 @@ module MultiTenant
223
209
  def join_to_update(update, *args)
224
210
  update = super(update, *args)
225
211
  model = MultiTenant.multi_tenant_model_for_table(update.ast.relation.table_name)
226
- if model.present? && !MultiTenant.with_write_only_mode_enabled?
212
+ if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
227
213
  update.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
228
214
  end
229
215
  update
@@ -232,28 +218,26 @@ module MultiTenant
232
218
  def join_to_delete(delete, *args)
233
219
  delete = super(delete, *args)
234
220
  model = MultiTenant.multi_tenant_model_for_table(delete.ast.left.table_name)
235
- if model.present? && !MultiTenant.with_write_only_mode_enabled?
221
+ if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
236
222
  delete.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
237
223
  end
238
224
  delete
239
225
  end
240
226
 
241
- if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2
242
- def update(arel, name = nil, binds = [])
243
- model = MultiTenant.multi_tenant_model_for_table(arel.ast.relation.table_name)
244
- if model.present? && !MultiTenant.with_write_only_mode_enabled?
245
- arel.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
246
- end
247
- super(arel, name, binds)
227
+ def update(arel, name = nil, binds = [])
228
+ model = MultiTenant.multi_tenant_model_for_arel(arel)
229
+ if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
230
+ arel.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
248
231
  end
232
+ super(arel, name, binds)
233
+ end
249
234
 
250
- def delete(arel, name = nil, binds = [])
251
- model = MultiTenant.multi_tenant_model_for_table(arel.ast.left.table_name)
252
- if model.present? && !MultiTenant.with_write_only_mode_enabled?
253
- arel.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
254
- end
255
- super(arel, name, binds)
235
+ def delete(arel, name = nil, binds = [])
236
+ model = MultiTenant.multi_tenant_model_for_arel(arel)
237
+ if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
238
+ arel.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
256
239
  end
240
+ super(arel, name, binds)
257
241
  end
258
242
  end
259
243
  end
@@ -296,7 +280,6 @@ module ActiveRecord
296
280
  end
297
281
 
298
282
  if node.is_a?(Arel::Nodes::SelectCore) || node.is_a?(Arel::Nodes::Join)
299
-
300
283
  if node.is_a?Arel::Nodes::Join
301
284
  node_list = [node]
302
285
  else
@@ -304,14 +287,18 @@ module ActiveRecord
304
287
  end
305
288
 
306
289
  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)
290
+ if (!node_join.right ||
291
+ (ActiveRecord::VERSION::MAJOR == 5 &&
292
+ !node_join.right.expr.right.is_a?(Arel::Attributes::Attribute)))
308
293
  next
309
294
  end
310
295
 
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)
296
+ relation_right, relation_left = relations_from_node_join(node_join)
297
+
298
+ next unless relation_right && relation_left
299
+
314
300
  model_right = MultiTenant.multi_tenant_model_for_table(relation_left.table_name)
301
+ model_left = MultiTenant.multi_tenant_model_for_table(relation_right.table_name)
315
302
  if model_right && model_left
316
303
  join_enforcement_clause = MultiTenant::TenantJoinEnforcementClause.new(relation_left[model_left.partition_key], relation_right)
317
304
  node_join.right.expr = node_join.right.expr.and(join_enforcement_clause)
@@ -324,69 +311,36 @@ module ActiveRecord
324
311
 
325
312
  arel
326
313
  end
327
- end
328
- end
329
-
330
- require 'active_record/base'
331
- module MultiTenantFindBy
332
- if ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2
333
- # Disable caching for find and find_by in Rails 4.2 - we don't have a good
334
- # way to prevent caching problems here when prepared statements are enabled
335
- def find_by(*args)
336
- return super unless respond_to?(:scoped_by_tenant?) && scoped_by_tenant?
337
-
338
- # This duplicates a bunch of code from AR's find() method
339
- return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
340
- return super if default_scopes.any?
341
314
 
342
- hash = args.first
315
+ private
316
+ def relations_from_node_join(node_join)
317
+ if ActiveRecord::VERSION::MAJOR == 5 || node_join.right.expr.is_a?(Arel::Nodes::Equality)
318
+ return node_join.right.expr.right.relation, node_join.right.expr.left.relation
319
+ end
343
320
 
344
- return super if hash.values.any? { |v| v.nil? || Array === v || Hash === v }
345
- return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
321
+ children = node_join.right.expr.children
346
322
 
347
- key = hash.keys
323
+ tenant_applied = children.any?(MultiTenant::TenantEnforcementClause) || children.any?(MultiTenant::TenantJoinEnforcementClause)
324
+ if tenant_applied || children.empty?
325
+ return nil, nil
326
+ end
348
327
 
349
- # Ensure we never use the cached version
350
- find_by_statement_cache.synchronize { find_by_statement_cache[key] = nil }
328
+ if children[0].right.respond_to?('relation') && children[0].left.respond_to?('relation')
329
+ return children[0].right.relation, children[0].left.relation
330
+ end
351
331
 
352
- super
332
+ return nil, nil
353
333
  end
334
+ end
335
+ end
354
336
 
355
- def find(*ids)
356
- return super unless respond_to?(:scoped_by_tenant?) && scoped_by_tenant?
357
-
358
- # This duplicates a bunch of code from AR's find() method
359
- return super unless ids.length == 1
360
- return super if ids.first.kind_of?(Symbol)
361
- return super if block_given? ||
362
- primary_key.nil? ||
363
- default_scopes.any? ||
364
- current_scope ||
365
- columns_hash.include?(inheritance_column) ||
366
- ids.first.kind_of?(Array)
367
-
368
- id = ids.first
369
- if ActiveRecord::Base === id
370
- id = id.id
371
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
372
- You are passing an instance of ActiveRecord::Base to `find`.
373
- Please pass the id of the object by calling `.id`
374
- MSG
375
- end
376
- key = primary_key
377
-
378
- # Ensure we never use the cached version
379
- find_by_statement_cache.synchronize { find_by_statement_cache[key] = nil }
380
-
381
- super
382
- end
383
- elsif ActiveRecord::VERSION::MAJOR > 4
384
- def cached_find_by_statement(key, &block)
385
- return super unless respond_to?(:scoped_by_tenant?) && scoped_by_tenant?
337
+ require 'active_record/base'
338
+ module MultiTenantFindBy
339
+ def cached_find_by_statement(key, &block)
340
+ return super unless respond_to?(:scoped_by_tenant?) && scoped_by_tenant?
386
341
 
387
- key = Array.wrap(key) + [MultiTenant.current_tenant_id.to_s]
388
- super(key, &block)
389
- end
342
+ key = Array.wrap(key) + [MultiTenant.current_tenant_id.to_s]
343
+ super(key, &block)
390
344
  end
391
345
  end
392
346