aasm 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.0.0
4
+
5
+ * switched documentation to the new DSL
6
+ * whiny transactions: by default, raise an exception if an event transition is not possible
7
+ * you may disable whiny transactions
8
+
3
9
  ## 2.4.0
4
10
 
5
11
  * supporting new DSL (which is much shorter)
data/README.md CHANGED
@@ -59,26 +59,24 @@ gem 'aasm'
59
59
  Here's a quick example highlighting some of the features.
60
60
 
61
61
  ```ruby
62
- class Conversation
63
- include AASM
64
-
65
- aasm_column :current_state # defaults to aasm_state
66
-
67
- aasm_initial_state :unread
62
+ class Conversation
63
+ include AASM
68
64
 
69
- aasm_state :unread
70
- aasm_state :read
71
- aasm_state :closed
65
+ aasm :column => :current_state do # defaults to aasm_state
66
+ state :unread, :initial => true
67
+ state :read
68
+ state :closed
72
69
 
73
-
74
- aasm_event :view do
70
+ event :view do
75
71
  transitions :to => :read, :from => [:unread]
76
72
  end
77
73
 
78
- aasm_event :close do
74
+ event :close do
79
75
  transitions :to => :closed, :from => [:read, :unread]
80
76
  end
81
77
  end
78
+
79
+ end
82
80
  ```
83
81
 
84
82
  ## A Slightly More Complex Example ##
@@ -89,22 +87,21 @@ This example uses a few of the more complex features available.
89
87
  class Relationship
90
88
  include AASM
91
89
 
92
- aasm_column :status
93
-
94
- aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
95
-
96
- aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
97
- aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
98
- aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
99
-
100
- aasm_event :get_intimate do
101
- transitions :to => :intimate, :from => [:dating], :guard => :drunk?
102
- end
103
-
104
- aasm_event :get_married do
105
- transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
90
+ aasm :column => :status
91
+ state :dating, :enter => :make_happy, :exit => :make_depressed
92
+ state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
93
+ state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
94
+
95
+ event :get_intimate do
96
+ transitions :to => :intimate, :from => [:dating], :guard => :drunk?
97
+ end
98
+
99
+ event :get_married do
100
+ transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
101
+ end
106
102
  end
107
-
103
+ aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
104
+
108
105
  def strictly_for_fun?; end
109
106
  def drunk?; end
110
107
  def willing_to_give_up_manhood?; end
@@ -122,14 +119,15 @@ This example uses a few of the more complex features available.
122
119
  class Relationship
123
120
  include AASM
124
121
 
125
- aasm_state :dating
126
- aasm_state :married
122
+ aasm do
123
+ state :dating
124
+ state :married
127
125
 
128
- aasm_event :get_married,
129
- :before => :make_vows,
130
- :after => :eat_wedding_cake do
131
- transitions :to => :married, :from => [:dating]
132
- end
126
+ event :get_married,
127
+ :before => :make_vows,
128
+ :after => :eat_wedding_cake do
129
+ transitions :to => :married, :from => [:dating]
130
+ end
133
131
  end
134
132
  ```
135
133
 
@@ -142,7 +140,7 @@ Look at the [CHANGELOG](https://github.com/rubyist/aasm/blob/master/CHANGELOG.md
142
140
 
143
141
  * [Scott Barron](https://github.com/rubyist)
144
142
  * [Travis Tilley](https://github.com/ttilley)
145
- * [Thorsten Böttger](http://github.com/alto),
143
+ * [Thorsten Böttger](http://github.com/alto)
146
144
 
147
145
 
148
146
  ## Warranty ##
@@ -1,10 +1,15 @@
1
- module AASM
2
- end
3
-
4
1
  require 'ostruct'
5
2
 
6
- require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes')
3
+ require File.join(File.dirname(__FILE__), 'aasm', 'version')
4
+ require File.join(File.dirname(__FILE__), 'aasm', 'errors')
5
+ require File.join(File.dirname(__FILE__), 'aasm', 'base')
6
+ require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'state_transition')
7
+ require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'event')
8
+ require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'state')
9
+ require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'localizer')
7
10
  require File.join(File.dirname(__FILE__), 'aasm', 'state_machine')
8
11
  require File.join(File.dirname(__FILE__), 'aasm', 'persistence')
9
12
  require File.join(File.dirname(__FILE__), 'aasm', 'aasm')
10
- require File.join(File.dirname(__FILE__), 'aasm', 'localizer')
13
+
14
+ # load the deprecated methods and modules
15
+ Dir[File.join(File.dirname(__FILE__), 'aasm', 'deprecated', '*.rb')].sort.each { |f| require File.expand_path(f) }
@@ -1,9 +1,4 @@
1
1
  module AASM
2
- class InvalidTransition < RuntimeError
3
- end
4
-
5
- class UndefinedState < RuntimeError
6
- end
7
2
 
8
3
  def self.included(base) #:nodoc:
9
4
  base.extend AASM::ClassMethods
@@ -45,7 +40,7 @@ module AASM
45
40
  def aasm_state(name, options={})
46
41
  aasm.state(name, options)
47
42
  end
48
-
43
+
49
44
  # deprecated
50
45
  def aasm_event(name, options = {}, &block)
51
46
  aasm.event(name, options, &block)
@@ -66,35 +61,31 @@ module AASM
66
61
  aasm.states_for_select
67
62
  end
68
63
 
69
- def human_event_name(event)
70
- AASM::Localizer.new.human_event_name(self, event)
64
+ def aasm_human_event_name(event)
65
+ AASM::SupportingClasses::Localizer.new.human_event_name(self, event)
71
66
  end
72
67
  end
73
-
74
- # Instance methods
75
- def aasm_current_state
76
- return @aasm_current_state if @aasm_current_state
77
68
 
78
- if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
79
- @aasm_current_state = aasm_read_state
80
- end
81
- return @aasm_current_state if @aasm_current_state
82
-
83
- aasm_enter_initial_state
69
+ # this method does what? does it deliver the current state?
70
+ def aasm_current_state
71
+ @aasm_current_state ||=
72
+ aasm_persistable? ? aasm_read_state : aasm_enter_initial_state
84
73
  end
85
74
 
75
+ # private?
86
76
  def aasm_enter_initial_state
87
77
  state_name = aasm_determine_state_name(self.class.aasm_initial_state)
88
78
  state = aasm_state_object_for_state(state_name)
89
79
 
90
- state.call_action(:before_enter, self)
91
- state.call_action(:enter, self)
80
+ state.fire_callbacks(:before_enter, self)
81
+ state.fire_callbacks(:enter, self)
92
82
  self.aasm_current_state = state_name
93
- state.call_action(:after_enter, self)
83
+ state.fire_callbacks(:after_enter, self)
94
84
 
95
85
  state_name
96
86
  end
97
87
 
88
+ # private?
98
89
  def aasm_events_for_current_state
99
90
  aasm_events_for_state(aasm_current_state)
100
91
  end
@@ -104,19 +95,23 @@ module AASM
104
95
  def aasm_permissible_events_for_current_state
105
96
  aasm_events_for_current_state.select{ |e| self.send(("may_" + e.to_s + "?").to_sym) }
106
97
  end
107
-
98
+
108
99
  def aasm_events_for_state(state)
109
100
  events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
110
101
  events.map {|event| event.name}
111
102
  end
112
103
 
113
- def human_state
114
- AASM::Localizer.new.human_state(self)
104
+ def aasm_human_state
105
+ AASM::SupportingClasses::Localizer.new.human_state(self)
115
106
  end
116
107
 
117
108
  private
118
109
 
119
- def set_aasm_current_state_with_persistence(state)
110
+ def aasm_persistable?
111
+ self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
112
+ end
113
+
114
+ def aasm_set_current_state_with_persistence(state)
120
115
  save_success = true
121
116
  if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
122
117
  save_success = aasm_write_state(state)
@@ -150,45 +145,45 @@ private
150
145
  obj
151
146
  end
152
147
 
153
- def aasm_test_event(name, *args)
148
+ def aasm_may_fire_event?(name, *args)
154
149
  event = self.class.aasm_events[name]
155
150
  event.may_fire?(self, *args)
156
151
  end
157
-
158
- def aasm_fire_event(name, persist, *args)
152
+
153
+ def aasm_fire_event(name, options, *args)
154
+ persist = options[:persist]
155
+
159
156
  event = self.class.aasm_events[name]
160
157
  begin
161
158
  old_state = aasm_state_object_for_state(aasm_current_state)
162
159
 
163
160
 
164
- old_state.call_action(:exit, self)
161
+ old_state.fire_callbacks(:exit, self)
165
162
 
166
163
  # new event before callback
167
- event.call_action(:before, self)
164
+ event.fire_callbacks(:before, self)
168
165
 
169
- new_state_name = event.fire(self, *args)
170
-
171
- unless new_state_name.nil?
166
+ if new_state_name = event.fire(self, *args)
172
167
  new_state = aasm_state_object_for_state(new_state_name)
173
168
 
174
169
  # new before_ callbacks
175
- old_state.call_action(:before_exit, self)
176
- new_state.call_action(:before_enter, self)
170
+ old_state.fire_callbacks(:before_exit, self)
171
+ new_state.fire_callbacks(:before_enter, self)
177
172
 
178
- new_state.call_action(:enter, self)
173
+ new_state.fire_callbacks(:enter, self)
179
174
 
180
175
  persist_successful = true
181
176
  if persist
182
- persist_successful = set_aasm_current_state_with_persistence(new_state_name)
177
+ persist_successful = aasm_set_current_state_with_persistence(new_state_name)
183
178
  event.execute_success_callback(self) if persist_successful
184
179
  else
185
180
  self.aasm_current_state = new_state_name
186
181
  end
187
182
 
188
183
  if persist_successful
189
- old_state.call_action(:after_exit, self)
190
- new_state.call_action(:after_enter, self)
191
- event.call_action(:after, self)
184
+ old_state.fire_callbacks(:after_exit, self)
185
+ new_state.fire_callbacks(:after_enter, self)
186
+ event.fire_callbacks(:after, self)
192
187
 
193
188
  self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
194
189
  else
@@ -196,12 +191,17 @@ private
196
191
  end
197
192
 
198
193
  persist_successful
194
+
199
195
  else
200
196
  if self.respond_to?(:aasm_event_failed)
201
197
  self.aasm_event_failed(name, old_state.name)
202
198
  end
203
199
 
204
- false
200
+ if AASM::StateMachine[self.class].config.whiny_transitions
201
+ raise AASM::InvalidTransition, "Event '#{event.name}' cannot transition from '#{self.aasm_current_state}'"
202
+ else
203
+ false
204
+ end
205
205
  end
206
206
  rescue StandardError => e
207
207
  event.execute_error_callback(self, e)
@@ -4,6 +4,11 @@ module AASM
4
4
  @clazz = clazz
5
5
  sm = AASM::StateMachine[@clazz]
6
6
  sm.config.column = options[:column].to_sym if options[:column]
7
+ if options.key?(:whiny_transitions)
8
+ sm.config.whiny_transitions = options[:whiny_transitions]
9
+ else
10
+ sm.config.whiny_transitions = true # this is the default, so let's cry
11
+ end
7
12
  end
8
13
 
9
14
  def state(name, options={})
@@ -29,15 +34,15 @@ module AASM
29
34
  # may_event? and get back a boolean that tells you whether the guard method
30
35
  # on the transition will let this happen.
31
36
  @clazz.send(:define_method, "may_#{name.to_s}?") do |*args|
32
- aasm_test_event(name, *args)
37
+ aasm_may_fire_event?(name, *args)
33
38
  end
34
39
 
35
40
  @clazz.send(:define_method, "#{name.to_s}!") do |*args|
36
- aasm_fire_event(name, true, *args)
41
+ aasm_fire_event(name, {:persist => true}, *args)
37
42
  end
38
43
 
39
44
  @clazz.send(:define_method, "#{name.to_s}") do |*args|
40
- aasm_fire_event(name, false, *args)
45
+ aasm_fire_event(name, {:persist => false}, *args)
41
46
  end
42
47
  end
43
48
 
@@ -52,5 +57,6 @@ module AASM
52
57
  def states_for_select
53
58
  states.map { |state| state.for_select }
54
59
  end
60
+
55
61
  end
56
62
  end
@@ -0,0 +1,15 @@
1
+ module AASM
2
+
3
+ module ClassMethods
4
+ def human_event_name(*args)
5
+ warn "AASM.human_event_name is deprecated and will be removed in version 3.1.0; please use AASM.aasm_human_event_name instead!"
6
+ aasm_human_event_name(*args)
7
+ end
8
+ end
9
+
10
+ def human_state
11
+ warn "AASM#human_state is deprecated and will be removed in version 3.1.0; please use AASM#aasm_human_state instead!"
12
+ aasm_human_state
13
+ end
14
+
15
+ end
@@ -0,0 +1,4 @@
1
+ module AASM
2
+ class InvalidTransition < RuntimeError; end
3
+ class UndefinedState < RuntimeError; end
4
+ end
@@ -1,14 +1,15 @@
1
- module AASM::Persistence
1
+ module AASM
2
+ module Persistence
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}
2
8
 
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}
8
-
9
- if hierarchy.include?("ActiveRecord::Base")
10
- require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
11
- base.send(:include, AASM::Persistence::ActiveRecordPersistence)
9
+ if hierarchy.include?("ActiveRecord::Base")
10
+ require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
11
+ base.send(:include, AASM::Persistence::ActiveRecordPersistence)
12
+ end
12
13
  end
13
14
  end
14
- end
15
+ end # AASM
@@ -1,31 +1,33 @@
1
- class AASM::StateMachine
2
- def self.[](clazz)
3
- (@machines ||= {})[clazz.to_s]
4
- end
1
+ module AASM
2
+ class StateMachine
3
+ def self.[](clazz)
4
+ (@machines ||= {})[clazz.to_s]
5
+ end
5
6
 
6
- def self.[]=(clazz, machine)
7
- (@machines ||= {})[clazz.to_s] = machine
8
- end
7
+ def self.[]=(clazz, machine)
8
+ (@machines ||= {})[clazz.to_s] = machine
9
+ end
9
10
 
10
- attr_accessor :states, :events, :initial_state, :config
11
- attr_reader :name
11
+ attr_accessor :states, :events, :initial_state, :config
12
+ attr_reader :name
12
13
 
13
- def initialize(name)
14
- @name = name
15
- @initial_state = nil
16
- @states = []
17
- @events = {}
18
- @config = OpenStruct.new
19
- end
14
+ def initialize(name)
15
+ @name = name
16
+ @initial_state = nil
17
+ @states = []
18
+ @events = {}
19
+ @config = OpenStruct.new
20
+ end
20
21
 
21
- def clone
22
- klone = super
23
- klone.states = states.clone
24
- klone.events = events.clone
25
- klone
26
- end
22
+ def clone
23
+ klone = super
24
+ klone.states = states.clone
25
+ klone.events = events.clone
26
+ klone
27
+ end
27
28
 
28
- def create_state(name, options)
29
- @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
29
+ def create_state(name, options)
30
+ @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
31
+ end
30
32
  end
31
- end
33
+ end # AASM