aasm 4.2.0 → 4.3.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 (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