- 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!"
- 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
- 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
- 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)
- 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
- 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"
- 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]
- 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
- 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!
- 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 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
- 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
- DG.success
- should succeed
- DG.success(42)
- should succeed with 42
- DG.success(:foo, :bar, :baz)
- should succeed with [:foo, :bar, :baz]
- DG.const("Hello")
- should succeed with "Hello"
- 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