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.
@@ -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