RSpec Code Examples

 

 

DeferrableGratification::Combinators
#>>
DummyDB.query(:id, :name => "Sam") >> lambda {|id| DummyDB.query(:age, :id => id) }
should succeed with 26
DummyDB.query(:id, :name => :nonexistent) >> lambda {|id| DummyDB.query(:age, :id => id) }
should fail with "No such user"
DummyDB.query(:name, :id => 42) >> lambda {|name| "Hello #{name}!" }
should succeed with "Hello Sam!"
DummyDB.query(:id, :name => "Sam") >> lambda {|id| raise "User #{id} not allowed!" }
should fail with "User 42 not allowed!"
#transform
operation.transform(&:upcase)
if the operation succeeds with "Hello"
should succeed with "HELLO"
if the operation fails with "bad robot"
should fail with "bad robot"
operation.transform { raise "kaboom!" }
if the operation succeeds
should catch the exception
should fail and pass through the exception
@results = []; operation.transform(&@results.method(:<<))
if operation succeeds with :wahey!
should add :wahey! to @results
if operation fails
should not touch @results
#transform_error
operation.transform_error {|msg| RuntimeError.new(msg) }
if the operation succeeds with "Hello"
should succeed with "Hello"
if the operation fails with the string "bad robot"
should fail with RuntimeError and "bad robot"
operation.transform_error { raise "kaboom!" }
if the operation succeeds
should succeed with "Hello"
if the operation fails
should catch the exception
should fail and pass through the exception
@errors = []; operation.transform_error(&@errors.method(:<<))
if operation succeeds with :wahey!
should not touch @errors
if operation fails
should add RuntimeError("Boom!") to @errors
#bind!
DummyDB.query(:id, :name => "Sam").bind! {|id| DummyDB.query(:location, :id => id) }
if first query succeeds with id 42
should pass id 42 to the second query
if the second query succeeds with "San Francisco"
return value
should succeed with "San Francisco"
if the second query fails with "no location found"
return value
should fail with "no location found"
if first query fails
should not run the second query
DummyDB.query(:id, :name => "Sam").bind! {|id| raise "id #{id} not authorised" }
if first query succeeds with id 42
should not stop the first query from succeeding (in case other code subscribed to it)
#guard
operation.guard("must be even", &:even?)
if the operation succeeds with 300
should succeed with 300
if the operation succeeds with 317
should fail with DeferrableGratification::GuardFailed and /must be even/
should fail with DeferrableGratification::GuardFailed and /317/
if the operation fails with "bad robot"
should fail with "bad robot"
operation.guard("must have job") {|person| person[:job] }.guard("must have title") {|person| person[:job][:title] }
if the operation succeeds with {:name => "Sam"}
should not raise an exception
should not fail with NoMethodError
should fail with DeferrableGratification::GuardFailed and /job/
if the operation succeeds with {:job => {:company => "Rapportive"}}
should fail with DeferrableGratification::GuardFailed and /title/
if the operation succeeds with {:job => {:title => "CTO", :company => "Rapportive"}}
should succeed with {:job=>{:company=>"Rapportive", :title=>"CTO"}}
operation.callback {|value| @results << value }.guard(&:even?).callback {|value| @even_results << value }
if the value passes the guard
both callbacks should fire
if the value fails the guard
the first callback should fire
the second callback should not fire
operation.guard { raise "kaboom!" }
if the operation succeeds
should catch the exception
should fail and pass through the exception
.chain
DG.chain()
should succeed
DG.chain(lambda { 2 })
should succeed with 2
DG.chain( lambda { DummyDB.query(:person_id, :name => 'Sam') }, lambda {|person_id| DummyDB.query(:products, :buyer_id => person_id) }, lambda {|products| DummyDB.query(:description, :product_id => products.map(&:id)) }, lambda {|descriptions| descriptions.map(&:brief) })
should succeed with ["Car", "Dishwasher", "Laptop"]
DG.chain( lambda { DummyDB.query(:person_id, :name => :nonexistent) }, lambda {|person_id| DummyDB.query(:products, :buyer_id => person_id) }, lambda {|products| DummyDB.query(:description, :product_id => products.map(&:id)) }, lambda {|descriptions| descriptions.map(&:brief) })
should fail with "No such person"
.join_successes
DG.join_successes()
should succeed with []
DG.join_successes(first, second)
should not succeed or fail
after first succeeds with :one
should not succeed or fail
after second succeeds with :two
should succeed with [:one, :two]
after second fails
should succeed with [:one]
after both fail
should succeed with []
preserving order of operations
if second succeeds before first does
should still succeed with [:one, :two]
.join_first_success
DG.join_first_success()
should not succeed
DG.join_first_success(first, second)
should not succeed or fail
after first succeeds with :one
should succeed with :one
after second succeeds with :two
should still succeed with :one
after second fails
should still succeed with :one
after second succeeds with :two
should succeed with :two
after first fails
should not succeed or fail
after second succeeds with :two
should succeed with :two
after second also fails
should not succeed or fail
.loop_until_success
outside EventMachine
DG.loop_until_success { ui.wait_for_click(1) }
should call ui.wait_for_click at least once
if the user already clicked
should succeed with :click!
should not call ui.wait_for_click again
if ui.wait_for_click throws an exception
should fail with "User eaten by weasel"
should not call ui.wait_for_click again
if the user takes 4 seconds to click
should try 4 times
should succeed with :click!
in EventMachine
DG.loop_until_success { ui.wait_for_click(1) }
should call ui.wait_for_click at least once
if the user already clicked
should succeed with :click!
should not call ui.wait_for_click again
if ui.wait_for_click throws an exception
should fail with "User eaten by weasel"
should not call ui.wait_for_click again
if the user takes 4 seconds to click
should try 4 times
should succeed with :click!
DeferrableGratification::DefaultDeferrable
should include DeferrableGratification::Fluent
should include DeferrableGratification::Bothback
should include DeferrableGratification::Combinators
it should behave like a Deferrable
should respond to #callback
should respond to #errback
DeferrableGratification::Combinators::Bind
constructed with a block returning a deferrable
side effects on first deferrable
if block behaves itself
if first Deferrable succeeds
should invoke the block with the value passed to #succeed
if first Deferrable fails
should not invoke the block
if block calls first_deferrable.fail
should fail with RuntimeError and "found invalid user"
the DeferrableGratification::Combinators::Bind Deferrable itself
it should behave like a Deferrable
should respond to #callback
should respond to #errback
if first Deferrable succeeds
if the Deferrable returned by the block succeeds
should succeed with the value that Deferrable succeeded with
if the Deferrable returned by the block fails
should fail with the error that Deferrable failed with
if block calls first_deferrable.fail
should fail with RuntimeError and "found invalid user"
if first Deferrable fails with RuntimeError
should fail with RuntimeError
constructed with a block not returning a Deferrable
side effects
if first Deferrable succeeds
should invoke the block anyway
if block raises an exception
should not prevent the first Deferrable succeeding
the DeferrableGratification::Combinators::Bind Deferrable itself
it should behave like a Deferrable
should respond to #callback
should respond to #errback
if first Deferrable succeeds
should succeed with whatever the block returned
if block raises an exception
should fail and pass through the exception
nested bind! equivalent to chained bind! (monad law: associativity)
good_op(0).bind! do |x| transform(x) end.bind! do |y| good_op(y) end
should NOT behave the same as: good_op(0).bind! do |x| transform(x).bind! do |y| good_op(y) end end
good_op(0).bind! do |x| boom(x) end.bind! do |y| good_op(y) end
should behave the same as: good_op(0).bind! do |x| boom(x).bind! do |y| good_op(y) end end
good_op(0).bind! do |x| good_op(x) end.bind! do |y| boom(y) end
should behave the same as: good_op(0).bind! do |x| good_op(x).bind! do |y| boom(y) end end
good_op(0).bind! do |x| good_op(x) end.bind! do |y| bad_op(y) end
should behave the same as: good_op(0).bind! do |x| good_op(x).bind! do |y| bad_op(y) end end
good_op(0).bind! do |x| transform(x) end.bind! do |y| transform(y) end
should NOT behave the same as: good_op(0).bind! do |x| transform(x).bind! do |y| transform(y) end end
bad_op(0).bind! do |x| good_op(x) end.bind! do |y| good_op(y) end
should behave the same as: bad_op(0).bind! do |x| good_op(x).bind! do |y| good_op(y) end end
good_op(0).bind! do |x| bad_op(x) end.bind! do |y| good_op(y) end
should behave the same as: good_op(0).bind! do |x| bad_op(x).bind! do |y| good_op(y) end end
good_op(0).bind! do |x| good_op(x) end.bind! do |y| transform(y) end
should behave the same as: good_op(0).bind! do |x| good_op(x).bind! do |y| transform(y) end end
good_op(0).bind! do |x| good_op(x) end.bind! do |y| good_op(y) end
should behave the same as: good_op(0).bind! do |x| good_op(x).bind! do |y| good_op(y) end end
.setup! without a block
.new should raise ArgumentError
DeferrableGratification.enhance!
EnhancedDeferrable
should include DeferrableGratification::Fluent
should include DeferrableGratification::Bothback
should include DeferrableGratification::Combinators
DeferrableGratification::Fluent
should allow fluently adding multiple callbacks
should allow fluently adding multiple errbacks
should allow fluently setting a timeout
should allow fluently mixing callbacks and errbacks
after fluently registering a bunch of callbacks and errbacks
on success
should call all the callbacks
should call none of the errbacks
on failure
should call none of the callbacks
should call all the errbacks
DeferrableGratification::Primitives
.success
DG.success
should succeed
DG.success(42)
should succeed with 42
DG.success(:foo, :bar, :baz)
should succeed with [:foo, :bar, :baz]
.const
DG.const("Hello")
should succeed with "Hello"
.failure
DG.failure("does not compute")
should fail with RuntimeError and "does not compute"
DG.failure(ArgumentError)
should fail with ArgumentError
DG.failure(ArgumentError, "unacceptable command")
should fail with ArgumentError and "unacceptable command"
DG.failure(RangeError.new("you shall not pass!"))
should fail with RangeError and "you shall not pass!"
DeferrableGratification::Bothback
should allow registering a "bothback"
should support fluent syntax to register several "bothbacks"
after registering a bothback
on success
should call the bothback
on failure
should call the bothback