aasm 2.0.7 → 2.1.1

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