Skip to content

Instantly share code, notes, and snippets.

@kschiess
Last active December 18, 2015 23:59
Show Gist options
  • Save kschiess/5865739 to your computer and use it in GitHub Desktop.
Save kschiess/5865739 to your computer and use it in GitHub Desktop.
Taking the transaction behaviour discussion into code. Use 'rspec transactions_spec.rb'
require 'rspec'
require 'dm-transactions'
RSpec.configure do |rspec|
rspec.mock_with :flexmock
end
describe 'transaction block behaviour' do
let(:t) { DataMapper::Transaction.new() }
def transaction &block
t.commit &block
end
def be_committed
be_commit
end
def be_rolled_back
be_rollback
end
# Remove comments on this code block for an alternate implementation that
# fixes most of these problems. NOTE The code in this implementation is
# not correct, since it drops a few of the original aspects. They should be
# easy to add back in.
#
# def transaction &block
# t.begin
# begin
# t.within(&block)
# rescue Exception => e
# t.rollback
# raise
# ensure
# t.commit if t.begin?
# end
# end
describe "directly called" do
it "can return" do
# When not inside the context of a method call, returning should not
# work.
expect {
transaction { return :something }
}.to raise_error(LocalJumpError)
end
it "can call next with a value" do
transaction { next :something }.should == :something
t.should be_committed
end
it "can call break with a value" do
loop do
transaction { break }
fail "SHOULD NOT BE REACHED"
end
pending "break is real hard to catch..." do
t.should be_committed
end
end
it "can raise an exception" do
e = Exception.new
expect {
transaction { raise e }
}.to raise_error(e)
t.should be_rolled_back
end
it "can throw a value" do
pending 'Throwing?' do
expect {
transaction { throw :value }
}.to throw_symbol(:value)
t.should be_committed
end
end
end
describe "wrapped in a method call" do
def mc &block
c = Class.new
i = c.new
m = i.define_singleton_method(:foo, &block)
m.call
end
it "can return" do
pending 'Would be nice to have return working.' do
mc {
transaction { return :something }
}.should == :something
t.should be_committed
end
end
it "can call next with a value" do
mc {
transaction { next :something }
}.should == :something
t.should be_committed
end
it "can call break with a value" do
# Break from inside a method call is not something one would attempt.
expect {
mc {
loop do
transaction { break :something }
fail "SHOULD NOT BE REACHED"
end
}
}.to raise_error(RuntimeError)
end
it "can raise an exception" do
e = Exception.new
expect {
mc {
transaction { raise e }
}
}.to raise_error(e)
t.should be_rolled_back
end
it "can throw a value" do
pending 'Throwing as a means to exit many levels of call stack quickly...' do
expect {
mc {
transaction { throw :value }
}
}.to throw_symbol(:value)
t.should be_committed
end
end
end
end
@kschiess
Copy link
Author

Running with 1.9.3 - dm-transactions implementation:

transaction block behaviour
  directly called
    can return
    can call next with a value
    can call break with a value (FAILED - 1)
    can raise an exception
    can throw a value (PENDING: Throwing?)
  wrapped in a method call
    can return (PENDING: Would be nice to have return working.)
    can call next with a value
    can call break with a value
    can raise an exception
    can throw a value (PENDING: Throwing as a means to exit many levels of call stack quickly...)

Pending:
  transaction block behaviour directly called can throw a value
    # Throwing?
    # ./transactions_spec.rb:71
  transaction block behaviour wrapped in a method call can return
    # Would be nice to have return working.
    # ./transactions_spec.rb:89
  transaction block behaviour wrapped in a method call can throw a value
    # Throwing as a means to exit many levels of call stack quickly...
    # ./transactions_spec.rb:126

Failures:

  1) transaction block behaviour directly called can call break with a value
     Failure/Error: fail "SHOULD NOT BE REACHED"
     RuntimeError:
       SHOULD NOT BE REACHED
     # ./transactions_spec.rb:56:in `block (4 levels) in <top (required)>'
     # ./transactions_spec.rb:54:in `loop'
     # ./transactions_spec.rb:54:in `block (3 levels) in <top (required)>'

Finished in 0.00788 seconds
10 examples, 1 failure, 3 pending

Running with my transactions implementation:

transaction block behaviour
  directly called
    can return
    can call next with a value
    can call break with a value (FAILED - 1)
    can raise an exception
    can throw a value (FAILED - 2)
  wrapped in a method call
    can return (FAILED - 3)
    can call next with a value
    can call break with a value
    can raise an exception
    can throw a value (FAILED - 4)

Failures:

  1) transaction block behaviour directly called can call break with a value
     Failure/Error: fail "SHOULD NOT BE REACHED"
     RuntimeError:
       SHOULD NOT BE REACHED
     # ./transactions_spec.rb:56:in `block (4 levels) in <top (required)>'
     # ./transactions_spec.rb:54:in `loop'
     # ./transactions_spec.rb:54:in `block (3 levels) in <top (required)>'

  2) transaction block behaviour directly called can throw a value FIXED
     Expected pending 'Throwing?' to fail. No Error was raised.
     # ./transactions_spec.rb:72:in `block (3 levels) in <top (required)>'

  3) transaction block behaviour wrapped in a method call can return FIXED
     Expected pending 'Would be nice to have return working.' to fail. No Error was raised.
     # ./transactions_spec.rb:90:in `block (3 levels) in <top (required)>'

  4) transaction block behaviour wrapped in a method call can throw a value FIXED
     Expected pending 'Throwing as a means to exit many levels of call stack quickly...' to fail. No Error was raised.
     # ./transactions_spec.rb:127:in `block (3 levels) in <top (required)>'

Finished in 0.00741 seconds
10 examples, 4 failures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment