activerecord-wrapped_transaction 0.5.1 → 0.9.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 +5 -5
- data/.github/workflows/main.yml +89 -0
- data/.rspec +0 -1
- data/Appraisals +29 -0
- data/README.md +43 -16
- data/Rakefile +1 -1
- data/activerecord-wrapped_transaction.gemspec +5 -7
- data/bin/appraisal +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +1 -1
- data/db/connection.rb +34 -16
- data/db/models.rb +1 -1
- data/db/schema.rb +2 -0
- data/gemfiles/rails_5_mysql2.gemfile +8 -0
- data/gemfiles/rails_5_mysql2.gemfile.lock +80 -0
- data/gemfiles/rails_5_pg.gemfile +8 -0
- data/gemfiles/rails_5_pg.gemfile.lock +80 -0
- data/gemfiles/rails_5_sqlite3.gemfile +8 -0
- data/gemfiles/rails_5_sqlite3.gemfile.lock +80 -0
- data/gemfiles/rails_6_mysql2.gemfile +8 -0
- data/gemfiles/rails_6_mysql2.gemfile.lock +80 -0
- data/gemfiles/rails_6_pg.gemfile +8 -0
- data/gemfiles/rails_6_pg.gemfile.lock +80 -0
- data/gemfiles/rails_6_sqlite3.gemfile +8 -0
- data/gemfiles/rails_6_sqlite3.gemfile.lock +80 -0
- data/lib/activerecord/wrapped_transaction.rb +22 -3
- data/lib/activerecord/wrapped_transaction/context.rb +95 -0
- data/lib/activerecord/wrapped_transaction/result.rb +34 -29
- data/lib/activerecord/wrapped_transaction/version.rb +1 -1
- metadata +41 -53
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ec2e05ea513d0e8840e54718aa1c20e2e7427e47386dc95c9174312ae4912bae
|
4
|
+
data.tar.gz: 1d8278fcfc51110911a860b1434b26bac62056967c30c73272c7b4f5cf811f4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc8889bed01589292453020f988dc5249cdb26841988348e8f845b7c744fdb2f7578bc7c682c8368fc7da2f76a5e66dc6ec04d22cca1a51b21cc29db5e5931f1
|
7
|
+
data.tar.gz: fa0e8e95052fa790cbded234bf2f464f95b5706b4349a3f00b6761623b8730470dafd4148af451853a8f90cc06606c2b9e9ec60c79e8a8379701a5231db17e49
|
@@ -0,0 +1,89 @@
|
|
1
|
+
name: "Main"
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
paths:
|
8
|
+
- 'lib/**'
|
9
|
+
- 'spec/**'
|
10
|
+
- '.github/workflows/main.yml'
|
11
|
+
pull_request:
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
rspec:
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
strategy:
|
17
|
+
fail-fast: false
|
18
|
+
matrix:
|
19
|
+
ruby: [2.4, 2.5, 2.6, 2.7]
|
20
|
+
rails_version: [5, 6]
|
21
|
+
db: [pg, mysql2, sqlite3]
|
22
|
+
exclude:
|
23
|
+
- rails_version: 6
|
24
|
+
ruby: 2.4
|
25
|
+
env:
|
26
|
+
APPRAISAL_NAME: rails-${{ matrix.rails_version }}-${{ matrix.db }}
|
27
|
+
BUNDLE_GEMFILE: ${{ format('./gemfiles/rails_{0}_{1}.gemfile', matrix.rails_version, matrix.db) }}
|
28
|
+
DB_NAME: wrapped_transaction_test
|
29
|
+
PG_USER: test
|
30
|
+
PG_PASS: test
|
31
|
+
MYSQL_USER: root
|
32
|
+
MYSQL_PASS: root
|
33
|
+
steps:
|
34
|
+
- uses: actions/checkout@v2
|
35
|
+
- uses: ruby/setup-ruby@v1
|
36
|
+
with:
|
37
|
+
ruby-version: ${{ matrix.ruby }}
|
38
|
+
- name: "Install postgresql client"
|
39
|
+
if: ${{ matrix.db == 'pg' }}
|
40
|
+
run: |
|
41
|
+
sudo apt-get update --fix-missing
|
42
|
+
sudo apt-get -yqq install libpq-dev
|
43
|
+
- name: "Set up PG"
|
44
|
+
uses: harmon758/postgresql-action@v1
|
45
|
+
if: ${{ matrix.db == 'pg' }}
|
46
|
+
with:
|
47
|
+
postgresql version: '11'
|
48
|
+
postgresql db: ${{ env.DB_NAME }}
|
49
|
+
postgresql user: ${{ env.PG_USER }}
|
50
|
+
postgresql password: ${{ env.PG_PASS }}
|
51
|
+
- name: "Set PG DATABASE_URL"
|
52
|
+
if: ${{ matrix.db == 'pg' }}
|
53
|
+
run: |
|
54
|
+
echo "::set-env name=DATABASE_URL::postgres://${PG_USER}:${PG_PASS}@localhost/${DB_NAME}"
|
55
|
+
- name: "Shut down default MySQL database"
|
56
|
+
if: ${{ matrix.db == 'mysql2' }}
|
57
|
+
run: |
|
58
|
+
sudo systemctl stop mysql.service
|
59
|
+
- name: "Set Up MySQL"
|
60
|
+
if: ${{ matrix.db == 'mysql2' }}
|
61
|
+
uses: mirromutth/mysql-action@v1.1
|
62
|
+
with:
|
63
|
+
mysql database: ${{ env.DB_NAME }}
|
64
|
+
mysql root password: ${{ env.MYSQL_PASS }}
|
65
|
+
- name: "Set MySQL DATABASE_URL"
|
66
|
+
if: ${{ matrix.db == 'mysql2' }}
|
67
|
+
run: |
|
68
|
+
echo "::set-env name=DATABASE_URL::mysql2://${MYSQL_USER}:${MYSQL_PASS}@127.0.0.1/${DB_NAME}"
|
69
|
+
- name: "Install sqlite3"
|
70
|
+
if: ${{ matrix.db == 'sqlite3' }}
|
71
|
+
run: |
|
72
|
+
sudo apt-get update --fix-missing
|
73
|
+
sudo apt-get -yqq install sqlite3 libsqlite3-dev
|
74
|
+
echo "::set-env name=DATABASE_URL::sqlite3::memory:"
|
75
|
+
- uses: actions/cache@v2
|
76
|
+
with:
|
77
|
+
path: vendor/bundle
|
78
|
+
key: bundle-use-ruby-ubuntu-latest-${{ matrix.ruby }}-${{ env.APPRAISAL_NAME }}-${{ hashFiles(format('{0}.lock', env.BUNDLE_GEMFILE)) }}
|
79
|
+
restore-keys: |
|
80
|
+
bundle-use-ruby-ubuntu-latest-${{ matrix.ruby }}-${{ env.APPRAISAL_NAME }}
|
81
|
+
- name: bundle install
|
82
|
+
run: |
|
83
|
+
gem update --system
|
84
|
+
bundle config deployment true
|
85
|
+
bundle config path vendor/bundle
|
86
|
+
bundle install --jobs 4
|
87
|
+
- name: "Run RSpec"
|
88
|
+
run: |
|
89
|
+
bin/rspec
|
data/.rspec
CHANGED
data/Appraisals
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
appraise "rails-5-pg" do
|
2
|
+
gem "activerecord", "~> 5", "< 6"
|
3
|
+
gem "pg"
|
4
|
+
end
|
5
|
+
|
6
|
+
appraise "rails-6-pg" do
|
7
|
+
gem "activerecord", "~> 6", "< 7"
|
8
|
+
gem "pg"
|
9
|
+
end
|
10
|
+
|
11
|
+
appraise "rails-5-mysql2" do
|
12
|
+
gem "activerecord", "~> 5", "< 6"
|
13
|
+
gem "mysql2"
|
14
|
+
end
|
15
|
+
|
16
|
+
appraise "rails-6-mysql2" do
|
17
|
+
gem "activerecord", "~> 6", "< 7"
|
18
|
+
gem "mysql2"
|
19
|
+
end
|
20
|
+
|
21
|
+
appraise "rails-5-sqlite3" do
|
22
|
+
gem "activerecord", "~> 5", "< 6"
|
23
|
+
gem "sqlite3"
|
24
|
+
end
|
25
|
+
|
26
|
+
appraise "rails-6-sqlite3" do
|
27
|
+
gem "activerecord", "~> 6", "< 7"
|
28
|
+
gem "sqlite3"
|
29
|
+
end
|
data/README.md
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
# Activerecord::WrappedTransaction
|
2
2
|
|
3
|
-
Wrap transactions in
|
4
|
-
|
3
|
+
Wrap transactions in an object-oriented way so that you can tell if an individual transaction
|
4
|
+
succeeded, rolled back, or was cancelled.
|
5
5
|
|
6
|
-
|
6
|
+
Supported versions and databases:
|
7
|
+
|
8
|
+
* Rails 5 and 6
|
9
|
+
* MySQL, PostgreSQL, and SQLite
|
10
|
+
* Ruby 2.4, 2.5, 2.6, 2.7
|
7
11
|
|
8
12
|
## Installation
|
9
13
|
|
10
14
|
Add this line to your application's Gemfile:
|
11
15
|
|
12
16
|
```ruby
|
13
|
-
gem
|
17
|
+
gem "activerecord-wrapped_transaction", "~> 0.9"
|
14
18
|
```
|
15
19
|
|
16
20
|
And then execute:
|
@@ -19,29 +23,53 @@ And then execute:
|
|
19
23
|
|
20
24
|
Or install it yourself as:
|
21
25
|
|
22
|
-
$ gem install activerecord-wrapped_transaction
|
26
|
+
$ gem install activerecord-wrapped_transaction -v "~> 0.9"
|
23
27
|
|
24
28
|
## Usage
|
25
29
|
|
30
|
+
Contrived example:
|
31
|
+
|
26
32
|
```ruby
|
27
33
|
ActiveRecord::Base.include ActiveRecord::WrappedTransaction
|
28
34
|
|
29
|
-
wrapped_result = ActiveRecord::Base.wrapped_transaction do
|
30
|
-
|
35
|
+
wrapped_result = ActiveRecord::Base.wrapped_transaction do |context|
|
36
|
+
user = User.create! attributes
|
37
|
+
|
38
|
+
failable_result = OptionalThing.wrapped_transaction requires_new: true do
|
39
|
+
# This can fail, but we'll let it
|
40
|
+
OptionalThing.create! user: user, foo: "bar"
|
41
|
+
end
|
42
|
+
|
43
|
+
failable_result.rolled_back? # => true
|
44
|
+
|
45
|
+
# There is also a shorthand that uses the optional context helper
|
46
|
+
# This creates a new transaction layer that has requires_new: true
|
47
|
+
# set implicitly.
|
48
|
+
other_failable = context.maybe do
|
49
|
+
Something.explodes!
|
50
|
+
end
|
51
|
+
|
52
|
+
other_failable.rolled_back? # => true
|
53
|
+
|
54
|
+
cancelled = context.maybe do |inner_context|
|
55
|
+
inner_context.cancel! "arbitrarily"
|
56
|
+
end
|
57
|
+
|
58
|
+
cancelled.cancelled? # => true
|
59
|
+
cancelled.rolled_back? # => true
|
60
|
+
cancelled.cancellation_reason # => "arbitrarily"
|
61
|
+
|
62
|
+
# return our result
|
63
|
+
user
|
31
64
|
end
|
32
65
|
|
33
|
-
wrapped_result.result #
|
34
|
-
wrapped_result.success?
|
35
|
-
wrapped_result.rolled_back?
|
66
|
+
wrapped_result.result # => user
|
67
|
+
wrapped_result.success? # => true
|
68
|
+
wrapped_result.rolled_back? # => false
|
36
69
|
```
|
37
70
|
|
38
71
|
You can pass the same options you would to `ActiveRecord::Base.transaction`: `requires_new`, `isolation`, `joinable`
|
39
72
|
|
40
|
-
## Todo
|
41
|
-
|
42
|
-
* Test coverage for multiple connections (should be supported, but not guaranteed)
|
43
|
-
* `Maybe` monad support for being able to execute complex logic more fluently.
|
44
|
-
|
45
73
|
## Development
|
46
74
|
|
47
75
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -56,4 +84,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/scrypt
|
|
56
84
|
## License
|
57
85
|
|
58
86
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
59
|
-
|
data/Rakefile
CHANGED
@@ -19,14 +19,12 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency "activerecord", ">=
|
22
|
+
spec.add_dependency "activerecord", ">= 5", "< 7"
|
23
23
|
|
24
|
-
spec.add_development_dependency "
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
24
|
+
spec.add_development_dependency "appraisal", "~> 2.3.0"
|
25
|
+
spec.add_development_dependency "simplecov", "~> 0.18.5"
|
26
|
+
spec.add_development_dependency "database_cleaner-active_record", "~> 1.8.0"
|
27
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
27
28
|
spec.add_development_dependency "rspec", "~> 3.5"
|
28
29
|
spec.add_development_dependency "pry"
|
29
|
-
spec.add_development_dependency "pg", "~> 0.18.0"
|
30
|
-
spec.add_development_dependency "mysql2", "~> 0.4.4"
|
31
|
-
spec.add_development_dependency "sqlite3"
|
32
30
|
end
|
data/bin/appraisal
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'appraisal' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("appraisal", "appraisal")
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/setup
CHANGED
@@ -16,5 +16,5 @@ IFS=$'\n\t'
|
|
16
16
|
bundle install >/dev/null
|
17
17
|
|
18
18
|
sqlite3 "$PROJECT_ROOT/db/wrapped_transaction_test.sqlite3" '.databases' >/dev/null
|
19
|
-
mysql -e 'CREATE DATABASE IF NOT EXISTS wrapped_transaction_test;' >/dev/null
|
19
|
+
mysql -u root -e 'CREATE DATABASE IF NOT EXISTS wrapped_transaction_test;' >/dev/null
|
20
20
|
psql -U $PG_USER -tc "SELECT 1 FROM pg_database WHERE datname = 'wrapped_transaction_test'" | grep -q 1 || psql -U $PG_USER -c "CREATE DATABASE wrapped_transaction_test"
|
data/db/connection.rb
CHANGED
@@ -1,32 +1,50 @@
|
|
1
|
-
require
|
1
|
+
require "active_record"
|
2
2
|
|
3
3
|
class DBConnector
|
4
4
|
attr_reader :options
|
5
|
+
attr_reader :rails_version
|
6
|
+
attr_reader :type
|
7
|
+
attr_reader :url
|
5
8
|
|
6
9
|
def initialize
|
7
10
|
@options = {}
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
raise "No BUNDLE_GEMFILE set" if ENV["BUNDLE_GEMFILE"].blank?
|
13
|
+
|
14
|
+
@gemfile = File.basename(ENV["BUNDLE_GEMFILE"] || "", ".gemfile")
|
15
|
+
|
16
|
+
@url = ENV["DATABASE_URL"]
|
17
|
+
|
18
|
+
@options[:url] = @url if @url.present?
|
19
|
+
|
20
|
+
_, @rails_version, @type = *@gemfile.match(/\Arails_(\d+)_(.+)\z/)
|
21
|
+
|
22
|
+
case @type
|
23
|
+
when /mysql2\z/
|
24
|
+
options[:adapter] = "mysql2"
|
25
|
+
options[:username] = ENV.fetch("MYSQL_USER", "root")
|
26
|
+
options[:password] = ENV.fetch("MYSQL_PASS", nil)
|
27
|
+
options[:database] = ENV.fetch("DB_NAME", "wrapped_transaction_test")
|
28
|
+
options[:encoding] = "utf8"
|
29
|
+
when /pg\z/
|
30
|
+
options[:adapter] = "postgresql"
|
31
|
+
options[:database] = ENV.fetch("DB_NAME", "wrapped_transaction_test")
|
32
|
+
when /sqlite3\z/
|
33
|
+
options[:adapter] = "sqlite3"
|
34
|
+
options[:database] = File.join(__dir__, "wrapped_transaction_test.sqlite3")
|
18
35
|
else
|
19
|
-
|
20
|
-
options[:database] = File.join(__dir__, 'wrapped_transaction_test.sqlite3')
|
36
|
+
raise "Unknown db adapter: #{@type}"
|
21
37
|
end
|
22
38
|
end
|
23
39
|
|
24
40
|
def ci?
|
25
|
-
ENV[
|
41
|
+
ENV["CI"].present?
|
26
42
|
end
|
27
43
|
end
|
28
44
|
|
29
|
-
|
45
|
+
DBENV = DBConnector.new
|
46
|
+
|
47
|
+
ActiveRecord::Base.establish_connection DBENV.url || DBENV.options
|
30
48
|
|
31
|
-
require_relative
|
32
|
-
require_relative
|
49
|
+
require_relative "./schema"
|
50
|
+
require_relative "./models"
|
data/db/models.rb
CHANGED
data/db/schema.rb
CHANGED