So imagine you have 2 models, Person and Address, and only one address per person can be marked as 'Main'. So if I wanna change a person's main address, I need to use a transaction, to mark the new one as main and unmark the old one. And as far as I know using transactions in controllers is not good so I have a special method in model, thats what I've got:
AddressesController < ApplicationController def update @new_address = Address.find(params[:id]) @old_address = Address.find(params[:id2]) @new_address.exchange_status_with(@old_address) end end
Model:
class Address < ActiveRecord::Base def exchange_status_with(address) ActiveRecord::Base.transaction do self.save! address.save! end end end
So thequestion is, if the transaction in the model method fails, I need to rescue it and notify the user about the error, how do I do that? Is there a way to make this model method return true or false depending on whether the transaction was successful or not, like save method does?
I probably could put that transaction in the controller and render the error message in the rescue part, but I guess its not right or I could put that method in a callback, but imagine there is some reason why I cant do that, whats the alternative?
PS dont pay attention to finding instances with params id and id2, just random thing to show that I have 2 instances
The code between “begin” and “rescue” is where a probable exception might occur. If an exception occurs, the rescue block will execute. You should try to be specific about what exception you're rescuing because it's considered a bad practice to capture all exceptions.
A raised exception can be rescued to prevent it from crashing your application once it reaches the top of the call stack. In Ruby, we use the rescue keyword for that. When rescuing an exception in Ruby, you can specify a specific error class that should be rescued from.
For each rescue clause, the raised Ruby exception is compared against each parameter and the match succeeds if the exception in the clause is the same as or a superclass of the thrown exception. If the thrown Ruby exception does not match any of the specified exception types, the else block gets executed.
Exception handling in Ruby on Rails is similar to exception handling in Ruby. Which means, we enclose the code that could raise an exception in a begin/end block and use rescue clauses to tell Ruby the types of exceptions we want to handle.
def exchange_status_with(address) ActiveRecord::Base.transaction do self.save! address.save! end rescue ActiveRecord::RecordInvalid => exception # do something with exception here end
FYI, an exception looks like:
#<ActiveRecord::RecordInvalid: Validation failed: Email can't be blank>
And:
exception.message # => "Validation failed: Email can't be blank"
Side note, you can change self.save!
to save!
Alternate solution if you want to keep your active model errors:
class MyCustomErrorClass < StandardError; end def exchange_status_with(address) ActiveRecord::Base.transaction do raise MyCustomErrorClass unless self.save raise MyCustomErrorClass unless address.save end rescue MyCustomErrorClass # here you have to check self.errors OR address.errors end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With