aasm 4.9.0 → 4.10.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -4
  3. data/CHANGELOG.md +15 -0
  4. data/Gemfile +3 -0
  5. data/README.md +27 -11
  6. data/gemfiles/rails_4.0.gemfile +1 -0
  7. data/gemfiles/rails_4.1.gemfile +1 -0
  8. data/gemfiles/rails_4.2_mongo_mapper.gemfile +1 -0
  9. data/lib/aasm.rb +3 -1
  10. data/lib/aasm/aasm.rb +13 -9
  11. data/lib/aasm/base.rb +67 -28
  12. data/lib/aasm/configuration.rb +3 -0
  13. data/lib/aasm/core/event.rb +8 -1
  14. data/lib/aasm/core/transition.rb +32 -4
  15. data/lib/aasm/errors.rb +4 -4
  16. data/lib/aasm/persistence.rb +14 -1
  17. data/lib/aasm/persistence/active_record_persistence.rb +27 -13
  18. data/lib/aasm/persistence/base.rb +2 -44
  19. data/lib/aasm/persistence/core_data_query_persistence.rb +93 -0
  20. data/lib/aasm/persistence/dynamoid_persistence.rb +2 -4
  21. data/lib/aasm/persistence/mongo_mapper_persistence.rb +15 -9
  22. data/lib/aasm/persistence/mongoid_persistence.rb +24 -4
  23. data/lib/aasm/persistence/redis_persistence.rb +107 -0
  24. data/lib/aasm/persistence/sequel_persistence.rb +1 -3
  25. data/lib/aasm/state_machine.rb +1 -9
  26. data/lib/aasm/state_machine_store.rb +73 -0
  27. data/lib/aasm/version.rb +1 -1
  28. data/lib/generators/active_record/templates/migration_existing.rb +2 -6
  29. data/lib/motion-aasm.rb +35 -0
  30. data/spec/models/callbacks/basic.rb +12 -2
  31. data/spec/models/callbacks/guard_within_block.rb +2 -1
  32. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +2 -1
  33. data/spec/models/callbacks/with_args.rb +2 -1
  34. data/spec/models/callbacks/with_state_arg.rb +6 -2
  35. data/spec/models/mongoid/mongoid_relationships.rb +26 -0
  36. data/spec/models/namespaced_multiple_example.rb +28 -0
  37. data/spec/models/parametrised_event.rb +9 -3
  38. data/spec/models/states_on_one_line_example.rb +8 -0
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/unit/api_spec.rb +20 -0
  41. data/spec/unit/callbacks_spec.rb +17 -5
  42. data/spec/unit/event_spec.rb +19 -1
  43. data/spec/unit/exception_spec.rb +11 -0
  44. data/spec/unit/memory_leak_spec.rb +1 -1
  45. data/spec/unit/namespaced_multiple_example_spec.rb +53 -0
  46. data/spec/unit/override_warning_spec.rb +43 -0
  47. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +1 -1
  48. data/spec/unit/persistence/active_record_persistence_spec.rb +1 -1
  49. data/spec/unit/persistence/mongoid_persistence_spec.rb +11 -0
  50. data/spec/unit/persistence/redis_persistence_spec.rb +77 -0
  51. data/spec/unit/readme_spec.rb +1 -2
  52. data/spec/unit/states_on_one_line_example_spec.rb +16 -0
  53. data/spec/unit/transition_spec.rb +60 -1
  54. metadata +22 -2
@@ -12,18 +12,24 @@ class ParametrisedEvent
12
12
  end
13
13
 
14
14
  event :dress do
15
- transitions :from => :sleeping, :to => :working, :after => :wear_clothes
16
- transitions :from => :showering, :to => [:working, :dating], :after => Proc.new { |*args| wear_clothes(*args) }
17
- transitions :from => :showering, :to => :prettying_up, :after => [:condition_hair, :fix_hair]
15
+ transitions :from => :sleeping, :to => :working, :after => :wear_clothes, :success => :wear_makeup
16
+ transitions :from => :showering, :to => [:working, :dating], :after => Proc.new { |*args| wear_clothes(*args) }, :success => proc { |*args| wear_makeup(*args) }
17
+ transitions :from => :showering, :to => :prettying_up, :after => [:condition_hair, :fix_hair], :success => [:touch_up_hair]
18
18
  end
19
19
  end
20
20
 
21
21
  def wear_clothes(shirt_color, trouser_type=nil)
22
22
  end
23
23
 
24
+ def wear_makeup(makeup, moisturizer)
25
+ end
26
+
24
27
  def condition_hair
25
28
  end
26
29
 
27
30
  def fix_hair
28
31
  end
32
+
33
+ def touch_up_hair
34
+ end
29
35
  end
@@ -0,0 +1,8 @@
1
+ class StatesOnOneLineExample
2
+ include AASM
3
+
4
+ aasm :one_line do
5
+ state :initial, :initial => true
6
+ state :first, :second
7
+ end
8
+ end
@@ -3,6 +3,7 @@ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib
3
3
  require 'aasm'
4
4
  require 'rspec'
5
5
  require 'aasm/rspec'
6
+ require 'pry'
6
7
 
7
8
  # require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
8
9
  # require 'ruby-debug/completion'
@@ -22,6 +22,26 @@ describe "reading the current state" do
22
22
  it "uses the provided method even if persisted" do
23
23
  expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
24
24
  end
25
+
26
+ context "after dup" do
27
+ it "uses the persistence storage" do
28
+ source = PersistedState.create!
29
+ copy = source.dup
30
+ copy.save!
31
+
32
+ copy.release!
33
+
34
+ expect(source.aasm_state).to eql 'alpha'
35
+ expect(source.aasm.current_state).to eql :alpha
36
+
37
+ source2 = PersistedState.find(source.id)
38
+ expect(source2.reload.aasm_state).to eql 'alpha'
39
+ expect(source2.aasm.current_state).to eql :alpha
40
+
41
+ expect(copy.aasm_state).to eql 'beta'
42
+ expect(copy.aasm.current_state).to eql :beta
43
+ end
44
+ end
25
45
  end
26
46
 
27
47
  describe "writing and persisting the current state" do
@@ -109,7 +109,7 @@ describe 'callbacks for the new DSL' do
109
109
  expect(callback).to receive(:before_event).once.ordered
110
110
  expect(callback).to receive(:event_guard).once.ordered.and_return(true)
111
111
  expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
112
- expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
112
+ expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
113
113
  expect(callback).to receive(:exit_open).once.ordered
114
114
  # expect(callback).to receive(:event_guard).once.ordered.and_return(true)
115
115
  # expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
@@ -117,8 +117,9 @@ describe 'callbacks for the new DSL' do
117
117
  expect(callback).to receive(:after_transition).once.ordered
118
118
  expect(callback).to receive(:before_enter_closed).once.ordered
119
119
  expect(callback).to receive(:enter_closed).once.ordered
120
- expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
121
- expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
120
+ expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
121
+ expect(callback).to receive(:success_transition).once.ordered.and_return(true) # these should be after the state changes
122
+ expect(callback).to receive(:after_exit_open).once.ordered
122
123
  expect(callback).to receive(:after_enter_closed).once.ordered
123
124
  expect(callback).to receive(:after_event).once.ordered
124
125
  end
@@ -142,6 +143,7 @@ describe 'callbacks for the new DSL' do
142
143
  expect(callback).to_not receive(:before_enter_closed)
143
144
  expect(callback).to_not receive(:enter_closed)
144
145
  expect(callback).to_not receive(:aasm_write_state)
146
+ expect(callback).to_not receive(:success_transition)
145
147
  expect(callback).to_not receive(:after_exit_open)
146
148
  expect(callback).to_not receive(:after_enter_closed)
147
149
  expect(callback).to_not receive(:after_event)
@@ -184,6 +186,7 @@ describe 'callbacks for the new DSL' do
184
186
  expect(callback).to_not receive(:before_enter_closed)
185
187
  expect(callback).to_not receive(:enter_closed)
186
188
  expect(callback).to_not receive(:aasm_write_state)
189
+ expect(callback).to_not receive(:success_transition)
187
190
  expect(callback).to_not receive(:after_exit_open)
188
191
  expect(callback).to_not receive(:after_enter_closed)
189
192
  expect(callback).to_not receive(:after_event)
@@ -214,6 +217,7 @@ describe 'callbacks for the new DSL' do
214
217
  expect(callback).to receive(:after).once.ordered
215
218
 
216
219
  expect(callback).to_not receive(:transitioning)
220
+ expect(callback).to_not receive(:success_transition)
217
221
  expect(callback).to_not receive(:before_enter_closed)
218
222
  expect(callback).to_not receive(:enter_closed)
219
223
  expect(callback).to_not receive(:after_enter_closed)
@@ -236,6 +240,7 @@ describe 'callbacks for the new DSL' do
236
240
  expect(callback).to_not receive(:before_enter_closed)
237
241
  expect(callback).to_not receive(:enter_closed)
238
242
  expect(callback).to_not receive(:aasm_write_state)
243
+ expect(callback).to_not receive(:success_transition)
239
244
  expect(callback).to_not receive(:after_exit_open)
240
245
  expect(callback).to_not receive(:after_enter_closed)
241
246
  expect(callback).to_not receive(:after)
@@ -252,14 +257,16 @@ describe 'callbacks for the new DSL' do
252
257
 
253
258
  cb.reset_data
254
259
  cb.close!(:arg1, :arg2)
255
- expect(cb.data).to eql 'before(:arg1,:arg2) before_exit_open(:arg1,:arg2) transition_proc(:arg1,:arg2) before_enter_closed(:arg1,:arg2) aasm_write_state after_exit_open(:arg1,:arg2) after_enter_closed(:arg1,:arg2) after(:arg1,:arg2)'
260
+ expect(cb.data).to eql 'before(:arg1,:arg2) before_exit_open(:arg1,:arg2) transition_proc(:arg1,:arg2) before_enter_closed(:arg1,:arg2) aasm_write_state transition_success(:arg1,:arg2) after_exit_open(:arg1,:arg2) after_enter_closed(:arg1,:arg2) after(:arg1,:arg2)'
256
261
  end
257
262
 
258
263
  it "should call the callbacks given the to-state as argument" do
259
264
  cb = Callbacks::WithStateArg.new
260
265
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
261
266
  expect(cb).to receive(:transition_method).never
267
+ expect(cb).to receive(:success_method).never
262
268
  expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
269
+ expect(cb).to receive(:success_method2).with(:arg1).once.ordered
263
270
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
264
271
  cb.close!(:out_to_lunch, :arg1)
265
272
 
@@ -267,6 +274,7 @@ describe 'callbacks for the new DSL' do
267
274
  some_object = double('some object')
268
275
  expect(cb).to receive(:before_method).with(some_object).once.ordered
269
276
  expect(cb).to receive(:transition_method2).with(some_object).once.ordered
277
+ expect(cb).to receive(:success_method2).with(some_object).once.ordered
270
278
  expect(cb).to receive(:after_method).with(some_object).once.ordered
271
279
  cb.close!(:out_to_lunch, some_object)
272
280
  end
@@ -276,6 +284,8 @@ describe 'callbacks for the new DSL' do
276
284
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
277
285
  expect(cb).to receive(:transition_method).with(:arg1).once.ordered
278
286
  expect(cb).to receive(:transition_method).never
287
+ expect(cb).to receive(:success_method).with(:arg1).once.ordered
288
+ expect(cb).to receive(:success_method).never
279
289
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
280
290
  cb.close!(:arg1)
281
291
 
@@ -284,6 +294,8 @@ describe 'callbacks for the new DSL' do
284
294
  expect(cb).to receive(:before_method).with(some_object).once.ordered
285
295
  expect(cb).to receive(:transition_method).with(some_object).once.ordered
286
296
  expect(cb).to receive(:transition_method).never
297
+ expect(cb).to receive(:success_method).with(some_object).once.ordered
298
+ expect(cb).to receive(:success_method).never
287
299
  expect(cb).to receive(:after_method).with(some_object).once.ordered
288
300
  cb.close!(some_object)
289
301
  end
@@ -299,7 +311,7 @@ describe 'event callbacks' do
299
311
 
300
312
  aasm do
301
313
  event :safe_close, :success => :success_callback, :error => :error_callback do
302
- transitions :to => :closed, :from => [:open]
314
+ transitions :to => :closed, :from => [:open], :success => :transition_success_callback
303
315
  end
304
316
  end
305
317
  end
@@ -6,7 +6,7 @@ describe 'adding an event' do
6
6
  AASM::Core::Event.new(:close_order, state_machine, {:success => :success_callback}) do
7
7
  before :before_callback
8
8
  after :after_callback
9
- transitions :to => :closed, :from => [:open, :received]
9
+ transitions :to => :closed, :from => [:open, :received], success: [:transition_success_callback]
10
10
  end
11
11
  end
12
12
 
@@ -337,6 +337,24 @@ describe 'parametrised events' do
337
337
  expect(pe).to receive(:fix_hair)
338
338
  pe.dress!(:prettying_up)
339
339
  end
340
+
341
+ it 'should call :success transition method with args' do
342
+ pe.wakeup!(:showering)
343
+ expect(pe).to receive(:wear_makeup).with('foundation', 'SPF')
344
+ pe.dress!(:working, 'foundation', 'SPF')
345
+ end
346
+
347
+ it 'should call :success transition proc' do
348
+ pe.wakeup!(:showering)
349
+ expect(pe).to receive(:wear_makeup).with('purple', 'slacks')
350
+ pe.dress!(:dating, 'purple', 'slacks')
351
+ end
352
+
353
+ it 'should call :success transition with an array of methods' do
354
+ pe.wakeup!(:showering)
355
+ expect(pe).to receive(:touch_up_hair)
356
+ pe.dress!(:prettying_up)
357
+ end
340
358
  end
341
359
 
342
360
  describe 'event firing without persistence' do
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe AASM::InvalidTransition do
4
+ it 'should not be lazy detecting originating state' do
5
+ process = ProcessWithNewDsl.new
6
+ expect { process.stop! }.to raise_error do |err|
7
+ process.start
8
+ expect(err.message).to eql("Event 'stop' cannot transition from 'sleeping'. ")
9
+ end
10
+ end
11
+ end
@@ -7,7 +7,7 @@
7
7
  # end
8
8
 
9
9
  # def machines
10
- # AASM::StateMachine.instance_variable_get("@machines")
10
+ # AASM::StateMachineStore.instance_variable_get("@stores")
11
11
  # end
12
12
 
13
13
  # it "should be created without memory leak" do
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'state machine' do
4
+ let(:namespaced) { NamespacedMultipleExample.new }
5
+
6
+ it 'starts with an initial state' do
7
+ expect(namespaced.aasm(:status).current_state).to eq(:unapproved)
8
+ expect(namespaced).to respond_to(:unapproved?)
9
+ expect(namespaced).to be_unapproved
10
+
11
+ expect(namespaced.aasm(:review_status).current_state).to eq(:unapproved)
12
+ expect(namespaced).to respond_to(:review_unapproved?)
13
+ expect(namespaced).to be_review_unapproved
14
+ end
15
+
16
+ it 'allows transitions to other states' do
17
+ expect(namespaced).to respond_to(:approve)
18
+ expect(namespaced).to respond_to(:approve!)
19
+ namespaced.approve!
20
+ expect(namespaced).to respond_to(:approved?)
21
+ expect(namespaced).to be_approved
22
+
23
+ expect(namespaced).to respond_to(:approve_review)
24
+ expect(namespaced).to respond_to(:approve_review!)
25
+ namespaced.approve_review!
26
+ expect(namespaced).to respond_to(:review_approved?)
27
+ expect(namespaced).to be_review_approved
28
+ end
29
+
30
+ it 'denies transitions to other states' do
31
+ expect {namespaced.unapprove}.to raise_error(AASM::InvalidTransition)
32
+ expect {namespaced.unapprove!}.to raise_error(AASM::InvalidTransition)
33
+ namespaced.approve
34
+ expect {namespaced.approve}.to raise_error(AASM::InvalidTransition)
35
+ expect {namespaced.approve!}.to raise_error(AASM::InvalidTransition)
36
+ namespaced.unapprove
37
+
38
+ expect {namespaced.unapprove_review}.to raise_error(AASM::InvalidTransition)
39
+ expect {namespaced.unapprove_review!}.to raise_error(AASM::InvalidTransition)
40
+ namespaced.approve_review
41
+ expect {namespaced.approve_review}.to raise_error(AASM::InvalidTransition)
42
+ expect {namespaced.approve_review!}.to raise_error(AASM::InvalidTransition)
43
+ namespaced.unapprove_review
44
+ end
45
+
46
+ it 'defines constants for each state name' do
47
+ expect(NamespacedMultipleExample::STATE_UNAPPROVED).to eq(:unapproved)
48
+ expect(NamespacedMultipleExample::STATE_APPROVED).to eq(:approved)
49
+
50
+ expect(NamespacedMultipleExample::STATE_REVIEW_UNAPPROVED).to eq(:unapproved)
51
+ expect(NamespacedMultipleExample::STATE_REVIEW_APPROVED).to eq(:approved)
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'warns when overrides a method' do
4
+ module Clumsy
5
+ def self.included base
6
+ base.send :include, AASM
7
+
8
+ base.aasm do
9
+ state :valid
10
+ event(:save) { }
11
+ end
12
+ end
13
+ end
14
+
15
+ describe 'state' do
16
+ class Base
17
+ def valid?; end
18
+ end
19
+ it do
20
+ expect { Base.send :include, Clumsy }.
21
+ to output(/Base: overriding method 'valid\?'!/).to_stderr
22
+ end
23
+ end
24
+
25
+ describe 'event' do
26
+ context 'may?' do
27
+ class Base
28
+ def may_save?; end
29
+ def save!; end
30
+ def save; end
31
+ end
32
+ let(:klass) { Base }
33
+ it do
34
+ expect { Base.send :include, Clumsy }.
35
+ to output(/Base: overriding method 'may_save\?'!/).to_stderr
36
+ expect { Base.send :include, Clumsy }.
37
+ to output(/Base: overriding method 'save!'!/).to_stderr
38
+ expect { Base.send :include, Clumsy }.
39
+ to output(/Base: overriding method 'save'!/).to_stderr
40
+ end
41
+ end
42
+ end
43
+ end
@@ -452,7 +452,7 @@ describe 'transitions with persistence' do
452
452
 
453
453
  it "should only rollback changes in the main transaction not the nested one" do
454
454
  # change configuration to not require new transaction
455
- AASM::StateMachine[MultipleTransactor][:left].config.requires_new_transaction = false
455
+ AASM::StateMachineStore[MultipleTransactor][:left].config.requires_new_transaction = false
456
456
 
457
457
  expect(transactor).to be_sleeping
458
458
  expect(worker.status).to eq('sleeping')
@@ -486,7 +486,7 @@ describe 'transitions with persistence' do
486
486
 
487
487
  it "should only rollback changes in the main transaction not the nested one" do
488
488
  # change configuration to not require new transaction
489
- AASM::StateMachine[Transactor][:default].config.requires_new_transaction = false
489
+ AASM::StateMachineStore[Transactor][:default].config.requires_new_transaction = false
490
490
 
491
491
  expect(transactor).to be_sleeping
492
492
  expect(worker.status).to eq('sleeping')
@@ -71,6 +71,17 @@ describe 'mongoid' do
71
71
 
72
72
  end
73
73
 
74
+ describe "relations object" do
75
+
76
+ it "should load relations object ids" do
77
+ parent = Parent.create
78
+ child_1 = Child.create(parent_id: parent.id)
79
+ child_2 = Child.create(parent_id: parent.id)
80
+ expect(parent.child_ids).to eql [child_1.id, child_2.id]
81
+ end
82
+
83
+ end
84
+
74
85
  rescue LoadError
75
86
  puts "--------------------------------------------------------------------------"
76
87
  puts "Not running Mongoid specs because mongoid gem is not installed!!!"
@@ -0,0 +1,77 @@
1
+
2
+ describe 'redis' do
3
+ begin
4
+ require 'redis-objects'
5
+ require 'logger'
6
+ require 'spec_helper'
7
+
8
+ before(:all) do
9
+ Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
10
+
11
+ @model = Class.new do
12
+ attr_accessor :default
13
+
14
+ include Redis::Objects
15
+ include AASM
16
+
17
+ value :status
18
+
19
+ def id
20
+ 1
21
+ end
22
+
23
+ aasm column: :status
24
+ aasm do
25
+ state :alpha, initial: true
26
+ state :beta
27
+ state :gamma
28
+ event :release do
29
+ transitions from: [:alpha, :beta, :gamma], to: :beta
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "instance methods" do
36
+ let(:model) {@model.new}
37
+
38
+ it "should respond to aasm persistence methods" do
39
+ expect(model).to respond_to(:aasm_read_state)
40
+ expect(model).to respond_to(:aasm_write_state)
41
+ expect(model).to respond_to(:aasm_write_state_without_persistence)
42
+ end
43
+
44
+ it "should return the initial state when new and the aasm field is nil" do
45
+ expect(model.aasm.current_state).to eq(:alpha)
46
+ end
47
+
48
+ it "should return the aasm column when new and the aasm field is not nil" do
49
+ model.status = "beta"
50
+ expect(model.aasm.current_state).to eq(:beta)
51
+ end
52
+
53
+ it "should allow a nil state" do
54
+ model.status = nil
55
+ expect(model.aasm.current_state).to be_nil
56
+ end
57
+ end
58
+
59
+ describe 'subclasses' do
60
+ it "should have the same states as its parent class" do
61
+ expect(Class.new(@model).aasm.states).to eq(@model.aasm.states)
62
+ end
63
+
64
+ it "should have the same events as its parent class" do
65
+ expect(Class.new(@model).aasm.events).to eq(@model.aasm.events)
66
+ end
67
+
68
+ it "should have the same column as its parent even for the new dsl" do
69
+ expect(@model.aasm.attribute_name).to eq(:status)
70
+ expect(Class.new(@model).aasm.attribute_name).to eq(:status)
71
+ end
72
+ end
73
+
74
+ rescue LoadError
75
+ puts "Not running Redis specs because sequel gem is not installed!!!"
76
+ end
77
+ end