Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Raise instead of returning false in AR callbacks
We never check whether a `#destroy` fails in our controllers: def destroy article = Article.find(params[:id]) article.destroy redirect_to articles_url end Because of this history, it would cause confusion and potential bugs if we introduced a normal `before_destroy` into any of our models. Not only will we need to check the result of `#destroy` for any controller using that model, but we'd also need to check the return value for any controller using models that associate with that model. Since callbacks are used for validated database objects and are expected to succeed, we should raise on failure. This will roll back the transaction and notify the exception handler (e.g. Airbrake). This is true of all callbacks, not just `before_destroy`.
- Loading branch information
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike-burns this sounds to me like more of an issue of not verifying if the object was destroyed first. A validation could provide helpful feedback to a user as to why you can't delete a specific object, yet they are now going to see a 500 error.
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MikeSilvis what's an example error message that would be helpful for the user in this case?
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike-burns what if the business posts an arbitrary constraint such that you can't destroy an article after it has been live for 24 hours.
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validations are different than callbacks. During callbacks, it doesn't make sense to add error messages, because they would just be cleared on the next
valid?
orsave
run.Can you add validations which will run on
destroy
? If so, that may be a good approach for some situations, but it still wouldn't provide a reason to returnfalse
in a callback.8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes valid. My key complaint was looking at the following code snippet:
And the philosophy that you should not do:
Furthermore if you really just wanted an error to be thrown you could just do a
article.destroy!
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MikeSilvis You are correct, that is how the
#destroy
action should be written. However, that is not how it is traditionally written, it is not how it was written in the past, and it is not how the Rails scaffold generator produces it today. This guideline is for those cases.8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mike-burns This guideline seems fine to me, but just for the sake of discussion, that Rails scaffold doesn't work this way and that controllers haven't previously been written this way don't feel like compelling arguments for a guideline.
It makes more sense to have a guideline around how to best destroy a record than a guideline on how to adapt to the wrong way of destroying a record.
8ddc91b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I submitted this I was looking at thoughtbot's existing project repos -- all of which used a pattern that would cause issue -- many of which were legacy apps written by other teams. We needed a way to handle
#destroy
in those safely and without fanfare.I call
#destroy!
when on greenfield projects. Legacy projects, especially from other teams, cause the solution presented in this PR to still be relevant.So I guess: pragmaticism vs idealism.