activerecord-multi-tenant 1.0.0 → 1.1.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.
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