transaction_retry_continued 1.1.0 → 1.2.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/main.yml +38 -18
- data/.gitignore +2 -0
- data/Gemfile +4 -17
- data/README.md +23 -8
- data/Rakefile +5 -3
- data/docker/ruby-2.5/Dockerfile +6 -0
- data/docker/ruby-2.7/Dockerfile +6 -0
- data/docker/ruby-3.0/Dockerfile +6 -0
- data/docker/ruby-3.1/Dockerfile +6 -0
- data/docker/test-ruby-2.5.sh +26 -0
- data/docker/test-ruby-2.7.sh +29 -0
- data/docker/test-ruby-3.0.sh +26 -0
- data/docker/test-ruby-3.1.sh +26 -0
- data/docker/test-ruby.sh +8 -0
- data/docker-compose.yml +60 -0
- data/gemfiles/Gemfile.base +5 -2
- data/lib/transaction_retry/active_record/base.rb +39 -39
- data/lib/transaction_retry/version.rb +3 -1
- data/lib/transaction_retry.rb +9 -9
- data/lib/transaction_retry_continued.rb +2 -0
- data/test/db/all.rb +2 -0
- data/test/db/db.rb +15 -15
- data/test/db/migrations.rb +6 -7
- data/test/db/queued_job.rb +2 -0
- data/test/integration/active_record/base/transaction_with_retry_test.rb +37 -42
- data/test/library_setup.rb +11 -9
- data/test/test_helper.rb +9 -6
- data/test/test_runner.rb +3 -1
- data/transaction_retry_continued.gemspec +15 -14
- metadata +22 -16
- data/d +0 -1
- data/test/test_console.rb +0 -11
- data/tests +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9593b4c28a4caca5aefedbd6543e7407b194216469bbab86f82e346afea00af1
|
4
|
+
data.tar.gz: 61ce1f23b2dbbc67af572171fcb5c369e9133d3aca95987d6148d333ee34bd76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a502cf67b25e82cda4ab5aa3f1630a81b7e8effaf04cbbce10d1f94be7cba8768640c5d8ccd0da855909dc8156298b301cbfc802209bd69b4d33bc79ca8b7890
|
7
|
+
data.tar.gz: 817ecdee03df757c9f001e43fd68ef5943bad51435411234747ee4a8fe95a74b3a63b58d86094b099c69f5726ff8158654db6ec81687c814eb4dccf7839df23d
|
data/.github/workflows/main.yml
CHANGED
@@ -108,7 +108,7 @@ jobs:
|
|
108
108
|
POSTGRESQL_DB_PASS: database
|
109
109
|
POSTGRESQL_DB_NAME: transaction_retry_continued_test
|
110
110
|
steps:
|
111
|
-
- uses: actions/checkout@
|
111
|
+
- uses: actions/checkout@v4
|
112
112
|
- name: Set up Ruby
|
113
113
|
uses: ruby/setup-ruby@v1
|
114
114
|
with:
|
@@ -116,6 +116,12 @@ jobs:
|
|
116
116
|
bundler-cache: true
|
117
117
|
- name: Run bundle update
|
118
118
|
run: bundle update
|
119
|
+
- name: Setup Code Climate test-reporter
|
120
|
+
if: github.ref_name == 'master'
|
121
|
+
run: |
|
122
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
123
|
+
chmod +x ./cc-test-reporter
|
124
|
+
./cc-test-reporter before-build
|
119
125
|
- name: Start Mysql
|
120
126
|
if: matrix.db == 'mysql2'
|
121
127
|
run: |
|
@@ -158,20 +164,34 @@ jobs:
|
|
158
164
|
- name: Shutdown database
|
159
165
|
if: always() && (matrix.db == 'postgresql' || matrix.db == 'mysql2')
|
160
166
|
run: docker stop database
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
167
|
+
- name: Format coverage
|
168
|
+
if: github.ref_name == 'master'
|
169
|
+
run: |
|
170
|
+
./cc-test-reporter format-coverage -t simplecov -o coverage/cc_resultset.json
|
171
|
+
- name: Upload reports' artifacts
|
172
|
+
if: success() || failure()
|
173
|
+
uses: actions/upload-artifact@v4
|
174
|
+
with:
|
175
|
+
name: coverage_artifact_ruby_${{ matrix.ruby }}_ar_${{ matrix.activerecord }}_db_${{ matrix.db }}
|
176
|
+
if-no-files-found: ignore
|
177
|
+
path: coverage
|
178
|
+
retention-days: 1
|
179
|
+
finish:
|
180
|
+
needs: test
|
181
|
+
runs-on: ubuntu-latest
|
182
|
+
if: github.ref_name == 'master'
|
183
|
+
steps:
|
184
|
+
- uses: actions/checkout@v4
|
185
|
+
- name: Download Code Climate test-reporter
|
186
|
+
run: |
|
187
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
188
|
+
chmod +x ./cc-test-reporter
|
189
|
+
- name: Download reports' artifacts
|
190
|
+
uses: actions/download-artifact@v4
|
191
|
+
with:
|
192
|
+
path: coverage_reports
|
193
|
+
pattern: coverage_artifact_*
|
194
|
+
- name: sum-coverage
|
195
|
+
run: |
|
196
|
+
./cc-test-reporter sum-coverage coverage_reports/coverage_artifact_ruby*/cc_resultset.json -o coverage/codeclimate.json
|
197
|
+
./cc-test-reporter upload-coverage -r ${{secrets.CC_TEST_REPORTER_ID}} -i coverage/codeclimate.json
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,23 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'http://rubygems.org'
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
gemfile_local = File.expand_path '
|
7
|
+
gemfile_local = File.expand_path 'Gemfile.local', __dir__
|
8
8
|
eval File.read(gemfile_local), binding, gemfile_local if File.exist? gemfile_local
|
9
|
-
|
10
|
-
|
11
|
-
# gem 'transaction_isolation', git: 'https://github.com/alittlebit/transaction_isolation'
|
12
|
-
# # Use the gem instead of a dated version bundled with Ruby
|
13
|
-
# gem 'minitest', '5.3.4'
|
14
|
-
# gem 'simplecov', :require => false
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# group :development do
|
18
|
-
# gem 'transaction_isolation', git: 'https://github.com/alittlebit/transaction_isolation'
|
19
|
-
# gem 'rake'
|
20
|
-
# # enhance irb
|
21
|
-
# gem 'awesome_print', :require => false
|
22
|
-
# gem 'pry', :require => false
|
23
|
-
# end
|
9
|
+
|
10
|
+
gem 'rubocop', '~> 1.64'
|
data/README.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/transaction_retry_continued.svg)](https://badge.fury.io/rb/transaction_retry_continued)
|
2
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/2a527711b8077f4be2a2/maintainability)](https://codeclimate.com/github/iagopiimenta/transaction_retry_continued/maintainability)
|
3
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/2a527711b8077f4be2a2/test_coverage)](https://codeclimate.com/github/iagopiimenta/transaction_retry_continued/test_coverage)
|
4
|
+
[![CI PR Builds](https://github.com/iagopiimenta/transaction_retry_continued/actions/workflows/main.yml/badge.svg)](https://github.com/iagopiimenta/transaction_retry_continued/actions/workflows/main.yml)
|
5
|
+
|
1
6
|
# Transaction Retry Continued
|
2
7
|
|
3
8
|
> This is a community-driven continuation of the [`transaction_retry`](https://github.com/qertoip/transaction_retry) gem
|
@@ -31,7 +36,7 @@ __It works out of the box with Ruby on Rails__.
|
|
31
36
|
If you have a standalone ActiveRecord-based project you'll need to call:
|
32
37
|
|
33
38
|
```ruby
|
34
|
-
|
39
|
+
TransactionRetry.apply_activerecord_patch # after connecting to the database
|
35
40
|
```
|
36
41
|
|
37
42
|
## Database deadlock and serialization errors that are retried
|
@@ -83,13 +88,23 @@ This gem was initially developed for and successfully works in production at [Ko
|
|
83
88
|
|
84
89
|
Run tests on the selected database (mysql2 by default):
|
85
90
|
|
86
|
-
|
87
|
-
|
88
|
-
|
91
|
+
```bash
|
92
|
+
# passing desired database, active record version and ruby version
|
93
|
+
docker compose run -e db=sqlite3 -e BUNDLE_GEMFILE=gemfiles/activerecord-7.0/Gemfile.sqlite3 ruby_2_5 bash -c ./docker/test-ruby.sh
|
94
|
+
|
95
|
+
# db options: mysql2, postgresql, sqlite3
|
96
|
+
# active record version options: 5.2, 6.0, 6.1, 7.0
|
97
|
+
# ruby version options: 2.5, 2.7, 3.0, 3.1
|
98
|
+
```
|
89
99
|
|
90
|
-
Run tests on all supported databases:
|
100
|
+
Run tests on all supported databases by ruby version:
|
91
101
|
|
92
|
-
|
102
|
+
```bash
|
103
|
+
docker compose up ruby_2_5
|
104
|
+
docker compose up ruby_2_7
|
105
|
+
docker compose up ruby_3_0
|
106
|
+
docker compose up ruby_3_1
|
107
|
+
```
|
93
108
|
|
94
109
|
Database configuration is hardcoded in test/db/db.rb; feel free to improve this and submit a pull request.
|
95
110
|
|
@@ -99,8 +114,8 @@ You should be very suspicious about any gem that monkey patches your stock Ruby
|
|
99
114
|
|
100
115
|
This gem is carefully written to not be more intrusive than it needs to be:
|
101
116
|
|
102
|
-
|
103
|
-
|
117
|
+
- wraps ActiveRecord::Base#transaction class method using alias_method to add new behaviour
|
118
|
+
- introduces two new private class methods in ActiveRecord::Base (with names that should never collide)
|
104
119
|
|
105
120
|
## License
|
106
121
|
|
data/Rakefile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
2
4
|
|
3
5
|
require 'rake/testtask'
|
4
6
|
|
5
7
|
Rake::TestTask.new do |t|
|
6
|
-
t.libs += [
|
8
|
+
t.libs += %w[test lib]
|
7
9
|
t.pattern = 'test/integration/**/*_test.rb'
|
8
10
|
t.verbose = true
|
9
11
|
end
|
10
12
|
|
11
|
-
task :
|
13
|
+
task default: [:test]
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# fix conflicts between different ruby versions
|
4
|
+
rm -rf gemfiles/**/Gemfile.*.lock
|
5
|
+
|
6
|
+
gemfiles=(
|
7
|
+
"gemfiles/activerecord-5.2/Gemfile.sqlite3"
|
8
|
+
"gemfiles/activerecord-6.0/Gemfile.sqlite3"
|
9
|
+
"gemfiles/activerecord-6.1/Gemfile.sqlite3"
|
10
|
+
|
11
|
+
"gemfiles/activerecord-5.2/Gemfile.mysql2"
|
12
|
+
"gemfiles/activerecord-6.0/Gemfile.mysql2"
|
13
|
+
"gemfiles/activerecord-6.1/Gemfile.mysql2"
|
14
|
+
|
15
|
+
"gemfiles/activerecord-5.2/Gemfile.postgresql"
|
16
|
+
"gemfiles/activerecord-6.0/Gemfile.postgresql"
|
17
|
+
"gemfiles/activerecord-6.1/Gemfile.postgresql"
|
18
|
+
)
|
19
|
+
|
20
|
+
for gemfile in "${gemfiles[@]}"; do
|
21
|
+
database=$(echo $gemfile | cut -d'.' -f3)
|
22
|
+
|
23
|
+
echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
24
|
+
BUNDLE_GEMFILE=$gemfile bundle install
|
25
|
+
BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
26
|
+
done
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# fix conflicts between different ruby versions
|
4
|
+
rm -rf gemfiles/**/Gemfile.*.lock
|
5
|
+
|
6
|
+
gemfiles=(
|
7
|
+
"gemfiles/activerecord-5.2/Gemfile.sqlite3"
|
8
|
+
"gemfiles/activerecord-6.0/Gemfile.sqlite3"
|
9
|
+
"gemfiles/activerecord-6.1/Gemfile.sqlite3"
|
10
|
+
"gemfiles/activerecord-7.0/Gemfile.sqlite3"
|
11
|
+
|
12
|
+
"gemfiles/activerecord-5.2/Gemfile.mysql2"
|
13
|
+
"gemfiles/activerecord-6.0/Gemfile.mysql2"
|
14
|
+
"gemfiles/activerecord-6.1/Gemfile.mysql2"
|
15
|
+
"gemfiles/activerecord-7.0/Gemfile.mysql2"
|
16
|
+
|
17
|
+
"gemfiles/activerecord-5.2/Gemfile.postgresql"
|
18
|
+
"gemfiles/activerecord-6.0/Gemfile.postgresql"
|
19
|
+
"gemfiles/activerecord-6.1/Gemfile.postgresql"
|
20
|
+
"gemfiles/activerecord-7.0/Gemfile.postgresql"
|
21
|
+
)
|
22
|
+
|
23
|
+
for gemfile in "${gemfiles[@]}"; do
|
24
|
+
database=$(echo $gemfile | cut -d'.' -f3)
|
25
|
+
|
26
|
+
echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
27
|
+
BUNDLE_GEMFILE=$gemfile bundle install
|
28
|
+
BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
29
|
+
done
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# fix conflicts between different ruby versions
|
4
|
+
rm -rf gemfiles/**/Gemfile.*.lock
|
5
|
+
|
6
|
+
gemfiles=(
|
7
|
+
"gemfiles/activerecord-6.0/Gemfile.sqlite3"
|
8
|
+
"gemfiles/activerecord-6.1/Gemfile.sqlite3"
|
9
|
+
"gemfiles/activerecord-7.0/Gemfile.sqlite3"
|
10
|
+
|
11
|
+
"gemfiles/activerecord-6.0/Gemfile.mysql2"
|
12
|
+
"gemfiles/activerecord-6.1/Gemfile.mysql2"
|
13
|
+
"gemfiles/activerecord-7.0/Gemfile.mysql2"
|
14
|
+
|
15
|
+
"gemfiles/activerecord-6.0/Gemfile.postgresql"
|
16
|
+
"gemfiles/activerecord-6.1/Gemfile.postgresql"
|
17
|
+
"gemfiles/activerecord-7.0/Gemfile.postgresql"
|
18
|
+
)
|
19
|
+
|
20
|
+
for gemfile in "${gemfiles[@]}"; do
|
21
|
+
database=$(echo $gemfile | cut -d'.' -f3)
|
22
|
+
|
23
|
+
echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
24
|
+
BUNDLE_GEMFILE=$gemfile bundle install
|
25
|
+
BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
26
|
+
done
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# fix conflicts between different ruby versions
|
4
|
+
rm -rf gemfiles/**/Gemfile.*.lock
|
5
|
+
|
6
|
+
gemfiles=(
|
7
|
+
"gemfiles/activerecord-6.0/Gemfile.sqlite3"
|
8
|
+
"gemfiles/activerecord-6.1/Gemfile.sqlite3"
|
9
|
+
"gemfiles/activerecord-7.0/Gemfile.sqlite3"
|
10
|
+
|
11
|
+
"gemfiles/activerecord-6.0/Gemfile.mysql2"
|
12
|
+
"gemfiles/activerecord-6.1/Gemfile.mysql2"
|
13
|
+
"gemfiles/activerecord-7.0/Gemfile.mysql2"
|
14
|
+
|
15
|
+
"gemfiles/activerecord-6.0/Gemfile.postgresql"
|
16
|
+
"gemfiles/activerecord-6.1/Gemfile.postgresql"
|
17
|
+
"gemfiles/activerecord-7.0/Gemfile.postgresql"
|
18
|
+
)
|
19
|
+
|
20
|
+
for gemfile in "${gemfiles[@]}"; do
|
21
|
+
database=$(echo $gemfile | cut -d'.' -f3)
|
22
|
+
|
23
|
+
echo BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
24
|
+
BUNDLE_GEMFILE=$gemfile bundle install
|
25
|
+
BUNDLE_GEMFILE=$gemfile db=$database bundle exec rake test
|
26
|
+
done
|
data/docker/test-ruby.sh
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# fix conflicts between different ruby versions
|
4
|
+
rm -rf gemfiles/**/Gemfile.*.lock
|
5
|
+
|
6
|
+
echo BUNDLE_GEMFILE=$BUNDLE_GEMFILE db=$db bundle exec rake test
|
7
|
+
BUNDLE_GEMFILE=$BUNDLE_GEMFILE bundle install
|
8
|
+
BUNDLE_GEMFILE=$BUNDLE_GEMFILE db=$db bundle exec rake test
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
services:
|
2
|
+
ruby_2_5: &ruby_2_5
|
3
|
+
working_dir: /app
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
dockerfile: docker/ruby-2.5/Dockerfile
|
7
|
+
volumes:
|
8
|
+
- .:/app
|
9
|
+
command: bash -c "bundle install && bundle exec rake test"
|
10
|
+
environment:
|
11
|
+
POSTGRESQL_DB_HOST: postgres
|
12
|
+
POSTGRESQL_DB_USER: transaction_retry_continued
|
13
|
+
POSTGRESQL_DB_PASS: database
|
14
|
+
POSTGRESQL_DB_NAME: transaction_isolation_continued_test
|
15
|
+
MYSQL_DB_HOST: mysql
|
16
|
+
MYSQL_DB_USER: root
|
17
|
+
MYSQL_DB_PASS: database
|
18
|
+
MYSQL_DB_NAME: transaction_isolation_continued_test
|
19
|
+
depends_on:
|
20
|
+
- mysql
|
21
|
+
- postgres
|
22
|
+
ruby_2_7:
|
23
|
+
<<: *ruby_2_5
|
24
|
+
build:
|
25
|
+
context: .
|
26
|
+
dockerfile: docker/ruby-2.7/Dockerfile
|
27
|
+
ruby_3_0:
|
28
|
+
<<: *ruby_2_5
|
29
|
+
command: ./docker/test-ruby-3.0.sh
|
30
|
+
build:
|
31
|
+
context: .
|
32
|
+
dockerfile: docker/ruby-3.0/Dockerfile
|
33
|
+
ruby_3_1:
|
34
|
+
<<: *ruby_2_5
|
35
|
+
command: ./docker/test-ruby-3.1.sh
|
36
|
+
build:
|
37
|
+
context: .
|
38
|
+
dockerfile: docker/ruby-3.1/Dockerfile
|
39
|
+
postgres:
|
40
|
+
image: postgres:9.6
|
41
|
+
environment:
|
42
|
+
POSTGRES_USER: transaction_retry_continued
|
43
|
+
POSTGRES_PASSWORD: database
|
44
|
+
POSTGRES_DB: transaction_isolation_continued_test
|
45
|
+
ports:
|
46
|
+
- "5432:5432"
|
47
|
+
healthcheck:
|
48
|
+
test: [ "CMD-SHELL", "pg_isready -q" ]
|
49
|
+
interval: 5s
|
50
|
+
timeout: 5s
|
51
|
+
retries: 5
|
52
|
+
mysql:
|
53
|
+
image: biarms/mysql:5.7
|
54
|
+
environment:
|
55
|
+
MYSQL_ROOT_PASSWORD: database
|
56
|
+
MYSQL_DATABASE: transaction_isolation_continued_test
|
57
|
+
ports:
|
58
|
+
- "3306:3306"
|
59
|
+
healthcheck:
|
60
|
+
test: "mysqladmin ping --host=0.0.0.0 --password=database --silent"
|
data/gemfiles/Gemfile.base
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
gemspec path: Dir.getwd
|
3
3
|
|
4
|
-
gem '
|
4
|
+
gem 'transaction_isolation_continued'
|
5
5
|
|
6
|
-
File.exist?(gemfile_local = File.expand_path('
|
6
|
+
File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local
|
7
|
+
|
8
|
+
gem 'simplecov'
|
9
|
+
gem 'simplecov_json_formatter'
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
1
4
|
require 'active_record/base'
|
2
5
|
|
3
6
|
module TransactionRetry
|
4
7
|
module ActiveRecord
|
5
8
|
module Base
|
6
|
-
|
7
|
-
|
8
|
-
base.extend( ClassMethods )
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
9
11
|
base.class_eval do
|
10
12
|
class << self
|
11
13
|
alias_method :transaction_without_retry, :transaction
|
@@ -13,70 +15,68 @@ module TransactionRetry
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
16
|
-
|
18
|
+
|
17
19
|
module ClassMethods
|
18
|
-
|
19
20
|
def transaction_with_retry(**objects, &block)
|
20
21
|
retry_count = 0
|
21
22
|
|
22
23
|
opts = if objects.is_a? Hash
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
objects
|
25
|
+
else
|
26
|
+
{}
|
27
|
+
end
|
27
28
|
|
28
29
|
retry_on = opts.delete(:retry_on)
|
29
30
|
max_retries = opts.delete(:max_retries) || TransactionRetry.max_retries
|
30
31
|
|
31
32
|
begin
|
32
33
|
transaction_without_retry(**objects, &block)
|
33
|
-
rescue
|
34
|
+
rescue ::ActiveRecord::TransactionIsolationConflict, *retry_on
|
34
35
|
raise if retry_count >= max_retries
|
35
36
|
raise if tr_in_nested_transaction?
|
36
|
-
|
37
|
+
|
37
38
|
retry_count += 1
|
38
39
|
postfix = { 1 => 'st', 2 => 'nd', 3 => 'rd' }[retry_count] || 'th'
|
39
40
|
|
40
|
-
type_s = case
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
type_s = case $ERROR_INFO
|
42
|
+
when ::ActiveRecord::TransactionIsolationConflict
|
43
|
+
'Transaction isolation conflict'
|
44
|
+
else
|
45
|
+
$ERROR_INFO.class.name
|
46
|
+
end
|
46
47
|
|
47
|
-
logger
|
48
|
-
tr_exponential_pause(
|
48
|
+
logger&.warn "#{type_s} detected. Retrying for the #{retry_count}-#{postfix} time..."
|
49
|
+
tr_exponential_pause(retry_count)
|
49
50
|
retry
|
50
51
|
end
|
51
52
|
end
|
52
|
-
|
53
|
-
private
|
54
53
|
|
55
|
-
|
56
|
-
# Cap the sleep time at 32 seconds.
|
57
|
-
# An ugly tr_ prefix is used to minimize the risk of method clash in the future.
|
58
|
-
def tr_exponential_pause( count )
|
59
|
-
seconds = TransactionRetry.wait_times[count-1] || 32
|
54
|
+
private
|
60
55
|
|
61
|
-
|
62
|
-
|
56
|
+
# Sleep 0, 1, 2, 4, ... seconds up to the TransactionRetry.max_retries.
|
57
|
+
# Cap the sleep time at 32 seconds.
|
58
|
+
# An ugly tr_ prefix is used to minimize the risk of method clash in the future.
|
59
|
+
def tr_exponential_pause(count)
|
60
|
+
seconds = TransactionRetry.wait_times[count - 1] || 32
|
63
61
|
|
64
|
-
|
65
|
-
|
62
|
+
if TransactionRetry.fuzz
|
63
|
+
fuzz_factor = [seconds * 0.25, 1].max
|
66
64
|
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
# Returns true if we are in the nested transaction (the one with :requires_new => true).
|
71
|
-
# Returns false otherwise.
|
72
|
-
# An ugly tr_ prefix is used to minimize the risk of method clash in the future.
|
73
|
-
def tr_in_nested_transaction?
|
74
|
-
connection.open_transactions != 0
|
65
|
+
seconds += rand * (fuzz_factor * 2) - fuzz_factor
|
75
66
|
end
|
76
67
|
|
68
|
+
sleep(seconds) if seconds.positive?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if we are in the nested transaction (the one with :requires_new => true).
|
72
|
+
# Returns false otherwise.
|
73
|
+
# An ugly tr_ prefix is used to minimize the risk of method clash in the future.
|
74
|
+
def tr_in_nested_transaction?
|
75
|
+
connection.open_transactions != 0
|
76
|
+
end
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
ActiveRecord::Base.
|
82
|
+
ActiveRecord::Base.include TransactionRetry::ActiveRecord::Base
|
data/lib/transaction_retry.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
2
|
-
require "transaction_isolation"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require 'active_record'
|
4
|
+
require 'transaction_isolation'
|
5
5
|
|
6
|
-
|
6
|
+
require_relative 'transaction_retry/version'
|
7
7
|
|
8
|
+
module TransactionRetry
|
8
9
|
# Must be called after ActiveRecord established a connection.
|
9
10
|
# Only then we know which connection adapter is actually loaded and can be enhanced.
|
10
11
|
# Please note ActiveRecord does not load unused adapters.
|
@@ -13,7 +14,7 @@ module TransactionRetry
|
|
13
14
|
require_relative 'transaction_retry/active_record/base'
|
14
15
|
end
|
15
16
|
|
16
|
-
if defined?(
|
17
|
+
if defined?(::Rails)
|
17
18
|
# Setup applying the patch after Rails is initialized.
|
18
19
|
class Railtie < ::Rails::Railtie
|
19
20
|
config.after_initialize do
|
@@ -26,7 +27,7 @@ module TransactionRetry
|
|
26
27
|
@@max_retries ||= 3
|
27
28
|
end
|
28
29
|
|
29
|
-
def self.max_retries=(
|
30
|
+
def self.max_retries=(n)
|
30
31
|
@@max_retries = n
|
31
32
|
end
|
32
33
|
|
@@ -34,7 +35,7 @@ module TransactionRetry
|
|
34
35
|
@@wait_times ||= [0, 1, 2, 4, 8, 16, 32]
|
35
36
|
end
|
36
37
|
|
37
|
-
def self.wait_times=(
|
38
|
+
def self.wait_times=(array_of_seconds)
|
38
39
|
@@wait_times = array_of_seconds
|
39
40
|
end
|
40
41
|
|
@@ -42,8 +43,7 @@ module TransactionRetry
|
|
42
43
|
@@fuzz ||= true
|
43
44
|
end
|
44
45
|
|
45
|
-
def self.fuzz=(
|
46
|
+
def self.fuzz=(val)
|
46
47
|
@@fuzz = val
|
47
48
|
end
|
48
|
-
|
49
49
|
end
|
data/test/db/all.rb
CHANGED
data/test/db/db.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
|
3
5
|
module TransactionRetry
|
4
6
|
module Test
|
5
7
|
module Db
|
6
|
-
|
7
8
|
def self.connect_to_mysql2
|
8
9
|
::ActiveRecord::Base.establish_connection(
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
10
|
+
adapter: 'mysql2',
|
11
|
+
database: ENV['MYSQL_DB_NAME'],
|
12
|
+
host: ENV['MYSQL_DB_HOST'],
|
13
|
+
user: 'root',
|
14
|
+
password: ENV['MYSQL_DB_PASS']
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
17
18
|
def self.connect_to_postgresql
|
18
19
|
::ActiveRecord::Base.establish_connection(
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
20
|
+
adapter: 'postgresql',
|
21
|
+
database: ENV['POSTGRESQL_DB_NAME'],
|
22
|
+
host: ENV['POSTGRESQL_DB_HOST'],
|
23
|
+
user: ENV['POSTGRESQL_DB_USER'],
|
24
|
+
password: ENV['POSTGRESQL_DB_PASS']
|
24
25
|
)
|
25
26
|
end
|
26
27
|
|
27
28
|
def self.connect_to_sqlite3
|
28
29
|
ActiveRecord::Base.establish_connection(
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
30
|
+
adapter: 'sqlite3',
|
31
|
+
database: ':memory:',
|
32
|
+
verbosity: 'silent'
|
32
33
|
)
|
33
34
|
end
|
34
|
-
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/test/db/migrations.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TransactionRetry
|
2
4
|
module Test
|
3
5
|
module Migrations
|
4
|
-
|
5
6
|
def self.run!
|
6
7
|
c = ::ActiveRecord::Base.connection
|
7
8
|
|
8
9
|
# Queued Jobs
|
9
|
-
|
10
|
-
c.create_table
|
11
|
-
t.text
|
12
|
-
t.integer
|
10
|
+
|
11
|
+
c.create_table 'queued_jobs', force: true do |t|
|
12
|
+
t.text 'job', null: false
|
13
|
+
t.integer 'status', default: 0, null: false
|
13
14
|
t.timestamps
|
14
15
|
end
|
15
|
-
|
16
16
|
end
|
17
|
-
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
data/test/db/queued_job.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
|
@@ -16,22 +16,22 @@ class TransactionWithRetryTest < MiniTest::Unit::TestCase
|
|
16
16
|
TransactionRetry.wait_times = @original_wait_times
|
17
17
|
QueuedJob.delete_all
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def test_does_not_break_transaction
|
21
21
|
ActiveRecord::Base.transaction do
|
22
|
-
QueuedJob.create!(
|
23
|
-
assert_equal(
|
22
|
+
QueuedJob.create!(job: 'is fun!')
|
23
|
+
assert_equal(1, QueuedJob.count)
|
24
24
|
end
|
25
|
-
assert_equal(
|
25
|
+
assert_equal(1, QueuedJob.count)
|
26
26
|
QueuedJob.first.destroy
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_does_not_break_transaction_rollback
|
30
30
|
ActiveRecord::Base.transaction do
|
31
|
-
QueuedJob.create!(
|
31
|
+
QueuedJob.create!(job: 'gives money!')
|
32
32
|
raise ActiveRecord::Rollback
|
33
33
|
end
|
34
|
-
assert_equal(
|
34
|
+
assert_equal(0, QueuedJob.count)
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_retries_transaction_on_transaction_isolation_conflict
|
@@ -40,30 +40,29 @@ class TransactionWithRetryTest < MiniTest::Unit::TestCase
|
|
40
40
|
ActiveRecord::Base.transaction do
|
41
41
|
if first_run
|
42
42
|
first_run = false
|
43
|
-
message =
|
44
|
-
raise ActiveRecord::TransactionIsolationConflict
|
43
|
+
message = 'Deadlock found when trying to get lock'
|
44
|
+
raise ActiveRecord::TransactionIsolationConflict, message
|
45
45
|
end
|
46
|
-
QueuedJob.create!(
|
46
|
+
QueuedJob.create!(job: 'is cool!')
|
47
47
|
end
|
48
|
-
assert_equal(
|
49
|
-
|
48
|
+
assert_equal(1, QueuedJob.count)
|
49
|
+
|
50
50
|
QueuedJob.first.destroy
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_does_not_retry_on_unknown_error
|
54
54
|
first_run = true
|
55
55
|
|
56
|
-
assert_raises(
|
56
|
+
assert_raises(CustomError) do
|
57
57
|
ActiveRecord::Base.transaction do
|
58
58
|
if first_run
|
59
59
|
first_run = false
|
60
|
-
|
61
|
-
raise CustomError, "random error"
|
60
|
+
raise CustomError, 'random error'
|
62
61
|
end
|
63
|
-
QueuedJob.create!(
|
62
|
+
QueuedJob.create!(job: 'is cool!')
|
64
63
|
end
|
65
64
|
end
|
66
|
-
assert_equal(
|
65
|
+
assert_equal(0, QueuedJob.count)
|
67
66
|
end
|
68
67
|
|
69
68
|
def test_retries_on_custom_error
|
@@ -72,13 +71,12 @@ class TransactionWithRetryTest < MiniTest::Unit::TestCase
|
|
72
71
|
ActiveRecord::Base.transaction(retry_on: CustomError) do
|
73
72
|
if first_run
|
74
73
|
first_run = false
|
75
|
-
|
76
|
-
raise CustomError, "random error"
|
74
|
+
raise CustomError, 'random error'
|
77
75
|
end
|
78
|
-
QueuedJob.create!(
|
76
|
+
QueuedJob.create!(job: 'is cool!')
|
79
77
|
end
|
80
|
-
assert_equal(
|
81
|
-
|
78
|
+
assert_equal(1, QueuedJob.count)
|
79
|
+
|
82
80
|
QueuedJob.first.destroy
|
83
81
|
end
|
84
82
|
|
@@ -86,50 +84,47 @@ class TransactionWithRetryTest < MiniTest::Unit::TestCase
|
|
86
84
|
TransactionRetry.max_retries = 1
|
87
85
|
run = 0
|
88
86
|
|
89
|
-
assert_raises(
|
87
|
+
assert_raises(ActiveRecord::TransactionIsolationConflict) do
|
90
88
|
ActiveRecord::Base.transaction do
|
91
89
|
run += 1
|
92
|
-
message =
|
93
|
-
raise ActiveRecord::TransactionIsolationConflict
|
90
|
+
message = 'Deadlock found when trying to get lock'
|
91
|
+
raise ActiveRecord::TransactionIsolationConflict, message
|
94
92
|
end
|
95
93
|
end
|
96
|
-
|
97
|
-
assert_equal(
|
94
|
+
|
95
|
+
assert_equal(2, run) # normal run + one retry
|
98
96
|
|
99
97
|
TransactionRetry.max_retries = 3
|
100
98
|
|
101
99
|
run = 0
|
102
100
|
|
103
|
-
assert_raises(
|
101
|
+
assert_raises(ActiveRecord::TransactionIsolationConflict) do
|
104
102
|
ActiveRecord::Base.transaction(max_retries: 1) do
|
105
103
|
run += 1
|
106
|
-
message =
|
107
|
-
raise ActiveRecord::TransactionIsolationConflict
|
104
|
+
message = 'Deadlock found when trying to get lock'
|
105
|
+
raise ActiveRecord::TransactionIsolationConflict, message
|
108
106
|
end
|
109
107
|
end
|
110
|
-
|
111
|
-
assert_equal(
|
108
|
+
|
109
|
+
assert_equal(2, run) # normal run + one retry
|
112
110
|
end
|
113
111
|
|
114
112
|
def test_does_not_retry_nested_transaction
|
115
113
|
first_try = true
|
116
114
|
|
117
115
|
ActiveRecord::Base.transaction do
|
118
|
-
|
119
|
-
|
120
|
-
ActiveRecord::Base.transaction( :requires_new => true ) do
|
116
|
+
assert_raises(ActiveRecord::TransactionIsolationConflict) do
|
117
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
121
118
|
if first_try
|
122
119
|
first_try = false
|
123
|
-
message =
|
124
|
-
raise ActiveRecord::TransactionIsolationConflict
|
120
|
+
message = 'Deadlock found when trying to get lock'
|
121
|
+
raise ActiveRecord::TransactionIsolationConflict, message
|
125
122
|
end
|
126
|
-
QueuedJob.create!(
|
123
|
+
QueuedJob.create!(job: 'is cool!')
|
127
124
|
end
|
128
125
|
end
|
129
|
-
|
130
126
|
end
|
131
|
-
|
132
|
-
assert_equal( 0, QueuedJob.count )
|
133
|
-
end
|
134
127
|
|
128
|
+
assert_equal(0, QueuedJob.count)
|
129
|
+
end
|
135
130
|
end
|
data/test/library_setup.rb
CHANGED
@@ -1,21 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Prepares application to be tested (requires files, connects to db, resets schema and data, applies patches, etc.)
|
2
4
|
|
3
5
|
# Initialize database
|
4
6
|
require 'db/all'
|
5
7
|
|
6
8
|
case ENV['db']
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
when 'mysql2'
|
10
|
+
TransactionRetry::Test::Db.connect_to_mysql2
|
11
|
+
when 'postgresql'
|
12
|
+
TransactionRetry::Test::Db.connect_to_postgresql
|
13
|
+
when 'sqlite3'
|
14
|
+
TransactionRetry::Test::Db.connect_to_sqlite3
|
15
|
+
else
|
16
|
+
TransactionRetry::Test::Db.connect_to_mysql2
|
15
17
|
end
|
16
18
|
|
17
19
|
require 'logger'
|
18
|
-
ActiveRecord::Base.logger = Logger.new(
|
20
|
+
ActiveRecord::Base.logger = Logger.new(File.expand_path("#{File.dirname(__FILE__)}/log/test.log"))
|
19
21
|
|
20
22
|
TransactionRetry::Test::Migrations.run!
|
21
23
|
|
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
require 'simplecov_json_formatter'
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter '/test/'
|
9
|
+
end
|
7
10
|
|
8
11
|
# Load and initialize the application to be tested
|
9
12
|
require 'library_setup'
|
data/test/test_runner.rb
CHANGED
@@ -1,23 +1,24 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
4
|
+
require 'transaction_retry/version'
|
4
5
|
|
5
6
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
7
|
+
s.name = 'transaction_retry_continued'
|
7
8
|
s.version = TransactionRetry::VERSION
|
8
|
-
s.authors = [
|
9
|
-
s.homepage =
|
10
|
-
s.summary =
|
11
|
-
s.description =
|
9
|
+
s.authors = ['Iago Pimenta']
|
10
|
+
s.homepage = 'https://github.com/iagopiimenta/transaction_retry_continued'
|
11
|
+
s.summary = 'Retries database transaction on deadlock and transaction serialization errors. Supports MySQL, PostgreSQL and SQLite.'
|
12
|
+
s.description = 'Retries database transaction on deadlock and transaction serialization errors. Supports MySQL, PostgreSQL and SQLite.'
|
12
13
|
s.required_ruby_version = '>= 2.5'
|
13
14
|
|
14
15
|
s.files = `git ls-files`.split("\n")
|
15
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
-
s.require_paths = [
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
|
+
s.require_paths = ['lib']
|
18
19
|
|
19
|
-
s.add_development_dependency 'rake', '~> 13.0'
|
20
20
|
s.add_development_dependency 'minitest', '5.3.4'
|
21
|
-
s.
|
22
|
-
s.add_runtime_dependency
|
23
|
-
|
21
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
22
|
+
s.add_runtime_dependency 'activerecord', '>= 5.2'
|
23
|
+
s.add_runtime_dependency 'transaction_isolation_continued', '>= 1.0'
|
24
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transaction_retry_continued
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Iago Pimenta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: minitest
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.3.4
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.3.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,7 +79,16 @@ files:
|
|
79
79
|
- LICENSE
|
80
80
|
- README.md
|
81
81
|
- Rakefile
|
82
|
-
-
|
82
|
+
- docker-compose.yml
|
83
|
+
- docker/ruby-2.5/Dockerfile
|
84
|
+
- docker/ruby-2.7/Dockerfile
|
85
|
+
- docker/ruby-3.0/Dockerfile
|
86
|
+
- docker/ruby-3.1/Dockerfile
|
87
|
+
- docker/test-ruby-2.5.sh
|
88
|
+
- docker/test-ruby-2.7.sh
|
89
|
+
- docker/test-ruby-3.0.sh
|
90
|
+
- docker/test-ruby-3.1.sh
|
91
|
+
- docker/test-ruby.sh
|
83
92
|
- gemfiles/Gemfile.base
|
84
93
|
- gemfiles/activerecord-5.2/Gemfile.base
|
85
94
|
- gemfiles/activerecord-5.2/Gemfile.mysql2
|
@@ -108,10 +117,8 @@ files:
|
|
108
117
|
- test/integration/active_record/base/transaction_with_retry_test.rb
|
109
118
|
- test/library_setup.rb
|
110
119
|
- test/log/.gitkeep
|
111
|
-
- test/test_console.rb
|
112
120
|
- test/test_helper.rb
|
113
121
|
- test/test_runner.rb
|
114
|
-
- tests
|
115
122
|
- transaction_retry_continued.gemspec
|
116
123
|
homepage: https://github.com/iagopiimenta/transaction_retry_continued
|
117
124
|
licenses: []
|
@@ -144,6 +151,5 @@ test_files:
|
|
144
151
|
- test/integration/active_record/base/transaction_with_retry_test.rb
|
145
152
|
- test/library_setup.rb
|
146
153
|
- test/log/.gitkeep
|
147
|
-
- test/test_console.rb
|
148
154
|
- test/test_helper.rb
|
149
155
|
- test/test_runner.rb
|
data/test/test_console.rb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# Ensure that LOAD_PATH is the same as when running "rake test"; normally rake takes care of that
|
2
|
-
$LOAD_PATH << File.expand_path( ".", File.dirname( __FILE__ ) )
|
3
|
-
$LOAD_PATH << File.expand_path( "./lib", File.dirname( __FILE__ ) )
|
4
|
-
$LOAD_PATH << File.expand_path( "./test", File.dirname( __FILE__ ) )
|
5
|
-
|
6
|
-
# Boot the app
|
7
|
-
require_relative 'library_setup'
|
8
|
-
|
9
|
-
# Fire the console
|
10
|
-
require 'pry'
|
11
|
-
binding.pry
|