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.
- data/CHANGELOG.md +6 -0
- data/README.md +33 -35
- data/lib/aasm.rb +10 -5
- data/lib/aasm/aasm.rb +41 -41
- data/lib/aasm/base.rb +9 -3
- data/lib/aasm/deprecated/aasm.rb +15 -0
- data/lib/aasm/errors.rb +4 -0
- data/lib/aasm/persistence.rb +12 -11
- data/lib/aasm/state_machine.rb +27 -25
- data/lib/aasm/supporting_classes/event.rb +132 -0
- data/lib/aasm/supporting_classes/localizer.rb +40 -0
- data/lib/aasm/supporting_classes/state.rb +57 -0
- data/lib/aasm/supporting_classes/state_transition.rb +50 -0
- data/lib/aasm/version.rb +1 -1
- data/spec/models/conversation.rb +21 -21
- data/spec/models/silencer.rb +17 -0
- data/spec/spec_helpers/models_spec_helper.rb +83 -72
- data/spec/unit/aasm_spec.rb +4 -18
- data/spec/unit/active_record_persistence_spec.rb +6 -4
- data/spec/unit/event_spec.rb +2 -2
- data/spec/unit/localizer_spec.rb +7 -7
- data/spec/unit/state_spec.rb +5 -5
- data/spec/unit/state_transition_spec.rb +22 -6
- metadata +12 -9
- data/lib/aasm/event.rb +0 -127
- data/lib/aasm/localizer.rb +0 -36
- data/lib/aasm/state.rb +0 -53
- data/lib/aasm/state_transition.rb +0 -46
- data/lib/aasm/supporting_classes.rb +0 -7
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
63
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
126
|
-
|
122
|
+
aasm do
|
123
|
+
state :dating
|
124
|
+
state :married
|
127
125
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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 ##
|
data/lib/aasm.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
module AASM
|
2
|
-
end
|
3
|
-
|
4
1
|
require 'ostruct'
|
5
2
|
|
6
|
-
require File.join(File.dirname(__FILE__), 'aasm', '
|
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
|
-
|
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) }
|
data/lib/aasm/aasm.rb
CHANGED
@@ -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
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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.
|
91
|
-
state.
|
80
|
+
state.fire_callbacks(:before_enter, self)
|
81
|
+
state.fire_callbacks(:enter, self)
|
92
82
|
self.aasm_current_state = state_name
|
93
|
-
state.
|
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
|
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
|
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
|
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,
|
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.
|
161
|
+
old_state.fire_callbacks(:exit, self)
|
165
162
|
|
166
163
|
# new event before callback
|
167
|
-
event.
|
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.
|
176
|
-
new_state.
|
170
|
+
old_state.fire_callbacks(:before_exit, self)
|
171
|
+
new_state.fire_callbacks(:before_enter, self)
|
177
172
|
|
178
|
-
new_state.
|
173
|
+
new_state.fire_callbacks(:enter, self)
|
179
174
|
|
180
175
|
persist_successful = true
|
181
176
|
if persist
|
182
|
-
persist_successful =
|
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.
|
190
|
-
new_state.
|
191
|
-
event.
|
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
|
-
|
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)
|
data/lib/aasm/base.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/aasm/errors.rb
ADDED
data/lib/aasm/persistence.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
module AASM
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
data/lib/aasm/state_machine.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module AASM
|
2
|
+
class StateMachine
|
3
|
+
def self.[](clazz)
|
4
|
+
(@machines ||= {})[clazz.to_s]
|
5
|
+
end
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def self.[]=(clazz, machine)
|
8
|
+
(@machines ||= {})[clazz.to_s] = machine
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
attr_accessor :states, :events, :initial_state, :config
|
12
|
+
attr_reader :name
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
@initial_state = nil
|
17
|
+
@states = []
|
18
|
+
@events = {}
|
19
|
+
@config = OpenStruct.new
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
def clone
|
23
|
+
klone = super
|
24
|
+
klone.states = states.clone
|
25
|
+
klone.events = events.clone
|
26
|
+
klone
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
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
|