1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

Add use transaction flag

This commit is contained in:
Peter Lampesberger 2017-05-30 23:22:38 +02:00 committed by Anil Kumar Maurya
parent 6f493c4553
commit c5b7efaab2
7 changed files with 73 additions and 2 deletions

View file

@ -957,6 +957,24 @@ end
which then leads to `transaction(:requires_new => false)`, the Rails default.
Additionally, if you do not want any of your active record actions to be
wrapped in a transaction, you can specify the `use_transactions` flag. This can
be useful if you want want to persist things to the database that happen as a
result of a transaction or callback, even when some error occurs. The
`use_transactions` flag is true by default.
```ruby
class Job < ActiveRecord::Base
include AASM
aasm :use_transactions => false do
...
end
...
end
```
### Pessimistic Locking
AASM supports [Active Record pessimistic locking via `with_lock`](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html#method-i-with_lock) for database persistence layers.

View file

@ -26,6 +26,9 @@ module AASM
# raise if the model is invalid (in ActiveRecord)
configure :whiny_persistence, false
# Use transactions (in ActiveRecord)
configure :use_transactions, true
# use requires_new for nested transactions (in ActiveRecord)
configure :requires_new_transaction, true

View file

@ -15,6 +15,9 @@ module AASM
# for ActiveRecord: store the new state even if the model is invalid and return true
attr_accessor :skip_validation_on_save
# for ActiveRecord: use transactions
attr_accessor :use_transactions
# for ActiveRecord: use requires_new for nested transactions?
attr_accessor :requires_new_transaction

View file

@ -102,6 +102,10 @@ module AASM
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
end
def use_transactions?(state_machine_name)
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.use_transactions
end
def requires_new?(state_machine_name)
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.requires_new_transaction
end
@ -118,7 +122,11 @@ module AASM
event.fire_global_callbacks(:before_all_transactions, self, *args)
begin
success = aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
success = if options[:persist] && use_transactions?(state_machine_name)
aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
super
end
else
super
end

View file

@ -24,7 +24,7 @@ ActiveRecord::Migration.suppress_messages do
end
end
%w(transactors no_lock_transactors lock_transactors lock_no_wait_transactors multiple_transactors).each do |table_name|
%w(transactors no_lock_transactors lock_transactors lock_no_wait_transactors no_transactors multiple_transactors).each do |table_name|
ActiveRecord::Migration.create_table table_name, :force => true do |t|
t.string "name"
t.string "status"

View file

@ -72,6 +72,31 @@ class LockNoWaitTransactor < ActiveRecord::Base
end
end
class NoTransactor < ActiveRecord::Base
belongs_to :worker
include AASM
aasm :column => :status, use_transactions: false do
state :sleeping, :initial => true
state :running, :before_enter => :start_worker, :after_enter => :fail
event :run do
transitions :to => :running, :from => :sleeping
end
end
private
def start_worker
worker.update_attribute(:status, 'running')
end
def fail
raise StandardError.new('failed on purpose')
end
end
class MultipleTransactor < ActiveRecord::Base
belongs_to :worker

View file

@ -506,6 +506,20 @@ if defined?(ActiveRecord)
end
end
describe 'without transactions' do
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
let(:no_transactor) { NoTransactor.create!(:name => 'transactor', :worker => worker) }
it 'should not rollback all changes' do
expect(no_transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
expect {no_transactor.run!}.to raise_error(StandardError, 'failed on purpose')
expect(no_transactor).to be_running
expect(worker.reload.status).to eq('running')
end
end
describe 'transactions' do
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }