rack-transaction 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7344d9a199dd181db1cef5252d21aa6b4b11ff51
4
+ data.tar.gz: 4e50793aef557b46c300ce3f0fa2940ae8af2d93
5
+ SHA512:
6
+ metadata.gz: db561213fd61612450a54cfb64a5ca4905d22e5d01210e7a0b7be7ae9094f1ae9606e321a4d12288641938c040a31690374e0a4c704854bd7093f78e55654ac6
7
+ data.tar.gz: 2d91aa10d58c39b847c99543af4ef29f05b517d8aeb9de4f439ebfe1a5f327efe114620a39a01f96bbae41091372ad614aec704bac88ce14052a9ae3a22968d3
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/bundle
19
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rack-test'
7
+ gem 'rake'
8
+ gem 'sequel'
9
+ gem 'sqlite3'
10
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'minitest' do
2
+ watch(%r|^spec/.+_spec\.rb|)
3
+ watch(%r|^lib/(.+)\.rb$|) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r|^spec/spec_helper\.rb|) { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2015, peer60
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ - Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ - Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ - Neither the name of Katalus nor the names of its contributors may be used
16
+ to endorse or promote products derived from this software without specific
17
+ prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Rack::Transaction #
2
+
3
+ Rack::Transaction is a rack middleware that automatically wraps any incoming
4
+ requests with potential side effects (i.e. POST, PUT, or DELETE).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your Gemfile:
9
+
10
+ ```ruby
11
+ gem 'rack-transaction', :require => 'rack/transaction'
12
+ ```
13
+
14
+ or
15
+
16
+ ```
17
+ gem install rack-transaction
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Add the following:
23
+
24
+ ```ruby
25
+ use Rack::Transaction,
26
+ provider: Sequel.connect('sqlite:///') #required
27
+ rollback: Sequel::Rollback #required (it also accepts the string version of the constant)
28
+ ```
29
+
30
+ Do note that `:rollback` will use the type specified to raise an error, which
31
+ in turn, causes the transaction to rollback. Depending on the `:provider`
32
+ providing the transaction, you may want to specify an error type provided by
33
+ the library being used to allow for more graceful error handling. For example,
34
+ Sequel has `Sequel::Rollback` and ActiveRecord has `ActiveRecord::Rollback`.
35
+
36
+ It also supports an optional error callback to check for errors in the
37
+ environment outside of the normal client or server errors. For example, Sinatra
38
+ sets `sinatra.error` on the `env` in the event of an error, so we'll probably
39
+ want to rollback. We can check for this by specifying the `:error` setting.
40
+
41
+ ```ruby
42
+ use Rack::Transaction,
43
+ provider: Sequel.connect('sqlite:///')
44
+ rollback: Sequel::Rollback
45
+ error: ->(env){ env['sinatra.error'] }
46
+ ```
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs = ['spec', 'lib']
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ end
@@ -0,0 +1,40 @@
1
+ module Rack
2
+ class Transaction
3
+ VERSION = "0.1.0".freeze
4
+
5
+ def initialize(inner, settings)
6
+ @inner = inner
7
+ @provider = settings.fetch(:provider)
8
+ @rollback = settings.fetch(:rollback)
9
+ @error = settings[:error]
10
+ end
11
+
12
+ def call(env)
13
+ return @inner.call(env) unless use_transaction?(env)
14
+
15
+ result = nil
16
+ @provider.transaction do
17
+ result = @inner.call(env)
18
+ rollback if has_error?(env, *result)
19
+ end
20
+ result
21
+ end
22
+
23
+ private
24
+
25
+ def has_error?(env, status, headers, body)
26
+ response = Response.new body, status, headers
27
+ response.client_error? || response.server_error? || (@error.respond_to?(:call) && @error.call(env))
28
+ end
29
+
30
+ def rollback
31
+ klass = @rollback.is_a?(String) ? Object.const_get(@rollback) : @rollback
32
+ raise klass
33
+ end
34
+
35
+ def use_transaction?(env)
36
+ request = Request.new env
37
+ !(request.get? || request.head? || request.options?)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rack/transaction', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.author = 'vyrak.bunleang@gmail.com'
6
+ gem.homepage = 'https://github.com/p60/rack-transaction'
7
+ gem.description = 'Middleware for transactions'
8
+ gem.summary = 'Middleware for transactions'
9
+
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ gem.name = 'rack-transaction'
13
+ gem.require_paths = ["lib"]
14
+ gem.version = Rack::Transaction::VERSION
15
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require 'rack/transaction'
3
+
4
+ describe Rack::Transaction do
5
+ let(:inner){ mock }
6
+ let(:env){ {'field' => 'variable'} }
7
+ let(:table_name){ :rack }
8
+ let(:dataset){ connection[table_name] }
9
+ let(:rollback){ Sequel::Rollback }
10
+ let(:settings){ {provider: connection, rollback: rollback} }
11
+
12
+ subject { Rack::Transaction.new inner, settings }
13
+
14
+ before do
15
+ connection.create_table table_name do
16
+ column :name, String, null: false
17
+ end
18
+ end
19
+
20
+ after do
21
+ connection.drop_table table_name
22
+ inner.verify
23
+ end
24
+
25
+ def expect_call(status)
26
+ inner.expect :call, [status, {}, []] do |(environment), *args|
27
+ dataset.insert(name: 'insert') if args.empty? && environment == env
28
+ end
29
+ end
30
+
31
+ it 'wont rollback when ok' do
32
+ expect_call 200
33
+ result = subject.call env
34
+ result.must_equal [200, {}, []]
35
+ dataset.wont_be :empty?
36
+ end
37
+
38
+ it 'wont roll back on redirect' do
39
+ expect_call 301
40
+ result = subject.call env
41
+ result.must_equal [301, {}, []]
42
+ dataset.wont_be :empty?
43
+ end
44
+
45
+ it 'rolls back on server error' do
46
+ expect_call 500
47
+ result = subject.call env
48
+ result.must_equal [500, {}, []]
49
+ dataset.must_be :empty?
50
+ end
51
+
52
+ it 'rolls back on client error' do
53
+ expect_call 400
54
+ result = subject.call env
55
+ result.must_equal [400, {}, []]
56
+ dataset.must_be :empty?
57
+ end
58
+
59
+ it 'rolls back on custom error callback' do
60
+ args = nil
61
+ settings[:error] = ->(*a){ args = a; true }
62
+ expect_call 200
63
+ result = subject.call env
64
+ result.must_equal [200, {}, []]
65
+ dataset.must_be :empty?
66
+ args.must_equal [env]
67
+ end
68
+
69
+ it 'rolls back on string rollback' do
70
+ settings[:rollback] = "Sequel::Rollback"
71
+ expect_call 400
72
+ result = subject.call env
73
+ result.must_equal [400, {}, []]
74
+ dataset.must_be :empty?
75
+ end
76
+
77
+ %w{ GET HEAD OPTIONS }.each do |method|
78
+ # shouldn't be modifying anything on these types of requests; modifying for assertion purposes
79
+
80
+ describe "on #{method} request" do
81
+ before { env['REQUEST_METHOD'] = method }
82
+
83
+ it 'wont rollback on custom error callback' do
84
+ settings[:error] = ->{ true }
85
+ expect_call 200
86
+ subject.call env
87
+ dataset.wont_be :empty?
88
+ end
89
+
90
+ it 'wont rollback on server error' do
91
+ expect_call 500
92
+ subject.call env
93
+ dataset.wont_be :empty?
94
+ end
95
+
96
+ it 'wont rollback on client error' do
97
+ expect_call 400
98
+ subject.call env
99
+ dataset.wont_be :empty?
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,16 @@
1
+ require 'bundler/setup'
2
+ Bundler.require :development
3
+
4
+ $:.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'minitest/pride'
6
+ require 'minitest/autorun'
7
+
8
+ class Minitest::Spec
9
+ def mock
10
+ Minitest::Mock.new
11
+ end
12
+
13
+ def connection
14
+ @connection ||= Sequel.connect 'sqlite:///'
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-transaction
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - vyrak.bunleang@gmail.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Middleware for transactions
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".gitignore"
20
+ - Gemfile
21
+ - Guardfile
22
+ - LICENSE
23
+ - README.md
24
+ - Rakefile
25
+ - lib/rack/transaction.rb
26
+ - rack-transaction.gemspec
27
+ - spec/rack/transaction_spec.rb
28
+ - spec/spec_helper.rb
29
+ homepage: https://github.com/p60/rack-transaction
30
+ licenses: []
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 2.2.2
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Middleware for transactions
52
+ test_files:
53
+ - spec/rack/transaction_spec.rb
54
+ - spec/spec_helper.rb