allow configuring behavior of nested transactions #107

This commit is contained in:
Thorsten Böttger 2014-01-22 21:28:40 +01:00
parent f6c903fae4
commit ca7306d0bb
5 changed files with 58 additions and 8 deletions

View File

@ -6,6 +6,7 @@
## 3.1.0 (not yet released)
* allow configuring behavior of nested transactions (see [issue #107](https://github.com/aasm/aasm/issues/107))
* validating the current state (see [issue #95](https://github.com/aasm/aasm/issues/95), thanks to [@ivantsepp](https://github.com/ivantsepp))
## 3.0.26

View File

@ -339,6 +339,28 @@ class Job < ActiveRecord::Base
end
```
If you want to encapsulate state changes within an own transaction, the behavior
of this nested transaction might be confusing. Take a look at
[ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
if you want to know more about this. Nevertheless, AASM by default requires a new transaction
`transaction(:requires_new => true)`. You can override this behavior by changing
the configuration
```ruby
class Job < ActiveRecord::Base
include AASM
aasm :requires_new_transaction => false do
...
end
...
end
```
which then leads to `transaction(:requires_new => false)`, the Rails default.
### Column name & migration
As a default AASM uses the column `aasm_state` to store the states. You can override

View File

@ -23,6 +23,12 @@ module AASM
elsif @state_machine.config.skip_validation_on_save.nil?
@state_machine.config.skip_validation_on_save = false # this is the default, so don't store any new state if the model is invalid
end
if options.key?(:requires_new_transaction)
@state_machine.config.requires_new_transaction = options[:requires_new_transaction]
elsif @state_machine.config.requires_new_transaction.nil?
@state_machine.config.requires_new_transaction = true # use requires_new for nested transactions
end
end
def initial_state(new_initial_state=nil)

View File

@ -138,7 +138,7 @@ module AASM
end
def aasm_fire_event(name, options, *args, &block)
success = self.class.transaction(:requires_new => true) do
success = self.class.transaction(:requires_new => requires_new?) do
super
end
@ -150,6 +150,10 @@ module AASM
success
end
def requires_new?
AASM::StateMachine[self.class].config.requires_new_transaction
end
def aasm_validate_states
unless AASM::StateMachine[self.class].config.skip_validation_on_save
if aasm.current_state && !aasm.states.include?(aasm.current_state)

View File

@ -173,16 +173,33 @@ describe 'transitions with persistence' do
expect(worker.reload.status).to eq('sleeping')
end
it "should rollback all changes in nested transaction" do
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
context "nested transactions" do
it "should rollback all changes in nested transaction" do
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
Worker.transaction do
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
Worker.transaction do
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
end
expect(transactor).to be_running
expect(worker.reload.status).to eq('sleeping')
end
expect(transactor).to be_running
expect(worker.reload.status).to eq('sleeping')
it "should only rollback changes in the main transaction not the nested one" do
# change configuration to not require new transaction
AASM::StateMachine[Transactor].config.requires_new_transaction = false
expect(transactor).to be_sleeping
expect(worker.status).to eq('sleeping')
Worker.transaction do
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
end
expect(transactor).to be_running
expect(worker.reload.status).to eq('running')
end
end
describe "after_commit callback" do