collectiveidea/interactor — pull request #38 by errinlarsen (2014-04-01 22:34:53+00:00)
Title: Modify V3's control flow to use throw/catch

> Although the exception mechanism of raise and rescue is great for abandoning
> execution when things go wrong, it’s sometimes nice to be able to jump out of
> some deeply nested construct during normal processing. This is where catch and
> throw come in handy.
>
> -- Programming Ruby 1.9 & 2.0: The Pragmatic Programmers' Guide,
> Thomas, Dave et al. (December, 2013), Pragmatic Bookshelf, p. 151

As I began to think about how to make this change, I discovered that removing
the use of raise/rescue for control flow reduces the need to actually raise any
Exceptions down to only one circumstance: when #fail! has been called from
within #perform!. For this reason, I decided to flip the logic around #perform
and #perform!.

* From: #perform always calling #perform!, and rescuing Failure
* To: #perform! always calling #perform, and raising Failure `if failure?`

I rewrote and reorganized a large number of specs in spec/support/lint.rb.
First, the logical flipping of #perform/#perform! necessitated some changes.
Second, I wanted to reduce the specs' dependence on the underlying mechanism of
control flow (e.g. raise/rescue, throw/catch, etc.) and focus on the results of
that control flow, instead. With that in mind, I'm testing for expected: order
of before/run/after methods, expected `success?` or `failure?` value, whether or
not the execution halts, and whether or not #rollback is called.

I thought the best place to `throw` a symbol was in the Interactor#succeed! and
Interactor#fail! methods themselves. The symbol being thrown is, appropriately,
`:halt!`, and the second parameter defines the reason for halting. This has the
effect of returning that second parameter as the value of the `catch`, making
it easy to check why we halted, and act accordingly.

I changed the #before/#after methods to #before_run/#after_run because I
anticipate a few more "around" hooks in the future. For example, there
is already a request for an #after_failure method. With that in mind, I wanted
to be a bit more explicit with these method names.

Finally, I remove the Interactor::Success class as it is no longer needed.