aasm 2.1.3 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
- *.gemspec
2
1
  *.sw?
3
2
  *~
4
3
  .DS_Store
data/README.rdoc CHANGED
@@ -54,7 +54,8 @@ The latest AASM can currently be pulled from the git repository on github.
54
54
 
55
55
  === Building your own gems
56
56
 
57
- % rake gem
57
+ % rake gemspec
58
+ % rake build
58
59
  % sudo gem install pkg/aasm-2.1.gem
59
60
 
60
61
 
@@ -65,6 +66,8 @@ Here's a quick example highlighting some of the features.
65
66
  class Conversation
66
67
  include AASM
67
68
 
69
+ aasm_column :current_state # defaults to aasm_state
70
+
68
71
  aasm_initial_state :unread
69
72
 
70
73
  aasm_state :unread
@@ -87,6 +90,8 @@ This example uses a few of the more complex features available.
87
90
 
88
91
  class Relationship
89
92
  include AASM
93
+
94
+ aasm_column :status
90
95
 
91
96
  aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
92
97
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.3
1
+ 2.1.4
data/lib/aasm.rb CHANGED
@@ -1 +1,9 @@
1
+ module AASM
2
+ end
3
+
4
+ require 'ostruct'
5
+
6
+ require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes')
7
+ require File.join(File.dirname(__FILE__), 'aasm', 'state_machine')
8
+ require File.join(File.dirname(__FILE__), 'aasm', 'persistence')
1
9
  require File.join(File.dirname(__FILE__), 'aasm', 'aasm')
data/lib/aasm/aasm.rb CHANGED
@@ -1,8 +1,3 @@
1
- require File.join(File.dirname(__FILE__), 'event')
2
- require File.join(File.dirname(__FILE__), 'state')
3
- require File.join(File.dirname(__FILE__), 'state_machine')
4
- require File.join(File.dirname(__FILE__), 'persistence')
5
-
6
1
  module AASM
7
2
  class InvalidTransition < RuntimeError
8
3
  end
@@ -146,50 +141,55 @@ module AASM
146
141
  end
147
142
 
148
143
  def aasm_fire_event(name, persist, *args)
149
- old_state = aasm_state_object_for_state(aasm_current_state)
150
144
  event = self.class.aasm_events[name]
145
+ begin
146
+ old_state = aasm_state_object_for_state(aasm_current_state)
151
147
 
152
- old_state.call_action(:exit, self)
153
148
 
154
- # new event before callback
155
- event.call_action(:before, self)
149
+ old_state.call_action(:exit, self)
156
150
 
157
- new_state_name = event.fire(self, *args)
151
+ # new event before callback
152
+ event.call_action(:before, self)
158
153
 
159
- unless new_state_name.nil?
160
- new_state = aasm_state_object_for_state(new_state_name)
154
+ new_state_name = event.fire(self, *args)
161
155
 
162
- # new before_ callbacks
163
- old_state.call_action(:before_exit, self)
164
- new_state.call_action(:before_enter, self)
156
+ unless new_state_name.nil?
157
+ new_state = aasm_state_object_for_state(new_state_name)
165
158
 
166
- new_state.call_action(:enter, self)
159
+ # new before_ callbacks
160
+ old_state.call_action(:before_exit, self)
161
+ new_state.call_action(:before_enter, self)
167
162
 
168
- persist_successful = true
169
- if persist
170
- persist_successful = set_aasm_current_state_with_persistence(new_state_name)
171
- event.execute_success_callback(self) if persist_successful
172
- else
173
- self.aasm_current_state = new_state_name
174
- end
163
+ new_state.call_action(:enter, self)
164
+
165
+ persist_successful = true
166
+ if persist
167
+ persist_successful = set_aasm_current_state_with_persistence(new_state_name)
168
+ event.execute_success_callback(self) if persist_successful
169
+ else
170
+ self.aasm_current_state = new_state_name
171
+ end
175
172
 
176
- if persist_successful
177
- old_state.call_action(:after_exit, self)
178
- new_state.call_action(:after_enter, self)
179
- event.call_action(:after, self)
173
+ if persist_successful
174
+ old_state.call_action(:after_exit, self)
175
+ new_state.call_action(:after_enter, self)
176
+ event.call_action(:after, self)
180
177
 
181
- self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
178
+ self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
179
+ else
180
+ self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
181
+ end
182
+
183
+ persist_successful
182
184
  else
183
- self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
184
- end
185
+ if self.respond_to?(:aasm_event_failed)
186
+ self.aasm_event_failed(name, old_state.name)
187
+ end
185
188
 
186
- persist_successful
187
- else
188
- if self.respond_to?(:aasm_event_failed)
189
- self.aasm_event_failed(name, old_state.name)
189
+ false
190
190
  end
191
-
192
- false
191
+ rescue StandardError => e
192
+ event.execute_error_callback(self, e)
193
193
  end
194
194
  end
195
195
  end
data/lib/aasm/event.rb CHANGED
@@ -1,98 +1,109 @@
1
- require File.join(File.dirname(__FILE__), 'state_transition')
1
+ class AASM::SupportingClasses::Event
2
+ attr_reader :name, :success, :options
2
3
 
3
- module AASM
4
- module SupportingClasses
5
- class Event
6
- attr_reader :name, :success, :options
7
-
8
- def initialize(name, options = {}, &block)
9
- @name = name
10
- @transitions = []
11
- update(options, &block)
12
- end
4
+ def initialize(name, options = {}, &block)
5
+ @name = name
6
+ @transitions = []
7
+ update(options, &block)
8
+ end
13
9
 
14
- def fire(obj, to_state=nil, *args)
15
- transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
16
- raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0
10
+ def fire(obj, to_state=nil, *args)
11
+ transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
12
+ raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0
17
13
 
18
- next_state = nil
19
- transitions.each do |transition|
20
- next if to_state and !Array(transition.to).include?(to_state)
21
- if transition.perform(obj)
22
- next_state = to_state || Array(transition.to).first
23
- transition.execute(obj, *args)
24
- break
25
- end
26
- end
27
- next_state
14
+ next_state = nil
15
+ transitions.each do |transition|
16
+ next if to_state and !Array(transition.to).include?(to_state)
17
+ if transition.perform(obj)
18
+ next_state = to_state || Array(transition.to).first
19
+ transition.execute(obj, *args)
20
+ break
28
21
  end
22
+ end
23
+ next_state
24
+ end
29
25
 
30
- def transitions_from_state?(state)
31
- @transitions.any? { |t| t.from == state }
32
- end
26
+ def transitions_from_state?(state)
27
+ @transitions.any? { |t| t.from == state }
28
+ end
33
29
 
34
- def transitions_from_state(state)
35
- @transitions.select { |t| t.from == state }
36
- end
30
+ def transitions_from_state(state)
31
+ @transitions.select { |t| t.from == state }
32
+ end
37
33
 
38
- def all_transitions
39
- @transitions
40
- end
34
+ def all_transitions
35
+ @transitions
36
+ end
41
37
 
42
- def call_action(action, record)
43
- action = @options[action]
44
- action.is_a?(Array) ?
45
- action.each {|a| _call_action(a, record)} :
46
- _call_action(action, record)
47
- end
38
+ def call_action(action, record)
39
+ action = @options[action]
40
+ action.is_a?(Array) ?
41
+ action.each {|a| _call_action(a, record)} :
42
+ _call_action(action, record)
43
+ end
48
44
 
49
- def ==(event)
50
- if event.is_a? Symbol
51
- name == event
52
- else
53
- name == event.name
54
- end
55
- end
45
+ def ==(event)
46
+ if event.is_a? Symbol
47
+ name == event
48
+ else
49
+ name == event.name
50
+ end
51
+ end
56
52
 
57
- def update(options = {}, &block)
58
- if options.key?(:success) then
59
- @success = options[:success]
60
- end
61
- if block then
62
- instance_eval(&block)
63
- end
64
- @options = options
65
- self
66
- end
53
+ def update(options = {}, &block)
54
+ if options.key?(:success) then
55
+ @success = options[:success]
56
+ end
57
+ if options.key?(:error) then
58
+ @error = options[:error]
59
+ end
60
+ if block then
61
+ instance_eval(&block)
62
+ end
63
+ @options = options
64
+ self
65
+ end
67
66
 
68
- def execute_success_callback(obj, success = nil)
69
- callback = success || @success
70
- case(callback)
71
- when String, Symbol
72
- obj.send(callback)
73
- when Proc
74
- callback.call(obj)
75
- when Array
76
- callback.each{|meth|self.execute_success_callback(obj, meth)}
77
- end
78
- end
67
+ def execute_success_callback(obj, success = nil)
68
+ callback = success || @success
69
+ case(callback)
70
+ when String, Symbol
71
+ obj.send(callback)
72
+ when Proc
73
+ callback.call(obj)
74
+ when Array
75
+ callback.each{|meth|self.execute_success_callback(obj, meth)}
76
+ end
77
+ end
79
78
 
80
- private
79
+ def execute_error_callback(obj, error, error_callback=nil)
80
+ callback = error_callback || @error
81
+ raise error unless callback
82
+ case(callback)
83
+ when String, Symbol
84
+ raise NoMethodError unless obj.respond_to?(callback.to_sym)
85
+ obj.send(callback, error)
86
+ when Proc
87
+ callback.call(obj, error)
88
+ when Array
89
+ callback.each{|meth|self.execute_error_callback(obj, error, meth)}
90
+ end
91
+ end
81
92
 
82
- def _call_action(action, record)
83
- case action
84
- when Symbol, String
85
- record.send(action)
86
- when Proc
87
- action.call(record)
88
- end
89
- end
93
+ private
90
94
 
91
- def transitions(trans_opts)
92
- Array(trans_opts[:from]).each do |s|
93
- @transitions << SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
94
- end
95
- end
95
+ def _call_action(action, record)
96
+ case action
97
+ when Symbol, String
98
+ record.send(action)
99
+ when Proc
100
+ action.call(record)
101
+ end
102
+ end
103
+
104
+ def transitions(trans_opts)
105
+ Array(trans_opts[:from]).each do |s|
106
+ @transitions << AASM::SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
96
107
  end
97
108
  end
98
109
  end
@@ -1,16 +1,14 @@
1
- module AASM
2
- module Persistence
1
+ module AASM::Persistence
3
2
 
4
- # Checks to see this class or any of it's superclasses inherit from
5
- # ActiveRecord::Base and if so includes ActiveRecordPersistence
6
- def self.set_persistence(base)
7
- # Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
8
- hierarchy = base.ancestors.map {|klass| klass.to_s}
3
+ # Checks to see this class or any of it's superclasses inherit from
4
+ # ActiveRecord::Base and if so includes ActiveRecordPersistence
5
+ def self.set_persistence(base)
6
+ # Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
7
+ hierarchy = base.ancestors.map {|klass| klass.to_s}
9
8
 
10
- if hierarchy.include?("ActiveRecord::Base")
11
- require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
12
- base.send(:include, AASM::Persistence::ActiveRecordPersistence)
13
- end
9
+ if hierarchy.include?("ActiveRecord::Base")
10
+ require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
11
+ base.send(:include, AASM::Persistence::ActiveRecordPersistence)
14
12
  end
15
13
  end
16
14
  end
data/lib/aasm/state.rb CHANGED
@@ -1,55 +1,53 @@
1
- module AASM
2
- module SupportingClasses
3
- class State
4
- attr_reader :name, :options
5
-
6
- def initialize(name, options={})
7
- @name = name
8
- update(options)
9
- end
10
-
11
- def ==(state)
12
- if state.is_a? Symbol
13
- name == state
14
- else
15
- name == state.name
16
- end
17
- end
18
-
19
- def call_action(action, record)
20
- action = @options[action]
21
- action.is_a?(Array) ?
22
- action.each {|a| _call_action(a, record)} :
23
- _call_action(action, record)
24
- end
25
-
26
- def display_name
27
- @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
28
- end
29
-
30
- def for_select
31
- [display_name, name.to_s]
32
- end
33
-
34
- def update(options = {})
35
- if options.key?(:display) then
36
- @display_name = options.delete(:display)
37
- end
38
- @options = options
39
- self
40
- end
41
-
42
- private
43
-
44
- def _call_action(action, record)
45
- case action
46
- when Symbol, String
47
- record.send(action)
48
- when Proc
49
- action.call(record)
50
- end
51
- end
1
+ class AASM::SupportingClasses::State
2
+ attr_reader :name, :options
52
3
 
4
+ def initialize(name, options={})
5
+ @name = name
6
+ update(options)
7
+ end
8
+
9
+ def ==(state)
10
+ if state.is_a? Symbol
11
+ name == state
12
+ else
13
+ name == state.name
14
+ end
15
+ end
16
+
17
+ def call_action(action, record)
18
+ action = @options[action]
19
+ catch :halt_aasm_chain do
20
+ action.is_a?(Array) ?
21
+ action.each {|a| _call_action(a, record)} :
22
+ _call_action(action, record)
53
23
  end
54
24
  end
25
+
26
+ def display_name
27
+ @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
28
+ end
29
+
30
+ def for_select
31
+ [display_name, name.to_s]
32
+ end
33
+
34
+ def update(options = {})
35
+ if options.key?(:display) then
36
+ @display_name = options.delete(:display)
37
+ end
38
+ @options = options
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def _call_action(action, record)
45
+ case action
46
+ when Symbol, String
47
+ record.send(action)
48
+ when Proc
49
+ action.call(record)
50
+ end
51
+ end
52
+
55
53
  end
@@ -1,36 +1,32 @@
1
- require 'ostruct'
2
-
3
- module AASM
4
- class StateMachine
5
- def self.[](*args)
6
- (@machines ||= {})[args]
7
- end
1
+ class AASM::StateMachine
2
+ def self.[](*args)
3
+ (@machines ||= {})[args]
4
+ end
8
5
 
9
- def self.[]=(*args)
10
- val = args.pop
11
- (@machines ||= {})[args] = val
12
- end
6
+ def self.[]=(*args)
7
+ val = args.pop
8
+ (@machines ||= {})[args] = val
9
+ end
13
10
 
14
- attr_accessor :states, :events, :initial_state, :config
15
- attr_reader :name
11
+ attr_accessor :states, :events, :initial_state, :config
12
+ attr_reader :name
16
13
 
17
- def initialize(name)
18
- @name = name
19
- @initial_state = nil
20
- @states = []
21
- @events = {}
22
- @config = OpenStruct.new
23
- end
14
+ def initialize(name)
15
+ @name = name
16
+ @initial_state = nil
17
+ @states = []
18
+ @events = {}
19
+ @config = OpenStruct.new
20
+ end
24
21
 
25
- def clone
26
- klone = super
27
- klone.states = states.clone
28
- klone.events = events.clone
29
- klone
30
- end
22
+ def clone
23
+ klone = super
24
+ klone.states = states.clone
25
+ klone.events = events.clone
26
+ klone
27
+ end
31
28
 
32
- def create_state(name, options)
33
- @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
34
- end
29
+ def create_state(name, options)
30
+ @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
35
31
  end
36
32
  end
@@ -1,50 +1,46 @@
1
- module AASM
2
- module SupportingClasses
3
- class StateTransition
4
- attr_reader :from, :to, :opts
5
- alias_method :options, :opts
6
-
7
- def initialize(opts)
8
- @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
9
- @opts = opts
10
- end
11
-
12
- def perform(obj)
13
- case @guard
14
- when Symbol, String
15
- obj.send(@guard)
16
- when Proc
17
- @guard.call(obj)
18
- else
19
- true
20
- end
21
- end
22
-
23
- def execute(obj, *args)
24
- @on_transition.is_a?(Array) ?
25
- @on_transition.each {|ot| _execute(obj, ot, *args)} :
26
- _execute(obj, @on_transition, *args)
27
- end
28
-
29
- def ==(obj)
30
- @from == obj.from && @to == obj.to
31
- end
32
-
33
- def from?(value)
34
- @from == value
35
- end
36
-
37
- private
38
-
39
- def _execute(obj, on_transition, *args)
40
- case on_transition
41
- when Symbol, String
42
- obj.send(on_transition, *args)
43
- when Proc
44
- on_transition.call(obj, *args)
45
- end
46
- end
1
+ class AASM::SupportingClasses::StateTransition
2
+ attr_reader :from, :to, :opts
3
+ alias_method :options, :opts
47
4
 
5
+ def initialize(opts)
6
+ @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
7
+ @opts = opts
8
+ end
9
+
10
+ def perform(obj)
11
+ case @guard
12
+ when Symbol, String
13
+ obj.send(@guard)
14
+ when Proc
15
+ @guard.call(obj)
16
+ else
17
+ true
48
18
  end
49
19
  end
20
+
21
+ def execute(obj, *args)
22
+ @on_transition.is_a?(Array) ?
23
+ @on_transition.each {|ot| _execute(obj, ot, *args)} :
24
+ _execute(obj, @on_transition, *args)
25
+ end
26
+
27
+ def ==(obj)
28
+ @from == obj.from && @to == obj.to
29
+ end
30
+
31
+ def from?(value)
32
+ @from == value
33
+ end
34
+
35
+ private
36
+
37
+ def _execute(obj, on_transition, *args)
38
+ case on_transition
39
+ when Symbol, String
40
+ obj.send(on_transition, *args)
41
+ when Proc
42
+ on_transition.call(obj, *args)
43
+ end
44
+ end
45
+
50
46
  end
@@ -0,0 +1,6 @@
1
+ module AASM::SupportingClasses
2
+ end
3
+
4
+ require File.join(File.dirname(__FILE__), 'state_transition')
5
+ require File.join(File.dirname(__FILE__), 'event')
6
+ require File.join(File.dirname(__FILE__), 'state')
@@ -92,7 +92,7 @@ describe AASM, '- subclassing' do
92
92
  FooTwo.aasm_states.should include(state)
93
93
  end
94
94
  end
95
-
95
+
96
96
  it 'should not add the child states to the parent machine' do
97
97
  Foo.aasm_states.should_not include(:foo)
98
98
  end
@@ -270,6 +270,36 @@ describe AASM, '- getting events for a state' do
270
270
  end
271
271
 
272
272
  describe AASM, '- event callbacks' do
273
+ describe "with an error callback defined" do
274
+ before do
275
+ class Foo
276
+ aasm_event :safe_close, :success => :success_callback, :error => :error_callback do
277
+ transitions :to => :closed, :from => [:open]
278
+ end
279
+ end
280
+
281
+ @foo = Foo.new
282
+ end
283
+
284
+ it "should run error_callback if an exception is raised and error_callback defined" do
285
+ def @foo.error_callback(e)
286
+ end
287
+ @foo.stub!(:enter).and_raise(e=StandardError.new)
288
+ @foo.should_receive(:error_callback).with(e)
289
+ @foo.safe_close!
290
+ end
291
+
292
+ it "should raise NoMethodError if exceptionis raised and error_callback is declared but not defined" do
293
+ @foo.stub!(:enter).and_raise(StandardError)
294
+ lambda{@foo.safe_close!}.should raise_error(NoMethodError)
295
+ end
296
+
297
+ it "should propagate an error if no error callback is declared" do
298
+ @foo.stub!(:enter).and_raise("Cannot enter safe")
299
+ lambda{@foo.close!}.should raise_error(StandardError, "Cannot enter safe")
300
+ end
301
+ end
302
+
273
303
  describe "with aasm_event_fired defined" do
274
304
  before do
275
305
  @foo = Foo.new
@@ -63,6 +63,17 @@ describe AASM::SupportingClasses::State do
63
63
  state.call_action(:entering, record)
64
64
  end
65
65
 
66
+ it "should stop calling actions if one of them raises :halt_aasm_chain" do
67
+ state = new_state(:entering => [:a, :b, :c])
68
+
69
+ record = mock('record')
70
+ record.should_receive(:a)
71
+ record.should_receive(:b).and_throw(:halt_aasm_chain)
72
+ record.should_not_receive(:c)
73
+
74
+ state.call_action(:entering, record)
75
+ end
76
+
66
77
  it 'should call a proc, passing in the record for an action if the action is present' do
67
78
  state = new_state(:entering => Proc.new {|r| r.foobar})
68
79
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Barron
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-10-23 00:00:00 -04:00
14
+ date: 2010-01-17 00:00:00 -05:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -68,6 +68,7 @@ files:
68
68
  - lib/aasm/state.rb
69
69
  - lib/aasm/state_machine.rb
70
70
  - lib/aasm/state_transition.rb
71
+ - lib/aasm/supporting_classes.rb
71
72
  - spec/functional/conversation.rb
72
73
  - spec/functional/conversation_spec.rb
73
74
  - spec/spec_helper.rb