aasm 3.0.13 → 3.0.14
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 +10 -6
- data/README.md +217 -109
- data/aasm.gemspec +1 -0
- data/lib/aasm/persistence.rb +2 -0
- data/lib/aasm/persistence/active_record_persistence.rb +2 -71
- data/lib/aasm/persistence/base.rb +46 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +3 -92
- data/lib/aasm/persistence/read_state.rb +40 -0
- data/lib/aasm/supporting_classes/event.rb +6 -2
- data/lib/aasm/supporting_classes/localizer.rb +15 -13
- data/lib/aasm/supporting_classes/state.rb +8 -0
- data/lib/aasm/version.rb +1 -1
- data/spec/models/auth_machine.rb +84 -0
- data/spec/models/callback_new_dsl.rb +38 -0
- data/spec/models/callback_old_dsl.rb +36 -0
- data/spec/models/persistence.rb +71 -0
- data/spec/spec_helpers/models_spec_helper.rb +0 -85
- data/spec/unit/aasm_spec.rb +1 -1
- data/spec/unit/callbacks_new_dsl_spec.rb +33 -0
- data/spec/unit/callbacks_old_dsl_spec.rb +33 -0
- data/spec/unit/complex_example_spec.rb +75 -0
- data/spec/unit/event_spec.rb +31 -12
- data/spec/unit/inspection_spec.rb +7 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +228 -0
- metadata +41 -29
- data/spec/unit/active_record_persistence_spec.rb +0 -334
- data/spec/unit/auth_machine_spec.rb +0 -83
- data/spec/unit/before_after_callbacks_spec.rb +0 -79
- data/spec/unit/conversation_spec.rb +0 -7
@@ -0,0 +1,46 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module Base
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# Maps to the aasm_column in the database. Defaults to "aasm_state". You can write
|
7
|
+
# (example provided here for ActiveRecord, but it's true for Mongoid as well):
|
8
|
+
#
|
9
|
+
# create_table :foos do |t|
|
10
|
+
# t.string :name
|
11
|
+
# t.string :aasm_state
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class Foo < ActiveRecord::Base
|
15
|
+
# include AASM
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# OR:
|
19
|
+
#
|
20
|
+
# create_table :foos do |t|
|
21
|
+
# t.string :name
|
22
|
+
# t.string :status
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class Foo < ActiveRecord::Base
|
26
|
+
# include AASM
|
27
|
+
# aasm_column :status
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# This method is both a getter and a setter
|
31
|
+
def aasm_column(column_name=nil)
|
32
|
+
if column_name
|
33
|
+
AASM::StateMachine[self].config.column = column_name.to_sym
|
34
|
+
# @aasm_column = column_name.to_sym
|
35
|
+
else
|
36
|
+
AASM::StateMachine[self].config.column ||= :aasm_state
|
37
|
+
# @aasm_column ||= :aasm_state
|
38
|
+
end
|
39
|
+
# @aasm_column
|
40
|
+
AASM::StateMachine[self].config.column
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end # Base
|
45
|
+
end # Persistence
|
46
|
+
end # AASM
|
@@ -34,15 +34,16 @@ module AASM
|
|
34
34
|
# end
|
35
35
|
#
|
36
36
|
def self.included(base)
|
37
|
+
base.extend AASM::Persistence::Base::ClassMethods
|
37
38
|
base.extend AASM::Persistence::MongoidPersistence::ClassMethods
|
38
39
|
base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
|
39
|
-
base.send(:include, AASM::Persistence::
|
40
|
+
base.send(:include, AASM::Persistence::ReadState) unless base.method_defined?(:aasm_read_state)
|
40
41
|
base.send(:include, AASM::Persistence::MongoidPersistence::WriteState) unless base.method_defined?(:aasm_write_state)
|
41
42
|
base.send(:include, AASM::Persistence::MongoidPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence)
|
42
43
|
|
43
44
|
# if base.respond_to?(:named_scope)
|
44
45
|
# base.extend(AASM::Persistence::MongoidPersistence::NamedScopeMethods)
|
45
|
-
#
|
46
|
+
#
|
46
47
|
# base.class_eval do
|
47
48
|
# class << self
|
48
49
|
# unless method_defined?(:aasm_state_without_named_scope)
|
@@ -59,60 +60,6 @@ module AASM
|
|
59
60
|
end
|
60
61
|
|
61
62
|
module ClassMethods
|
62
|
-
# Maps to the aasm_column in the database. Deafults to "aasm_state". You can write:
|
63
|
-
#
|
64
|
-
# class Foo
|
65
|
-
# include Mongoid::Document
|
66
|
-
# include AASM
|
67
|
-
# field :aasm_state
|
68
|
-
# end
|
69
|
-
#
|
70
|
-
# OR:
|
71
|
-
#
|
72
|
-
# class Foo
|
73
|
-
# include Mongoid::Document
|
74
|
-
# include AASM
|
75
|
-
# field :status
|
76
|
-
# aasm_column :status
|
77
|
-
# end
|
78
|
-
#
|
79
|
-
# This method is both a getter and a setter
|
80
|
-
def aasm_column(column_name=nil)
|
81
|
-
if column_name
|
82
|
-
AASM::StateMachine[self].config.column = column_name.to_sym
|
83
|
-
# @aasm_column = column_name.to_sym
|
84
|
-
else
|
85
|
-
AASM::StateMachine[self].config.column ||= :aasm_state
|
86
|
-
# @aasm_column ||= :aasm_state
|
87
|
-
end
|
88
|
-
# @aasm_column
|
89
|
-
AASM::StateMachine[self].config.column
|
90
|
-
end
|
91
|
-
|
92
|
-
# def find_in_state(number, state, *args)
|
93
|
-
# with_state_scope state do
|
94
|
-
# find(number, *args)
|
95
|
-
# end
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# def count_in_state(state, *args)
|
99
|
-
# with_state_scope state do
|
100
|
-
# count(*args)
|
101
|
-
# end
|
102
|
-
# end
|
103
|
-
#
|
104
|
-
# def calculate_in_state(state, *args)
|
105
|
-
# with_state_scope state do
|
106
|
-
# calculate(*args)
|
107
|
-
# end
|
108
|
-
# end
|
109
|
-
|
110
|
-
protected
|
111
|
-
def with_state_scope(state)
|
112
|
-
with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
|
113
|
-
yield if block_given?
|
114
|
-
end
|
115
|
-
end
|
116
63
|
end
|
117
64
|
|
118
65
|
module InstanceMethods
|
@@ -200,42 +147,6 @@ module AASM
|
|
200
147
|
end
|
201
148
|
end
|
202
149
|
|
203
|
-
module ReadState
|
204
|
-
|
205
|
-
# Returns the value of the aasm_column - called from <tt>aasm_current_state</tt>
|
206
|
-
#
|
207
|
-
# If it's a new record, and the aasm state column is blank it returns the initial state:
|
208
|
-
#
|
209
|
-
# class Foo
|
210
|
-
# include Mongoid::Document
|
211
|
-
# include AASM
|
212
|
-
# aasm_column :status
|
213
|
-
# aasm_state :opened
|
214
|
-
# aasm_state :closed
|
215
|
-
# end
|
216
|
-
#
|
217
|
-
# foo = Foo.new
|
218
|
-
# foo.current_state # => :opened
|
219
|
-
# foo.close
|
220
|
-
# foo.current_state # => :closed
|
221
|
-
#
|
222
|
-
# foo = Foo.find(1)
|
223
|
-
# foo.current_state # => :opened
|
224
|
-
# foo.aasm_state = nil
|
225
|
-
# foo.current_state # => nil
|
226
|
-
#
|
227
|
-
# NOTE: intended to be called from an event
|
228
|
-
#
|
229
|
-
# This allows for nil aasm states - be sure to add validation to your model
|
230
|
-
def aasm_read_state
|
231
|
-
if new_record?
|
232
|
-
send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
|
233
|
-
else
|
234
|
-
send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
150
|
module NamedScopeMethods
|
240
151
|
def aasm_state_with_named_scope name, options = {}
|
241
152
|
aasm_state_without_named_scope name, options
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module ReadState
|
4
|
+
|
5
|
+
# Returns the value of the aasm_column - called from <tt>aasm_current_state</tt>
|
6
|
+
#
|
7
|
+
# If it's a new record, and the aasm state column is blank it returns the initial state
|
8
|
+
# (example provided here for ActiveRecord, but it's true for Mongoid as well):
|
9
|
+
#
|
10
|
+
# class Foo < ActiveRecord::Base
|
11
|
+
# include AASM
|
12
|
+
# aasm_column :status
|
13
|
+
# aasm_state :opened
|
14
|
+
# aasm_state :closed
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# foo = Foo.new
|
18
|
+
# foo.current_state # => :opened
|
19
|
+
# foo.close
|
20
|
+
# foo.current_state # => :closed
|
21
|
+
#
|
22
|
+
# foo = Foo.find(1)
|
23
|
+
# foo.current_state # => :opened
|
24
|
+
# foo.aasm_state = nil
|
25
|
+
# foo.current_state # => nil
|
26
|
+
#
|
27
|
+
# NOTE: intended to be called from an event
|
28
|
+
#
|
29
|
+
# This allows for nil aasm states - be sure to add validation to your model
|
30
|
+
def aasm_read_state
|
31
|
+
if new_record?
|
32
|
+
send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
|
33
|
+
else
|
34
|
+
send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end # ReadState
|
39
|
+
end # Persistence
|
40
|
+
end # AASM
|
@@ -15,19 +15,23 @@ module AASM
|
|
15
15
|
def may_fire?(obj, to_state=nil, *args)
|
16
16
|
_fire(obj, true, to_state, *args) # true indicates test firing
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def fire(obj, to_state=nil, *args)
|
20
20
|
_fire(obj, false, to_state, *args) # false indicates this is not a test (fire!)
|
21
21
|
end
|
22
22
|
|
23
23
|
def transitions_from_state?(state)
|
24
|
-
|
24
|
+
transitions_from_state(state).any?
|
25
25
|
end
|
26
26
|
|
27
27
|
def transitions_from_state(state)
|
28
28
|
@transitions.select { |t| t.from == state }
|
29
29
|
end
|
30
30
|
|
31
|
+
def transitions_to_state?(state)
|
32
|
+
transitions_to_state(state).any?
|
33
|
+
end
|
34
|
+
|
31
35
|
def transitions_to_state(state)
|
32
36
|
@transitions.select { |t| t.to == state }
|
33
37
|
end
|
@@ -6,23 +6,27 @@ module AASM
|
|
6
6
|
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
|
7
7
|
list
|
8
8
|
end
|
9
|
-
(
|
10
|
-
begin
|
11
|
-
return I18n.translate(checklist.shift, :raise => true)
|
12
|
-
rescue I18n::MissingTranslationData
|
13
|
-
# that's okay
|
14
|
-
end
|
15
|
-
end
|
16
|
-
I18n.translate(checklist.shift, :default => event.to_s.humanize)
|
9
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event.to_s.humanize)
|
17
10
|
end
|
18
11
|
|
19
12
|
def human_state(obj)
|
20
13
|
klass = obj.class
|
21
14
|
checklist = ancestors_list(klass).inject([]) do |list, ancestor|
|
22
|
-
list <<
|
23
|
-
list <<
|
15
|
+
list << item_for(obj, klass, ancestor)
|
16
|
+
list << item_for(obj, klass, ancestor, :old_style => true)
|
24
17
|
list
|
25
18
|
end
|
19
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => obj.aasm_current_state.to_s.humanize)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def item_for(obj, klass, ancestor, options={})
|
25
|
+
separator = options[:old_style] ? '.' : '/'
|
26
|
+
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm_column}#{separator}#{obj.aasm_current_state}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def translate_queue(checklist)
|
26
30
|
(0...(checklist.size-1)).each do |i|
|
27
31
|
begin
|
28
32
|
return I18n.translate(checklist.shift, :raise => true)
|
@@ -30,11 +34,9 @@ module AASM
|
|
30
34
|
# that's okay
|
31
35
|
end
|
32
36
|
end
|
33
|
-
|
37
|
+
nil
|
34
38
|
end
|
35
39
|
|
36
|
-
private
|
37
|
-
|
38
40
|
# added for rails 2.x compatibility
|
39
41
|
def i18n_scope(klass)
|
40
42
|
klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
|
data/lib/aasm/version.rb
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
class AuthMachine
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
attr_accessor :activation_code, :activated_at, :deleted_at
|
5
|
+
|
6
|
+
aasm do
|
7
|
+
state :passive
|
8
|
+
state :pending, :initial => true, :enter => :make_activation_code
|
9
|
+
state :active, :enter => :do_activate
|
10
|
+
state :suspended
|
11
|
+
state :deleted, :enter => :do_delete, :exit => :do_undelete
|
12
|
+
state :waiting
|
13
|
+
|
14
|
+
event :register do
|
15
|
+
transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
|
16
|
+
end
|
17
|
+
|
18
|
+
event :activate do
|
19
|
+
transitions :from => :pending, :to => :active
|
20
|
+
end
|
21
|
+
|
22
|
+
event :suspend do
|
23
|
+
transitions :from => [:passive, :pending, :active], :to => :suspended
|
24
|
+
end
|
25
|
+
|
26
|
+
event :delete do
|
27
|
+
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
28
|
+
end
|
29
|
+
|
30
|
+
# a dummy event that can never happen
|
31
|
+
event :unpassify do
|
32
|
+
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
|
33
|
+
end
|
34
|
+
|
35
|
+
event :unsuspend do
|
36
|
+
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
|
37
|
+
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
|
38
|
+
transitions :from => :suspended, :to => :passive
|
39
|
+
end
|
40
|
+
|
41
|
+
event :wait do
|
42
|
+
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
|
48
|
+
# lets do something similar here for testing purposes.
|
49
|
+
aasm_enter_initial_state
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_activation_code
|
53
|
+
@activation_code = 'moo'
|
54
|
+
end
|
55
|
+
|
56
|
+
def do_activate
|
57
|
+
@activated_at = Time.now
|
58
|
+
@activation_code = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def do_delete
|
62
|
+
@deleted_at = Time.now
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_undelete
|
66
|
+
@deleted_at = false
|
67
|
+
end
|
68
|
+
|
69
|
+
def can_register?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def has_activated?
|
74
|
+
!!@activated_at
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_activation_code?
|
78
|
+
!!@activation_code
|
79
|
+
end
|
80
|
+
|
81
|
+
def if_polite?(phrase = nil)
|
82
|
+
phrase == :please
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class CallbackNewDsl
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
aasm do
|
5
|
+
state :open, :initial => true,
|
6
|
+
:before_enter => :before_enter_open,
|
7
|
+
:after_enter => :after_enter_open,
|
8
|
+
:before_exit => :before_exit_open,
|
9
|
+
:after_exit => :after_exit_open
|
10
|
+
|
11
|
+
state :closed,
|
12
|
+
:before_enter => :before_enter_closed,
|
13
|
+
:after_enter => :after_enter_closed,
|
14
|
+
:before_exit => :before_exit_closed,
|
15
|
+
:after_exit => :after_exit_closed
|
16
|
+
|
17
|
+
event :close, :before => :before, :after => :after do
|
18
|
+
transitions :to => :closed, :from => [:open]
|
19
|
+
end
|
20
|
+
|
21
|
+
event :open, :before => :before, :after => :after do
|
22
|
+
transitions :to => :open, :from => :closed
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def before_enter_open; end
|
27
|
+
def before_exit_open; end
|
28
|
+
def after_enter_open; end
|
29
|
+
def after_exit_open; end
|
30
|
+
|
31
|
+
def before_enter_closed; end
|
32
|
+
def before_exit_closed; end
|
33
|
+
def after_enter_closed; end
|
34
|
+
def after_exit_closed; end
|
35
|
+
|
36
|
+
def before; end
|
37
|
+
def after; end
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class CallbackOldDsl
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
aasm_initial_state :open
|
5
|
+
aasm_state :open,
|
6
|
+
:before_enter => :before_enter_open,
|
7
|
+
:before_exit => :before_exit_open,
|
8
|
+
:after_enter => :after_enter_open,
|
9
|
+
:after_exit => :after_exit_open
|
10
|
+
aasm_state :closed,
|
11
|
+
:before_enter => :before_enter_closed,
|
12
|
+
:before_exit => :before_exit_closed,
|
13
|
+
:after_enter => :after_enter_closed,
|
14
|
+
:after_exit => :after_exit_closed
|
15
|
+
|
16
|
+
aasm_event :close, :before => :before, :after => :after do
|
17
|
+
transitions :to => :closed, :from => [:open]
|
18
|
+
end
|
19
|
+
|
20
|
+
aasm_event :open, :before => :before, :after => :after do
|
21
|
+
transitions :to => :open, :from => :closed
|
22
|
+
end
|
23
|
+
|
24
|
+
def before_enter_open; end
|
25
|
+
def before_exit_open; end
|
26
|
+
def after_enter_open; end
|
27
|
+
def after_exit_open; end
|
28
|
+
|
29
|
+
def before_enter_closed; end
|
30
|
+
def before_exit_closed; end
|
31
|
+
def after_enter_closed; end
|
32
|
+
def after_exit_closed; end
|
33
|
+
|
34
|
+
def before; end
|
35
|
+
def after; end
|
36
|
+
end
|