activerecord-multi-tenant 2.3.0 → 2.4.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 +4 -4
- data/.github/workflows/active-record-multi-tenant-tests.yml +4 -1
- data/Appraisals +8 -0
- data/CHANGELOG.md +7 -0
- data/docs/requirements.txt +1 -1
- data/lib/activerecord-multi-tenant/habtm.rb +2 -2
- data/lib/activerecord-multi-tenant/model_extensions.rb +2 -2
- data/lib/activerecord-multi-tenant/multi_tenant.rb +9 -4
- data/lib/activerecord-multi-tenant/query_rewriter.rb +11 -9
- data/lib/activerecord-multi-tenant/table_node.rb +13 -0
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/lib/activerecord_multi_tenant.rb +1 -0
- data/spec/activerecord-multi-tenant/multi_tenant_spec.rb +54 -0
- data/spec/schema.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f277cdbd6175c03dcbbca13e06220f5612fdf40cefb539a3619b4afa0077d369
|
4
|
+
data.tar.gz: bae03db825cc60a9833a2287245b7ff5f62ede9aa67497594e867832a75eaedf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fad6c663f7f588e1359ea4f831b1c66d24de31fa9895aee3ac42d59851597b3ceabf2576c4e2eb49f55baef6eaeb787fee946085b43909450580d4b6a66817cb
|
7
|
+
data.tar.gz: c58700fda6a8e99b928435f2bd5abfdc62eb6ccb249ab21350d12f25d9b6ff29860d12451e5e8a77aa2666302d8a9dbfa9bc062dab450ecfdfce2226452c0d43
|
@@ -51,13 +51,16 @@ jobs:
|
|
51
51
|
- rails-6.0
|
52
52
|
- rails-6.1
|
53
53
|
- rails-7.0
|
54
|
+
- rails-7.1
|
54
55
|
- active-record-6.0
|
55
56
|
- active-record-6.1
|
56
57
|
- active-record-7.0
|
58
|
+
- active-record-7.1
|
57
59
|
citus_version:
|
58
60
|
- '10'
|
59
61
|
- '11'
|
60
|
-
|
62
|
+
- '12'
|
63
|
+
|
61
64
|
name: Ruby ${{ matrix.ruby }}/${{ matrix.gemfile }} / Citus ${{ matrix.citus_version }}
|
62
65
|
env:
|
63
66
|
APPRAISAL: ${{ matrix.appraisal }}
|
data/Appraisals
CHANGED
@@ -10,6 +10,10 @@ appraise 'rails-7.0' do
|
|
10
10
|
gem 'rails', '~> 7.0.0'
|
11
11
|
end
|
12
12
|
|
13
|
+
appraise 'rails-7.1' do
|
14
|
+
gem 'rails', '~> 7.1.0.beta1'
|
15
|
+
end
|
16
|
+
|
13
17
|
appraise 'active-record-6.0' do
|
14
18
|
gem 'activerecord', '~> 6.0.3'
|
15
19
|
end
|
@@ -21,3 +25,7 @@ end
|
|
21
25
|
appraise 'active-record-7.0' do
|
22
26
|
gem 'activerecord', '~> 7.0.0'
|
23
27
|
end
|
28
|
+
|
29
|
+
appraise 'active-record-7.1' do
|
30
|
+
gem 'activerecord', '~> 7.1.0.beta1'
|
31
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
# Changelog
|
2
|
+
|
3
|
+
## 2.4.0 2023-06-05
|
4
|
+
* Adds citus 12 to test matrix (#210)
|
5
|
+
* Adds Support for rails 7.1 (#208)
|
6
|
+
* Fix missing scope in habtm.rb (#207)
|
7
|
+
* Update logic inside the tenant_klass_defined? method (#202)
|
8
|
+
|
2
9
|
## 2.3.0 2023-06-05
|
3
10
|
* Adds has_and_belongs_to_many feature with tenant (#193)
|
4
11
|
* Removes eol ruby versions
|
data/docs/requirements.txt
CHANGED
@@ -8,9 +8,9 @@ module ActiveRecord
|
|
8
8
|
module Associations
|
9
9
|
module ClassMethods
|
10
10
|
# rubocop:disable Naming/PredicateName
|
11
|
-
def has_and_belongs_to_many_with_tenant(name,
|
11
|
+
def has_and_belongs_to_many_with_tenant(name, scope = nil, **options, &extension)
|
12
12
|
# rubocop:enable Naming/PredicateName
|
13
|
-
has_and_belongs_to_many_without_tenant(name, **options, &extension)
|
13
|
+
has_and_belongs_to_many_without_tenant(name, scope, **options, &extension)
|
14
14
|
|
15
15
|
middle_reflection = _reflections[name.to_s].through_reflection
|
16
16
|
join_model = middle_reflection.klass
|
@@ -67,7 +67,7 @@ module MultiTenant
|
|
67
67
|
partition_key = @partition_key
|
68
68
|
|
69
69
|
# Create an implicit belongs_to association only if tenant class exists
|
70
|
-
if MultiTenant.tenant_klass_defined?(tenant_name)
|
70
|
+
if MultiTenant.tenant_klass_defined?(tenant_name, options)
|
71
71
|
belongs_to tenant_name, **options.slice(:class_name, :inverse_of, :optional)
|
72
72
|
.merge(foreign_key: options[:partition_key])
|
73
73
|
end
|
@@ -103,7 +103,7 @@ module MultiTenant
|
|
103
103
|
tenant_id
|
104
104
|
end
|
105
105
|
|
106
|
-
if MultiTenant.tenant_klass_defined?(tenant_name)
|
106
|
+
if MultiTenant.tenant_klass_defined?(tenant_name, options)
|
107
107
|
define_method "#{tenant_name}=" do |model|
|
108
108
|
super(model)
|
109
109
|
if send("#{partition_key}_changed?") && persisted? && !send("#{partition_key}_was").nil?
|
@@ -5,8 +5,13 @@ module MultiTenant
|
|
5
5
|
attribute :tenant
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.tenant_klass_defined?(tenant_name)
|
9
|
-
|
8
|
+
def self.tenant_klass_defined?(tenant_name, options = {})
|
9
|
+
class_name = if options[:class_name].present?
|
10
|
+
options[:class_name]
|
11
|
+
else
|
12
|
+
tenant_name.to_s.classify
|
13
|
+
end
|
14
|
+
!!class_name.safe_constantize
|
10
15
|
end
|
11
16
|
|
12
17
|
def self.partition_key(tenant_name)
|
@@ -58,9 +63,9 @@ module MultiTenant
|
|
58
63
|
return nil unless arel.respond_to?(:ast)
|
59
64
|
|
60
65
|
if arel.ast.relation.is_a? Arel::Nodes::JoinSource
|
61
|
-
MultiTenant.multi_tenant_model_for_table(arel.ast.relation.left
|
66
|
+
MultiTenant.multi_tenant_model_for_table(TableNode.table_name(arel.ast.relation.left))
|
62
67
|
else
|
63
|
-
MultiTenant.multi_tenant_model_for_table(arel.ast.relation
|
68
|
+
MultiTenant.multi_tenant_model_for_table(TableNode.table_name(arel.ast.relation))
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
@@ -83,7 +83,7 @@ module MultiTenant
|
|
83
83
|
|
84
84
|
def visit_Arel_Nodes_Equality(obj, *args)
|
85
85
|
if obj.left.is_a?(Arel::Attributes::Attribute)
|
86
|
-
table_name = obj.left.relation
|
86
|
+
table_name = MultiTenant::TableNode.table_name(obj.left.relation)
|
87
87
|
model = MultiTenant.multi_tenant_model_for_table(table_name)
|
88
88
|
if model.present? && obj.left.name.to_s == model.partition_key.to_s
|
89
89
|
@current_context.visited_handled_relation(obj.left.relation)
|
@@ -101,7 +101,7 @@ module MultiTenant
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def visit_Arel_Table(obj, _collector = nil)
|
104
|
-
@current_context.visited_relation(obj) if tenant_relation?(
|
104
|
+
@current_context.visited_relation(obj) if tenant_relation?(MultiTenant::TableNode.table_name(obj))
|
105
105
|
end
|
106
106
|
|
107
107
|
alias visit_Arel_Nodes_TableAlias visit_Arel_Table
|
@@ -179,7 +179,9 @@ module MultiTenant
|
|
179
179
|
def initialize(tenant_attribute)
|
180
180
|
super()
|
181
181
|
@tenant_attribute = tenant_attribute
|
182
|
-
@tenant_model = MultiTenant.multi_tenant_model_for_table(
|
182
|
+
@tenant_model = MultiTenant.multi_tenant_model_for_table(
|
183
|
+
MultiTenant::TableNode.table_name(tenant_attribute.relation)
|
184
|
+
)
|
183
185
|
end
|
184
186
|
|
185
187
|
def to_s
|
@@ -215,7 +217,7 @@ module MultiTenant
|
|
215
217
|
def initialize(tenant_attribute, table_left)
|
216
218
|
super(tenant_attribute)
|
217
219
|
@table_left = table_left
|
218
|
-
@model_left = MultiTenant.multi_tenant_model_for_table(
|
220
|
+
@model_left = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(table_left))
|
219
221
|
end
|
220
222
|
|
221
223
|
private
|
@@ -241,7 +243,7 @@ module MultiTenant
|
|
241
243
|
module DatabaseStatements
|
242
244
|
def join_to_update(update, *args)
|
243
245
|
update = super(update, *args)
|
244
|
-
model = MultiTenant.multi_tenant_model_for_table(update.ast.relation
|
246
|
+
model = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(update.ast.relation))
|
245
247
|
if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
|
246
248
|
update.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
|
247
249
|
end
|
@@ -250,7 +252,7 @@ module MultiTenant
|
|
250
252
|
|
251
253
|
def join_to_delete(delete, *args)
|
252
254
|
delete = super(delete, *args)
|
253
|
-
model = MultiTenant.multi_tenant_model_for_table(delete.ast.left
|
255
|
+
model = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(delete.ast.left))
|
254
256
|
if model.present? && !MultiTenant.with_write_only_mode_enabled? && MultiTenant.current_tenant_id.present?
|
255
257
|
delete.where(MultiTenant::TenantEnforcementClause.new(model.arel_table[model.partition_key]))
|
256
258
|
end
|
@@ -295,7 +297,7 @@ module ActiveRecord
|
|
295
297
|
node = context.arel_node
|
296
298
|
|
297
299
|
context.unhandled_relations.each do |relation|
|
298
|
-
model = MultiTenant.multi_tenant_model_for_table(relation.arel_table
|
300
|
+
model = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(relation.arel_table))
|
299
301
|
|
300
302
|
if MultiTenant.current_tenant_id
|
301
303
|
enforcement_clause = MultiTenant::TenantEnforcementClause.new(relation.arel_table[model.partition_key])
|
@@ -330,8 +332,8 @@ module ActiveRecord
|
|
330
332
|
|
331
333
|
next unless relation_right && relation_left
|
332
334
|
|
333
|
-
model_right = MultiTenant.multi_tenant_model_for_table(
|
334
|
-
model_left = MultiTenant.multi_tenant_model_for_table(
|
335
|
+
model_right = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(relation_left))
|
336
|
+
model_left = MultiTenant.multi_tenant_model_for_table(MultiTenant::TableNode.table_name(relation_right))
|
335
337
|
next unless model_right && model_left
|
336
338
|
|
337
339
|
join_enforcement_clause = MultiTenant::TenantJoinEnforcementClause.new(
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
module TableNode
|
3
|
+
# Return table name
|
4
|
+
def self.table_name(node)
|
5
|
+
# NOTE: Arel::Nodes::Table#table_name is removed in Rails 7.1
|
6
|
+
if node.is_a?(Arel::Nodes::TableAlias)
|
7
|
+
node.table_name
|
8
|
+
else
|
9
|
+
node.name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -8,5 +8,6 @@ require_relative 'activerecord-multi-tenant/model_extensions'
|
|
8
8
|
require_relative 'activerecord-multi-tenant/multi_tenant'
|
9
9
|
require_relative 'activerecord-multi-tenant/query_rewriter'
|
10
10
|
require_relative 'activerecord-multi-tenant/query_monitor'
|
11
|
+
require_relative 'activerecord-multi-tenant/table_node'
|
11
12
|
require_relative 'activerecord-multi-tenant/version'
|
12
13
|
require_relative 'activerecord-multi-tenant/habtm'
|
@@ -64,4 +64,58 @@ RSpec.describe MultiTenant do
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
68
|
+
describe '.tenant_klass_defined?' do
|
69
|
+
context 'without options' do
|
70
|
+
before(:all) do
|
71
|
+
class SampleTenant < ActiveRecord::Base
|
72
|
+
multi_tenant :sample_tenant
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'return true with valid tenant_name' do
|
77
|
+
expect(MultiTenant.tenant_klass_defined?(:sample_tenant)).to eq(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'return false with invalid_tenant_name' do
|
81
|
+
invalid_tenant_name = :tenant
|
82
|
+
expect(MultiTenant.tenant_klass_defined?(invalid_tenant_name)).to eq(false)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with options' do
|
87
|
+
context 'and valid class_name' do
|
88
|
+
it 'return true' do
|
89
|
+
class SampleTenant < ActiveRecord::Base
|
90
|
+
multi_tenant :tenant
|
91
|
+
end
|
92
|
+
|
93
|
+
tenant_name = :tenant
|
94
|
+
options = {
|
95
|
+
class_name: 'SampleTenant'
|
96
|
+
}
|
97
|
+
expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'return true when tenant class is nested' do
|
101
|
+
module SampleModule
|
102
|
+
class SampleNestedTenant < ActiveRecord::Base
|
103
|
+
multi_tenant :tenant
|
104
|
+
end
|
105
|
+
# rubocop:disable Layout/TrailingWhitespace
|
106
|
+
# Trailing whitespace is intentionally left here
|
107
|
+
|
108
|
+
class AnotherTenant < ActiveRecord::Base
|
109
|
+
end
|
110
|
+
# rubocop:enable Layout/TrailingWhitespace
|
111
|
+
end
|
112
|
+
tenant_name = :tenant
|
113
|
+
options = {
|
114
|
+
class_name: 'SampleModule::SampleNestedTenant'
|
115
|
+
}
|
116
|
+
expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
67
121
|
end
|
data/spec/schema.rb
CHANGED
@@ -174,8 +174,8 @@ end
|
|
174
174
|
class Manager < ActiveRecord::Base
|
175
175
|
multi_tenant :account
|
176
176
|
belongs_to :project
|
177
|
-
has_and_belongs_to_many :tasks,
|
178
|
-
|
177
|
+
has_and_belongs_to_many :tasks, tenant_column: :account_id, tenant_enabled: true,
|
178
|
+
tenant_class_name: 'Account'
|
179
179
|
end
|
180
180
|
|
181
181
|
class Task < ActiveRecord::Base
|
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: 2.
|
4
|
+
version: 2.4.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: 2023-
|
11
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -276,6 +276,7 @@ files:
|
|
276
276
|
- lib/activerecord-multi-tenant/query_monitor.rb
|
277
277
|
- lib/activerecord-multi-tenant/query_rewriter.rb
|
278
278
|
- lib/activerecord-multi-tenant/sidekiq.rb
|
279
|
+
- lib/activerecord-multi-tenant/table_node.rb
|
279
280
|
- lib/activerecord-multi-tenant/version.rb
|
280
281
|
- lib/activerecord_multi_tenant.rb
|
281
282
|
- spec/activerecord-multi-tenant/associations_spec.rb
|