Ответ 1
Как это работает:
Модуль сохранения определяет create
: https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/persistence.rb#L46
def create!(attributes = nil, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr, &block) }
else
object = new(attributes, &block)
object.save!
object
end
end
Создает объект и вызывает #save!
Он не задокументирован в общедоступном api, но вызывает https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/transactions.rb#L290
def save!(*) #:nodoc:
with_transaction_returning_status { super }
end
В этот момент транзакция завершает сохранение (супер), которое снова находится в модуле Persistence: https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/persistence.rb#L141
def save!(*)
create_or_update || raise(RecordNotSaved.new(nil, self))
end
Позвольте взломать это с помощью некоторых новых методов:
module ActiveRecord
module Persistence
module ClassMethods
def atomic_create!(attributes = nil, &block)
if attributes.is_a?(Array)
raise "An array of records can't be atomic"
else
object = new(attributes, &block)
object.atomic_save!
object
end
end
end
alias_method :atomic_save!, :save!
end
end
module ActiveRecord
module Transactions
def atomic_save!(*)
super
end
end
end
Возможно, вы хотите использовать стандартный метод create!
, тогда вам нужно переопределить его. Я определяю первый необязательный параметр :atomic
, а когда он присутствует, вы хотите использовать метод atomic_save!
.
module ActiveRecord
module Persistence
module ClassMethods
def create_with_atomic!(first = nil, second = nil, &block)
attributes, atomic = second == nil ? [first, second] : [second, first]
if attributes.is_a?(Array)
create_without_atomic!(attributes, &block)
else
object = new(attributes, &block)
atomic == :atomic ? object.atomic_save! : object.save!
object
end
end
alias_method_chain :create!, :atomic
end
end
end
С этим в config/initializers/<any_name>.rb
он может работать.
Как он работает на консоли:
~/rails/r41example (development) > Product.atomic_create!(name: 'atomic_create')
SQL (99.4ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:50:07.558473"], ["name", "atomic_create"], ["updated_at", "2015-03-22 03:50:07.558473"]]
=> #<Product:0x000000083b1340> {
:id => 1,
:name => "atomic_create",
:created_at => Sun, 22 Mar 2015 03:50:07 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:50:07 UTC +00:00
}
~/rails/r41example (development) > Product.create!(name: 'create with commit')
(0.1ms) begin transaction
SQL (0.1ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:50:20.790566"], ["name", "create with commit"], ["updated_at", "2015-03-22 03:50:20.790566"]]
(109.3ms) commit transaction
=> #<Product:0x000000082f3138> {
:id => 2,
:name => "create with commit",
:created_at => Sun, 22 Mar 2015 03:50:20 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:50:20 UTC +00:00
}
~/rails/r41example (development) > Product.create!(:atomic, name: 'create! atomic')
SQL (137.3ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:51:03.001423"], ["name", "create! atomic"], ["updated_at", "2015-03-22 03:51:03.001423"]]
=> #<Product:0x000000082a0bb8> {
:id => 3,
:name => "create! atomic",
:created_at => Sun, 22 Mar 2015 03:51:03 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:51:03 UTC +00:00
}
Предостережение: вы потеряете after_rollback и after_commit обратные вызовы!
Примечание: в 4.1 методы создаются! и сохранить! находятся в модуле Validations. На Rails 4.2 находятся в Persistence.
Изменить. Возможно, вы считаете, что можете заработать истекшее время транзакции. В моих примерах время фиксации идет на вставки (у меня стандартный HD, и я думаю, что у вас SSD).