aasm 2.0.7 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,14 +11,32 @@ AASM has the following features:
11
11
  * Events
12
12
  * Transitions
13
13
 
14
- == Download
14
+ == New Callbacks
15
15
 
16
- The latest AASM can currently be pulled from the git repository on github.
16
+ The callback chain & order on a successful event looks like:
17
+
18
+ oldstate:exit*
19
+ event:before
20
+ __find transition, if possible__
21
+ transition:on_transition*
22
+ oldstate:before_exit
23
+ newstate:before_enter
24
+ newstate:enter*
25
+ __update state__
26
+ event:success*
27
+ oldstate:after_exit
28
+ oldstate:after_enter
29
+ event:after
30
+ obj:aasm_event_fired*
31
+
32
+ (*) marks old callbacks
17
33
 
18
- * http://github.com/rubyist/aasm/tree/master
19
34
 
20
- A release and a gem are forthcoming.
35
+ == Download
36
+
37
+ The latest AASM can currently be pulled from the git repository on github.
21
38
 
39
+ * http://github.com/ttilley/aasm/tree/master
22
40
 
23
41
 
24
42
  == Installation
@@ -26,12 +44,12 @@ A release and a gem are forthcoming.
26
44
  === From GitHub hosted gems
27
45
 
28
46
  % sudo gem sources -a http://gems.github.com # (you only need to do this once)
29
- % sudo gem install rubyist-aasm
47
+ % sudo gem install ttilley-aasm
30
48
 
31
49
  === Building your own gems
32
50
 
33
51
  % rake gem
34
- % sudo gem install pkg/aasm-2.0.1.gem
52
+ % sudo gem install pkg/aasm-2.1.gem
35
53
 
36
54
 
37
55
  == Simple Example
@@ -57,14 +75,44 @@ Here's a quick example highlighting some of the features.
57
75
  end
58
76
  end
59
77
 
78
+ == A Slightly More Complex Example
79
+
80
+ This example uses a few of the more complex features available.
81
+
82
+ class Relationship
83
+ include AASM
84
+
85
+ aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
86
+
87
+ aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
88
+ aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
89
+ aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
90
+
91
+ aasm_event :get_intimate do
92
+ transitions :to => :intimate, :from => [:dating], :guard => :drunk?
93
+ end
94
+
95
+ aasm_event :get_married do
96
+ transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
97
+ end
98
+
99
+ def strictly_for_fun?; end
100
+ def drunk?; end
101
+ def willing_to_give_up_manhood?; end
102
+ def make_happy; end
103
+ def make_depressed; end
104
+ def make_very_happy; end
105
+ def never_speak_again; end
106
+ def give_up_intimacy; end
107
+ def buy_exotic_car_and_wear_a_combover; end
108
+ end
109
+
60
110
  = Other Stuff
61
111
 
62
112
  Author:: Scott Barron <scott at elitists dot net>
63
- License:: Copyright 2006, 2007, 2008 by Scott Barron.
113
+ License:: Original code Copyright 2006, 2007, 2008 by Scott Barron.
64
114
  Released under an MIT-style license. See the LICENSE file
65
115
  included in the distribution.
66
- Bugs:: http://rubyist.lighthouseapp.com/projects/13207-aasm/
67
- GitHub:: http://github.com/rubyist/aasm/tree/master
68
116
 
69
117
  == Warranty
70
118
 
data/Rakefile CHANGED
@@ -22,8 +22,8 @@ end
22
22
  $package_version = CURRENT_VERSION
23
23
 
24
24
  PKG_FILES = FileList['[A-Z]*',
25
- 'lib/**/*.rb',
26
- 'doc/**/*'
25
+ 'lib/**/*.rb',
26
+ 'doc/**/*'
27
27
  ]
28
28
 
29
29
  desc 'Generate documentation for the acts as state machine plugin.'
@@ -44,18 +44,19 @@ else
44
44
  s.name = 'aasm'
45
45
  s.version = $package_version
46
46
  s.summary = 'State machine mixin for Ruby objects'
47
- s.description = <<-EOF
48
- AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.
49
- EOF
47
+ s.description = <<EOF
48
+ AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.
49
+ EOF
50
+
50
51
  s.files = PKG_FILES.to_a
51
52
  s.require_path = 'lib'
52
53
  s.has_rdoc = true
53
54
  s.extra_rdoc_files = rd.rdoc_files.reject {|fn| fn =~ /\.rb$/}.to_a
54
55
  s.rdoc_options = rd.options
55
56
 
56
- s.author = 'Scott Barron'
57
- s.email = 'scott@elitists.net'
58
- s.homepage = 'http://rubyi.st/aasm'
57
+ s.authors = ['Scott Barron', 'Scott Petersen', 'Travis Tilley']
58
+ s.email = 'ttilley@gmail.com'
59
+ s.homepage = 'http://github.com/ttilley/aasm'
59
60
  end
60
61
 
61
62
  package_task = Rake::GemPackageTask.new(spec) do |pkg|
@@ -5,7 +5,7 @@ require File.join(File.dirname(__FILE__), 'persistence')
5
5
 
6
6
  module AASM
7
7
  def self.Version
8
- '2.0.7'
8
+ '2.1.1'
9
9
  end
10
10
 
11
11
  class InvalidTransition < RuntimeError
@@ -13,7 +13,7 @@ module AASM
13
13
 
14
14
  class UndefinedState < RuntimeError
15
15
  end
16
-
16
+
17
17
  def self.included(base) #:nodoc:
18
18
  base.extend AASM::ClassMethods
19
19
  AASM::Persistence.set_persistence(base)
@@ -35,11 +35,11 @@ module AASM
35
35
  AASM::StateMachine[self].initial_state
36
36
  end
37
37
  end
38
-
38
+
39
39
  def aasm_initial_state=(state)
40
40
  AASM::StateMachine[self].initial_state = state
41
41
  end
42
-
42
+
43
43
  def aasm_state(name, options={})
44
44
  sm = AASM::StateMachine[self]
45
45
  sm.create_state(name, options)
@@ -49,10 +49,10 @@ module AASM
49
49
  aasm_current_state == name
50
50
  end
51
51
  end
52
-
52
+
53
53
  def aasm_event(name, options = {}, &block)
54
54
  sm = AASM::StateMachine[self]
55
-
55
+
56
56
  unless sm.events.has_key?(name)
57
57
  sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
58
58
  end
@@ -73,11 +73,11 @@ module AASM
73
73
  def aasm_events
74
74
  AASM::StateMachine[self].events
75
75
  end
76
-
76
+
77
77
  def aasm_states_for_select
78
78
  AASM::StateMachine[self].states.map { |state| state.for_select }
79
79
  end
80
-
80
+
81
81
  end
82
82
 
83
83
  # Instance methods
@@ -88,7 +88,7 @@ module AASM
88
88
  @aasm_current_state = aasm_read_state
89
89
  end
90
90
  return @aasm_current_state if @aasm_current_state
91
- self.class.aasm_initial_state
91
+ aasm_determine_state_name(self.class.aasm_initial_state)
92
92
  end
93
93
 
94
94
  def aasm_events_for_current_state
@@ -118,6 +118,17 @@ module AASM
118
118
  @aasm_current_state = state
119
119
  end
120
120
 
121
+ def aasm_determine_state_name(state)
122
+ case state
123
+ when Symbol, String
124
+ state
125
+ when Proc
126
+ state.call(self)
127
+ else
128
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
129
+ end
130
+ end
131
+
121
132
  def aasm_state_object_for_state(name)
122
133
  obj = self.class.aasm_states.find {|s| s == name}
123
134
  raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
@@ -125,33 +136,49 @@ module AASM
125
136
  end
126
137
 
127
138
  def aasm_fire_event(name, persist, *args)
128
- aasm_state_object_for_state(aasm_current_state).call_action(:exit, self)
139
+ old_state = aasm_state_object_for_state(aasm_current_state)
140
+ event = self.class.aasm_events[name]
141
+
142
+ old_state.call_action(:exit, self)
129
143
 
130
- new_state = self.class.aasm_events[name].fire(self, *args)
144
+ # new event before callback
145
+ event.call_action(:before, self)
146
+
147
+ new_state_name = event.fire(self, *args)
148
+
149
+ unless new_state_name.nil?
150
+ new_state = aasm_state_object_for_state(new_state_name)
131
151
 
132
- unless new_state.nil?
133
- aasm_state_object_for_state(new_state).call_action(:enter, self)
152
+ # new before_ callbacks
153
+ old_state.call_action(:before_exit, self)
154
+ new_state.call_action(:before_enter, self)
155
+
156
+ new_state.call_action(:enter, self)
134
157
 
135
158
  persist_successful = true
136
159
  if persist
137
- persist_successful = set_aasm_current_state_with_persistence(new_state)
138
- self.class.aasm_events[name].execute_success_callback(self) if persist_successful
160
+ persist_successful = set_aasm_current_state_with_persistence(new_state_name)
161
+ event.execute_success_callback(self) if persist_successful
139
162
  else
140
- self.aasm_current_state = new_state
163
+ self.aasm_current_state = new_state_name
141
164
  end
142
165
 
143
166
  if persist_successful
144
- self.aasm_event_fired(self.aasm_current_state, new_state) if self.respond_to?(:aasm_event_fired)
167
+ old_state.call_action(:after_exit, self)
168
+ new_state.call_action(:after_enter, self)
169
+ event.call_action(:after, self)
170
+
171
+ self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
145
172
  else
146
- self.aasm_event_failed(name) if self.respond_to?(:aasm_event_failed)
173
+ self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
147
174
  end
148
175
 
149
176
  persist_successful
150
177
  else
151
178
  if self.respond_to?(:aasm_event_failed)
152
- self.aasm_event_failed(name)
179
+ self.aasm_event_failed(name, old_state.name)
153
180
  end
154
-
181
+
155
182
  false
156
183
  end
157
184
  end
@@ -3,12 +3,13 @@ require File.join(File.dirname(__FILE__), 'state_transition')
3
3
  module AASM
4
4
  module SupportingClasses
5
5
  class Event
6
- attr_reader :name, :success
6
+ attr_reader :name, :success, :options
7
7
 
8
8
  def initialize(name, options = {}, &block)
9
9
  @name = name
10
10
  @success = options[:success]
11
11
  @transitions = []
12
+ @options = options
12
13
  instance_eval(&block) if block
13
14
  end
14
15
 
@@ -31,18 +32,39 @@ module AASM
31
32
  def transitions_from_state?(state)
32
33
  @transitions.any? { |t| t.from == state }
33
34
  end
34
-
35
- def execute_success_callback(obj)
36
- case success
35
+
36
+ def transitions_from_state(state)
37
+ @transitions.select { |t| t.from == state }
38
+ end
39
+
40
+ def execute_success_callback(obj, success = nil)
41
+ callback = success || @success
42
+ case(callback)
37
43
  when String, Symbol
38
- obj.send(success)
44
+ obj.send(callback)
45
+ when Proc
46
+ callback.call(obj)
39
47
  when Array
40
- success.each { |meth| obj.send(meth) }
48
+ callback.each{|meth|self.execute_success_callback(obj, meth)}
49
+ end
50
+ end
51
+
52
+ def call_action(action, record)
53
+ action = @options[action]
54
+ case action
55
+ when Symbol, String
56
+ record.send(action)
41
57
  when Proc
42
- success.call(obj)
58
+ action.call(record)
59
+ when Array
60
+ action.each { |a| record.send(a) }
43
61
  end
44
62
  end
45
63
 
64
+ def all_transitions
65
+ @transitions
66
+ end
67
+
46
68
  private
47
69
  def transitions(trans_opts)
48
70
  Array(trans_opts[:from]).each do |s|
@@ -1,12 +1,12 @@
1
1
  module AASM
2
2
  module Persistence
3
-
3
+
4
4
  # Checks to see this class or any of it's superclasses inherit from
5
5
  # ActiveRecord::Base and if so includes ActiveRecordPersistence
6
6
  def self.set_persistence(base)
7
7
  # Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
8
8
  hierarchy = base.ancestors.map {|klass| klass.to_s}
9
-
9
+
10
10
  if hierarchy.include?("ActiveRecord::Base")
11
11
  require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
12
12
  base.send(:include, AASM::Persistence::ActiveRecordPersistence)
@@ -43,8 +43,10 @@ module AASM
43
43
 
44
44
  base.class_eval do
45
45
  class << self
46
- alias_method :aasm_state_without_named_scope, :aasm_state
47
- alias_method :aasm_state, :aasm_state_with_named_scope
46
+ unless method_defined?(:aasm_state_without_named_scope)
47
+ alias_method :aasm_state_without_named_scope, :aasm_state
48
+ alias_method :aasm_state, :aasm_state_with_named_scope
49
+ end
48
50
  end
49
51
  end
50
52
  end
@@ -191,7 +193,7 @@ module AASM
191
193
  old_value = read_attribute(self.class.aasm_column)
192
194
  write_attribute(self.class.aasm_column, state.to_s)
193
195
 
194
- unless self.save
196
+ unless self.save(false)
195
197
  write_attribute(self.class.aasm_column, old_value)
196
198
  return false
197
199
  end
@@ -228,7 +230,7 @@ module AASM
228
230
  # This allows for nil aasm states - be sure to add validation to your model
229
231
  def aasm_read_state
230
232
  if new_record?
231
- send(self.class.aasm_column).blank? ? self.class.aasm_initial_state : send(self.class.aasm_column).to_sym
233
+ send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
232
234
  else
233
235
  send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
234
236
  end
@@ -238,7 +240,7 @@ module AASM
238
240
  module NamedScopeMethods
239
241
  def aasm_state_with_named_scope name, options = {}
240
242
  aasm_state_without_named_scope name, options
241
- self.named_scope name, :conditions => {self.aasm_column => name.to_s} unless self.respond_to?(name)
243
+ self.named_scope name, :conditions => { "#{table_name}.#{self.aasm_column}" => name.to_s} unless self.respond_to?(name)
242
244
  end
243
245
  end
244
246
  end
@@ -22,6 +22,8 @@ module AASM
22
22
  record.send(action)
23
23
  when Proc
24
24
  action.call(record)
25
+ when Array
26
+ action.each { |a| record.send(a) }
25
27
  end
26
28
  end
27
29
 
@@ -10,10 +10,10 @@ module AASM
10
10
  val = args.pop
11
11
  (@machines ||= {})[args] = val
12
12
  end
13
-
13
+
14
14
  attr_accessor :states, :events, :initial_state, :config
15
15
  attr_reader :name
16
-
16
+
17
17
  def initialize(name)
18
18
  @name = name
19
19
  @initial_state = nil
@@ -18,7 +18,7 @@ module AASM
18
18
  true
19
19
  end
20
20
  end
21
-
21
+
22
22
  def execute(obj, *args)
23
23
  case @on_transition
24
24
  when Symbol, String
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Barron