activerecord-multi-tenant 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3923b91001e63778ed81b5d797c2204857ef31d8
4
- data.tar.gz: 8b4c76c828bcfa5b29d80fb0c7e8b4bdbb0ff7c1
3
+ metadata.gz: 0ad5f8df62dcf4cb4308e7b52736ba20212cc154
4
+ data.tar.gz: 244c9cdcd00b6d62a982a9dfa6917dade6aacf60
5
5
  SHA512:
6
- metadata.gz: 259c8320b79b56b232a3d981609b3419757cac0315087350e27c5c9613852c899487d773fd3fb56f4a684cad7fc7633fa208b0b3b203e0331c3b78400f83decc
7
- data.tar.gz: 7ec81cf673bf8eae9e53267c65222d104421b2cee3df0d12e72fe5d12f8723da069617ced3b74c75651a4fd5aa2d091d1bd6e0437492ec959b472a88d1bfeffd
6
+ metadata.gz: d2cde81a900904e331d3f4558b5ebfbb55547bebef5ec841087081a23598bcc89bbbd7d077fab260cc70a0fb5b0aa8ab535d4b89040cb4f784b6299e2d27231c
7
+ data.tar.gz: 406344742c98800907c67436dc7d711e85a11d73d78b262504586de9c0a943cef854181493f6985dac3a0dcf5feb902ddff00b0ea0f323edfb5446d846483bad
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 2017-07-18
4
+
5
+ * Switch back to Relation-based mechanism of hooking into ActiveRecord (this resolves issues for simple queries that didn't get rewritten)
6
+ * Query rewriter improvements
7
+ * Handle OUTER JOIN correctly
8
+ * Correctly rewrite sub-selects
9
+ * Model tenant method: Only return cached object if not loaded
10
+ * Fix support for inherited model classes that only have `multi-tenant` on a higher level object [Aaron Severs](https://github.com/webandtech) [#13](https://github.com/citusdata/activerecord-multi-tenant/pull/13)
11
+ * Sidekiq middleware: Don't automatically perform a find for the tenant object for every job [Scott Mitchell](https://github.com/smitchelus) [#14](https://github.com/citusdata/activerecord-multi-tenant/pull/14)
12
+ * Fix automatic inverse of on singular associations [Kyle Bock](https://github.com/kwbock) [#15](https://github.com/citusdata/activerecord-multi-tenant/pull/15)
13
+ * Fix bug that prevents fast truncate from running [Kyle Bock](https://github.com/kwbock) [#17](https://github.com/citusdata/activerecord-multi-tenant/pull/17)
14
+
15
+
3
16
  ## 0.6.0 2017-06-09
4
17
 
5
18
  * Query rewriter
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-multi-tenant (0.6.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -52,6 +52,7 @@ GEM
52
52
  arel (7.1.4)
53
53
  builder (3.2.2)
54
54
  concurrent-ruby (1.0.4)
55
+ connection_pool (2.2.1)
55
56
  diff-lcs (1.2.5)
56
57
  erubis (2.7.0)
57
58
  globalid (0.4.0)
@@ -72,6 +73,8 @@ GEM
72
73
  mini_portile2 (~> 2.1.0)
73
74
  pg (0.19.0)
74
75
  rack (2.0.1)
76
+ rack-protection (1.5.3)
77
+ rack
75
78
  rack-test (0.6.3)
76
79
  rack (>= 1.0)
77
80
  rails (5.0.1)
@@ -98,6 +101,7 @@ GEM
98
101
  rake (>= 0.8.7)
99
102
  thor (>= 0.18.1, < 2.0)
100
103
  rake (12.0.0)
104
+ redis (3.3.3)
101
105
  request_store (1.3.2)
102
106
  rspec (3.5.0)
103
107
  rspec-core (~> 3.5.0)
@@ -120,6 +124,11 @@ GEM
120
124
  rspec-mocks (~> 3.5.0)
121
125
  rspec-support (~> 3.5.0)
122
126
  rspec-support (3.5.0)
127
+ sidekiq (4.2.10)
128
+ concurrent-ruby (~> 1.0)
129
+ connection_pool (~> 2.2, >= 2.2.0)
130
+ rack-protection (>= 1.5.0)
131
+ redis (~> 3.2, >= 3.2.1)
123
132
  sprockets (3.7.1)
124
133
  concurrent-ruby (~> 1.0)
125
134
  rack (> 1, < 3)
@@ -128,8 +137,8 @@ GEM
128
137
  activesupport (>= 4.0)
129
138
  sprockets (>= 3.0.0)
130
139
  thor (0.19.4)
131
- thread_safe (0.3.5)
132
- tzinfo (1.2.2)
140
+ thread_safe (0.3.6)
141
+ tzinfo (1.2.3)
133
142
  thread_safe (~> 0.1)
134
143
  websocket-driver (0.6.5)
135
144
  websocket-extensions (>= 0.1.0)
@@ -145,7 +154,8 @@ DEPENDENCIES
145
154
  rake
146
155
  rspec (>= 3.0)
147
156
  rspec-rails
157
+ sidekiq
148
158
  thor
149
159
 
150
160
  BUNDLED WITH
151
- 1.13.6
161
+ 1.15.1
@@ -22,5 +22,6 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency 'rspec-rails'
23
23
  s.add_development_dependency 'pg'
24
24
  s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'sidekiq'
25
26
  s.add_development_dependency 'thor'
26
27
  end
data/docker-compose.yml CHANGED
@@ -2,15 +2,15 @@ version: '2'
2
2
 
3
3
  services:
4
4
  master:
5
- image: 'citusdata/citus:6.2.1'
5
+ image: 'citusdata/citus:6.2.3'
6
6
  ports: ['5600:5432']
7
7
  labels: ['com.citusdata.role=Master']
8
8
  volumes: ['/var/run/postgresql']
9
9
  worker1:
10
- image: 'citusdata/citus:6.2.1'
10
+ image: 'citusdata/citus:6.2.3'
11
11
  labels: ['com.citusdata.role=Worker']
12
12
  worker2:
13
- image: 'citusdata/citus:6.2.1'
13
+ image: 'citusdata/citus:6.2.3'
14
14
  labels: ['com.citusdata.role=Worker']
15
15
  config:
16
16
  image: 'citusdata/workerlist-gen:2.0.0'
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -52,6 +52,7 @@ GEM
52
52
  arel (8.0.0)
53
53
  builder (3.2.3)
54
54
  concurrent-ruby (1.0.5)
55
+ connection_pool (2.2.1)
55
56
  diff-lcs (1.3)
56
57
  erubi (1.6.0)
57
58
  globalid (0.4.0)
@@ -72,6 +73,8 @@ GEM
72
73
  mini_portile2 (~> 2.1.0)
73
74
  pg (0.20.0)
74
75
  rack (2.0.1)
76
+ rack-protection (1.5.3)
77
+ rack
75
78
  rack-test (0.6.3)
76
79
  rack (>= 1.0)
77
80
  rails (5.1.0)
@@ -98,6 +101,7 @@ GEM
98
101
  rake (>= 0.8.7)
99
102
  thor (>= 0.18.1, < 2.0)
100
103
  rake (12.0.0)
104
+ redis (3.3.3)
101
105
  request_store (1.3.2)
102
106
  rspec (3.6.0)
103
107
  rspec-core (~> 3.6.0)
@@ -120,6 +124,11 @@ GEM
120
124
  rspec-mocks (~> 3.6.0)
121
125
  rspec-support (~> 3.6.0)
122
126
  rspec-support (3.6.0)
127
+ sidekiq (4.2.10)
128
+ concurrent-ruby (~> 1.0)
129
+ connection_pool (~> 2.2, >= 2.2.0)
130
+ rack-protection (>= 1.5.0)
131
+ redis (~> 3.2, >= 3.2.1)
123
132
  sprockets (3.7.1)
124
133
  concurrent-ruby (~> 1.0)
125
134
  rack (> 1, < 3)
@@ -146,7 +155,8 @@ DEPENDENCIES
146
155
  rake
147
156
  rspec (>= 3.0)
148
157
  rspec-rails
158
+ sidekiq
149
159
  thor
150
160
 
151
161
  BUNDLED WITH
152
- 1.12.5
162
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -41,6 +41,8 @@ GEM
41
41
  thor (>= 0.14.0)
42
42
  arel (3.0.3)
43
43
  builder (3.0.4)
44
+ concurrent-ruby (1.0.5)
45
+ connection_pool (2.2.1)
44
46
  diff-lcs (1.3)
45
47
  erubis (2.7.0)
46
48
  hike (1.2.3)
@@ -58,6 +60,8 @@ GEM
58
60
  rack (1.4.7)
59
61
  rack-cache (1.7.0)
60
62
  rack (>= 0.4)
63
+ rack-protection (2.0.0)
64
+ rack
61
65
  rack-ssl (1.3.4)
62
66
  rack
63
67
  rack-test (0.6.3)
@@ -80,6 +84,7 @@ GEM
80
84
  rake (12.0.0)
81
85
  rdoc (3.12.2)
82
86
  json (~> 1.4)
87
+ redis (3.3.3)
83
88
  request_store (1.3.2)
84
89
  rspec (3.5.0)
85
90
  rspec-core (~> 3.5.0)
@@ -102,6 +107,11 @@ GEM
102
107
  rspec-mocks (~> 3.5.0)
103
108
  rspec-support (~> 3.5.0)
104
109
  rspec-support (3.5.0)
110
+ sidekiq (5.0.3)
111
+ concurrent-ruby (~> 1.0)
112
+ connection_pool (~> 2.2, >= 2.2.0)
113
+ rack-protection (>= 1.5.0)
114
+ redis (~> 3.3, >= 3.3.3)
105
115
  sprockets (2.2.3)
106
116
  hike (~> 1.2)
107
117
  multi_json (~> 1.0)
@@ -127,8 +137,9 @@ DEPENDENCIES
127
137
  rake
128
138
  rspec (>= 3.0)
129
139
  rspec-rails
140
+ sidekiq
130
141
  test-unit (~> 3.0)
131
142
  thor
132
143
 
133
144
  BUNDLED WITH
134
- 1.13.6
145
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -39,6 +39,7 @@ GEM
39
39
  arel (4.0.2)
40
40
  builder (3.1.4)
41
41
  concurrent-ruby (1.0.5)
42
+ connection_pool (2.2.1)
42
43
  diff-lcs (1.3)
43
44
  erubis (2.7.0)
44
45
  i18n (0.8.1)
@@ -51,6 +52,8 @@ GEM
51
52
  multi_json (1.12.1)
52
53
  pg (0.20.0)
53
54
  rack (1.5.5)
55
+ rack-protection (2.0.0)
56
+ rack
54
57
  rack-test (0.6.3)
55
58
  rack (>= 1.0)
56
59
  rails (4.0.13)
@@ -67,6 +70,7 @@ GEM
67
70
  rake (>= 0.8.7)
68
71
  thor (>= 0.18.1, < 2.0)
69
72
  rake (12.0.0)
73
+ redis (3.3.3)
70
74
  request_store (1.3.2)
71
75
  rspec (3.5.0)
72
76
  rspec-core (~> 3.5.0)
@@ -89,6 +93,11 @@ GEM
89
93
  rspec-mocks (~> 3.5.0)
90
94
  rspec-support (~> 3.5.0)
91
95
  rspec-support (3.5.0)
96
+ sidekiq (5.0.3)
97
+ concurrent-ruby (~> 1.0)
98
+ connection_pool (~> 2.2, >= 2.2.0)
99
+ rack-protection (>= 1.5.0)
100
+ redis (~> 3.3, >= 3.3.3)
92
101
  sprockets (3.7.1)
93
102
  concurrent-ruby (~> 1.0)
94
103
  rack (> 1, < 3)
@@ -111,7 +120,8 @@ DEPENDENCIES
111
120
  rake
112
121
  rspec (>= 3.0)
113
122
  rspec-rails
123
+ sidekiq
114
124
  thor
115
125
 
116
126
  BUNDLED WITH
117
- 1.13.6
127
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -41,6 +41,7 @@ GEM
41
41
  arel (5.0.1.20140414130214)
42
42
  builder (3.2.3)
43
43
  concurrent-ruby (1.0.5)
44
+ connection_pool (2.2.1)
44
45
  diff-lcs (1.3)
45
46
  erubis (2.7.0)
46
47
  i18n (0.8.1)
@@ -53,6 +54,8 @@ GEM
53
54
  minitest (5.10.1)
54
55
  pg (0.20.0)
55
56
  rack (1.5.5)
57
+ rack-protection (2.0.0)
58
+ rack
56
59
  rack-test (0.6.3)
57
60
  rack (>= 1.0)
58
61
  rails (4.1.16)
@@ -71,6 +74,7 @@ GEM
71
74
  rake (>= 0.8.7)
72
75
  thor (>= 0.18.1, < 2.0)
73
76
  rake (12.0.0)
77
+ redis (3.3.3)
74
78
  request_store (1.3.2)
75
79
  rspec (3.5.0)
76
80
  rspec-core (~> 3.5.0)
@@ -93,6 +97,11 @@ GEM
93
97
  rspec-mocks (~> 3.5.0)
94
98
  rspec-support (~> 3.5.0)
95
99
  rspec-support (3.5.0)
100
+ sidekiq (5.0.3)
101
+ concurrent-ruby (~> 1.0)
102
+ connection_pool (~> 2.2, >= 2.2.0)
103
+ rack-protection (>= 1.5.0)
104
+ redis (~> 3.3, >= 3.3.3)
96
105
  sprockets (3.7.1)
97
106
  concurrent-ruby (~> 1.0)
98
107
  rack (> 1, < 3)
@@ -116,7 +125,8 @@ DEPENDENCIES
116
125
  rake
117
126
  rspec (>= 3.0)
118
127
  rspec-rails
128
+ sidekiq
119
129
  thor
120
130
 
121
131
  BUNDLED WITH
122
- 1.13.6
132
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -49,6 +49,7 @@ GEM
49
49
  arel (6.0.4)
50
50
  builder (3.2.3)
51
51
  concurrent-ruby (1.0.5)
52
+ connection_pool (2.2.1)
52
53
  diff-lcs (1.3)
53
54
  erubis (2.7.0)
54
55
  globalid (0.3.7)
@@ -67,6 +68,8 @@ GEM
67
68
  mini_portile2 (~> 2.1.0)
68
69
  pg (0.20.0)
69
70
  rack (1.6.5)
71
+ rack-protection (1.5.3)
72
+ rack
70
73
  rack-test (0.6.3)
71
74
  rack (>= 1.0)
72
75
  rails (4.2.8)
@@ -94,6 +97,7 @@ GEM
94
97
  rake (>= 0.8.7)
95
98
  thor (>= 0.18.1, < 2.0)
96
99
  rake (12.0.0)
100
+ redis (3.3.3)
97
101
  request_store (1.3.2)
98
102
  rspec (3.5.0)
99
103
  rspec-core (~> 3.5.0)
@@ -116,6 +120,11 @@ GEM
116
120
  rspec-mocks (~> 3.5.0)
117
121
  rspec-support (~> 3.5.0)
118
122
  rspec-support (3.5.0)
123
+ sidekiq (4.2.10)
124
+ concurrent-ruby (~> 1.0)
125
+ connection_pool (~> 2.2, >= 2.2.0)
126
+ rack-protection (>= 1.5.0)
127
+ redis (~> 3.2, >= 3.2.1)
119
128
  sprockets (3.7.1)
120
129
  concurrent-ruby (~> 1.0)
121
130
  rack (> 1, < 3)
@@ -139,7 +148,8 @@ DEPENDENCIES
139
148
  rake
140
149
  rspec (>= 3.0)
141
150
  rspec-rails
151
+ sidekiq
142
152
  thor
143
153
 
144
154
  BUNDLED WITH
145
- 1.13.6
155
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -52,6 +52,7 @@ GEM
52
52
  arel (7.1.4)
53
53
  builder (3.2.3)
54
54
  concurrent-ruby (1.0.5)
55
+ connection_pool (2.2.1)
55
56
  diff-lcs (1.3)
56
57
  erubis (2.7.0)
57
58
  globalid (0.3.7)
@@ -72,6 +73,8 @@ GEM
72
73
  mini_portile2 (~> 2.1.0)
73
74
  pg (0.20.0)
74
75
  rack (2.0.1)
76
+ rack-protection (1.5.3)
77
+ rack
75
78
  rack-test (0.6.3)
76
79
  rack (>= 1.0)
77
80
  rails (5.0.1)
@@ -98,6 +101,7 @@ GEM
98
101
  rake (>= 0.8.7)
99
102
  thor (>= 0.18.1, < 2.0)
100
103
  rake (12.0.0)
104
+ redis (3.3.3)
101
105
  request_store (1.3.2)
102
106
  rspec (3.5.0)
103
107
  rspec-core (~> 3.5.0)
@@ -120,6 +124,11 @@ GEM
120
124
  rspec-mocks (~> 3.5.0)
121
125
  rspec-support (~> 3.5.0)
122
126
  rspec-support (3.5.0)
127
+ sidekiq (4.2.10)
128
+ concurrent-ruby (~> 1.0)
129
+ connection_pool (~> 2.2, >= 2.2.0)
130
+ rack-protection (>= 1.5.0)
131
+ redis (~> 3.2, >= 3.2.1)
123
132
  sprockets (3.7.1)
124
133
  concurrent-ruby (~> 1.0)
125
134
  rack (> 1, < 3)
@@ -146,7 +155,8 @@ DEPENDENCIES
146
155
  rake
147
156
  rspec (>= 3.0)
148
157
  rspec-rails
158
+ sidekiq
149
159
  thor
150
160
 
151
161
  BUNDLED WITH
152
- 1.13.6
162
+ 1.15.1
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: ../
2
+ remote: ..
3
3
  specs:
4
- activerecord-multi-tenant (0.5.0)
4
+ activerecord-multi-tenant (0.7.0)
5
5
  rails (>= 3.1)
6
6
  request_store (>= 1.0.5)
7
7
 
@@ -52,6 +52,7 @@ GEM
52
52
  arel (8.0.0)
53
53
  builder (3.2.3)
54
54
  concurrent-ruby (1.0.5)
55
+ connection_pool (2.2.1)
55
56
  diff-lcs (1.3)
56
57
  erubi (1.6.0)
57
58
  globalid (0.4.0)
@@ -72,6 +73,8 @@ GEM
72
73
  mini_portile2 (~> 2.1.0)
73
74
  pg (0.20.0)
74
75
  rack (2.0.1)
76
+ rack-protection (1.5.3)
77
+ rack
75
78
  rack-test (0.6.3)
76
79
  rack (>= 1.0)
77
80
  rails (5.1.0)
@@ -98,6 +101,7 @@ GEM
98
101
  rake (>= 0.8.7)
99
102
  thor (>= 0.18.1, < 2.0)
100
103
  rake (12.0.0)
104
+ redis (3.3.3)
101
105
  request_store (1.3.2)
102
106
  rspec (3.6.0)
103
107
  rspec-core (~> 3.6.0)
@@ -120,6 +124,11 @@ GEM
120
124
  rspec-mocks (~> 3.6.0)
121
125
  rspec-support (~> 3.6.0)
122
126
  rspec-support (3.6.0)
127
+ sidekiq (4.2.10)
128
+ concurrent-ruby (~> 1.0)
129
+ connection_pool (~> 2.2, >= 2.2.0)
130
+ rack-protection (>= 1.5.0)
131
+ redis (~> 3.2, >= 3.2.1)
123
132
  sprockets (3.7.1)
124
133
  concurrent-ruby (~> 1.0)
125
134
  rack (> 1, < 3)
@@ -146,7 +155,8 @@ DEPENDENCIES
146
155
  rake
147
156
  rspec (>= 3.0)
148
157
  rspec-rails
158
+ sidekiq
149
159
  thor
150
160
 
151
161
  BUNDLED WITH
152
- 1.12.5
162
+ 1.15.1
@@ -15,7 +15,7 @@ module MultiTenant
15
15
  FOR t IN SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'public' AND tablename NOT IN (%s) LOOP
16
16
  EXECUTE 'SELECT EXISTS (SELECT * from pg_class c WHERE c.relkind = ''S'' AND c.relname=''' || t.tablename || '_id_seq'')' into seq_exists;
17
17
  IF seq_exists THEN
18
- EXECUTE 'SELECT last_value != start_value FROM ' || t.tablename || '_id_seq' INTO needs_truncate;
18
+ EXECUTE 'SELECT is_called FROM ' || t.tablename || '_id_seq' INTO needs_truncate;
19
19
  ELSE
20
20
  needs_truncate := true;
21
21
  END IF;
@@ -24,6 +24,8 @@ module MultiTenant
24
24
 
25
25
  def citus_version
26
26
  execute("SELECT extversion FROM pg_extension WHERE extname = 'citus'").getvalue(0,0).try(:split, '-').try(:first)
27
+ rescue ArgumentError => e
28
+ raise unless e.message == "invalid tuple number 0"
27
29
  end
28
30
  end
29
31
  end
@@ -32,16 +32,21 @@ module MultiTenant
32
32
  @primary_key = DEFAULT_ID_FIELD
33
33
  end
34
34
  end
35
+
36
+ def inherited(subclass)
37
+ super
38
+ MultiTenant.register_multi_tenant_model(subclass.table_name, subclass) if subclass.table_name
39
+ end
35
40
  end
36
41
 
37
- MultiTenant.register_multi_tenant_model(table_name, self)
42
+ MultiTenant.register_multi_tenant_model(table_name, self) if table_name
38
43
 
39
44
  @partition_key = options[:partition_key] || MultiTenant.partition_key(tenant_name)
40
45
  partition_key = @partition_key
41
46
 
42
47
  # Create an implicit belongs_to association only if tenant class exists
43
48
  if MultiTenant.tenant_klass_defined?(tenant_name)
44
- belongs_to tenant_name, options.slice(:class_name, :inverse_of).merge(foreign_key: partition_key)
49
+ belongs_to tenant_name, options.slice(:class_name, :inverse_of).merge(foreign_key: options[:partition_key])
45
50
  end
46
51
 
47
52
  # New instances should have the tenant set
@@ -66,7 +71,7 @@ module MultiTenant
66
71
  end
67
72
 
68
73
  define_method "#{tenant_name}" do
69
- if !MultiTenant.current_tenant_is_id? && MultiTenant.current_tenant_id && public_send(partition_key) == MultiTenant.current_tenant_id
74
+ if !association(tenant_name.to_sym).loaded? && !MultiTenant.current_tenant_is_id? && MultiTenant.current_tenant_id && public_send(partition_key) == MultiTenant.current_tenant_id
70
75
  return MultiTenant.current_tenant
71
76
  else
72
77
  super()
@@ -4,48 +4,114 @@ module MultiTenant
4
4
  class ArelTenantVisitor < Arel::Visitors::DepthFirst
5
5
  def initialize(arel)
6
6
  super(Proc.new {})
7
- @tenant_relations = []
7
+ @tenant_relations = {}
8
+ @existing_tenant_relations = {}
9
+ @outer_joins_by_table_name = {}
10
+ @statement_node_id = nil
8
11
 
9
12
  accept(arel.ast)
10
13
  end
11
14
 
12
15
  def tenant_relations
13
- @tenant_relations.uniq
16
+ @tenant_relations
14
17
  end
15
18
 
16
- def visit_Arel_Table(table, _collector = nil)
17
- @tenant_relations << table if tenant_relation?(table)
19
+ def existing_tenant_relations
20
+ @existing_tenant_relations
18
21
  end
19
22
 
20
- def visit_Arel_Nodes_TableAlias(table_alias, _collector = nil)
21
- @tenant_relations << table_alias if tenant_relation?(table_alias.left)
23
+ def outer_joins_by_table_name
24
+ @outer_joins_by_table_name
22
25
  end
23
26
 
27
+ def visit_Arel_Table(o, _collector = nil)
28
+ @tenant_relations[@statement_node_id] ||= []
29
+ @tenant_relations[@statement_node_id] << o if tenant_relation?(o.table_name)
30
+ end
31
+ alias :visit_Arel_Nodes_TableAlias :visit_Arel_Table
32
+
33
+ def visit_Arel_Nodes_SelectStatement(o, _collector = nil)
34
+ @statement_node_id = o.object_id
35
+ visit o.cores
36
+ visit o.orders
37
+ visit o.limit
38
+ visit o.lock
39
+ visit o.offset
40
+ end
41
+
42
+ def visit_Arel_Nodes_Equality(o, _collector = nil)
43
+ if o.left.is_a?(Arel::Attributes::Attribute)
44
+ table_name = o.left.relation.table_name
45
+ model = MultiTenant.multi_tenant_model_for_table(table_name)
46
+ @existing_tenant_relations[@statement_node_id] ||= []
47
+ @existing_tenant_relations[@statement_node_id] << o.left.relation if model.present? && o.left.name == model.partition_key
48
+ end
49
+ end
50
+
51
+ def visit_Arel_Nodes_OuterJoin(o, collector = nil)
52
+ if o.left.is_a?(Arel::Nodes::TableAlias) || o.left.is_a?(Arel::Table)
53
+ @outer_joins_by_table_name[@statement_node_id] ||= {}
54
+ @outer_joins_by_table_name[@statement_node_id][o.left.name] = o
55
+ end
56
+ visit o.left
57
+ visit o.right
58
+ end
59
+ alias :visit_Arel_Nodes_FullOuterJoin :visit_Arel_Nodes_OuterJoin
60
+ alias :visit_Arel_Nodes_RightOuterJoin :visit_Arel_Nodes_OuterJoin
61
+
24
62
  private
25
63
 
26
- def tenant_relation?(table)
27
- MultiTenant.multi_tenant_model_for_table(table.name).present?
64
+ def tenant_relation?(table_name)
65
+ MultiTenant.multi_tenant_model_for_table(table_name).present?
28
66
  end
29
67
  end
30
68
  end
31
69
 
70
+ require 'active_record/relation'
32
71
  module ActiveRecord
33
- module ConnectionAdapters # :nodoc:
34
- module DatabaseStatements
35
- alias :to_sql_orig :to_sql
36
- # Converts an arel AST to SQL
37
- def to_sql(arel, binds = [])
38
- if MultiTenant.current_tenant_id && !MultiTenant.with_write_only_mode_enabled? &&
39
- [Arel::SelectManager, Arel::UpdateManager, Arel::DeleteManager, ActiveRecord::Relation].include?(arel.class)
40
- relations_needing_tenant_id = MultiTenant::ArelTenantVisitor.new(arel).tenant_relations
41
- arel = relations_needing_tenant_id.reduce(arel) do |arel, relation|
72
+ module QueryMethods
73
+ alias :build_arel_orig :build_arel
74
+ def build_arel
75
+ arel = build_arel_orig
76
+
77
+ if MultiTenant.current_tenant_id && !MultiTenant.with_write_only_mode_enabled?
78
+ visitor = MultiTenant::ArelTenantVisitor.new(arel)
79
+ visitor.tenant_relations.each do |statement_node_id, relations|
80
+ # Process every level of the statement separately, so we don't mix subselects
81
+ known_relations = visitor.existing_tenant_relations[statement_node_id] || []
82
+ outer_joins_by_table_name = visitor.outer_joins_by_table_name[statement_node_id] || {}
83
+
84
+ relations.each do |relation|
42
85
  model = MultiTenant.multi_tenant_model_for_table(relation.table_name)
43
- next arel unless model.present?
44
- arel.where(relation[model.partition_key].eq(MultiTenant.current_tenant_id))
86
+ next unless model.present?
87
+ next if known_relations.map(&:name).include?(relation.name)
88
+
89
+ tenant_relation = known_relations.reject { |r| outer_joins_by_table_name.key?(r.name) }.first
90
+ tenant_value = if tenant_relation.present?
91
+ known_model = MultiTenant.multi_tenant_model_for_table(tenant_relation.table_name)
92
+ tenant_relation[known_model.partition_key]
93
+ else
94
+ MultiTenant.current_tenant_id
95
+ end
96
+
97
+ known_relations << relation
98
+
99
+ outer_join = outer_joins_by_table_name[relation.name]
100
+ if outer_join
101
+ outer_join.right.expr = Arel::Nodes::And.new([outer_join.right.expr, relation[model.partition_key].eq(tenant_value)])
102
+ else
103
+ ctx = arel.ast.cores.last
104
+ if ctx.wheres.size == 1
105
+ ctx.wheres = [Arel::Nodes::And.new([ctx.wheres.first, relation[model.partition_key].eq(tenant_value)])]
106
+ else
107
+ arel = arel.where(relation[model.partition_key].eq(tenant_value))
108
+ end
109
+ end
45
110
  end
46
111
  end
47
- to_sql_orig(arel, binds)
48
112
  end
113
+
114
+ arel
49
115
  end
50
116
  end
51
117
  end
@@ -18,8 +18,7 @@ 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'])
22
- MultiTenant.with(tenant) do
21
+ MultiTenant.with(msg['multi_tenant']['id']) do
23
22
  yield
24
23
  end
25
24
  else
@@ -1,3 +1,3 @@
1
1
  module MultiTenant
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe MultiTenant::FastTruncate do
4
+ before(:each) do
5
+ MultiTenant::FastTruncate.run
6
+ end
7
+
8
+ it "truncates tables that have exactly one row inserted" do
9
+ Account.create! name: 'foo'
10
+ expect {
11
+ MultiTenant::FastTruncate.run
12
+ }.to change { Account.count }.from(1).to(0)
13
+ end
14
+
15
+ it "truncates tables that have more than one row inserted" do
16
+ Account.create! name: 'foo'
17
+ Account.create! name: 'bar'
18
+
19
+ expect {
20
+ MultiTenant::FastTruncate.run
21
+ }.to change { Account.count }.from(2).to(0)
22
+ end
23
+ end
@@ -150,8 +150,8 @@ describe MultiTenant do
150
150
  let(:account) { Account.create!(name: 'foo') }
151
151
  let(:project) { Project.create!(name: 'project', account: account) }
152
152
  let(:manager) { Manager.create!(name: 'manager', account: account, project: project) }
153
- let(:task) { project.tasks.create!(name: 'task') }
154
- let(:sub_task) { task.sub_tasks.create!(name: 'sub task') }
153
+ let(:task) { project.tasks.create!(name: 'eager loading test task') }
154
+ let(:sub_task) { task.sub_tasks.create!(name: 'eager loading test sub task') }
155
155
 
156
156
  it 'handles table aliases through joins' do
157
157
  MultiTenant.with(account) do
@@ -162,11 +162,22 @@ describe MultiTenant do
162
162
  end
163
163
  end
164
164
 
165
- describe 'Subclass of Multi Tenant Model' do
165
+ describe 'sub selects' do
166
166
  let(:account) { Account.create!(name: 'foo') }
167
167
  let(:project) { Project.create!(name: 'project', account: account) }
168
- let(:task) { project.tasks.create!(name: 'task') }
169
- let(:sti_task) { StiSubTask.create!(task: task, name: 'sub task') }
168
+
169
+ it 'rewrites sub-selects correctly' do
170
+ MultiTenant.with(account) do
171
+ expect(Project.where(id: Project.where(id: project.id)).where(id: Project.where(id: project.id)).first).to eq project
172
+ end
173
+ end
174
+ end
175
+
176
+ describe 'STI Subclass of Multi Tenant Model' do
177
+ let(:account) { Account.create!(name: 'foo') }
178
+ let(:project) { Project.create!(name: 'project', account: account) }
179
+ let(:task) { project.tasks.create!(name: 'subclass test task') }
180
+ let(:sti_task) { StiSubTask.create!(task: task, name: 'subclass test sub task') }
170
181
 
171
182
  it 'has partition key' do
172
183
  expect(StiSubTask.partition_key).to eq 'account_id'
@@ -177,21 +188,43 @@ describe MultiTenant do
177
188
  expect(StiSubTask.primary_key).to eq 'id'
178
189
  end
179
190
 
180
- it 'handles belongs_to through' do
191
+ it 'handles associations' do
181
192
  MultiTenant.with(account) do
182
193
  expect(sti_task.project).to eq project
194
+ expect(project.sub_tasks.to_a).to eq [sti_task]
183
195
  end
184
196
  end
197
+ end
185
198
 
186
- it 'handles has_many through' do
187
- MultiTenant.with(account) do
188
- expect(project.sub_tasks).to eq [sti_task]
189
- end
199
+ describe 'non-STI Subclass of abstract Multi Tenant Model' do
200
+ let(:tenant_id_1) { 42 }
201
+ let(:tenant_id_2) { 314158 }
202
+ let(:name) { 'fooname' }
203
+ let(:subclass_task_1) do
204
+ MultiTenant.with(tenant_id_1) { SubclassTask.create! name: name }
205
+ end
206
+ let(:subclass_task_2) do
207
+ MultiTenant.with(tenant_id_2) { SubclassTask.create! name: name }
190
208
  end
191
209
 
192
- it 'handles unscoped' do
193
- MultiTenant.with(account) do
194
- expect(StiSubTask.unscoped.find(sti_task.id)).to eq sti_task
210
+ before do
211
+ subclass_task_1
212
+ subclass_task_2
213
+ end
214
+
215
+ it 'injects tenant_id on create' do
216
+ expect(subclass_task_1.non_model_id).to be tenant_id_1
217
+ expect(subclass_task_2.non_model_id).to be tenant_id_2
218
+ end
219
+
220
+ it 'rewrites query' do
221
+ MultiTenant.with(tenant_id_1) do
222
+ expect(SubclassTask.where(name: name).count).to eq 1
223
+ expect(SubclassTask.where(name: name).first).to eq subclass_task_1
224
+ end
225
+ MultiTenant.with(tenant_id_2) do
226
+ expect(SubclassTask.where(name: name).count).to eq 1
227
+ expect(SubclassTask.where(name: name).first).to eq subclass_task_2
195
228
  end
196
229
  end
197
230
  end
@@ -253,4 +286,20 @@ describe MultiTenant do
253
286
  end
254
287
  end
255
288
  end
289
+
290
+ if ActiveRecord::VERSION::MAJOR > 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR > 0)
291
+ # Reflection
292
+ describe 'with unsaved association' do
293
+ before do
294
+ @account = Account.create!(name: 'reflection tenant')
295
+ @manager = Manager.new(account: @account)
296
+ MultiTenant.current_tenant = @account
297
+ @account.update! name: 'reflection tenant update'
298
+ end
299
+
300
+ it 'persists the reflected association' do
301
+ expect(@manager.persisted?).to eq(true)
302
+ end
303
+ end
304
+ end
256
305
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'sidekiq/client'
3
+ require 'activerecord-multi-tenant/sidekiq'
4
+
5
+ describe MultiTenant, 'Sidekiq' do
6
+ let(:server) { Sidekiq::Middleware::MultiTenant::Server.new }
7
+
8
+ describe 'server middleware' do
9
+ it 'sets the multitenant context when provided in message' do
10
+ tenant_id = 1234
11
+ server.call(double, {'bogus' => 'message',
12
+ 'multi_tenant' => { 'class' => MultiTenant.current_tenant_class, 'id' => tenant_id}},
13
+ 'bogus_queue') do
14
+ expect(MultiTenant.current_tenant).to eq(tenant_id)
15
+ end
16
+ end
17
+
18
+ it 'does not set the multitenant context when no tenant provided' do
19
+ server.call(double, {'bogus' => 'message'}, 'bogus_queue') do
20
+ expect(MultiTenant.current_tenant).to be_nil
21
+ end
22
+ end
23
+ end
24
+ end
data/spec/schema.rb CHANGED
@@ -61,6 +61,11 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
61
61
  t.column :name, :string
62
62
  end
63
63
 
64
+ create_table :subclass_tasks, force: true, partition_key: :non_model_id do |t|
65
+ t.column :non_model_id, :integer
66
+ t.column :name, :string
67
+ end
68
+
64
69
  create_distributed_table :accounts, :id
65
70
  create_distributed_table :projects, :account_id
66
71
  create_distributed_table :managers, :account_id
@@ -70,11 +75,13 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
70
75
  create_distributed_table :custom_partition_key_tasks, :accountID
71
76
  create_distributed_table :comments, :account_id
72
77
  create_distributed_table :partition_key_not_model_tasks, :non_model_id
78
+ create_distributed_table :subclass_tasks, :non_model_id
73
79
  end
74
80
 
75
81
  class Account < ActiveRecord::Base
76
82
  multi_tenant :account
77
83
  has_many :projects
84
+ has_one :manager, inverse_of: :account
78
85
  end
79
86
 
80
87
  class Project < ActiveRecord::Base
@@ -137,6 +144,14 @@ class PartitionKeyNotModelTask < ActiveRecord::Base
137
144
  multi_tenant :non_model
138
145
  end
139
146
 
147
+ class AbstractTask < ActiveRecord::Base
148
+ self.abstract_class = true
149
+ multi_tenant :non_model
150
+ end
151
+
152
+ class SubclassTask < AbstractTask
153
+ end
154
+
140
155
  class Comment < ActiveRecord::Base
141
156
  multi_tenant :account
142
157
  belongs_to :commentable, polymorphic: true
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: 0.6.0
4
+ version: 0.7.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: 2017-06-10 00:00:00.000000000 Z
11
+ date: 2017-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: request_store
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sidekiq
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: thor
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -152,10 +166,12 @@ files:
152
166
  - lib/activerecord-multi-tenant/version.rb
153
167
  - lib/activerecord-multi-tenant/with_lock.rb
154
168
  - spec/activerecord-multi-tenant/controller_extensions_spec.rb
169
+ - spec/activerecord-multi-tenant/fast_truncate_spec.rb
155
170
  - spec/activerecord-multi-tenant/model_extensions_spec.rb
156
171
  - spec/activerecord-multi-tenant/record_callback_spec.rb
157
172
  - spec/activerecord-multi-tenant/record_finding_spec.rb
158
173
  - spec/activerecord-multi-tenant/record_modifications_spec.rb
174
+ - spec/activerecord-multi-tenant/sidekiq_spec.rb
159
175
  - spec/database.yml
160
176
  - spec/schema.rb
161
177
  - spec/spec_helper.rb