activerecord-wrapped_transaction 0.5.1 → 0.9.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
- SHA1:
3
- metadata.gz: 8516f90c26bfd324c7e7cb99cb66f3e1293d09f6
4
- data.tar.gz: 848e4b660600b027ca8f64a3a4aa669a2deebe0d
2
+ SHA256:
3
+ metadata.gz: ec2e05ea513d0e8840e54718aa1c20e2e7427e47386dc95c9174312ae4912bae
4
+ data.tar.gz: 1d8278fcfc51110911a860b1434b26bac62056967c30c73272c7b4f5cf811f4f
5
5
  SHA512:
6
- metadata.gz: bb0df845987d747f62e3079ca3ae7df3230f3932544ccaad7b048baa79b7a6cc77b95598e356f97673150e781d9e2b3713bb1ba1c6ff2ceed8890a3014c70c6f
7
- data.tar.gz: c53d50ac11583b99d463288c9f3342b1328df51f6970e1e432715ce69214b301a16a05b7367609b76ee14f18ea2c9eead4c545a7e799f6bce6db1e04c70ae9f7
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
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
@@ -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 a way that lets you easily detect if the block succeeded or rolled back
4
- for complex, procedural usage with an object interface.
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
- It supports MySQL, PostgreSQL, and SQLite.
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 'activerecord-wrapped_transaction'
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
- # Do something
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
@@ -5,4 +5,4 @@ RSpec::Core::RakeTask.new(:spec) do |c|
5
5
  c.verbose = false
6
6
  end
7
7
 
8
- task :default => :spec
8
+ task default: :spec
@@ -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", ">= 3.2", "< 6"
22
+ spec.add_dependency "activerecord", ">= 5", "< 7"
23
23
 
24
- spec.add_development_dependency "simplecov", "~> 0.12.0"
25
- spec.add_development_dependency "bundler", "~> 1.11"
26
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -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")
@@ -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")
@@ -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"
@@ -1,32 +1,50 @@
1
- require 'active_record'
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
- case ENV['DB']
10
- when /mysql/
11
- options[:adapter] = 'mysql2'
12
- options[:username] = 'root'
13
- options[:database] = 'wrapped_transaction_test'
14
- options[:encoding] = 'utf8'
15
- when /postgres/
16
- options[:adapter] = 'postgresql'
17
- options[:database] = 'wrapped_transaction_test'
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
- options[:adapter] = 'sqlite3'
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['CI'].present?
41
+ ENV["CI"].present?
26
42
  end
27
43
  end
28
44
 
29
- ActiveRecord::Base.establish_connection DBConnector.new.options
45
+ DBENV = DBConnector.new
46
+
47
+ ActiveRecord::Base.establish_connection DBENV.url || DBENV.options
30
48
 
31
- require_relative './schema'
32
- require_relative './models'
49
+ require_relative "./schema"
50
+ require_relative "./models"
@@ -5,5 +5,5 @@ class ApplicationRecord < ActiveRecord::Base
5
5
  end
6
6
 
7
7
  class Widget < ApplicationRecord
8
- validates_presence_of :name
8
+ validates :name, presence: true, uniqueness: true
9
9
  end
@@ -5,5 +5,7 @@ ActiveRecord::Schema.define do
5
5
  t.string :name
6
6
 
7
7
  t.timestamps null: false
8
+
9
+ t.index :name, unique: true
8
10
  end
9
11
  end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5", "< 6"
6
+ gem "mysql2"
7
+
8
+ gemspec path: "../"