aasm 3.4.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,7 +21,7 @@ module AASM
21
21
  state_object = state_object_for_name(state_name)
22
22
 
23
23
  state_object.fire_callbacks(:before_enter, @instance)
24
- state_object.fire_callbacks(:enter, @instance)
24
+ # state_object.fire_callbacks(:enter, @instance)
25
25
  self.current_state = state_name
26
26
  state_object.fire_callbacks(:after_enter, @instance)
27
27
 
@@ -33,9 +33,10 @@ module AASM
33
33
  end
34
34
 
35
35
  def states(options={})
36
- if options[:permissible]
36
+ if options[:permitted]
37
37
  # ugliness level 1000
38
- transitions = @instance.class.aasm.events.values_at(*permissible_events).compact.map {|e| e.transitions_from_state(current_state) }
38
+ permitted_event_names = events(:permitted => true).map(&:name)
39
+ transitions = @instance.class.aasm.state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
39
40
  tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq
40
41
  @instance.class.aasm.states.select {|s| tos.include?(s.name.to_sym)}
41
42
  else
@@ -43,18 +44,17 @@ module AASM
43
44
  end
44
45
  end
45
46
 
46
- # QUESTION: shouldn't events and permissible_events be the same thing?
47
- # QUESTION: shouldn't events return objects instead of strings?
48
- def events(state=current_state)
49
- events = @instance.class.aasm.events.values.select {|e| e.transitions_from_state?(state) }
50
- events.map {|e| e.name}
51
- end
47
+ def events(options={})
48
+ state = options[:state] || current_state
49
+ events = @instance.class.aasm.events.select {|e| e.transitions_from_state?(state) }
50
+
51
+ if options[:permitted]
52
+ # filters the results of events_for_current_state so that only those that
53
+ # are really currently possible (given transition guards) are shown.
54
+ events.select! { |e| @instance.send("may_#{e.name}?") }
55
+ end
52
56
 
53
- # filters the results of events_for_current_state so that only those that
54
- # are really currently possible (given transition guards) are shown.
55
- # QUESTION: what about events.permissible ?
56
- def permissible_events
57
- events.select{ |e| @instance.send(("may_" + e.to_s + "?").to_sym) }
57
+ events
58
58
  end
59
59
 
60
60
  def state_object_for_name(name)
@@ -75,7 +75,7 @@ module AASM
75
75
  end
76
76
 
77
77
  def may_fire_event?(name, *args)
78
- if event = @instance.class.aasm.events[name]
78
+ if event = @instance.class.aasm.state_machine.events[name]
79
79
  event.may_fire?(@instance, *args)
80
80
  else
81
81
  false # unknown event
@@ -174,8 +174,8 @@ module AASM
174
174
  success = options[:persist] ? self.class.transaction(:requires_new => requires_new?) { super } : super
175
175
 
176
176
  if success && options[:persist]
177
- new_state = aasm.state_object_for_name(aasm.current_state)
178
- new_state.fire_callbacks(:after_commit, self)
177
+ event = self.class.aasm.state_machine.events[name]
178
+ event.fire_callbacks(:after_commit, self)
179
179
  end
180
180
 
181
181
  success
@@ -16,7 +16,7 @@ module AASM
16
16
  @initial_state = nil
17
17
  @states = []
18
18
  @events = {}
19
- @config = OpenStruct.new
19
+ @config = AASM::Configuration.new
20
20
  end
21
21
 
22
22
  # called internally by Ruby 1.9 after clone()
@@ -1,33 +1,35 @@
1
1
  module AASM
2
2
  class Transition
3
+ include DslHelper
4
+
3
5
  attr_reader :from, :to, :opts
4
6
  alias_method :options, :opts
5
7
 
6
- def initialize(opts)
8
+ def initialize(opts, &block)
9
+ add_options_from_dsl(opts, [:on_transition, :guard, :after], &block) if block
10
+
7
11
  @from = opts[:from]
8
12
  @to = opts[:to]
9
- @guards = Array(opts[:guard] || opts[:guards])
10
- @on_transition = opts[:on_transition]
13
+ @guards = Array(opts[:guard] || opts[:guards] || opts[:if])
14
+ @unless = Array(opts[:unless]) #TODO: This could use a better name
15
+
16
+ if opts[:on_transition]
17
+ warn '[DEPRECATION] :on_transition is deprecated, use :after instead'
18
+ opts[:after] = Array(opts[:after]) + Array(opts[:on_transition])
19
+ end
20
+ @after = Array(opts[:after])
21
+ @after = @after[0] if @after.size == 1
22
+
11
23
  @opts = opts
12
24
  end
13
25
 
14
- # TODO: should be named allowed? or similar
15
- def perform(obj, *args)
16
- @guards.each do |guard|
17
- case guard
18
- when Symbol, String
19
- return false unless obj.send(guard, *args)
20
- when Proc
21
- return false unless guard.call(obj, *args)
22
- end
23
- end
24
- true
26
+ def allowed?(obj, *args)
27
+ invoke_callbacks_compatible_with_guard(@guards, obj, args, :guard => true) &&
28
+ invoke_callbacks_compatible_with_guard(@unless, obj, args, :unless => true)
25
29
  end
26
30
 
27
31
  def execute(obj, *args)
28
- @on_transition.is_a?(Array) ?
29
- @on_transition.each {|ot| _execute(obj, ot, *args)} :
30
- _execute(obj, @on_transition, *args)
32
+ invoke_callbacks_compatible_with_guard(@after, obj, args)
31
33
  end
32
34
 
33
35
  def ==(obj)
@@ -40,15 +42,33 @@ module AASM
40
42
 
41
43
  private
42
44
 
43
- def _execute(obj, on_transition, *args)
44
- obj.aasm.from_state = @from if obj.aasm.respond_to?(:from_state=)
45
- obj.aasm.to_state = @to if obj.aasm.respond_to?(:to_state=)
45
+ def invoke_callbacks_compatible_with_guard(code, record, args, options={})
46
+ if record.respond_to?(:aasm)
47
+ record.aasm.from_state = @from if record.aasm.respond_to?(:from_state=)
48
+ record.aasm.to_state = @to if record.aasm.respond_to?(:to_state=)
49
+ end
46
50
 
47
- case on_transition
48
- when Proc
49
- on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
51
+ case code
50
52
  when Symbol, String
51
- obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
53
+ # QUESTION : record.send(code, *args) ?
54
+ arity = record.send(:method, code.to_sym).arity
55
+ arity == 0 ? record.send(code) : record.send(code, *args)
56
+ when Proc
57
+ # QUESTION : record.instance_exec(*args, &code) ?
58
+ code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
59
+ when Array
60
+ if options[:guard]
61
+ # invoke guard callbacks
62
+ code.all? {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
63
+ elsif options[:unless]
64
+ # invoke unless callbacks
65
+ code.all? {|a| !invoke_callbacks_compatible_with_guard(a, record, args)}
66
+ else
67
+ # invoke after callbacks
68
+ code.map {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
69
+ end
70
+ else
71
+ true
52
72
  end
53
73
  end
54
74
 
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "3.4.0"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  ActiveRecord::Migration.suppress_messages do
2
- %w{gates readers writers transients simples simple_new_dsls no_scopes thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
2
+ %w{gates readers writers transients simples simple_new_dsls no_scopes no_direct_assignments thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
3
3
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
4
4
  t.string "aasm_state"
5
5
  end
@@ -5,14 +5,18 @@ class AuthMachine
5
5
 
6
6
  aasm do
7
7
  state :passive
8
- state :pending, :initial => true, :enter => :make_activation_code
9
- state :active, :enter => :do_activate
8
+ state :pending, :initial => true, :before_enter => :make_activation_code
9
+ state :active, :before_enter => :do_activate
10
10
  state :suspended
11
- state :deleted, :enter => :do_delete, :exit => :do_undelete
11
+ state :deleted, :before_enter => :do_delete#, :exit => :do_undelete
12
12
  state :waiting
13
13
 
14
14
  event :register do
15
- transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
15
+ transitions :from => :passive, :to => :pending do
16
+ guard do
17
+ can_register?
18
+ end
19
+ end
16
20
  end
17
21
 
18
22
  event :activate do
@@ -33,8 +37,8 @@ class AuthMachine
33
37
  end
34
38
 
35
39
  event :unsuspend do
36
- transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
37
- transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
40
+ transitions :from => :suspended, :to => :active, :guard => Proc.new { has_activated? }
41
+ transitions :from => :suspended, :to => :pending, :guard => :has_activation_code?
38
42
  transitions :from => :suspended, :to => :passive
39
43
  end
40
44
 
@@ -1,9 +1,16 @@
1
1
  class CallbackNewDsl
2
2
  include AASM
3
3
 
4
+ def initialize(options={})
5
+ @fail_event_guard = options[:fail_event_guard]
6
+ @fail_transition_guard = options[:fail_transition_guard]
7
+ @log = options[:log]
8
+ end
9
+
4
10
  aasm do
5
11
  state :open, :initial => true,
6
12
  :before_enter => :before_enter_open,
13
+ :enter => :enter_open,
7
14
  :after_enter => :after_enter_open,
8
15
  :before_exit => :before_exit_open,
9
16
  :exit => :exit_open,
@@ -14,10 +21,62 @@ class CallbackNewDsl
14
21
  :enter => :enter_closed,
15
22
  :after_enter => :after_enter_closed,
16
23
  :before_exit => :before_exit_closed,
24
+ :exit => :exit_closed,
25
+ :after_exit => :after_exit_closed
26
+
27
+ event :close, :before => :before, :after => :after, :guard => :event_guard do
28
+ transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :transitioning
29
+ end
30
+
31
+ event :open, :before => :before, :after => :after do
32
+ transitions :to => :open, :from => :closed
33
+ end
34
+ end
35
+
36
+ def log(text)
37
+ puts text if @log
38
+ end
39
+
40
+ def before_enter_open; log('before_enter_open'); end
41
+ def enter_open; log('enter_open'); end
42
+ def before_exit_open; log('before_exit_open'); end
43
+ def after_enter_open; log('after_enter_open'); end
44
+ def exit_open; log('exit_open'); end
45
+ def after_exit_open; log('after_exit_open'); end
46
+
47
+ def before_enter_closed; log('before_enter_closed'); end
48
+ def enter_closed; log('enter_closed'); end
49
+ def before_exit_closed; log('before_exit_closed'); end
50
+ def exit_closed; log('exit_closed'); end
51
+ def after_enter_closed; log('after_enter_closed'); end
52
+ def after_exit_closed; log('after_exit_closed'); end
53
+
54
+ def event_guard; log('event_guard'); !@fail_event_guard; end
55
+ def transition_guard; log('transition_guard'); !@fail_transition_guard; end
56
+ def transitioning; log('transitioning'); end
57
+
58
+ def before; log('before'); end
59
+ def after; log('after'); end
60
+ end
61
+
62
+ class CallbackNewDslArgs
63
+ include AASM
64
+
65
+ aasm do
66
+ state :open, :initial => true,
67
+ :before_enter => :before_enter_open,
68
+ :after_enter => :after_enter_open,
69
+ :before_exit => :before_exit_open,
70
+ :after_exit => :after_exit_open
71
+
72
+ state :closed,
73
+ :before_enter => :before_enter_closed,
74
+ :after_enter => :after_enter_closed,
75
+ :before_exit => :before_exit_closed,
17
76
  :after_exit => :after_exit_closed
18
77
 
19
78
  event :close, :before => :before, :after => :after do
20
- transitions :to => :closed, :from => [:open]
79
+ transitions :to => :closed, :from => [:open], :after => :transition_proc
21
80
  end
22
81
 
23
82
  event :open, :before => :before, :after => :after do
@@ -25,19 +84,46 @@ class CallbackNewDsl
25
84
  end
26
85
  end
27
86
 
28
- def before_enter_open; end
29
- def before_exit_open; end
30
- def after_enter_open; end
31
- def after_exit_open; end
87
+ def log(text)
88
+ # puts text
89
+ end
90
+
91
+ def before_enter_open; log('before_enter_open'); end
92
+ def before_exit_open; log('before_exit_open'); end
93
+ def after_enter_open; log('after_enter_open'); end
94
+ def after_exit_open; log('after_exit_open'); end
95
+
96
+ def before_enter_closed; log('before_enter_closed'); end
97
+ def before_exit_closed; log('before_enter_closed'); end
98
+ def after_enter_closed; log('after_enter_closed'); end
99
+ def after_exit_closed; log('after_exit_closed'); end
100
+
101
+ def before(*args); log('before'); end
102
+ def transition_proc(arg1, arg2); log('transition_proc'); end
103
+ def after(*args); log('after'); end
104
+ end
105
+
106
+ class CallbackWithStateArg
107
+
108
+ include AASM
109
+
110
+ aasm do
111
+ state :open, :inital => true
112
+ state :closed
113
+ state :out_to_lunch
114
+
115
+ event :close, :before => :before_method, :after => :after_method do
116
+ transitions :to => :closed, :from => [:open], :after => :transition_method
117
+ transitions :to => :out_to_lunch, :from => [:open], :after => :transition_method2
118
+ end
119
+ end
120
+
121
+ def before_method(arg); end
122
+
123
+ def after_method(arg); end
32
124
 
33
- def before_enter_closed; end
34
- def before_exit_closed; end
35
- def after_enter_closed; end
36
- def after_exit_closed; end
125
+ def transition_method(arg); end
37
126
 
38
- def before; end
39
- def after; end
127
+ def transition_method2(arg); end
40
128
 
41
- def enter_closed; end
42
- def exit_open; end
43
129
  end
@@ -10,9 +10,9 @@ class DoubleDefiner
10
10
  end
11
11
 
12
12
  # simulating a reload
13
- state :finished, :enter => :do_enter
13
+ state :finished, :before_enter => :do_enter
14
14
  event :finish do
15
- transitions :from => :started, :to => :finished, :on_transition => :do_on_transition
15
+ transitions :from => :started, :to => :finished, :after => :do_on_transition
16
16
  end
17
17
  end
18
18
 
@@ -1,8 +1,8 @@
1
1
  class Foo
2
2
  include AASM
3
3
  aasm do
4
- state :open, :initial => true, :exit => :exit
5
- state :closed, :enter => :enter
4
+ state :open, :initial => true, :before_exit => :before_exit
5
+ state :closed, :before_enter => :before_enter
6
6
  state :final
7
7
 
8
8
  event :close, :success => :success_callback do
@@ -21,9 +21,9 @@ class Foo
21
21
  def success_callback
22
22
  end
23
23
 
24
- def enter
24
+ def before_enter
25
25
  end
26
- def exit
26
+ def before_exit
27
27
  end
28
28
  end
29
29
 
@@ -12,9 +12,9 @@ class ParametrisedEvent
12
12
  end
13
13
 
14
14
  event :dress do
15
- transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
16
- transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
17
- transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
15
+ transitions :from => :sleeping, :to => :working, :after => :wear_clothes
16
+ transitions :from => :showering, :to => [:working, :dating], :after => Proc.new { |*args| wear_clothes(*args) }
17
+ transitions :from => :showering, :to => :prettying_up, :after => [:condition_hair, :fix_hair]
18
18
  end
19
19
  end
20
20
 
@@ -48,7 +48,22 @@ end
48
48
  class NoScope < ActiveRecord::Base
49
49
  include AASM
50
50
  aasm :create_scopes => false do
51
- state :ignored_scope
51
+ state :pending, :initial => true
52
+ state :running
53
+ event :run do
54
+ transitions :from => :pending, :to => :running
55
+ end
56
+ end
57
+ end
58
+
59
+ class NoDirectAssignment < ActiveRecord::Base
60
+ include AASM
61
+ aasm :no_direct_assignment => true do
62
+ state :pending, :initial => true
63
+ state :running
64
+ event :run do
65
+ transitions :from => :pending, :to => :running
66
+ end
52
67
  end
53
68
  end
54
69
 
@@ -1,12 +1,14 @@
1
1
  require 'active_record'
2
2
 
3
3
  class Validator < ActiveRecord::Base
4
+
4
5
  include AASM
5
6
  aasm :column => :status do
6
7
  state :sleeping, :initial => true
7
- state :running, :after_commit => :change_name!
8
- state :failed, :after_enter => :fail, :after_commit => :change_name!
9
- event :run do
8
+ state :running
9
+ state :failed, :after_enter => :fail
10
+
11
+ event :run, :after_commit => :change_name! do
10
12
  transitions :to => :running, :from => :sleeping
11
13
  end
12
14
  event :sleep do
@@ -16,6 +18,7 @@ class Validator < ActiveRecord::Base
16
18
  transitions :to => :failed, :from => [:sleeping, :running]
17
19
  end
18
20
  end
21
+
19
22
  validates_presence_of :name
20
23
 
21
24
  def change_name!