aasm 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ module AASM
2
+ module SupportingClasses
3
+ class Event
4
+ attr_reader :name, :success, :options
5
+
6
+ def initialize(name, options = {}, &block)
7
+ @name = name
8
+ @transitions = []
9
+ update(options, &block)
10
+ end
11
+
12
+ # a neutered version of fire - it doesn't actually fire the event, it just
13
+ # executes the transition guards to determine if a transition is even
14
+ # an option given current conditions.
15
+ def may_fire?(obj, to_state=nil)
16
+ transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
17
+ return false if transitions.size == 0
18
+
19
+ result = false
20
+ transitions.each do |transition|
21
+ next if to_state and !Array(transition.to).include?(to_state)
22
+ if transition.perform(obj)
23
+ result = true
24
+ break
25
+ end
26
+ end
27
+ result
28
+ end
29
+
30
+ def fire(obj, to_state=nil, *args)
31
+ transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
32
+ return nil if transitions.size == 0
33
+
34
+ next_state = nil
35
+ transitions.each do |transition|
36
+ next if to_state and !Array(transition.to).include?(to_state)
37
+ if transition.perform(obj, *args)
38
+ next_state = to_state || Array(transition.to).first
39
+ transition.execute(obj, *args)
40
+ break
41
+ end
42
+ end
43
+ next_state
44
+ end
45
+
46
+ def transitions_from_state?(state)
47
+ @transitions.any? { |t| t.from == state }
48
+ end
49
+
50
+ def transitions_from_state(state)
51
+ @transitions.select { |t| t.from == state }
52
+ end
53
+
54
+ def all_transitions
55
+ @transitions
56
+ end
57
+
58
+ def fire_callbacks(action, record)
59
+ action = @options[action]
60
+ action.is_a?(Array) ?
61
+ action.each {|a| _fire_callbacks(a, record)} :
62
+ _fire_callbacks(action, record)
63
+ end
64
+
65
+ def ==(event)
66
+ if event.is_a? Symbol
67
+ name == event
68
+ else
69
+ name == event.name
70
+ end
71
+ end
72
+
73
+ def execute_success_callback(obj, success = nil)
74
+ callback = success || @success
75
+ case(callback)
76
+ when String, Symbol
77
+ obj.send(callback)
78
+ when Proc
79
+ callback.call(obj)
80
+ when Array
81
+ callback.each{|meth|self.execute_success_callback(obj, meth)}
82
+ end
83
+ end
84
+
85
+ def execute_error_callback(obj, error, error_callback=nil)
86
+ callback = error_callback || @error
87
+ raise error unless callback
88
+ case(callback)
89
+ when String, Symbol
90
+ raise NoMethodError unless obj.respond_to?(callback.to_sym)
91
+ obj.send(callback, error)
92
+ when Proc
93
+ callback.call(obj, error)
94
+ when Array
95
+ callback.each{|meth|self.execute_error_callback(obj, error, meth)}
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def update(options = {}, &block)
102
+ if options.key?(:success) then
103
+ @success = options[:success]
104
+ end
105
+ if options.key?(:error) then
106
+ @error = options[:error]
107
+ end
108
+ if block then
109
+ instance_eval(&block)
110
+ end
111
+ @options = options
112
+ self
113
+ end
114
+
115
+ def _fire_callbacks(action, record)
116
+ case action
117
+ when Symbol, String
118
+ record.send(action)
119
+ when Proc
120
+ action.call(record)
121
+ end
122
+ end
123
+
124
+ def transitions(trans_opts)
125
+ Array(trans_opts[:from]).each do |s|
126
+ @transitions << AASM::SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
127
+ end
128
+ end
129
+
130
+ end
131
+ end # SupportingClasses
132
+ end # AASM
@@ -0,0 +1,40 @@
1
+ module AASM
2
+ module SupportingClasses
3
+ class Localizer
4
+ def human_event_name(klass, event)
5
+ defaults = ancestors_list(klass).map do |ancestor|
6
+ :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
7
+ end << event.to_s.humanize
8
+
9
+ I18n.translate(defaults.shift, :default => defaults, :raise => true)
10
+ end
11
+
12
+ def human_state(obj)
13
+ klass = obj.class
14
+ defaults = ancestors_list(klass).map do |ancestor|
15
+ :"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm_column}.#{obj.aasm_current_state}"
16
+ end << obj.aasm_current_state.to_s.humanize
17
+
18
+ I18n.translate(defaults.shift, :default => defaults, :raise => true)
19
+ end
20
+
21
+ private
22
+
23
+ # added for rails 2.x compatibility
24
+ def i18n_scope(klass)
25
+ klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
26
+ end
27
+
28
+ # added for rails < 3.0.3 compatibility
29
+ def i18n_klass(klass)
30
+ klass.model_name.respond_to?(:i18n_key) ? klass.model_name.i18n_key : klass.name.underscore
31
+ end
32
+
33
+ def ancestors_list(klass)
34
+ klass.ancestors.select do |ancestor|
35
+ ancestor.respond_to?(:model_name) unless ancestor == ActiveRecord::Base
36
+ end
37
+ end
38
+ end
39
+ end # SupportingClasses
40
+ end # AASM
@@ -0,0 +1,57 @@
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 fire_callbacks(action, record)
20
+ action = @options[action]
21
+ catch :halt_aasm_chain do
22
+ action.is_a?(Array) ?
23
+ action.each {|a| _fire_callbacks(a, record)} :
24
+ _fire_callbacks(action, record)
25
+ end
26
+ end
27
+
28
+ def display_name
29
+ @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
30
+ end
31
+
32
+ def for_select
33
+ [display_name, name.to_s]
34
+ end
35
+
36
+ private
37
+
38
+ def update(options = {})
39
+ if options.key?(:display) then
40
+ @display_name = options.delete(:display)
41
+ end
42
+ @options = options
43
+ self
44
+ end
45
+
46
+ def _fire_callbacks(action, record)
47
+ case action
48
+ when Symbol, String
49
+ record.send(action)
50
+ when Proc
51
+ action.call(record)
52
+ end
53
+ end
54
+
55
+ end
56
+ end # SupportingClasses
57
+ end # AASM
@@ -0,0 +1,50 @@
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, *args)
13
+ case @guard
14
+ when Symbol, String
15
+ obj.send(@guard, *args)
16
+ when Proc
17
+ @guard.call(obj, *args)
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 Proc
42
+ on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
43
+ when Symbol, String
44
+ obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
45
+ end
46
+ end
47
+
48
+ end
49
+ end # SupportingClasses
50
+ end # AASM
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "2.4.0"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -1,33 +1,33 @@
1
1
  class Conversation
2
2
  include AASM
3
3
 
4
- aasm_initial_state :needs_attention
4
+ aasm do
5
+ state :needs_attention, :initial => true
6
+ state :read
7
+ state :closed
8
+ state :awaiting_response
9
+ state :junk
5
10
 
6
- aasm_state :needs_attention
7
- aasm_state :read
8
- aasm_state :closed
9
- aasm_state :awaiting_response
10
- aasm_state :junk
11
+ event :new_message do
12
+ end
11
13
 
12
- aasm_event :new_message do
13
- end
14
-
15
- aasm_event :view do
16
- transitions :to => :read, :from => [:needs_attention]
17
- end
14
+ event :view do
15
+ transitions :to => :read, :from => [:needs_attention]
16
+ end
18
17
 
19
- aasm_event :reply do
20
- end
18
+ event :reply do
19
+ end
21
20
 
22
- aasm_event :close do
23
- transitions :to => :closed, :from => [:read, :awaiting_response]
24
- end
21
+ event :close do
22
+ transitions :to => :closed, :from => [:read, :awaiting_response]
23
+ end
25
24
 
26
- aasm_event :junk do
27
- transitions :to => :junk, :from => [:read]
28
- end
25
+ event :junk do
26
+ transitions :to => :junk, :from => [:read]
27
+ end
29
28
 
30
- aasm_event :unjunk do
29
+ event :unjunk do
30
+ end
31
31
  end
32
32
 
33
33
  def initialize(persister)
@@ -0,0 +1,17 @@
1
+ class Silencer
2
+ include AASM
3
+
4
+ aasm :whiny_transitions => false do
5
+ state :silent, :initial => true
6
+ state :crying
7
+ state :smiling
8
+
9
+ event :cry do
10
+ transitions :from => :silent, :to => :crying
11
+ end
12
+ event :smile do
13
+ transitions :from => :crying, :to => :smiling
14
+ end
15
+ end
16
+
17
+ end
@@ -2,16 +2,17 @@ Dir[File.dirname(__FILE__) + "/../models/*.rb"].each { |f| require File.expand_p
2
2
 
3
3
  class Foo
4
4
  include AASM
5
- aasm_initial_state :open
6
- aasm_state :open, :exit => :exit
7
- aasm_state :closed, :enter => :enter
5
+ aasm do
6
+ state :open, :initial => true, :exit => :exit
7
+ state :closed, :enter => :enter
8
8
 
9
- aasm_event :close, :success => :success_callback do
10
- transitions :to => :closed, :from => [:open]
11
- end
9
+ event :close, :success => :success_callback do
10
+ transitions :to => :closed, :from => [:open]
11
+ end
12
12
 
13
- aasm_event :null do
14
- transitions :to => :closed, :from => [:open], :guard => :always_false
13
+ event :null do
14
+ transitions :to => :closed, :from => [:open], :guard => :always_false
15
+ end
15
16
  end
16
17
 
17
18
  def always_false
@@ -29,17 +30,21 @@ end
29
30
 
30
31
  class FooTwo < Foo
31
32
  include AASM
32
- aasm_state :foo
33
+ aasm do
34
+ state :foo
35
+ end
33
36
  end
34
37
 
35
38
  class Bar
36
39
  include AASM
37
40
 
38
- aasm_state :read
39
- aasm_state :ended
41
+ aasm do
42
+ state :read
43
+ state :ended
40
44
 
41
- aasm_event :foo do
42
- transitions :to => :ended, :from => [:read]
45
+ event :foo do
46
+ transitions :to => :ended, :from => [:read]
47
+ end
43
48
  end
44
49
  end
45
50
 
@@ -48,9 +53,11 @@ end
48
53
 
49
54
  class Banker
50
55
  include AASM
56
+ aasm do
57
+ state :retired
58
+ state :selling_bad_mortgages
59
+ end
51
60
  aasm_initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages }
52
- aasm_state :retired
53
- aasm_state :selling_bad_mortgages
54
61
  RICH = 1_000_000
55
62
  attr_accessor :balance
56
63
  def initialize(balance = 0); self.balance = balance; end
@@ -59,12 +66,13 @@ end
59
66
 
60
67
  class Argument
61
68
  include AASM
62
- aasm_initial_state :invalid
63
- aasm_state :invalid
64
- aasm_state :valid
69
+ aasm do
70
+ state :invalid, :initial => true
71
+ state :valid
65
72
 
66
- aasm_event :valid do
67
- transitions :to => :valid, :from => [:invalid]
73
+ event :valid do
74
+ transitions :to => :valid, :from => [:invalid]
75
+ end
68
76
  end
69
77
  end
70
78
 
@@ -73,39 +81,39 @@ class AuthMachine
73
81
 
74
82
  attr_accessor :activation_code, :activated_at, :deleted_at
75
83
 
76
- aasm_initial_state :pending
77
-
78
- aasm_state :passive
79
- aasm_state :pending, :enter => :make_activation_code
80
- aasm_state :active, :enter => :do_activate
81
- aasm_state :suspended
82
- aasm_state :deleted, :enter => :do_delete, :exit => :do_undelete
83
-
84
- aasm_event :register do
85
- transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
86
- end
87
-
88
- aasm_event :activate do
89
- transitions :from => :pending, :to => :active
90
- end
91
-
92
- aasm_event :suspend do
93
- transitions :from => [:passive, :pending, :active], :to => :suspended
94
- end
95
-
96
- aasm_event :delete do
97
- transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
98
- end
99
-
100
- # a dummy event that can never happen
101
- aasm_event :unpassify do
102
- transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
103
- end
84
+ aasm do
85
+ state :passive
86
+ state :pending, :initial => true, :enter => :make_activation_code
87
+ state :active, :enter => :do_activate
88
+ state :suspended
89
+ state :deleted, :enter => :do_delete, :exit => :do_undelete
90
+
91
+ event :register do
92
+ transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
93
+ end
94
+
95
+ event :activate do
96
+ transitions :from => :pending, :to => :active
97
+ end
98
+
99
+ event :suspend do
100
+ transitions :from => [:passive, :pending, :active], :to => :suspended
101
+ end
102
+
103
+ event :delete do
104
+ transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
105
+ end
106
+
107
+ # a dummy event that can never happen
108
+ event :unpassify do
109
+ transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
110
+ end
104
111
 
105
- aasm_event :unsuspend do
106
- transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
107
- transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
108
- transitions :from => :suspended, :to => :passive
112
+ event :unsuspend do
113
+ transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
114
+ transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
115
+ transitions :from => :suspended, :to => :passive
116
+ end
109
117
  end
110
118
 
111
119
  def initialize
@@ -147,30 +155,33 @@ end
147
155
  class ThisNameBetterNotBeInUse
148
156
  include AASM
149
157
 
150
- aasm_state :initial
151
- aasm_state :symbol
152
- aasm_state :string
153
- aasm_state :array
154
- aasm_state :proc
158
+ aasm do
159
+ state :initial
160
+ state :symbol
161
+ state :string
162
+ state :array
163
+ state :proc
164
+ end
155
165
  end
156
166
 
157
167
  class ChetanPatil
158
168
  include AASM
159
- aasm_initial_state :sleeping
160
- aasm_state :sleeping
161
- aasm_state :showering
162
- aasm_state :working
163
- aasm_state :dating
164
- aasm_state :prettying_up
165
-
166
- aasm_event :wakeup do
167
- transitions :from => :sleeping, :to => [:showering, :working]
168
- end
169
-
170
- aasm_event :dress do
171
- transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
172
- transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
173
- transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
169
+ aasm do
170
+ state :sleeping, :initial => true
171
+ state :showering
172
+ state :working
173
+ state :dating
174
+ state :prettying_up
175
+
176
+ event :wakeup do
177
+ transitions :from => :sleeping, :to => [:showering, :working]
178
+ end
179
+
180
+ event :dress do
181
+ transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
182
+ transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
183
+ transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
184
+ end
174
185
  end
175
186
 
176
187
  def wear_clothes(shirt_color, trouser_type)