mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
ActiveRecord persistence can ignore validation when trying to save invalid models
This commit is contained in:
parent
c12be51c9c
commit
921cfdad5c
8 changed files with 110 additions and 28 deletions
|
@ -1,5 +1,9 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 3.0.x
|
||||||
|
|
||||||
|
* ActiveRecord persistence can ignore validation when trying to save invalid models
|
||||||
|
|
||||||
## 3.0.1
|
## 3.0.1
|
||||||
|
|
||||||
* added support for Mongoid (Thanks, Michał Taberski)
|
* added support for Mongoid (Thanks, Michał Taberski)
|
||||||
|
|
29
README.md
29
README.md
|
@ -57,7 +57,9 @@ gem 'aasm'
|
||||||
% sudo gem install pkg/aasm-x.y.z.gem
|
% sudo gem install pkg/aasm-x.y.z.gem
|
||||||
```
|
```
|
||||||
|
|
||||||
## Simple Example ##
|
## Examples ##
|
||||||
|
|
||||||
|
### Simple Example ###
|
||||||
|
|
||||||
Here's a quick example highlighting some of the features.
|
Here's a quick example highlighting some of the features.
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ class Conversation
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## A Slightly More Complex Example ##
|
### A Slightly More Complex Example ###
|
||||||
|
|
||||||
This example uses a few of the more complex features available.
|
This example uses a few of the more complex features available.
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ This example uses a few of the more complex features available.
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## Callbacks around events
|
### Callbacks around events ###
|
||||||
```ruby
|
```ruby
|
||||||
class Relationship
|
class Relationship
|
||||||
include AASM
|
include AASM
|
||||||
|
@ -135,6 +137,27 @@ This example uses a few of the more complex features available.
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Persistence example ###
|
||||||
|
```ruby
|
||||||
|
class InvalidPersistor < ActiveRecord::Base
|
||||||
|
include AASM
|
||||||
|
aasm :column => :status, :skip_validation_on_save => true do
|
||||||
|
state :sleeping, :initial => true
|
||||||
|
state :running
|
||||||
|
event :run do
|
||||||
|
transitions :to => :running, :from => :sleeping
|
||||||
|
end
|
||||||
|
event :sleep do
|
||||||
|
transitions :to => :sleeping, :from => :running
|
||||||
|
end
|
||||||
|
end
|
||||||
|
validates_presence_of :name
|
||||||
|
end
|
||||||
|
```
|
||||||
|
This model can change AASM states which are stored into the database, even if the model itself is invalid!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Changelog ##
|
## Changelog ##
|
||||||
|
|
||||||
Look at the [CHANGELOG](https://github.com/rubyist/aasm/blob/master/CHANGELOG.md) for details.
|
Look at the [CHANGELOG](https://github.com/rubyist/aasm/blob/master/CHANGELOG.md) for details.
|
||||||
|
|
|
@ -4,11 +4,18 @@ module AASM
|
||||||
@clazz = clazz
|
@clazz = clazz
|
||||||
sm = AASM::StateMachine[@clazz]
|
sm = AASM::StateMachine[@clazz]
|
||||||
sm.config.column = options[:column].to_sym if options[:column]
|
sm.config.column = options[:column].to_sym if options[:column]
|
||||||
|
|
||||||
if options.key?(:whiny_transitions)
|
if options.key?(:whiny_transitions)
|
||||||
sm.config.whiny_transitions = options[:whiny_transitions]
|
sm.config.whiny_transitions = options[:whiny_transitions]
|
||||||
else
|
else
|
||||||
sm.config.whiny_transitions = true # this is the default, so let's cry
|
sm.config.whiny_transitions = true # this is the default, so let's cry
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if options.key?(:skip_validation_on_save)
|
||||||
|
sm.config.skip_validation_on_save = options[:skip_validation_on_save]
|
||||||
|
else
|
||||||
|
sm.config.skip_validation_on_save = false # this is the default, so don't store any new state if the model is invalid
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def state(name, options={})
|
def state(name, options={})
|
||||||
|
|
|
@ -196,7 +196,12 @@ module AASM
|
||||||
old_value = read_attribute(self.class.aasm_column)
|
old_value = read_attribute(self.class.aasm_column)
|
||||||
write_attribute(self.class.aasm_column, state.to_s)
|
write_attribute(self.class.aasm_column, state.to_s)
|
||||||
|
|
||||||
unless self.save
|
success = if AASM::StateMachine[self.class].config.skip_validation_on_save && !self.valid?
|
||||||
|
self.class.update_all({ self.class.aasm_column => state.to_s }, self.class.primary_key => self.id) == 1
|
||||||
|
else
|
||||||
|
self.save
|
||||||
|
end
|
||||||
|
unless success
|
||||||
write_attribute(self.class.aasm_column, old_value)
|
write_attribute(self.class.aasm_column, old_value)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
16
spec/models/invalid_persistor.rb
Normal file
16
spec/models/invalid_persistor.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
require 'active_record'
|
||||||
|
|
||||||
|
class InvalidPersistor < ActiveRecord::Base
|
||||||
|
include AASM
|
||||||
|
aasm :column => :status, :skip_validation_on_save => true do
|
||||||
|
state :sleeping, :initial => true
|
||||||
|
state :running
|
||||||
|
event :run do
|
||||||
|
transitions :to => :running, :from => :sleeping
|
||||||
|
end
|
||||||
|
event :sleep do
|
||||||
|
transitions :to => :sleeping, :from => :running
|
||||||
|
end
|
||||||
|
end
|
||||||
|
validates_presence_of :name
|
||||||
|
end
|
16
spec/models/validator.rb
Normal file
16
spec/models/validator.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
require 'active_record'
|
||||||
|
|
||||||
|
class Validator < ActiveRecord::Base
|
||||||
|
include AASM
|
||||||
|
aasm :column => :status do
|
||||||
|
state :sleeping, :initial => true
|
||||||
|
state :running
|
||||||
|
event :run do
|
||||||
|
transitions :to => :running, :from => :sleeping
|
||||||
|
end
|
||||||
|
event :sleep do
|
||||||
|
transitions :to => :sleeping, :from => :running
|
||||||
|
end
|
||||||
|
end
|
||||||
|
validates_presence_of :name
|
||||||
|
end
|
|
@ -9,4 +9,9 @@ ActiveRecord::Schema.define(:version => 0) do
|
||||||
t.string "status"
|
t.string "status"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "invalid_persistors", :force => true do |t|
|
||||||
|
t.string "name"
|
||||||
|
t.string "status"
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,21 +68,6 @@ class Thief < ActiveRecord::Base
|
||||||
attr_accessor :skilled, :aasm_state
|
attr_accessor :skilled, :aasm_state
|
||||||
end
|
end
|
||||||
|
|
||||||
class Validator < ActiveRecord::Base
|
|
||||||
include AASM
|
|
||||||
aasm :column => :status do
|
|
||||||
state :sleeping, :initial => true
|
|
||||||
state :running
|
|
||||||
event :run do
|
|
||||||
transitions :to => :running, :from => :sleeping
|
|
||||||
end
|
|
||||||
event :sleep do
|
|
||||||
transitions :to => :sleeping, :from => :running
|
|
||||||
end
|
|
||||||
end
|
|
||||||
validates_presence_of :name
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples_for "aasm model" do
|
shared_examples_for "aasm model" do
|
||||||
it "should include AASM::Persistence::ActiveRecordPersistence" do
|
it "should include AASM::Persistence::ActiveRecordPersistence" do
|
||||||
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
|
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
|
||||||
|
@ -264,19 +249,19 @@ end
|
||||||
|
|
||||||
describe 'transitions with persistence' do
|
describe 'transitions with persistence' do
|
||||||
|
|
||||||
it 'should succeed and store the new state' do
|
it 'should not store states for invalid models' do
|
||||||
validator = Validator.create(:name => 'name')
|
validator = Validator.create(:name => 'name')
|
||||||
validator.should be_valid
|
validator.should be_valid
|
||||||
validator.should be_sleeping
|
validator.should be_sleeping
|
||||||
|
|
||||||
# validator.name = nil
|
validator.name = nil
|
||||||
# validator.should_not be_valid
|
validator.should_not be_valid
|
||||||
# validator.run!.should be_false
|
validator.run!.should be_false
|
||||||
# validator.should be_running
|
validator.should be_sleeping
|
||||||
#
|
|
||||||
# validator.reload
|
validator.reload
|
||||||
# validator.should_not be_running
|
validator.should_not be_running
|
||||||
# validator.should be_sleeping
|
validator.should be_sleeping
|
||||||
|
|
||||||
validator.name = 'another name'
|
validator.name = 'another name'
|
||||||
validator.should be_valid
|
validator.should be_valid
|
||||||
|
@ -288,4 +273,25 @@ describe 'transitions with persistence' do
|
||||||
validator.should_not be_sleeping
|
validator.should_not be_sleeping
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should store states for invalid models if configured' do
|
||||||
|
persistor = InvalidPersistor.create(:name => 'name')
|
||||||
|
persistor.should be_valid
|
||||||
|
persistor.should be_sleeping
|
||||||
|
|
||||||
|
persistor.name = nil
|
||||||
|
persistor.should_not be_valid
|
||||||
|
persistor.run!.should be_true
|
||||||
|
persistor.should be_running
|
||||||
|
|
||||||
|
persistor = InvalidPersistor.find(persistor.id)
|
||||||
|
persistor.valid?
|
||||||
|
persistor.should be_valid
|
||||||
|
persistor.should be_running
|
||||||
|
persistor.should_not be_sleeping
|
||||||
|
|
||||||
|
persistor.reload
|
||||||
|
persistor.should be_running
|
||||||
|
persistor.should_not be_sleeping
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue