aasm 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -1
  3. data/Gemfile +2 -2
  4. data/PLANNED_CHANGES.md +24 -4
  5. data/README.md +75 -5
  6. data/lib/aasm/aasm.rb +50 -36
  7. data/lib/aasm/base.rb +36 -18
  8. data/lib/aasm/core/event.rb +6 -5
  9. data/lib/aasm/core/state.rb +3 -2
  10. data/lib/aasm/core/transition.rb +5 -4
  11. data/lib/aasm/errors.rb +7 -4
  12. data/lib/aasm/instance_base.rb +14 -13
  13. data/lib/aasm/localizer.rb +1 -1
  14. data/lib/aasm/persistence/active_record_persistence.rb +41 -66
  15. data/lib/aasm/persistence/base.rb +7 -7
  16. data/lib/aasm/persistence/mongo_mapper_persistence.rb +34 -51
  17. data/lib/aasm/persistence/mongoid_persistence.rb +15 -36
  18. data/lib/aasm/persistence/plain_persistence.rb +8 -7
  19. data/lib/aasm/persistence/sequel_persistence.rb +12 -10
  20. data/lib/aasm/state_machine.rb +11 -6
  21. data/lib/aasm/version.rb +1 -1
  22. data/spec/database.rb +27 -1
  23. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
  24. data/spec/models/active_record/complex_active_record_example.rb +33 -0
  25. data/spec/models/active_record/derivate_new_dsl.rb +4 -0
  26. data/spec/models/active_record/false_state.rb +18 -0
  27. data/spec/models/active_record/gate.rb +20 -0
  28. data/spec/models/active_record/no_direct_assignment.rb +11 -0
  29. data/spec/models/active_record/no_scope.rb +11 -0
  30. data/spec/models/active_record/provided_and_persisted_state.rb +3 -3
  31. data/spec/models/active_record/simple_new_dsl.rb +9 -0
  32. data/spec/models/active_record/thief.rb +15 -0
  33. data/spec/models/active_record/with_enum.rb +20 -0
  34. data/spec/models/active_record/with_false_enum.rb +16 -0
  35. data/spec/models/active_record/with_true_enum.rb +20 -0
  36. data/spec/models/basic_two_state_machines_example.rb +25 -0
  37. data/spec/models/callbacks/basic_multiple.rb +75 -0
  38. data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
  39. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
  40. data/spec/models/callbacks/private_method_multiple.rb +44 -0
  41. data/spec/models/callbacks/with_args_multiple.rb +61 -0
  42. data/spec/models/callbacks/{with_state_args.rb → with_state_arg.rb} +0 -0
  43. data/spec/models/callbacks/with_state_arg_multiple.rb +26 -0
  44. data/spec/models/complex_example.rb +134 -0
  45. data/spec/models/conversation.rb +47 -1
  46. data/spec/models/foo.rb +57 -0
  47. data/spec/models/foo_callback_multiple.rb +45 -0
  48. data/spec/models/guardian_multiple.rb +48 -0
  49. data/spec/models/initial_state_proc.rb +16 -0
  50. data/spec/models/invalid_persistor.rb +15 -0
  51. data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +37 -0
  52. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +11 -0
  53. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +12 -0
  54. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +13 -0
  55. data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
  56. data/spec/models/mongoid/no_scope_mongoid.rb +11 -0
  57. data/spec/models/mongoid/simple_mongoid.rb +12 -0
  58. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +13 -0
  59. data/spec/models/no_initial_state.rb +13 -0
  60. data/spec/models/parametrised_event.rb +1 -1
  61. data/spec/models/parametrised_event_multiple.rb +29 -0
  62. data/spec/models/provided_state.rb +3 -3
  63. data/spec/models/sequel/complex_sequel_example.rb +45 -0
  64. data/spec/models/sequel/sequel_multiple.rb +25 -0
  65. data/spec/models/sequel/sequel_simple.rb +25 -0
  66. data/spec/models/simple_multiple_example.rb +30 -0
  67. data/spec/models/sub_class.rb +4 -0
  68. data/spec/models/sub_class_with_more_states.rb +11 -0
  69. data/spec/models/super_class.rb +28 -0
  70. data/spec/models/transactor.rb +27 -0
  71. data/spec/models/valid_state_name.rb +12 -0
  72. data/spec/models/validator.rb +39 -0
  73. data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
  74. data/spec/unit/callback_multiple_spec.rb +295 -0
  75. data/spec/unit/callbacks_spec.rb +1 -1
  76. data/spec/unit/complex_multiple_example_spec.rb +99 -0
  77. data/spec/unit/edge_cases_spec.rb +16 -0
  78. data/spec/unit/event_multiple_spec.rb +73 -0
  79. data/spec/unit/event_spec.rb +11 -6
  80. data/spec/unit/guard_multiple_spec.rb +60 -0
  81. data/spec/unit/initial_state_multiple_spec.rb +15 -0
  82. data/spec/unit/inspection_multiple_spec.rb +201 -0
  83. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +560 -0
  84. data/spec/unit/persistence/active_record_persistence_spec.rb +17 -12
  85. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +146 -0
  86. data/spec/unit/persistence/{mongo_mapper_persistance_spec.rb → mongo_mapper_persistence_spec.rb} +7 -49
  87. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +127 -0
  88. data/spec/unit/persistence/mongoid_persistence_spec.rb +79 -0
  89. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +153 -0
  90. data/spec/unit/persistence/sequel_persistence_spec.rb +7 -24
  91. data/spec/unit/reloading_spec.rb +1 -1
  92. data/spec/unit/simple_multiple_example_spec.rb +63 -0
  93. data/spec/unit/state_spec.rb +3 -1
  94. data/spec/unit/subclassing_multiple_spec.rb +39 -0
  95. data/spec/unit/transition_spec.rb +31 -22
  96. metadata +73 -9
  97. data/spec/unit/persistence/mongoid_persistance_spec.rb +0 -146
@@ -32,34 +32,11 @@ module AASM
32
32
  #
33
33
  def self.included(base)
34
34
  base.send(:include, AASM::Persistence::Base)
35
- base.extend AASM::Persistence::MongoidPersistence::ClassMethods
36
35
  base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
37
36
 
38
37
  base.after_initialize :aasm_ensure_initial_state
39
38
  end
40
39
 
41
- module ClassMethods
42
-
43
- def find_in_state(number, state, *args)
44
- with_state_scope state do
45
- find(number, *args)
46
- end
47
- end
48
-
49
- def count_in_state(state, *args)
50
- with_state_scope state do
51
- count(*args)
52
- end
53
- end
54
-
55
- def with_state_scope(state)
56
- with_scope where(aasm.attribute_name.to_sym => state.to_s) do
57
- yield if block_given?
58
- end
59
- end
60
-
61
- end
62
-
63
40
  module InstanceMethods
64
41
 
65
42
  # Writes <tt>state</tt> to the state column and persists it to the database
@@ -72,12 +49,12 @@ module AASM
72
49
  # Foo.find(1).aasm.current_state # => :closed
73
50
  #
74
51
  # NOTE: intended to be called from an event
75
- def aasm_write_state(state)
76
- old_value = read_attribute(self.class.aasm.attribute_name)
77
- write_attribute(self.class.aasm.attribute_name, state.to_s)
52
+ def aasm_write_state(state, name=:default)
53
+ old_value = read_attribute(self.class.aasm(name).attribute_name)
54
+ write_attribute(self.class.aasm(name).attribute_name, state.to_s)
78
55
 
79
56
  unless self.save(:validate => false)
80
- write_attribute(self.class.aasm.attribute_name, old_value)
57
+ write_attribute(self.class.aasm(name).attribute_name, old_value)
81
58
  return false
82
59
  end
83
60
 
@@ -96,8 +73,8 @@ module AASM
96
73
  # Foo.find(1).aasm.current_state # => :closed
97
74
  #
98
75
  # NOTE: intended to be called from an event
99
- def aasm_write_state_without_persistence(state)
100
- write_attribute(self.class.aasm.attribute_name, state.to_s)
76
+ def aasm_write_state_without_persistence(state, name=:default)
77
+ write_attribute(self.class.aasm(name).attribute_name, state.to_s)
101
78
  end
102
79
 
103
80
  private
@@ -118,16 +95,18 @@ module AASM
118
95
  # foo.aasm_state # => nil
119
96
  #
120
97
  def aasm_ensure_initial_state
121
- send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
98
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
99
+ send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
100
+ end
122
101
  end
123
102
  end # InstanceMethods
124
103
 
125
- module NamedScopeMethods
126
- def aasm_state_with_named_scope name, options = {}
127
- aasm_state_without_named_scope name, options
128
- self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
129
- end
130
- end
104
+ # module NamedScopeMethods
105
+ # def aasm_state_with_named_scope name, options = {}
106
+ # aasm_state_without_named_scope name, options
107
+ # self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
108
+ # end
109
+ # end
131
110
  end
132
111
  end # Persistence
133
112
  end # AASM
@@ -2,21 +2,22 @@ module AASM
2
2
  module Persistence
3
3
  module PlainPersistence
4
4
 
5
- def aasm_read_state
6
- # all the following lines behave like @current_state ||= aasm.enter_initial_state
7
- current = aasm.instance_variable_get("@current_state")
5
+ # may be overwritten by persistence mixins
6
+ def aasm_read_state(name=:default)
7
+ # all the following lines behave like @current_state ||= aasm(name).enter_initial_state
8
+ current = aasm(name).instance_variable_get("@current_state_#{name}")
8
9
  return current if current
9
- aasm.instance_variable_set("@current_state", aasm.enter_initial_state)
10
+ aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
10
11
  end
11
12
 
12
13
  # may be overwritten by persistence mixins
13
- def aasm_write_state(new_state)
14
+ def aasm_write_state(new_state, name=:default)
14
15
  true
15
16
  end
16
17
 
17
18
  # may be overwritten by persistence mixins
18
- def aasm_write_state_without_persistence(new_state)
19
- true
19
+ def aasm_write_state_without_persistence(new_state, name=:default)
20
+ aasm(name).instance_variable_set("@current_state_#{name}", new_state)
20
21
  end
21
22
 
22
23
  end
@@ -44,10 +44,10 @@ module AASM
44
44
  # NOTE: intended to be called from an event
45
45
  #
46
46
  # This allows for nil aasm states - be sure to add validation to your model
47
- def aasm_read_state
48
- state = send(self.class.aasm.attribute_name)
47
+ def aasm_read_state(name=:default)
48
+ state = send(self.class.aasm(name).attribute_name)
49
49
  if new? && state.to_s.strip.empty?
50
- aasm.determine_state_name(self.class.aasm.initial_state)
50
+ aasm(name).determine_state_name(self.class.aasm(name).initial_state)
51
51
  elsif state.nil?
52
52
  nil
53
53
  else
@@ -71,9 +71,11 @@ module AASM
71
71
  # foo.aasm_state # => nil
72
72
  #
73
73
  def aasm_ensure_initial_state
74
- aasm.enter_initial_state if
75
- (new? || values.key?(self.class.aasm.attribute_name)) &&
76
- send(self.class.aasm.attribute_name).to_s.strip.empty?
74
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
75
+ aasm(state_machine_name).enter_initial_state if
76
+ (new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
77
+ send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
78
+ end
77
79
  end
78
80
 
79
81
  # Writes <tt>state</tt> to the state column and persists it to the database
@@ -85,8 +87,8 @@ module AASM
85
87
  # Foo[1].aasm.current_state # => :closed
86
88
  #
87
89
  # NOTE: intended to be called from an event
88
- def aasm_write_state state
89
- aasm_column = self.class.aasm.attribute_name
90
+ def aasm_write_state state, name=:default
91
+ aasm_column = self.class.aasm(name).attribute_name
90
92
  update_only({aasm_column => state.to_s}, aasm_column)
91
93
  end
92
94
 
@@ -102,8 +104,8 @@ module AASM
102
104
  # Foo[1].aasm.current_state # => :closed
103
105
  #
104
106
  # NOTE: intended to be called from an event
105
- def aasm_write_state_without_persistence state
106
- send("#{self.class.aasm.attribute_name}=", state.to_s)
107
+ def aasm_write_state_without_persistence state, name=:default
108
+ send("#{self.class.aasm(name).attribute_name}=", state.to_s)
107
109
  end
108
110
  end
109
111
  end
@@ -10,13 +10,14 @@ module AASM
10
10
  (@machines ||= {})[klass.to_s] = machine
11
11
  end
12
12
 
13
- attr_accessor :states, :events, :initial_state, :config
13
+ attr_accessor :states, :events, :initial_state, :config, :name
14
14
 
15
- def initialize
15
+ def initialize(name)
16
16
  @initial_state = nil
17
17
  @states = []
18
18
  @events = {}
19
19
  @config = AASM::Configuration.new
20
+ @name = name
20
21
  end
21
22
 
22
23
  # called internally by Ruby 1.9 after clone()
@@ -26,13 +27,17 @@ module AASM
26
27
  @events = @events.dup
27
28
  end
28
29
 
29
- def add_state(name, klass, options)
30
- set_initial_state(name, options)
30
+ def add_state(state_name, klass, options)
31
+ set_initial_state(state_name, options)
31
32
 
32
33
  # allow reloading, extending or redefining a state
33
- @states.delete(name) if @states.include?(name)
34
+ @states.delete(state_name) if @states.include?(state_name)
34
35
 
35
- @states << AASM::Core::State.new(name, klass, options)
36
+ @states << AASM::Core::State.new(state_name, klass, self, options)
37
+ end
38
+
39
+ def add_event(name, options, &block)
40
+ @events[name] = AASM::Core::Event.new(name, self, options, &block)
36
41
  end
37
42
 
38
43
  private
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "4.2.0"
2
+ VERSION = "4.3.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  ActiveRecord::Migration.suppress_messages do
2
- %w{gates readers writers transients simples no_scopes no_direct_assignments thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums false_states}.each do |table_name|
2
+ %w{gates multiple_gates readers writers transients simples no_scopes multiple_no_scopes no_direct_assignments multiple_no_direct_assignments thieves multiple_thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums false_states multiple_with_enums multiple_with_true_enums multiple_with_false_enums multiple_false_states}.each do |table_name|
3
3
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
4
4
  t.string "aasm_state"
5
5
  end
@@ -8,17 +8,34 @@ ActiveRecord::Migration.suppress_messages do
8
8
  ActiveRecord::Migration.create_table "simple_new_dsls", :force => true do |t|
9
9
  t.string "status"
10
10
  end
11
+ ActiveRecord::Migration.create_table "multiple_simple_new_dsls", :force => true do |t|
12
+ t.string "status"
13
+ end
14
+
15
+ ActiveRecord::Migration.create_table "complex_active_record_examples", :force => true do |t|
16
+ t.string "left"
17
+ t.string "right"
18
+ end
11
19
 
12
20
  ActiveRecord::Migration.create_table "validators", :force => true do |t|
13
21
  t.string "name"
14
22
  t.string "status"
15
23
  end
24
+ ActiveRecord::Migration.create_table "multiple_validators", :force => true do |t|
25
+ t.string "name"
26
+ t.string "status"
27
+ end
16
28
 
17
29
  ActiveRecord::Migration.create_table "transactors", :force => true do |t|
18
30
  t.string "name"
19
31
  t.string "status"
20
32
  t.integer "worker_id"
21
33
  end
34
+ ActiveRecord::Migration.create_table "multiple_transactors", :force => true do |t|
35
+ t.string "name"
36
+ t.string "status"
37
+ t.integer "worker_id"
38
+ end
22
39
 
23
40
  ActiveRecord::Migration.create_table "workers", :force => true do |t|
24
41
  t.string "name"
@@ -29,9 +46,18 @@ ActiveRecord::Migration.suppress_messages do
29
46
  t.string "name"
30
47
  t.string "status"
31
48
  end
49
+ ActiveRecord::Migration.create_table "multiple_invalid_persistors", :force => true do |t|
50
+ t.string "name"
51
+ t.string "status"
52
+ end
32
53
 
33
54
  ActiveRecord::Migration.create_table "fathers", :force => true do |t|
34
55
  t.string "aasm_state"
35
56
  t.string "type"
36
57
  end
58
+
59
+ ActiveRecord::Migration.create_table "basic_active_record_two_state_machines_examples", :force => true do |t|
60
+ t.string "search"
61
+ t.string "sync"
62
+ end
37
63
  end
@@ -0,0 +1,25 @@
1
+ class BasicActiveRecordTwoStateMachinesExample < ActiveRecord::Base
2
+ include AASM
3
+
4
+ aasm :search do
5
+ state :initialised, :initial => true
6
+ state :queried
7
+ state :requested
8
+
9
+ event :query do
10
+ transitions :from => [:initialised, :requested], :to => :queried
11
+ end
12
+ event :request do
13
+ transitions :from => :queried, :to => :requested
14
+ end
15
+ end
16
+
17
+ aasm :sync do
18
+ state :unsynced, :initial => true
19
+ state :synced
20
+
21
+ event :synchronise do
22
+ transitions :from => :unsynced, :to => :synced
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ class ComplexActiveRecordExample < ActiveRecord::Base
2
+ include AASM
3
+
4
+ aasm :left, :column => 'left' do
5
+ state :one, :initial => true
6
+ state :two
7
+ state :three
8
+
9
+ event :increment do
10
+ transitions :from => :one, :to => :two
11
+ transitions :from => :two, :to => :three
12
+ end
13
+ event :reset do
14
+ transitions :from => :three, :to => :one
15
+ end
16
+ end
17
+
18
+ aasm :right, :column => 'right' do
19
+ state :alpha, :initial => true
20
+ state :beta
21
+ state :gamma
22
+
23
+ event :level_up do
24
+ transitions :from => :alpha, :to => :beta
25
+ transitions :from => :beta, :to => :gamma
26
+ end
27
+ event :level_down do
28
+ transitions :from => :gamma, :to => :beta
29
+ transitions :from => :beta, :to => :alpha
30
+ end
31
+ end
32
+
33
+ end
@@ -1,3 +1,7 @@
1
1
  require_relative 'simple_new_dsl'
2
+
2
3
  class DerivateNewDsl < SimpleNewDsl
3
4
  end
5
+
6
+ class MultipleDerivateNewDsl < MultipleSimpleNewDsl
7
+ end
@@ -15,3 +15,21 @@ class FalseState < ActiveRecord::Base
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ class MultipleFalseState < ActiveRecord::Base
20
+ include AASM
21
+
22
+ def initialize(*args)
23
+ super
24
+ self.aasm_state = false
25
+ end
26
+
27
+ aasm :left do
28
+ state :opened
29
+ state :closed
30
+
31
+ event :view do
32
+ transitions :to => :read, :from => [:needs_attention]
33
+ end
34
+ end
35
+ end
@@ -17,3 +17,23 @@ class Gate < ActiveRecord::Base
17
17
  end
18
18
  end
19
19
  end
20
+
21
+ class MultipleGate < ActiveRecord::Base
22
+ include AASM
23
+
24
+ # Fake this column for testing purposes
25
+ # attr_accessor :aasm_state
26
+
27
+ def value
28
+ 'value'
29
+ end
30
+
31
+ aasm :left, :column => :aasm_state do
32
+ state :opened
33
+ state :closed
34
+
35
+ event :view do
36
+ transitions :to => :read, :from => [:needs_attention]
37
+ end
38
+ end
39
+ end
@@ -8,3 +8,14 @@ class NoDirectAssignment < ActiveRecord::Base
8
8
  end
9
9
  end
10
10
  end
11
+
12
+ class MultipleNoDirectAssignment < ActiveRecord::Base
13
+ include AASM
14
+ aasm :left, :column => :aasm_state, :no_direct_assignment => true do
15
+ state :pending, :initial => true
16
+ state :running
17
+ event :run do
18
+ transitions :from => :pending, :to => :running
19
+ end
20
+ end
21
+ end
@@ -8,3 +8,14 @@ class NoScope < ActiveRecord::Base
8
8
  end
9
9
  end
10
10
  end
11
+
12
+ class MultipleNoScope < ActiveRecord::Base
13
+ include AASM
14
+ aasm :left, :column => :aasm_state, :create_scopes => false do
15
+ state :pending, :initial => true
16
+ state :running
17
+ event :run do
18
+ transitions :from => :pending, :to => :running
19
+ end
20
+ end
21
+ end
@@ -10,15 +10,15 @@ class ProvidedAndPersistedState < ActiveRecord::Base
10
10
  end
11
11
  end
12
12
 
13
- def aasm_read_state
13
+ def aasm_read_state(*args)
14
14
  :gamma
15
15
  end
16
16
 
17
- def aasm_write_state(new_state)
17
+ def aasm_write_state(new_state, *args)
18
18
  @persisted_store = new_state
19
19
  end
20
20
 
21
- def aasm_write_state_without_persistence(new_state)
21
+ def aasm_write_state_without_persistence(new_state, *args)
22
22
  @transient_store = new_state
23
23
  end
24
24
  end
@@ -6,3 +6,12 @@ class SimpleNewDsl < ActiveRecord::Base
6
6
  state :new
7
7
  end
8
8
  end
9
+
10
+ class MultipleSimpleNewDsl < ActiveRecord::Base
11
+ include AASM
12
+ aasm :left, :column => :status
13
+ aasm :left do
14
+ state :unknown_scope
15
+ state :new
16
+ end
17
+ end