aasm 4.9.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
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