Как повысить исключение ActiveRecord:: Rollback и вернуть значение вместе?
У меня есть модель, которая использует вилку acts_as_nested_set
, и я добавил метод модели, чтобы сохранить модель и переместить node в набор в одной транзакции. Этот метод вызывает метод проверки, чтобы убедиться, что перемещение действительно, которое возвращает true или false. Если проверка не удалась, я хочу, чтобы мой метод сохранения поднимал ActiveRecord::Rollback
, чтобы отменить транзакцию, но также возвращает false вызывающему.
Моя модель выглядит так:
class Category < ActiveRecord::Base
acts_as_nested_set :dependent => :destroy, :scope => :journal
def save_with_place_in_set(parent_id)
Category.transaction do
return false if !save_without_place_in_set
if !validate_move parent_id
raise ActiveRecord::Rollback and return false
else
place_in_nested_set parent_id
return true
end
end
end
alias_method_chain :save, :place_in_set
def validate_move(parent_id)
# return true or false if the move is valid
# ...
end
def place_in_nested_set(parent_id)
# place the node in the correct place in the set
# ...
end
end
Однако, когда я вызываю save в ситуации, которая потерпит неудачу, транзакция откатывается, но функция возвращает nil
:
>> c = Category.new(:name => "test")
=> #<Category id: nil, name: "test" parent_id: nil, lft: nil, rgt: nil>
>> c.save_with_place_in_set 47
=> nil
>> c.errors.full_messages
=> ["The specified parent is invalid"]
Ответы
Ответ 1
Вы можете сохранить значение, которое вы хотите вернуть из функции в переменной, и вернуть ее за пределы блока транзакций. Например.
def save_with_place_in_set(parent_id)
return_value = false
Category.transaction do
if !save_without_place_in_set
return_value = false
elsif !validate_move parent_id
return_value = false
raise ActiveRecord::Rollback
else
place_in_nested_set parent_id
return_value = true
end
end
return return_value
end
Я сначала установил значение return_value в false, так как единственный способ вытащить из этого блока транзакций - это если один из других методов поднимает ActiveRecord::Rollback
, я считаю.
Ответ 2
Поскольку исключение ActiveRecord::Rollback
обрабатывается, но не рефилируется ActiveRecord::Transaction
, я мог бы переместить мой возврат из блока транзакций и, таким образом, вернуть значение после откат транзакции.
С небольшим рефакторингом:
def save_with_place_in_set(parent_id = nil)
Category.transaction do
return false if !save_without_place_in_set
raise ActiveRecord::Rollback if !validate_move parent_id
place_in_nested_set parent_id
return true
end
return false
end
Ответ 3
Я знаю, что может быть немного поздно, но я столкнулся с той же проблемой и только что узнал, что в блоке транзакций вы можете просто создать исключение и спасти его... Rails неявно откатывает всю транзакцию. Таким образом, нет необходимости в ActiveRecord:: Rollback.
Например:
def create
begin
Model.transaction do
# using create! will cause Exception on validation errors
record = Model.create!({name: nil})
check_something_afterwards(record)
return true
end
rescue Exception => e
puts e.message
return false
end
end
def check_something_afterwards(record)
# just for demonstration purpose
raise Exception, "name is missing" if record.name.nil?
end
Я работаю с Rails 3.2.15 и Ruby 1.9.3.