activerecord-multi-tenant 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +73 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Appraisals +16 -0
- data/CHANGELOG.md +28 -0
- data/activerecord-multi-tenant.gemspec +1 -2
- data/gemfiles/active_record_5.2.3.gemfile +16 -0
- data/gemfiles/active_record_6.1.gemfile +8 -0
- data/gemfiles/active_record_7.0.gemfile +8 -0
- data/gemfiles/rails_5.2.3.gemfile +16 -0
- data/gemfiles/rails_6.1.gemfile +8 -0
- data/gemfiles/rails_7.0.gemfile +8 -0
- data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +200 -0
- data/lib/activerecord-multi-tenant/copy_from_client.rb +2 -2
- data/lib/activerecord-multi-tenant/model_extensions.rb +16 -4
- data/lib/activerecord-multi-tenant/multi_tenant.rb +22 -8
- data/lib/activerecord-multi-tenant/query_rewriter.rb +14 -9
- data/lib/activerecord-multi-tenant/sidekiq.rb +5 -1
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +109 -50
- data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +17 -0
- data/spec/activerecord-multi-tenant/record_finding_spec.rb +36 -0
- data/spec/activerecord-multi-tenant/sidekiq_spec.rb +11 -0
- data/spec/schema.rb +51 -4
- data/spec/spec_helper.rb +7 -0
- metadata +14 -25
- data/.travis.yml +0 -32
- data/Gemfile.lock +0 -197
- data/gemfiles/active_record_5.2.gemfile.lock +0 -188
- data/gemfiles/active_record_6.0.gemfile.lock +0 -198
- data/gemfiles/rails_5.2.gemfile.lock +0 -188
- data/gemfiles/rails_6.0.gemfile.lock +0 -198
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 457cfac4996b93bb7b0b25cd99c2687f2742a8bc580888008baa4f903255cd74
|
4
|
+
data.tar.gz: 8c369e04a906752deda52d9b7b78a2377551e4b85f74e3b6d4b0742d30147783
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7715dadcbd9cd13d86135518d3fc62283d9ab85e802016ab1be15fe39d815ec344c1d3e4699f96870b6e50b6d3b66b4a38f780d07ad6d3d4b8e21668deacca18
|
7
|
+
data.tar.gz: d1d364ebde87012cc5b91a6cf812a1a8cc0f89bb5ee8859b3de01ab0392dc5a62b74a3f93ac721d3e583343b703fc872d73c1f301cff9716e686492f06922d36
|
@@ -0,0 +1,73 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
fail-fast: false
|
14
|
+
matrix:
|
15
|
+
ruby:
|
16
|
+
- '2.5'
|
17
|
+
- '2.6'
|
18
|
+
- '2.7'
|
19
|
+
- '3.0'
|
20
|
+
- '3.1'
|
21
|
+
gemfile:
|
22
|
+
- rails_5.2.3
|
23
|
+
- rails_5.2
|
24
|
+
- rails_6.0
|
25
|
+
- rails_6.1
|
26
|
+
- rails_7.0
|
27
|
+
- active_record_5.2.3
|
28
|
+
- active_record_5.2
|
29
|
+
- active_record_6.0
|
30
|
+
- active_record_6.1
|
31
|
+
- active_record_7.0
|
32
|
+
prepared_statements: [true, false]
|
33
|
+
exclude:
|
34
|
+
# activesupport-7.0.0 requires ruby version >= 2.7.0
|
35
|
+
- ruby: '2.5'
|
36
|
+
gemfile: 'rails_7.0'
|
37
|
+
- ruby: '2.5'
|
38
|
+
gemfile: 'active_record_7.0'
|
39
|
+
- ruby: '2.6'
|
40
|
+
gemfile: 'rails_7.0'
|
41
|
+
- ruby: '2.6'
|
42
|
+
gemfile: 'active_record_7.0'
|
43
|
+
# ruby >3 and activesupport 5.2 are not compatible
|
44
|
+
- ruby: '3.0'
|
45
|
+
gemfile: 'rails_5.2'
|
46
|
+
- ruby: '3.0'
|
47
|
+
gemfile: 'active_record_5.2'
|
48
|
+
- ruby: '3.1'
|
49
|
+
gemfile: 'rails_5.2'
|
50
|
+
- ruby: '3.1'
|
51
|
+
gemfile: 'active_record_5.2'
|
52
|
+
- ruby: '3.0'
|
53
|
+
gemfile: 'rails_5.2.3'
|
54
|
+
- ruby: '3.0'
|
55
|
+
gemfile: 'active_record_5.2.3'
|
56
|
+
- ruby: '3.1'
|
57
|
+
gemfile: 'rails_5.2.3'
|
58
|
+
- ruby: '3.1'
|
59
|
+
gemfile: 'active_record_5.2.3'
|
60
|
+
name: Ruby ${{ matrix.ruby }} / ${{ matrix.gemfile }} ${{ (matrix.prepared_statements && 'w/ prepared statements') || '' }}
|
61
|
+
env:
|
62
|
+
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
|
63
|
+
PREPARED_STATEMENTS: ${{ matrix.prepared_statements && '1' }}
|
64
|
+
steps:
|
65
|
+
- uses: actions/checkout@v2
|
66
|
+
- run: |
|
67
|
+
docker-compose up -d
|
68
|
+
- uses: ruby/setup-ruby@v1
|
69
|
+
with:
|
70
|
+
ruby-version: ${{ matrix.ruby }}
|
71
|
+
bundler-cache: true
|
72
|
+
- run: |
|
73
|
+
bundle exec rake spec
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--force-color
|
data/Appraisals
CHANGED
@@ -14,6 +14,14 @@ appraise 'rails-6.0' do
|
|
14
14
|
gem 'rails', '~> 6.0.3'
|
15
15
|
end
|
16
16
|
|
17
|
+
appraise 'rails-6.1' do
|
18
|
+
gem 'rails', '~> 6.1.0'
|
19
|
+
end
|
20
|
+
|
21
|
+
appraise 'rails-7.0' do
|
22
|
+
gem 'rails', '~> 7.0.0'
|
23
|
+
end
|
24
|
+
|
17
25
|
appraise 'active-record-5.2' do
|
18
26
|
gem 'activerecord', '~> 5.2.0'
|
19
27
|
gem 'i18n', '~> 0.9.5'
|
@@ -29,3 +37,11 @@ end
|
|
29
37
|
appraise 'active-record-6.0' do
|
30
38
|
gem 'activerecord', '~> 6.0.3'
|
31
39
|
end
|
40
|
+
|
41
|
+
appraise 'active-record-6.1' do
|
42
|
+
gem 'activerecord', '~> 6.1.0'
|
43
|
+
end
|
44
|
+
|
45
|
+
appraise 'active-record-7.0' do
|
46
|
+
gem 'activerecord', '~> 7.0.0'
|
47
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.0.0 2022-05-19
|
4
|
+
|
5
|
+
* Replace RequestStore with CurrentAttributes [#139](https://github.com/citusdata/activerecord-multi-tenant/pull/139)
|
6
|
+
* Support changing table_name after calling multi_tenant [#128](https://github.com/citusdata/activerecord-multi-tenant/pull/128)
|
7
|
+
* Allow to use uuid as primary key on partition table [#112](https://github.com/citusdata/activerecord-multi-tenant/pull/112)
|
8
|
+
* Support latest Rails 5.2 [#145](https://github.com/citusdata/activerecord-multi-tenant/pull/145)
|
9
|
+
* Support optional: true for belongs_to [#147](https://github.com/citusdata/activerecord-multi-tenant/pull/147)
|
10
|
+
|
11
|
+
|
12
|
+
## 1.2.0 2022-03-29
|
13
|
+
|
14
|
+
* Test Rails 7 & Ruby 3
|
15
|
+
* Fix regression in 1.1.1 involving deleted tenants [#123](https://github.com/citusdata/activerecord-multi-tenant/pull/123)
|
16
|
+
* Fix incorrect SQL generated when joining two models and one has a default scope [#132](https://github.com/citusdata/activerecord-multi-tenant/pull/132)
|
17
|
+
* Update for Rails 5+ removal of type_cast_for_database [#135](https://github.com/citusdata/activerecord-multi-tenant/pull/135)
|
18
|
+
|
19
|
+
|
20
|
+
## 1.1.1 2021-01-15
|
21
|
+
|
22
|
+
* Add support for Rails 6.1 [#108](https://github.com/citusdata/activerecord-multi-tenant/pull/108)
|
23
|
+
* Fix statement cache for has_many through relations [#103](https://github.com/citusdata/activerecord-multi-tenant/pull/103)
|
24
|
+
|
25
|
+
|
26
|
+
## 1.1.0 2020-08-06
|
27
|
+
|
28
|
+
* See commits for changes:
|
29
|
+
https://github.com/citusdata/activerecord-multi-tenant/commits/v1.1.0
|
30
|
+
|
3
31
|
|
4
32
|
## 1.0.4 2019-10-30
|
5
33
|
|
@@ -15,8 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.homepage = 'https://github.com/citusdata/activerecord-multi-tenant'
|
16
16
|
s.license = 'MIT'
|
17
17
|
|
18
|
-
s.
|
19
|
-
s.add_dependency('rails','>= 4.2')
|
18
|
+
s.add_dependency 'rails', '>= 5.2'
|
20
19
|
|
21
20
|
s.add_development_dependency 'rspec', '>= 3.0'
|
22
21
|
s.add_development_dependency 'rspec-rails'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "activerecord", "~> 5.2.0", "< 5.2.4" # FIXME
|
7
|
+
gem "i18n", "~> 0.9.5"
|
8
|
+
gem "nokogiri", "~> 1.7.1"
|
9
|
+
gem "nio4r", "~> 2.3.1"
|
10
|
+
gem "sprockets", "~> 3.7.1"
|
11
|
+
gem "byebug", "~> 11.0"
|
12
|
+
gem "rake", "12.0.0"
|
13
|
+
gem "redis", "3.3.3"
|
14
|
+
gem "pry-byebug", "3.9.0"
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "rails", "~> 5.2.0", "< 5.2.4" # FIXME
|
7
|
+
gem "i18n", "~> 0.9.5"
|
8
|
+
gem "nokogiri", "~> 1.7.1"
|
9
|
+
gem "nio4r", "~> 2.3.1"
|
10
|
+
gem "sprockets", "~> 3.7.1"
|
11
|
+
gem "byebug", "~> 11.0"
|
12
|
+
gem "rake", "12.0.0"
|
13
|
+
gem "redis", "3.3.3"
|
14
|
+
gem "pry-byebug", "3.9.0"
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
class ArelVisitorsDepthFirst < Arel::Visitors::Visitor
|
3
|
+
def initialize(block = nil)
|
4
|
+
@block = block || Proc.new
|
5
|
+
super()
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def visit(o, _ = nil)
|
11
|
+
super
|
12
|
+
@block.call o
|
13
|
+
end
|
14
|
+
|
15
|
+
def unary(o)
|
16
|
+
visit o.expr
|
17
|
+
end
|
18
|
+
alias :visit_Arel_Nodes_Else :unary
|
19
|
+
alias :visit_Arel_Nodes_Group :unary
|
20
|
+
alias :visit_Arel_Nodes_Cube :unary
|
21
|
+
alias :visit_Arel_Nodes_RollUp :unary
|
22
|
+
alias :visit_Arel_Nodes_GroupingSet :unary
|
23
|
+
alias :visit_Arel_Nodes_GroupingElement :unary
|
24
|
+
alias :visit_Arel_Nodes_Grouping :unary
|
25
|
+
alias :visit_Arel_Nodes_Having :unary
|
26
|
+
alias :visit_Arel_Nodes_Lateral :unary
|
27
|
+
alias :visit_Arel_Nodes_Limit :unary
|
28
|
+
alias :visit_Arel_Nodes_Not :unary
|
29
|
+
alias :visit_Arel_Nodes_Offset :unary
|
30
|
+
alias :visit_Arel_Nodes_On :unary
|
31
|
+
alias :visit_Arel_Nodes_Ordering :unary
|
32
|
+
alias :visit_Arel_Nodes_Ascending :unary
|
33
|
+
alias :visit_Arel_Nodes_Descending :unary
|
34
|
+
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
|
35
|
+
alias :visit_Arel_Nodes_OptimizerHints :unary
|
36
|
+
alias :visit_Arel_Nodes_ValuesList :unary
|
37
|
+
|
38
|
+
def function(o)
|
39
|
+
visit o.expressions
|
40
|
+
visit o.alias
|
41
|
+
visit o.distinct
|
42
|
+
end
|
43
|
+
alias :visit_Arel_Nodes_Avg :function
|
44
|
+
alias :visit_Arel_Nodes_Exists :function
|
45
|
+
alias :visit_Arel_Nodes_Max :function
|
46
|
+
alias :visit_Arel_Nodes_Min :function
|
47
|
+
alias :visit_Arel_Nodes_Sum :function
|
48
|
+
|
49
|
+
def visit_Arel_Nodes_NamedFunction(o)
|
50
|
+
visit o.name
|
51
|
+
visit o.expressions
|
52
|
+
visit o.distinct
|
53
|
+
visit o.alias
|
54
|
+
end
|
55
|
+
|
56
|
+
def visit_Arel_Nodes_Count(o)
|
57
|
+
visit o.expressions
|
58
|
+
visit o.alias
|
59
|
+
visit o.distinct
|
60
|
+
end
|
61
|
+
|
62
|
+
def visit_Arel_Nodes_Case(o)
|
63
|
+
visit o.case
|
64
|
+
visit o.conditions
|
65
|
+
visit o.default
|
66
|
+
end
|
67
|
+
|
68
|
+
def nary(o)
|
69
|
+
o.children.each { |child| visit child }
|
70
|
+
end
|
71
|
+
alias :visit_Arel_Nodes_And :nary
|
72
|
+
|
73
|
+
def binary(o)
|
74
|
+
visit o.left
|
75
|
+
visit o.right
|
76
|
+
end
|
77
|
+
alias :visit_Arel_Nodes_As :binary
|
78
|
+
alias :visit_Arel_Nodes_Assignment :binary
|
79
|
+
alias :visit_Arel_Nodes_Between :binary
|
80
|
+
alias :visit_Arel_Nodes_Concat :binary
|
81
|
+
alias :visit_Arel_Nodes_DeleteStatement :binary
|
82
|
+
alias :visit_Arel_Nodes_DoesNotMatch :binary
|
83
|
+
alias :visit_Arel_Nodes_Equality :binary
|
84
|
+
alias :visit_Arel_Nodes_FullOuterJoin :binary
|
85
|
+
alias :visit_Arel_Nodes_GreaterThan :binary
|
86
|
+
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
|
87
|
+
alias :visit_Arel_Nodes_In :binary
|
88
|
+
alias :visit_Arel_Nodes_InfixOperation :binary
|
89
|
+
alias :visit_Arel_Nodes_JoinSource :binary
|
90
|
+
alias :visit_Arel_Nodes_InnerJoin :binary
|
91
|
+
alias :visit_Arel_Nodes_LessThan :binary
|
92
|
+
alias :visit_Arel_Nodes_LessThanOrEqual :binary
|
93
|
+
alias :visit_Arel_Nodes_Matches :binary
|
94
|
+
alias :visit_Arel_Nodes_NotEqual :binary
|
95
|
+
alias :visit_Arel_Nodes_NotIn :binary
|
96
|
+
alias :visit_Arel_Nodes_NotRegexp :binary
|
97
|
+
alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
|
98
|
+
alias :visit_Arel_Nodes_IsDistinctFrom :binary
|
99
|
+
alias :visit_Arel_Nodes_Or :binary
|
100
|
+
alias :visit_Arel_Nodes_OuterJoin :binary
|
101
|
+
alias :visit_Arel_Nodes_Regexp :binary
|
102
|
+
alias :visit_Arel_Nodes_RightOuterJoin :binary
|
103
|
+
alias :visit_Arel_Nodes_TableAlias :binary
|
104
|
+
alias :visit_Arel_Nodes_When :binary
|
105
|
+
|
106
|
+
def visit_Arel_Nodes_StringJoin(o)
|
107
|
+
visit o.left
|
108
|
+
end
|
109
|
+
|
110
|
+
def visit_Arel_Attribute(o)
|
111
|
+
visit o.relation
|
112
|
+
visit o.name
|
113
|
+
end
|
114
|
+
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
|
115
|
+
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
|
116
|
+
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
|
117
|
+
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
|
118
|
+
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
|
119
|
+
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
|
120
|
+
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
|
121
|
+
|
122
|
+
def visit_Arel_Table(o)
|
123
|
+
visit o.name
|
124
|
+
end
|
125
|
+
|
126
|
+
def terminal(o)
|
127
|
+
end
|
128
|
+
alias :visit_ActiveSupport_Multibyte_Chars :terminal
|
129
|
+
alias :visit_ActiveSupport_StringInquirer :terminal
|
130
|
+
alias :visit_Arel_Nodes_Lock :terminal
|
131
|
+
alias :visit_Arel_Nodes_Node :terminal
|
132
|
+
alias :visit_Arel_Nodes_SqlLiteral :terminal
|
133
|
+
alias :visit_Arel_Nodes_BindParam :terminal
|
134
|
+
alias :visit_Arel_Nodes_Window :terminal
|
135
|
+
alias :visit_Arel_Nodes_True :terminal
|
136
|
+
alias :visit_Arel_Nodes_False :terminal
|
137
|
+
alias :visit_BigDecimal :terminal
|
138
|
+
alias :visit_Class :terminal
|
139
|
+
alias :visit_Date :terminal
|
140
|
+
alias :visit_DateTime :terminal
|
141
|
+
alias :visit_FalseClass :terminal
|
142
|
+
alias :visit_Float :terminal
|
143
|
+
alias :visit_Integer :terminal
|
144
|
+
alias :visit_NilClass :terminal
|
145
|
+
alias :visit_String :terminal
|
146
|
+
alias :visit_Symbol :terminal
|
147
|
+
alias :visit_Time :terminal
|
148
|
+
alias :visit_TrueClass :terminal
|
149
|
+
|
150
|
+
def visit_Arel_Nodes_InsertStatement(o)
|
151
|
+
visit o.relation
|
152
|
+
visit o.columns
|
153
|
+
visit o.values
|
154
|
+
end
|
155
|
+
|
156
|
+
def visit_Arel_Nodes_SelectCore(o)
|
157
|
+
visit o.projections
|
158
|
+
visit o.source
|
159
|
+
visit o.wheres
|
160
|
+
visit o.groups
|
161
|
+
visit o.windows
|
162
|
+
visit o.havings
|
163
|
+
end
|
164
|
+
|
165
|
+
def visit_Arel_Nodes_SelectStatement(o)
|
166
|
+
visit o.cores
|
167
|
+
visit o.orders
|
168
|
+
visit o.limit
|
169
|
+
visit o.lock
|
170
|
+
visit o.offset
|
171
|
+
end
|
172
|
+
|
173
|
+
def visit_Arel_Nodes_UpdateStatement(o)
|
174
|
+
visit o.relation
|
175
|
+
visit o.values
|
176
|
+
visit o.wheres
|
177
|
+
visit o.orders
|
178
|
+
visit o.limit
|
179
|
+
end
|
180
|
+
|
181
|
+
def visit_Arel_Nodes_Comment(o)
|
182
|
+
visit o.values
|
183
|
+
end
|
184
|
+
|
185
|
+
def visit_Array(o)
|
186
|
+
o.each { |i| visit i }
|
187
|
+
end
|
188
|
+
alias :visit_Set :visit_Array
|
189
|
+
|
190
|
+
def visit_Hash(o)
|
191
|
+
o.each { |k, v| visit(k); visit(v) }
|
192
|
+
end
|
193
|
+
|
194
|
+
DISPATCH = dispatch_cache
|
195
|
+
|
196
|
+
def get_dispatch_cache
|
197
|
+
DISPATCH
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -9,7 +9,7 @@ module MultiTenant
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def <<(row)
|
12
|
-
row = row.map.with_index { |val, idx| @column_types[idx].
|
12
|
+
row = row.map.with_index { |val, idx| @column_types[idx].serialize(val) }
|
13
13
|
@conn.put_copy_data(row)
|
14
14
|
@count += 1
|
15
15
|
end
|
@@ -18,7 +18,7 @@ module MultiTenant
|
|
18
18
|
module CopyFromClient
|
19
19
|
def copy_from_client(columns, &block)
|
20
20
|
conn = connection.raw_connection
|
21
|
-
column_types = columns.map { |c|
|
21
|
+
column_types = columns.map { |c| type_for_attribute(c.to_s) }
|
22
22
|
helper = MultiTenant::CopyFromClientHelper.new(conn, column_types)
|
23
23
|
conn.copy_data %{COPY #{quoted_table_name}("#{columns.join('","')}") FROM STDIN}, PG::TextEncoder::CopyRow.new do
|
24
24
|
block.call helper
|
@@ -6,7 +6,13 @@ module MultiTenant
|
|
6
6
|
if to_s.underscore.to_sym == tenant_name
|
7
7
|
unless MultiTenant.with_write_only_mode_enabled?
|
8
8
|
# This is the tenant model itself. Workaround for https://github.com/citusdata/citus/issues/687
|
9
|
-
before_create ->
|
9
|
+
before_create -> do
|
10
|
+
if self.class.columns_hash[self.class.primary_key].type == :uuid
|
11
|
+
self.id ||= SecureRandom.uuid
|
12
|
+
else
|
13
|
+
self.id ||= self.class.connection.select_value("SELECT nextval('#{self.class.table_name}_#{self.class.primary_key}_seq'::regclass)")
|
14
|
+
end
|
15
|
+
end
|
10
16
|
end
|
11
17
|
else
|
12
18
|
class << self
|
@@ -38,18 +44,18 @@ module MultiTenant
|
|
38
44
|
|
39
45
|
def inherited(subclass)
|
40
46
|
super
|
41
|
-
MultiTenant.register_multi_tenant_model(subclass
|
47
|
+
MultiTenant.register_multi_tenant_model(subclass)
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
45
|
-
MultiTenant.register_multi_tenant_model(
|
51
|
+
MultiTenant.register_multi_tenant_model(self)
|
46
52
|
|
47
53
|
@partition_key = options[:partition_key] || MultiTenant.partition_key(tenant_name)
|
48
54
|
partition_key = @partition_key
|
49
55
|
|
50
56
|
# Create an implicit belongs_to association only if tenant class exists
|
51
57
|
if MultiTenant.tenant_klass_defined?(tenant_name)
|
52
|
-
belongs_to tenant_name, **options.slice(:class_name, :inverse_of).merge(foreign_key: options[:partition_key])
|
58
|
+
belongs_to tenant_name, **options.slice(:class_name, :inverse_of, :optional).merge(foreign_key: options[:partition_key])
|
53
59
|
end
|
54
60
|
|
55
61
|
# New instances should have the tenant set
|
@@ -129,6 +135,12 @@ class ActiveRecord::Associations::Association
|
|
129
135
|
alias skip_statement_cache_orig skip_statement_cache?
|
130
136
|
def skip_statement_cache?(*scope)
|
131
137
|
return true if klass.respond_to?(:scoped_by_tenant?) && klass.scoped_by_tenant?
|
138
|
+
|
139
|
+
if reflection.through_reflection
|
140
|
+
through_klass = reflection.through_reflection.klass
|
141
|
+
return true if through_klass.respond_to?(:scoped_by_tenant?) && through_klass.scoped_by_tenant?
|
142
|
+
end
|
143
|
+
|
132
144
|
skip_statement_cache_orig(*scope)
|
133
145
|
end
|
134
146
|
end
|
@@ -1,6 +1,10 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_support/current_attributes'
|
2
2
|
|
3
3
|
module MultiTenant
|
4
|
+
class Current < ::ActiveSupport::CurrentAttributes
|
5
|
+
attribute :tenant
|
6
|
+
end
|
7
|
+
|
4
8
|
def self.tenant_klass_defined?(tenant_name)
|
5
9
|
!!tenant_name.to_s.classify.safe_constantize
|
6
10
|
end
|
@@ -24,13 +28,23 @@ module MultiTenant
|
|
24
28
|
def self.with_lock_workaround_enabled?; @@enable_with_lock_workaround; end
|
25
29
|
|
26
30
|
# Registry that maps table names to models (used by the query rewriter)
|
27
|
-
def self.register_multi_tenant_model(
|
28
|
-
@@multi_tenant_models ||=
|
29
|
-
@@multi_tenant_models
|
31
|
+
def self.register_multi_tenant_model(model_klass)
|
32
|
+
@@multi_tenant_models ||= []
|
33
|
+
@@multi_tenant_models.push(model_klass)
|
34
|
+
|
35
|
+
remove_class_variable(:@@multi_tenant_model_table_names) if defined?(@@multi_tenant_model_table_names)
|
30
36
|
end
|
37
|
+
|
31
38
|
def self.multi_tenant_model_for_table(table_name)
|
32
|
-
@@multi_tenant_models ||=
|
33
|
-
|
39
|
+
@@multi_tenant_models ||= []
|
40
|
+
|
41
|
+
if !defined?(@@multi_tenant_model_table_names)
|
42
|
+
@@multi_tenant_model_table_names = @@multi_tenant_models.map { |model|
|
43
|
+
[model.table_name, model] if model.table_name
|
44
|
+
}.compact.to_h
|
45
|
+
end
|
46
|
+
|
47
|
+
@@multi_tenant_model_table_names[table_name.to_s]
|
34
48
|
end
|
35
49
|
|
36
50
|
def self.multi_tenant_model_for_arel(arel)
|
@@ -43,11 +57,11 @@ module MultiTenant
|
|
43
57
|
end
|
44
58
|
|
45
59
|
def self.current_tenant=(tenant)
|
46
|
-
|
60
|
+
Current.tenant = tenant
|
47
61
|
end
|
48
62
|
|
49
63
|
def self.current_tenant
|
50
|
-
|
64
|
+
Current.tenant
|
51
65
|
end
|
52
66
|
|
53
67
|
def self.current_tenant_id
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require_relative "./arel_visitors_depth_first.rb" unless Arel::Visitors.const_defined?(:DepthFirst)
|
2
3
|
|
3
4
|
module MultiTenant
|
4
5
|
class Table
|
@@ -31,6 +32,7 @@ module MultiTenant
|
|
31
32
|
@arel_node = arel_node
|
32
33
|
@known_relations = []
|
33
34
|
@handled_relations = []
|
35
|
+
@discovering = false
|
34
36
|
end
|
35
37
|
|
36
38
|
def discover_relations
|
@@ -54,7 +56,7 @@ module MultiTenant
|
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
|
-
class ArelTenantVisitor < Arel::Visitors::DepthFirst
|
59
|
+
class ArelTenantVisitor < Arel::Visitors.const_defined?(:DepthFirst) ? Arel::Visitors::DepthFirst : ::MultiTenant::ArelVisitorsDepthFirst
|
58
60
|
def initialize(arel)
|
59
61
|
super(Proc.new {})
|
60
62
|
@statement_node_id = nil
|
@@ -120,6 +122,8 @@ module MultiTenant
|
|
120
122
|
alias :visit_Arel_Nodes_FullOuterJoin :visit_Arel_Nodes_OuterJoin
|
121
123
|
alias :visit_Arel_Nodes_RightOuterJoin :visit_Arel_Nodes_OuterJoin
|
122
124
|
|
125
|
+
alias :visit_ActiveModel_Attribute :terminal
|
126
|
+
|
123
127
|
private
|
124
128
|
|
125
129
|
def tenant_relation?(table_name)
|
@@ -272,7 +276,11 @@ module ActiveRecord
|
|
272
276
|
if node.wheres.empty?
|
273
277
|
node.wheres = [enforcement_clause]
|
274
278
|
else
|
275
|
-
node.wheres[0]
|
279
|
+
if node.wheres[0].is_a?(Arel::Nodes::And)
|
280
|
+
node.wheres[0].children << enforcement_clause
|
281
|
+
else
|
282
|
+
node.wheres[0] = enforcement_clause.and(node.wheres[0])
|
283
|
+
end
|
276
284
|
end
|
277
285
|
else
|
278
286
|
raise "UnknownContext"
|
@@ -287,12 +295,9 @@ module ActiveRecord
|
|
287
295
|
end
|
288
296
|
|
289
297
|
node_list.select{ |n| n.is_a? Arel::Nodes::Join }.each do |node_join|
|
290
|
-
if
|
291
|
-
(ActiveRecord::VERSION::MAJOR == 5 &&
|
292
|
-
!node_join.right.expr.right.is_a?(Arel::Attributes::Attribute)))
|
298
|
+
if !node_join.right
|
293
299
|
next
|
294
300
|
end
|
295
|
-
|
296
301
|
relation_right, relation_left = relations_from_node_join(node_join)
|
297
302
|
|
298
303
|
next unless relation_right && relation_left
|
@@ -314,13 +319,13 @@ module ActiveRecord
|
|
314
319
|
|
315
320
|
private
|
316
321
|
def relations_from_node_join(node_join)
|
317
|
-
if
|
322
|
+
if node_join.right.expr.is_a?(Arel::Nodes::Equality)
|
318
323
|
return node_join.right.expr.right.relation, node_join.right.expr.left.relation
|
319
324
|
end
|
320
325
|
|
321
|
-
children = node_join.right.expr.children
|
326
|
+
children = [node_join.right.expr.children].flatten
|
322
327
|
|
323
|
-
tenant_applied = children.any?(MultiTenant::TenantEnforcementClause) ||
|
328
|
+
tenant_applied = children.any?{|c| c.is_a?(MultiTenant::TenantEnforcementClause) || c.is_a?(MultiTenant::TenantJoinEnforcementClause)}
|
324
329
|
if tenant_applied || children.empty?
|
325
330
|
return nil, nil
|
326
331
|
end
|
@@ -18,7 +18,11 @@ module Sidekiq::Middleware::MultiTenant
|
|
18
18
|
class Server
|
19
19
|
def call(worker_class, msg, queue)
|
20
20
|
if msg.has_key?('multi_tenant')
|
21
|
-
tenant =
|
21
|
+
tenant = begin
|
22
|
+
msg['multi_tenant']['class'].constantize.find(msg['multi_tenant']['id'])
|
23
|
+
rescue ActiveRecord::RecordNotFound
|
24
|
+
msg['multi_tenant']['id']
|
25
|
+
end
|
22
26
|
MultiTenant.with(tenant) do
|
23
27
|
yield
|
24
28
|
end
|