aasm 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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!