activerecord-multi-tenant 1.1.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7125d7827e44ce5347a6391c97e740f74f1b20e0295ee1d88d80750c5d04c222
4
- data.tar.gz: 5acb0ae83aef24141696b1683f759bed2ca7f5d8b6f9354b2ff9104f8c4d2f9c
3
+ metadata.gz: 457cfac4996b93bb7b0b25cd99c2687f2742a8bc580888008baa4f903255cd74
4
+ data.tar.gz: 8c369e04a906752deda52d9b7b78a2377551e4b85f74e3b6d4b0742d30147783
5
5
  SHA512:
6
- metadata.gz: 761e37256c39851708bf4963a679e7d77f1f4f8c7d3a51d187bc5b6943d311caf75f9dbbd4ad5fd19a410940f245697c823c215edd38735599554b7685a1600d
7
- data.tar.gz: 7e5063cd44d333a83afe565b713afc0af5d7e6aee2f849bd60afbb903e6aa11c4019e321019bccde91628051a558eca6e763f944b7dab327363949eabf76b27f
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
@@ -2,3 +2,5 @@ spec/debug.log
2
2
  pkg/
3
3
  *.rb#
4
4
  *.*~
5
+ Gemfile.lock
6
+ *.gemfile.lock
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.add_runtime_dependency('request_store', '>= 1.0.5')
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,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "activerecord", "~> 6.1.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "activerecord", "~> 7.0.0"
7
+
8
+ 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,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "rails", "~> 6.1.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "rails", "~> 7.0.0"
7
+
8
+ 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].type_cast_for_database(val) }
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| columns_hash[c.to_s] }
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 -> { self.id ||= self.class.connection.select_value("SELECT nextval('" + [self.class.table_name, self.class.primary_key, 'seq'].join('_') + "'::regclass)") }
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.table_name, subclass) if subclass.table_name
47
+ MultiTenant.register_multi_tenant_model(subclass)
42
48
  end
43
49
  end
44
50
 
45
- MultiTenant.register_multi_tenant_model(table_name, self) if table_name
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 'request_store'
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(table_name, model_klass)
28
- @@multi_tenant_models ||= {}
29
- @@multi_tenant_models[table_name.to_s] = model_klass
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
- @@multi_tenant_models[table_name.to_s]
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
- RequestStore.store[:current_tenant] = tenant
60
+ Current.tenant = tenant
47
61
  end
48
62
 
49
63
  def self.current_tenant
50
- RequestStore.store[:current_tenant]
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] = enforcement_clause.and(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 (!node_join.right ||
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 ActiveRecord::VERSION::MAJOR == 5 || node_join.right.expr.is_a?(Arel::Nodes::Equality)
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) || children.any?(MultiTenant::TenantJoinEnforcementClause)
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 = msg['multi_tenant']['class'].constantize.find(msg['multi_tenant']['id'])
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
@@ -1,3 +1,3 @@
1
1
  module MultiTenant
2
- VERSION = '1.1.0'
2
+ VERSION = '2.0.0'
3
3
  end