octoshark 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/ci-legacy.yml +66 -0
- data/.github/workflows/ci.yml +64 -0
- data/.ruby-version +1 -1
- data/Appraisals +20 -10
- data/CHANGELOG.md +19 -0
- data/Gemfile +1 -0
- data/README.md +187 -72
- data/Rakefile +4 -4
- data/gemfiles/rails3.0.gemfile +9 -0
- data/gemfiles/rails3.1.gemfile +3 -1
- data/gemfiles/rails3.2.gemfile +3 -1
- data/gemfiles/{rails4.gemfile → rails4.0.gemfile} +3 -1
- data/gemfiles/rails4.1.gemfile +3 -1
- data/gemfiles/rails4.2.gemfile +3 -1
- data/gemfiles/rails5.0.gemfile +9 -0
- data/gemfiles/rails5.1.gemfile +9 -0
- data/gemfiles/rails5.2.gemfile +9 -0
- data/gemfiles/rails6.0.gemfile +9 -0
- data/gemfiles/rails6.1.gemfile +9 -0
- data/lib/octoshark.rb +4 -19
- data/lib/octoshark/active_record_extensions.rb +18 -34
- data/lib/octoshark/connection_manager.rb +8 -106
- data/lib/octoshark/connection_pools_manager.rb +97 -0
- data/lib/octoshark/current_connection.rb +37 -0
- data/lib/octoshark/version.rb +1 -1
- data/octoshark.gemspec +3 -4
- data/spec/octoshark/active_record_extensions_spec.rb +4 -4
- data/spec/octoshark/connection_manager_spec.rb +22 -219
- data/spec/octoshark/connection_pools_manager_spec.rb +180 -0
- data/spec/octoshark/current_connection_spec.rb +74 -0
- data/spec/support/{config.yml.travis → config.yml.github} +6 -6
- metadata +30 -33
- data/.travis.yml +0 -18
- data/spec/octoshark_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 743be64d2aeb897827aec871dfd86150ea1ca7ce6505839cea1caf16e85676bc
|
4
|
+
data.tar.gz: a801773571a7388fed3d16179be6006d10a6e060fb514813a2e21ba3532816ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fa8e793d0eae00f5d99420e1f3b5bc6218b9b5a5b8c27b0c63dbbde080265d5797cc4d56cbce049cd25459e0f8961228f0acb5d677f35f1de45a42fbd82f610
|
7
|
+
data.tar.gz: 16f82b5d43d619ff5d8691229a8377189c10ef3b9d20752649d2e63725bff171afe9f232d2300916b573bff7260ff63d92be08bae567934ff7751285d6240728
|
@@ -0,0 +1,66 @@
|
|
1
|
+
name: Build Legacy
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches:
|
6
|
+
- "*"
|
7
|
+
push:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
runs-on: ubuntu-16.04
|
14
|
+
services:
|
15
|
+
mysql:
|
16
|
+
image: mysql:5.5
|
17
|
+
env:
|
18
|
+
MYSQL_ROOT_PASSWORD: pass
|
19
|
+
ports:
|
20
|
+
- "3306:3306"
|
21
|
+
options: >-
|
22
|
+
--health-cmd="mysqladmin ping"
|
23
|
+
--health-interval=10s
|
24
|
+
--health-timeout=5s
|
25
|
+
--health-retries=3
|
26
|
+
|
27
|
+
name: ruby-${{ matrix.ruby }} ${{ matrix.gemfile }}
|
28
|
+
strategy:
|
29
|
+
matrix:
|
30
|
+
include:
|
31
|
+
- gemfile: rails3.0
|
32
|
+
ruby: 2.2
|
33
|
+
- gemfile: rails3.1
|
34
|
+
ruby: 2.2
|
35
|
+
- gemfile: rails3.2
|
36
|
+
ruby: 2.2
|
37
|
+
|
38
|
+
- gemfile: rails4.0
|
39
|
+
ruby: 2.4
|
40
|
+
- gemfile: rails4.1
|
41
|
+
ruby: 2.4
|
42
|
+
- gemfile: rails4.2
|
43
|
+
ruby: 2.4
|
44
|
+
|
45
|
+
env:
|
46
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
47
|
+
BUNDLE_PATH_RELATIVE_TO_CWD: true
|
48
|
+
|
49
|
+
steps:
|
50
|
+
- uses: actions/checkout@master
|
51
|
+
|
52
|
+
- name: Set up Ruby
|
53
|
+
uses: ruby/setup-ruby@v1
|
54
|
+
with:
|
55
|
+
ruby-version: ${{ matrix.ruby }}
|
56
|
+
bundler: default
|
57
|
+
bundler-cache: true
|
58
|
+
|
59
|
+
- name: Set up database
|
60
|
+
run: |
|
61
|
+
cp spec/support/config.yml.github spec/support/config.yml
|
62
|
+
bundle exec rake db:create
|
63
|
+
|
64
|
+
- name: Run tests
|
65
|
+
run: |
|
66
|
+
bundle exec rspec spec
|
@@ -0,0 +1,64 @@
|
|
1
|
+
name: Build
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches:
|
6
|
+
- "*"
|
7
|
+
push:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
test:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
services:
|
15
|
+
mysql:
|
16
|
+
image: mysql:5.7
|
17
|
+
env:
|
18
|
+
MYSQL_ROOT_PASSWORD: pass
|
19
|
+
ports:
|
20
|
+
- "3306:3306"
|
21
|
+
options: >-
|
22
|
+
--health-cmd="mysqladmin ping"
|
23
|
+
--health-interval=10s
|
24
|
+
--health-timeout=5s
|
25
|
+
--health-retries=3
|
26
|
+
|
27
|
+
name: ruby-${{ matrix.ruby }} ${{ matrix.gemfile }}
|
28
|
+
strategy:
|
29
|
+
matrix:
|
30
|
+
include:
|
31
|
+
- gemfile: rails5.0
|
32
|
+
ruby: 2.6
|
33
|
+
- gemfile: rails5.1
|
34
|
+
ruby: 2.6
|
35
|
+
- gemfile: rails5.2
|
36
|
+
ruby: 2.6
|
37
|
+
|
38
|
+
- gemfile: rails6.0
|
39
|
+
ruby: 2.7
|
40
|
+
- gemfile: rails6.1
|
41
|
+
ruby: 2.7
|
42
|
+
|
43
|
+
env:
|
44
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
45
|
+
BUNDLE_PATH_RELATIVE_TO_CWD: true
|
46
|
+
|
47
|
+
steps:
|
48
|
+
- uses: actions/checkout@master
|
49
|
+
|
50
|
+
- name: Set up Ruby
|
51
|
+
uses: ruby/setup-ruby@v1
|
52
|
+
with:
|
53
|
+
ruby-version: ${{ matrix.ruby }}
|
54
|
+
bundler: default
|
55
|
+
bundler-cache: true
|
56
|
+
|
57
|
+
- name: Set up database
|
58
|
+
run: |
|
59
|
+
cp spec/support/config.yml.github spec/support/config.yml
|
60
|
+
bundle exec rake db:create
|
61
|
+
|
62
|
+
- name: Run tests
|
63
|
+
run: |
|
64
|
+
bundle exec rspec spec
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.4
|
data/Appraisals
CHANGED
@@ -1,19 +1,29 @@
|
|
1
|
-
appraise "
|
2
|
-
gem "activerecord", "~>
|
1
|
+
appraise "rails5.0" do
|
2
|
+
gem "activerecord", "~> 5.0.0"
|
3
|
+
gem "mysql2", "~> 0.5.2"
|
4
|
+
gem "sqlite3", "~> 1.3.13"
|
3
5
|
end
|
4
6
|
|
5
|
-
appraise "
|
6
|
-
gem "activerecord", "~>
|
7
|
+
appraise "rails5.1" do
|
8
|
+
gem "activerecord", "~> 5.1.0"
|
9
|
+
gem "mysql2", "~> 0.5.2"
|
10
|
+
gem "sqlite3", "~> 1.4.1"
|
7
11
|
end
|
8
12
|
|
9
|
-
appraise "
|
10
|
-
gem "activerecord", "~>
|
13
|
+
appraise "rails5.2" do
|
14
|
+
gem "activerecord", "~> 5.2.0"
|
15
|
+
gem "mysql2", "~> 0.5.2"
|
16
|
+
gem "sqlite3", "~> 1.4.1"
|
11
17
|
end
|
12
18
|
|
13
|
-
appraise "
|
14
|
-
gem "activerecord", "~>
|
19
|
+
appraise "rails6.0" do
|
20
|
+
gem "activerecord", "~> 6.0.0"
|
21
|
+
gem "mysql2", "~> 0.5.2"
|
22
|
+
gem "sqlite3", "~> 1.4.1"
|
15
23
|
end
|
16
24
|
|
17
|
-
appraise "
|
18
|
-
gem "activerecord", "~>
|
25
|
+
appraise "rails6.1" do
|
26
|
+
gem "activerecord", "~> 6.1.0"
|
27
|
+
gem "mysql2", "~> 0.5.2"
|
28
|
+
gem "sqlite3", "~> 1.4.1"
|
19
29
|
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## 0.3.0 2021-07-20
|
4
|
+
|
5
|
+
- Add support for Rails 6.1
|
6
|
+
|
7
|
+
## 0.2.2 2016-11-09
|
8
|
+
|
9
|
+
- Fix `alias_method_chain` deprecation in Rails 5
|
10
|
+
|
11
|
+
## 0.2.1 2016-09-09
|
12
|
+
|
13
|
+
- Add support for Rails 5 and Rails 3
|
14
|
+
|
15
|
+
## 0.2.0 2016-08-29
|
16
|
+
|
17
|
+
- `Octoshark::ConnectionManager` is split in two managers `Octoshark::ConnectionPoolsManager` for persistent connections and `Octoshark::ConnectionManager` for non-persistent connections.
|
18
|
+
- `Octoshark` class methods like `connection_managers`, `reset_connection_managers!`, `disconnect!` are only relevant and moved to `Octoshark::ConnectionPoolsManager` class.
|
19
|
+
- `Octoshark::ConnectionManager#use_database` method has been removed and the functionality moved to `Octoshark::ConnectionPoolsManager#with_connection(name, database_name)` where the second optional argument `database_name` when specified will switch the connection to the database using the `use database` MySQL statement.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
![Octoshark logo](
|
1
|
+
![Octoshark logo](http://dalibornasevic.com/images/octoshark.png)
|
2
2
|
|
3
3
|
![Travis status](https://travis-ci.org/dalibor/octoshark.png)
|
4
4
|
|
5
|
-
Octoshark is a simple ActiveRecord connection manager. It provides
|
5
|
+
Octoshark is a simple ActiveRecord connection manager. It provides connection switching mechanisms that can be used in various scenarios like master-slave, sharding or multi-tenant architecture.
|
6
6
|
|
7
7
|
|
8
8
|
## Installation
|
@@ -28,138 +28,142 @@ $ gem install octoshark
|
|
28
28
|
|
29
29
|
## Usage
|
30
30
|
|
31
|
-
|
31
|
+
Octoshark has two connection managers: `ConnectionPoolsManager` for managing connection pools using persistent connections and `ConnectionManager` for managing non-persistent connections. It depends on your application performance and scaling requirements which one to use.
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
```
|
33
|
+
- If you have a limited number of consumers (application and worker servers), `ConnectionPoolsManager` would be the preferred option. Standard Rails application has a single connection pool, and `ConnectionPoolsManager` just makes it possible for application models to work with multiple connection pools.
|
34
|
+
|
35
|
+
- If you have a very big infrastructure with lots of consumers (application and worker servers) and you are hitting max connections limit on database servers, i.e. you need to scale horizontally, `ConnectionManager` is the option to use. Because it uses non-persistent connections it comes up with a performance penalty because connections are re-established over and over again. Some ActiveRecord plugins that depend on having an active database connection all the time might need a change in order to work with non-persistent connections.
|
36
|
+
|
37
|
+
`ConnectionPoolsManager` and `ConnectionManager` can be combined together and many of them can be used at the same time.
|
39
38
|
|
40
|
-
|
39
|
+
Here is how to create connection pools manager:
|
41
40
|
|
42
41
|
```ruby
|
43
|
-
|
44
|
-
def self.connection
|
45
|
-
CONN_MANAGER.current_connection
|
46
|
-
end
|
47
|
-
end
|
42
|
+
CONN_MANAGER = Octoshark::ConnectionPoolsManager.new({ c1: config1, c2: config2 })
|
48
43
|
```
|
49
44
|
|
50
|
-
|
45
|
+
`config1` and `config2` are standard ActiveRecord database configs:
|
51
46
|
|
52
47
|
```ruby
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
config = {
|
49
|
+
adapter: 'mysql2',
|
50
|
+
host: 'localhost',
|
51
|
+
port: 3306,
|
52
|
+
database: 'database',
|
53
|
+
username: 'root',
|
54
|
+
password: 'pass',
|
55
|
+
pool: 1,
|
56
|
+
encoding: 'utf8',
|
57
|
+
reconnect: false
|
58
|
+
}
|
62
59
|
```
|
63
60
|
|
64
|
-
To
|
61
|
+
To switch a connection using a specific pool:
|
65
62
|
|
66
63
|
```ruby
|
67
|
-
CONN_MANAGER.with_connection(:
|
68
|
-
|
69
|
-
Post.first
|
64
|
+
CONN_MANAGER.with_connection(:c1) do |connection|
|
65
|
+
connection.execute("SELECT 1")
|
70
66
|
end
|
71
67
|
```
|
72
68
|
|
73
69
|
Multiple `with_connection` blocks can be nested:
|
74
70
|
|
75
71
|
```ruby
|
76
|
-
CONN_MANAGER.with_connection(
|
77
|
-
# run queries on
|
72
|
+
CONN_MANAGER.with_connection(config1) do
|
73
|
+
# run queries on connection specified with config1
|
78
74
|
|
79
|
-
CONN_MANAGER.with_connection(
|
80
|
-
# run queries on
|
75
|
+
CONN_MANAGER.with_connection(config2) do
|
76
|
+
# run queries on connection specified with config2
|
81
77
|
end
|
82
78
|
|
83
|
-
# run queries on
|
79
|
+
# run queries on connection specified with config1
|
84
80
|
end
|
85
81
|
```
|
86
82
|
|
87
|
-
`
|
83
|
+
If you establish a connection to database server instead of to database, then you can use the second optional argument `database_name` to tell the connection manager to switch the connection to that database within the same connection. This is useful when you want to have fewer connection pools and keep number of active connection per database server under control. This option is only MySQL specific for now, it uses `USE database_name` statement to switch the connection.
|
88
84
|
|
85
|
+
```ruby
|
86
|
+
CONN_MANAGER.with_connection(:c1, database_name) do |connection|
|
87
|
+
connection.execute("SELECT 1")
|
88
|
+
end
|
89
|
+
```
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
Some models are in the core DB, and others in shard DBs. Shard is selected based on a user attribute. For core models use the default ActiveRecord connection and for sharded models define and use Octoshark connections.
|
91
|
+
Using non-persistent connections with `Octoshark::ConnectionManager` has the same API:
|
93
92
|
|
94
|
-
Switch the connection in a controller with an around filter:
|
95
93
|
|
96
94
|
```ruby
|
97
|
-
|
95
|
+
CONN_MANAGER = Octoshark::ConnectionManager.new
|
96
|
+
```
|
98
97
|
|
99
|
-
|
100
|
-
|
98
|
+
Opening a new connection, executing query and closing the connection:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
CONN_MANAGER.with_connection(config) do |connection|
|
102
|
+
connection.execute("SELECT 1")
|
101
103
|
end
|
102
104
|
```
|
103
105
|
|
104
|
-
Similar approach applies to other application entry-points like background jobs.
|
105
|
-
|
106
106
|
|
107
|
-
##
|
107
|
+
## Using Octoshark with ActiveRecord models
|
108
108
|
|
109
|
-
|
109
|
+
To tell an ActiveRecord model to use the Octoshark connection we can override the `Model.connection` method.
|
110
110
|
|
111
111
|
```ruby
|
112
|
-
class ActiveRecord::Base
|
112
|
+
class Post < ActiveRecord::Base
|
113
113
|
def self.connection
|
114
|
-
|
115
|
-
CONN_MANAGER.current_or_default_connection
|
114
|
+
CONN_MANAGER.current_connection
|
116
115
|
end
|
117
116
|
end
|
118
117
|
```
|
119
118
|
|
120
|
-
|
121
|
-
|
119
|
+
Alternatively, we can extract it as a module and include in multiple models.
|
122
120
|
|
123
|
-
|
121
|
+
```ruby
|
122
|
+
module ShardingModel
|
123
|
+
extend ActiveSupport::Concern
|
124
124
|
|
125
|
-
|
125
|
+
module ClassMethods
|
126
|
+
def connection
|
127
|
+
CONN_MANAGER.current_connection
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
126
132
|
|
127
|
-
|
133
|
+
To use a specific database connection:
|
128
134
|
|
129
135
|
```ruby
|
130
|
-
|
131
|
-
|
136
|
+
CONN_MANAGER.with_connection(:c1) do
|
137
|
+
# run queries on c1
|
138
|
+
Post.first
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
This connection switching in Rails applications is usually done from within an `around_filter` for controllers and in a similar way for other application "entry-points" like background jobs:
|
132
143
|
|
133
|
-
|
144
|
+
```ruby
|
134
145
|
around_filter :select_shard
|
135
146
|
|
136
147
|
def select_shard(&block)
|
137
|
-
CONN_MANAGER.
|
148
|
+
CONN_MANAGER.with_connection(current_user.shard, &block)
|
138
149
|
end
|
139
150
|
```
|
140
151
|
|
141
|
-
`CONN_MANAGER.
|
152
|
+
`CONN_MANAGER.current_connection` returns the active connection while the execution is in the `with_connection` block or raises `Octoshark::Error::NoCurrentConnection` outside of the `with_connection` block. In some cases, falling back to the default database connection for the Rails app might be preferable which can be done using `CONN_MANAGER.current_or_default_connection`.
|
142
153
|
|
143
|
-
Alternatively, for better performance (only supported on MySQL), database connection can be switched with `use database` statement. Once connection manager is defined with connectino configs to database servers, selecting a database can be done with:
|
144
154
|
|
145
|
-
|
146
|
-
CONN_MANAGER.use_database(:db1, 'database') do
|
147
|
-
# run queries on database server identified by 'db1' using database 'database'
|
148
|
-
end
|
149
|
-
```
|
155
|
+
## Octoshark::ConnectionPoolsManager.reset_connection_managers!
|
150
156
|
|
151
|
-
|
152
|
-
|
153
|
-
Whenever ActiveRecord::Base calls `establish_connection` (usually by an ancestor process that must have subsequently forked), `Octoshark.reset_connection_managers!` is automatically called to re-establish the Octoshark connections. It prevents `ActiveRecord::ConnectionNotEstablished` in the scenarios like:
|
157
|
+
When using `Octoshark::ConnectionPoolsManager`, whenever ActiveRecord::Base calls `establish_connection` (usually by an ancestor process that must have subsequently forked), `Octoshark.reset_connection_managers!` is automatically called to re-establish the Octoshark connections. It prevents `ActiveRecord::ConnectionNotEstablished` in the scenarios like:
|
154
158
|
|
155
159
|
* Unicorn before/after fork
|
156
160
|
* Spring prefork/serve
|
157
161
|
* Some rake tasks like `rake db:test:prepare`
|
158
162
|
|
159
163
|
|
160
|
-
##
|
164
|
+
## Cleaning test databases
|
161
165
|
|
162
|
-
|
166
|
+
For `Octoshark::ConnectionPoolsManager`, we can use [DatabaseCleaner](https://github.com/DatabaseCleaner/database_cleaner) and RSpec like:
|
163
167
|
|
164
168
|
```ruby
|
165
169
|
config.before(:suite) do
|
@@ -173,13 +177,12 @@ config.before(:each) do
|
|
173
177
|
end
|
174
178
|
|
175
179
|
config.after(:each) do
|
176
|
-
setup_database_cleaner
|
177
180
|
DatabaseCleaner.clean_with(:transaction)
|
178
181
|
end
|
179
182
|
|
180
183
|
def setup_database_cleaner
|
181
184
|
DatabaseCleaner[:active_record, {connection: ActiveRecord::Base.connection_pool}]
|
182
|
-
Octoshark.connection_managers.each do |manager|
|
185
|
+
Octoshark::ConnectionPoolsManager.connection_managers.each do |manager|
|
183
186
|
manager.connection_pools.each_pair do |connection_name, connection_pool|
|
184
187
|
DatabaseCleaner[:active_record, {connection: connection_pool}]
|
185
188
|
end
|
@@ -187,6 +190,118 @@ def setup_database_cleaner
|
|
187
190
|
end
|
188
191
|
```
|
189
192
|
|
193
|
+
For `Octoshark::ConnectionManager` where connections and databases are dynamically created and cannot be configured in the test setup, we can write a custom database cleaner inspired by [DatabaseRewinder](https://github.com/amatsuda/database_rewinder). The example below is for a multi-tenant test setup with a main (core) database and a tenant database for the `CURRENT_USER` in the test suite. `CustomDatabaseCleaner.clean_all` cleans all core database tables before test suite and `CustomDatabaseCleaner.clean` cleans used tables in both core and tenant databases after each test.
|
194
|
+
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
module CustomDatabaseCleaner
|
198
|
+
INSERT_REGEX = /\AINSERT(?:\s+IGNORE)?\s+INTO\s+(?:\.*[`"]?(?<table>[^.\s`"]+)[`"]?)*/i
|
199
|
+
|
200
|
+
@@tables_with_inserts = []
|
201
|
+
|
202
|
+
class << self
|
203
|
+
def record_inserted_table(connection, sql)
|
204
|
+
match = sql.match(INSERT_REGEX)
|
205
|
+
|
206
|
+
if match && match[:table] && tables_with_inserts.exclude?(match[:table])
|
207
|
+
tables_with_inserts << match[:table]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def clean_all
|
212
|
+
with_core_db_connection do |connection|
|
213
|
+
clean_tables(connection)
|
214
|
+
end
|
215
|
+
|
216
|
+
reset_tables_with_inserts
|
217
|
+
end
|
218
|
+
|
219
|
+
def clean
|
220
|
+
with_core_db_connection do |connection|
|
221
|
+
clean_tables(connection, { 'users' => [CURRENT_USER.id] })
|
222
|
+
end
|
223
|
+
|
224
|
+
CURRENT_USER.with_tenant do |connection|
|
225
|
+
clean_tables(connection)
|
226
|
+
end
|
227
|
+
|
228
|
+
reset_tables_with_inserts
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
def with_core_db_connection(&block)
|
233
|
+
CoreDBManager.with_connection(ActiveRecord::Base.configurations[Rails.env].symbolize_keys, &block)
|
234
|
+
end
|
235
|
+
|
236
|
+
def clean_tables(connection, keep_data = {})
|
237
|
+
tables_to_clean = connection.tables.reject { |t| t == ActiveRecord::Migrator.schema_migrations_table_name }
|
238
|
+
tables_to_clean = tables_to_clean & tables_with_inserts if tables_with_inserts.present?
|
239
|
+
|
240
|
+
tables_to_clean.each do |table|
|
241
|
+
connection.disable_referential_integrity do
|
242
|
+
table_name = connection.quote_table_name(table)
|
243
|
+
keep_ids = keep_data[table]
|
244
|
+
|
245
|
+
if keep_ids
|
246
|
+
connection.execute("DELETE FROM #{table_name} WHERE id NOT IN (#{keep_ids.join(',')});")
|
247
|
+
else
|
248
|
+
connection.execute("DELETE FROM #{table_name};")
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def reset_tables_with_inserts
|
255
|
+
@@tables_with_inserts = []
|
256
|
+
end
|
257
|
+
|
258
|
+
def tables_with_inserts
|
259
|
+
@@tables_with_inserts
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
module CustomDatabaseCleaner
|
265
|
+
module InsertRecorder
|
266
|
+
def execute(sql, *)
|
267
|
+
CustomDatabaseCleaner.record_inserted_table(self, sql)
|
268
|
+
super
|
269
|
+
end
|
270
|
+
|
271
|
+
def exec_query(sql, *)
|
272
|
+
CustomDatabaseCleaner.record_inserted_table(self, sql)
|
273
|
+
super
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
279
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.send(:prepend, CustomDatabaseCleaner::InsertRecorder)
|
280
|
+
```
|
281
|
+
|
282
|
+
|
283
|
+
## Development Setup
|
284
|
+
|
285
|
+
Setup database config and create databases:
|
286
|
+
|
287
|
+
```bash
|
288
|
+
cp spec/support/config.yml.template spec/support/config.yml
|
289
|
+
rake db:create
|
290
|
+
```
|
291
|
+
|
292
|
+
Run specs:
|
293
|
+
|
294
|
+
```bash
|
295
|
+
bundle exec rspec spec
|
296
|
+
```
|
297
|
+
|
298
|
+
Install different active record versions defined in `Appraisals` and run specs for all of them:
|
299
|
+
|
300
|
+
```bash
|
301
|
+
bundle exec appraisal
|
302
|
+
bundle exec appraisal rspec spec
|
303
|
+
```
|
304
|
+
|
190
305
|
|
191
306
|
## Logo
|
192
307
|
|