Мне любопытно, что потребует такой конструкции!
Я думаю, что вы будете изо всех сил пытаться сделать это без небольшого количества хакерских атак, как вы описали. Например, вы можете в mysql установить тип хранения таблицы для Inner
на тип, который не поддерживает транзакции (например, MyIsam), в то же время сохраняя хранение таблиц других классов с чем-то, что поддерживает транзакции (YUK!).
Если вы можете, вам почти наверняка будет лучше отложить Inner.create
до завершения транзакции. Вы можете использовать начало, чтобы убедиться, что создание всегда происходит. Что-то вроде:
create_inner = false
begin
Outer.transaction.do
...
create_inner = true # instead of Inner.create(:blah)
...
end
ensure
if create_inner
Inner.create(:blah)
end
end
Это усложнилось бы, если бы остальная часть вашего блока зависела от созданного экземпляра Inner
. Вы, вероятно, могли бы создать экземпляр в блоке и установить created_inner
в false в конце блока, чтобы, если код выполняется без исключения, он был создан в транзакции, и вы не создали его снова в гарантированном.
Если вы хотите сделать это в общем случае, вы можете определить метод класса в Inner
для выполнения блока, но всегда создавать объект Inner
. Вам также нужно добавить after_create
к Inner
. Вы будете полагаться на вызов Inner.create
в блоке, чтобы создать его при успешном завершении транзакции, но если он откатывается, вам нужно будет создать его позже. Например:
class Inner < ActiveRecord::Base
def self.ensure_created(&block)
Thread.current[:created_inner] = false
begin
block.call
rescue => e
if Thread.current[:created_inner]
Inner.create(:blah)
end
raise e
end
end
def after_create
# Flag that an instance has been created in this thread so
# that if we rollback out of a transaction we can create again
Thread.current[:created_inner] = true
end
Затем вы бы назвали это как:
Inner.ensure_created do
Outer.transaction do
...
Inner.create(:blah)
...
end
end
ОДНАКО, у этого подхода много недостатков, и я не уверен, что буду защищать его. Это сложно. Это не будет работать, если ActiveRecord :: Rollback повышен, поскольку это исключение не будет пузыриться из Outer.transaction
, но приведет к тому, что экземпляр Inner
не будет создан. Это не будет работать должным образом, когда два или более вызовов вложены. И наконец, я не проверил это полностью - используйте с осторожностью!