aasm 2.4.0 → 3.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.
@@ -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)