aasm 3.0.16 → 3.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +2 -1
  3. data/API +34 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +1 -1
  6. data/HOWTO +12 -0
  7. data/README.md +57 -4
  8. data/aasm.gemspec +2 -0
  9. data/lib/aasm.rb +5 -4
  10. data/lib/aasm/aasm.rb +50 -75
  11. data/lib/aasm/base.rb +22 -18
  12. data/lib/aasm/event.rb +130 -0
  13. data/lib/aasm/instance_base.rb +87 -0
  14. data/lib/aasm/localizer.rb +54 -0
  15. data/lib/aasm/persistence.rb +22 -14
  16. data/lib/aasm/persistence/active_record_persistence.rb +38 -69
  17. data/lib/aasm/persistence/base.rb +42 -2
  18. data/lib/aasm/persistence/mongoid_persistence.rb +33 -64
  19. data/lib/aasm/state.rb +78 -0
  20. data/lib/aasm/state_machine.rb +2 -2
  21. data/lib/aasm/transition.rb +49 -0
  22. data/lib/aasm/version.rb +1 -1
  23. data/spec/models/active_record/api.rb +75 -0
  24. data/spec/models/auth_machine.rb +1 -1
  25. data/spec/models/bar.rb +15 -0
  26. data/spec/models/foo.rb +34 -0
  27. data/spec/models/mongoid/simple_mongoid.rb +10 -0
  28. data/spec/models/mongoid/{mongoid_models.rb → simple_new_dsl_mongoid.rb} +1 -12
  29. data/spec/models/persistence.rb +2 -1
  30. data/spec/models/this_name_better_not_be_in_use.rb +11 -0
  31. data/spec/schema.rb +1 -1
  32. data/spec/spec_helper.rb +8 -1
  33. data/spec/unit/api_spec.rb +72 -0
  34. data/spec/unit/callbacks_spec.rb +2 -2
  35. data/spec/unit/event_spec.rb +269 -0
  36. data/spec/unit/inspection_spec.rb +43 -5
  37. data/spec/unit/{supporting_classes/localizer_spec.rb → localizer_spec.rb} +2 -2
  38. data/spec/unit/memory_leak_spec.rb +12 -12
  39. data/spec/unit/persistence/active_record_persistence_spec.rb +0 -40
  40. data/spec/unit/persistence/mongoid_persistance_spec.rb +3 -2
  41. data/spec/unit/simple_example_spec.rb +6 -0
  42. data/spec/unit/{supporting_classes/state_spec.rb → state_spec.rb} +2 -2
  43. data/spec/unit/{supporting_classes/state_transition_spec.rb → transition_spec.rb} +18 -18
  44. metadata +127 -38
  45. data/lib/aasm/persistence/read_state.rb +0 -40
  46. data/lib/aasm/supporting_classes/event.rb +0 -146
  47. data/lib/aasm/supporting_classes/localizer.rb +0 -56
  48. data/lib/aasm/supporting_classes/state.rb +0 -80
  49. data/lib/aasm/supporting_classes/state_transition.rb +0 -51
  50. data/spec/spec_helpers/models_spec_helper.rb +0 -64
  51. data/spec/unit/supporting_classes/event_spec.rb +0 -203
@@ -0,0 +1,34 @@
1
+ class Foo
2
+ include AASM
3
+ aasm do
4
+ state :open, :initial => true, :exit => :exit
5
+ state :closed, :enter => :enter
6
+
7
+ event :close, :success => :success_callback do
8
+ transitions :from => [:open], :to => [:closed]
9
+ end
10
+
11
+ event :null do
12
+ transitions :from => [:open], :to => :closed, :guard => :always_false
13
+ end
14
+ end
15
+
16
+ def always_false
17
+ false
18
+ end
19
+
20
+ def success_callback
21
+ end
22
+
23
+ def enter
24
+ end
25
+ def exit
26
+ end
27
+ end
28
+
29
+ class FooTwo < Foo
30
+ include AASM
31
+ aasm do
32
+ state :foo
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ class SimpleMongoid
2
+ include Mongoid::Document
3
+ include AASM
4
+
5
+ field :status, type: String
6
+
7
+ aasm_column :status
8
+ aasm_state :unknown_scope
9
+ aasm_state :new
10
+ end
@@ -1,14 +1,3 @@
1
- class SimpleMongoid
2
- include Mongoid::Document
3
- include AASM
4
-
5
- field :status, type: String
6
-
7
- aasm_column :status
8
- aasm_state :unknown_scope
9
- aasm_state :new
10
- end
11
-
12
1
  class SimpleNewDslMongoid
13
2
  include Mongoid::Document
14
3
  include AASM
@@ -20,4 +9,4 @@ class SimpleNewDslMongoid
20
9
  state :unknown_scope
21
10
  state :new
22
11
  end
23
- end
12
+ end
@@ -15,10 +15,11 @@ class Gate < ActiveRecord::Base
15
15
  end
16
16
 
17
17
  class Reader < ActiveRecord::Base
18
+ include AASM
19
+
18
20
  def aasm_read_state
19
21
  "fi"
20
22
  end
21
- include AASM
22
23
  end
23
24
 
24
25
  class Writer < ActiveRecord::Base
@@ -0,0 +1,11 @@
1
+ class ThisNameBetterNotBeInUse
2
+ include AASM
3
+
4
+ aasm do
5
+ state :initial
6
+ state :symbol
7
+ state :string
8
+ state :array
9
+ state :proc
10
+ end
11
+ end
@@ -1,6 +1,6 @@
1
1
  ActiveRecord::Schema.define(:version => 0) do
2
2
 
3
- %w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models}.each do |table_name|
3
+ %w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
4
4
  create_table table_name, :force => true do |t|
5
5
  t.string "aasm_state"
6
6
  end
@@ -5,8 +5,12 @@ require 'aasm'
5
5
  require 'rspec'
6
6
  require 'rspec/autorun'
7
7
 
8
+ require 'coveralls'
9
+ Coveralls.wear!
10
+
8
11
  # require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
9
12
  # require 'ruby-debug/completion'
13
+ # require 'pry'
10
14
 
11
15
  def load_schema
12
16
  config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
@@ -15,5 +19,8 @@ def load_schema
15
19
  load(File.dirname(__FILE__) + "/schema.rb")
16
20
  end
17
21
 
18
- # Requiring custom spec helpers
22
+ # custom spec helpers
19
23
  Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].sort.each { |f| require File.expand_path(f) }
24
+
25
+ # example model classes
26
+ Dir[File.dirname(__FILE__) + "/models/*.rb"].sort.each { |f| require File.expand_path(f) }
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'models/active_record/api.rb'
3
+
4
+ describe "reading the current state" do
5
+ it "uses the AASM default" do
6
+ DefaultState.new.aasm.current_state.should eql :alpha
7
+ end
8
+
9
+ it "uses the provided method" do
10
+ ProvidedState.new.aasm.current_state.should eql :beta
11
+ end
12
+
13
+ it "uses the persistence storage" do
14
+ PersistedState.new.aasm.current_state.should eql :alpha
15
+ end
16
+
17
+ it "uses the provided method even if persisted" do
18
+ ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma
19
+ end
20
+ end
21
+
22
+ describe "writing and persisting the current state" do
23
+ it "uses the AASM default" do
24
+ o = DefaultState.new
25
+ o.release!
26
+ o.persisted_store.should be_nil
27
+ end
28
+
29
+ it "uses the provided method" do
30
+ o = ProvidedState.new
31
+ o.release!
32
+ o.persisted_store.should eql :beta
33
+ end
34
+
35
+ it "uses the persistence storage" do
36
+ o = PersistedState.new
37
+ o.release!
38
+ o.persisted_store.should be_nil
39
+ end
40
+
41
+ it "uses the provided method even if persisted" do
42
+ o = ProvidedAndPersistedState.new
43
+ o.release!
44
+ o.persisted_store.should eql :beta
45
+ end
46
+ end
47
+
48
+ describe "writing the current state without persisting it" do
49
+ it "uses the AASM default" do
50
+ o = DefaultState.new
51
+ o.release
52
+ o.transient_store.should be_nil
53
+ end
54
+
55
+ it "uses the provided method" do
56
+ o = ProvidedState.new
57
+ o.release
58
+ o.transient_store.should eql :beta
59
+ end
60
+
61
+ it "uses the persistence storage" do
62
+ o = PersistedState.new
63
+ o.release
64
+ o.transient_store.should be_nil
65
+ end
66
+
67
+ it "uses the provided method even if persisted" do
68
+ o = ProvidedAndPersistedState.new
69
+ o.release
70
+ o.transient_store.should eql :beta
71
+ end
72
+ end
@@ -85,7 +85,7 @@ describe 'event callbacks' do
85
85
  end
86
86
 
87
87
  it 'should not call it for failing bang fire' do
88
- @foo.stub!(:aasm_set_current_state_with_persistence).and_return(false)
88
+ @foo.aasm.stub!(:set_current_state_with_persistence).and_return(false)
89
89
  @foo.should_not_receive(:aasm_event_fired)
90
90
  @foo.close!
91
91
  end
@@ -108,7 +108,7 @@ describe 'event callbacks' do
108
108
  end
109
109
 
110
110
  it 'should not call it if persist fails for bang fire' do
111
- @foo.stub!(:aasm_set_current_state_with_persistence).and_return(false)
111
+ @foo.aasm.stub!(:set_current_state_with_persistence).and_return(false)
112
112
  @foo.should_receive(:aasm_event_failed)
113
113
  @foo.close!
114
114
  end
@@ -0,0 +1,269 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'adding an event' do
4
+ let(:event) do
5
+ AASM::Event.new(:close_order, {:success => :success_callback}) do
6
+ before :before_callback
7
+ after :after_callback
8
+ transitions :to => :closed, :from => [:open, :received]
9
+ end
10
+ end
11
+
12
+ it 'should set the name' do
13
+ event.name.should == :close_order
14
+ end
15
+
16
+ it 'should set the success callback' do
17
+ event.options[:success].should == :success_callback
18
+ end
19
+
20
+ it 'should set the after callback' do
21
+ event.options[:after].should == [:after_callback]
22
+ end
23
+
24
+ it 'should set the before callback' do
25
+ event.options[:before].should == [:before_callback]
26
+ end
27
+
28
+ it 'should create transitions' do
29
+ transitions = event.all_transitions
30
+ transitions[0].from.should == :open
31
+ transitions[0].to.should == :closed
32
+ transitions[1].from.should == :received
33
+ transitions[1].to.should == :closed
34
+ end
35
+ end
36
+
37
+ describe 'transition inspection' do
38
+ let(:event) do
39
+ AASM::Event.new(:run) do
40
+ transitions :to => :running, :from => :sleeping
41
+ end
42
+ end
43
+
44
+ it 'should support inspecting transitions from other states' do
45
+ event.transitions_from_state(:sleeping).map(&:to).should == [:running]
46
+ event.transitions_from_state?(:sleeping).should be_true
47
+
48
+ event.transitions_from_state(:cleaning).map(&:to).should == []
49
+ event.transitions_from_state?(:cleaning).should be_false
50
+ end
51
+
52
+ it 'should support inspecting transitions to other states' do
53
+ event.transitions_to_state(:running).map(&:from).should == [:sleeping]
54
+ event.transitions_to_state?(:running).should be_true
55
+
56
+ event.transitions_to_state(:cleaning).map(&:to).should == []
57
+ event.transitions_to_state?(:cleaning).should be_false
58
+ end
59
+ end
60
+
61
+ describe 'firing an event' do
62
+ it 'should return nil if the transitions are empty' do
63
+ obj = mock('object')
64
+ obj.stub!(:aasm_current_state)
65
+
66
+ event = AASM::Event.new(:event)
67
+ event.fire(obj).should be_nil
68
+ end
69
+
70
+ it 'should return the state of the first matching transition it finds' do
71
+ event = AASM::Event.new(:event) do
72
+ transitions :to => :closed, :from => [:open, :received]
73
+ end
74
+
75
+ obj = mock('object')
76
+ obj.stub!(:aasm_current_state).and_return(:open)
77
+
78
+ event.fire(obj).should == :closed
79
+ end
80
+
81
+ it 'should call the guard with the params passed in' do
82
+ event = AASM::Event.new(:event) do
83
+ transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
84
+ end
85
+
86
+ obj = mock('object')
87
+ obj.stub!(:aasm_current_state).and_return(:open)
88
+ obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
89
+
90
+ event.fire(obj, nil, 'arg1', 'arg2').should == :closed
91
+ end
92
+
93
+ end
94
+
95
+ describe 'should fire callbacks' do
96
+ describe 'success' do
97
+ it "if it's a symbol" do
98
+ ThisNameBetterNotBeInUse.instance_eval {
99
+ aasm_event :with_symbol, :success => :symbol_success_callback do
100
+ transitions :to => :symbol, :from => [:initial]
101
+ end
102
+ }
103
+
104
+ model = ThisNameBetterNotBeInUse.new
105
+ model.should_receive(:symbol_success_callback)
106
+ model.with_symbol!
107
+ end
108
+
109
+ it "if it's a string" do
110
+ ThisNameBetterNotBeInUse.instance_eval {
111
+ aasm_event :with_string, :success => 'string_success_callback' do
112
+ transitions :to => :string, :from => [:initial]
113
+ end
114
+ }
115
+
116
+ model = ThisNameBetterNotBeInUse.new
117
+ model.should_receive(:string_success_callback)
118
+ model.with_string!
119
+ end
120
+
121
+ it "if passed an array of strings and/or symbols" do
122
+ ThisNameBetterNotBeInUse.instance_eval {
123
+ aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
124
+ transitions :to => :array, :from => [:initial]
125
+ end
126
+ }
127
+
128
+ model = ThisNameBetterNotBeInUse.new
129
+ model.should_receive(:success_callback1)
130
+ model.should_receive(:success_callback2)
131
+ model.with_array!
132
+ end
133
+
134
+ it "if passed an array of strings and/or symbols and/or procs" do
135
+ ThisNameBetterNotBeInUse.instance_eval {
136
+ aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { proc_success_callback }] do
137
+ transitions :to => :array, :from => [:initial]
138
+ end
139
+ }
140
+
141
+ model = ThisNameBetterNotBeInUse.new
142
+ model.should_receive(:success_callback1)
143
+ model.should_receive(:success_callback2)
144
+ model.should_receive(:proc_success_callback)
145
+ model.with_array_including_procs!
146
+ end
147
+
148
+ it "if it's a proc" do
149
+ ThisNameBetterNotBeInUse.instance_eval {
150
+ aasm_event :with_proc, :success => lambda { proc_success_callback } do
151
+ transitions :to => :proc, :from => [:initial]
152
+ end
153
+ }
154
+
155
+ model = ThisNameBetterNotBeInUse.new
156
+ model.should_receive(:proc_success_callback)
157
+ model.with_proc!
158
+ end
159
+ end
160
+
161
+ describe 'after' do
162
+ it "if they set different ways" do
163
+ ThisNameBetterNotBeInUse.instance_eval do
164
+ aasm_event :with_afters, :after => :do_one_thing_after do
165
+ after do
166
+ do_another_thing_after_too
167
+ end
168
+ after do
169
+ do_third_thing_at_last
170
+ end
171
+ transitions :to => :proc, :from => [:initial]
172
+ end
173
+ end
174
+
175
+ model = ThisNameBetterNotBeInUse.new
176
+ model.should_receive(:do_one_thing_after).once.ordered
177
+ model.should_receive(:do_another_thing_after_too).once.ordered
178
+ model.should_receive(:do_third_thing_at_last).once.ordered
179
+ model.with_afters!
180
+ end
181
+ end
182
+
183
+ describe 'before' do
184
+ it "if it's a proc" do
185
+ ThisNameBetterNotBeInUse.instance_eval do
186
+ aasm_event :before_as_proc do
187
+ before do
188
+ do_something_before
189
+ end
190
+ transitions :to => :proc, :from => [:initial]
191
+ end
192
+ end
193
+
194
+ model = ThisNameBetterNotBeInUse.new
195
+ model.should_receive(:do_something_before).once
196
+ model.before_as_proc!
197
+ end
198
+ end
199
+
200
+ it 'in right order' do
201
+ ThisNameBetterNotBeInUse.instance_eval do
202
+ aasm_event :in_right_order, :after => :do_something_after do
203
+ before do
204
+ do_something_before
205
+ end
206
+ transitions :to => :proc, :from => [:initial]
207
+ end
208
+ end
209
+
210
+ model = ThisNameBetterNotBeInUse.new
211
+ model.should_receive(:do_something_before).once.ordered
212
+ model.should_receive(:do_something_after).once.ordered
213
+ model.in_right_order!
214
+ end
215
+ end
216
+
217
+ describe 'parametrised events' do
218
+ let(:pe) {ParametrisedEvent.new}
219
+
220
+ it 'should transition to specified next state (sleeping to showering)' do
221
+ pe.wakeup!(:showering)
222
+ pe.aasm_current_state.should == :showering
223
+ end
224
+
225
+ it 'should transition to specified next state (sleeping to working)' do
226
+ pe.wakeup!(:working)
227
+ pe.aasm_current_state.should == :working
228
+ end
229
+
230
+ it 'should transition to default (first or showering) state' do
231
+ pe.wakeup!
232
+ pe.aasm_current_state.should == :showering
233
+ end
234
+
235
+ it 'should transition to default state when on_transition invoked' do
236
+ pe.dress!(nil, 'purple', 'dressy')
237
+ pe.aasm_current_state.should == :working
238
+ end
239
+
240
+ it 'should call on_transition method with args' do
241
+ pe.wakeup!(:showering)
242
+ pe.should_receive(:wear_clothes).with('blue', 'jeans')
243
+ pe.dress!(:working, 'blue', 'jeans')
244
+ end
245
+
246
+ it 'should call on_transition proc' do
247
+ pe.wakeup!(:showering)
248
+ pe.should_receive(:wear_clothes).with('purple', 'slacks')
249
+ pe.dress!(:dating, 'purple', 'slacks')
250
+ end
251
+
252
+ it 'should call on_transition with an array of methods' do
253
+ pe.wakeup!(:showering)
254
+ pe.should_receive(:condition_hair)
255
+ pe.should_receive(:fix_hair)
256
+ pe.dress!(:prettying_up)
257
+ end
258
+ end
259
+
260
+ describe 'event firing without persistence' do
261
+ it 'should attempt to persist if aasm_write_state is defined' do
262
+ foo = Foo.new
263
+ def foo.aasm_write_state; end
264
+ foo.should be_open
265
+
266
+ foo.should_receive(:aasm_write_state_without_persistence)
267
+ foo.close
268
+ end
269
+ end