Skip ActiveRecord Callbacks
Do you miss update_without_callbacks from Rails 2? You shouldn't, because ActiveRecord callbacks are not a particularly good way to implement system behavior. If you have to skip them routinely in the course of doing business, something smells.
If you're upgrading a Rails 2 app though, you've probably got a bunch of smells to contend with, so this plugin lets you put off the inevitable a little while longer. I'm sorry.
update_without_callbacks method edits the model's metaclass, hides
run_callbacks method, and inserts a replacement. The
replacement method calls the original, unless its argument is
which case it deletes itself, replaces the original
method, and yields to the given block to do the work of saving the
destroy_without_callbacks method does the same ninja editing for
Yeah, I know. But it seems to work just fine. The undecoration is the first
thing that happens when running the
:save callbacks, so it would seem
the only way this could not work is if ActiveRecord failed to call the
run_callbacks method on save, or if ActiveSupport needed to call the
method twice in a save for some reason. Or if ActiveSupport stopped
implementing callbacks with the
run_callbacks method, but the tests
should catch that.
What Could Possibly Go Wrong?
destroy_without_callbacks seems to behave a little bit differently
than the Rails 2 version. In Rails 2, it would apparently not call the
destroy method if it happened to define one. In the project
for which I wrote this, we had a guard protecting models from being
destroyed except via
def destroy raise "hell" end
This is trivially fixable by moving the guard to a
callback, or more correctly by moving the persistent object lifecycle
concerns up into a service model.
update_without_callbacks used to save the model without updating its
dirty attribute changes hash. This is no longer the case. I regard the
new behavior as more correct, to the extent that a hack built on a pile
of hacks can be considered to exhibit correctness, but if it's
problematic, it would be relatively easy to clone the changes hash and
push it back afterwards.