aasm 3.0.24 → 3.4.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.travis.yml +29 -4
  4. data/CHANGELOG.md +56 -0
  5. data/Gemfile +10 -1
  6. data/LICENSE +1 -1
  7. data/README.md +151 -20
  8. data/aasm.gemspec +5 -6
  9. data/gemfiles/rails_3.2.gemfile +13 -0
  10. data/gemfiles/rails_4.0.gemfile +16 -0
  11. data/gemfiles/rails_4.1.gemfile +16 -0
  12. data/lib/aasm/aasm.rb +36 -32
  13. data/lib/aasm/base.rb +49 -31
  14. data/lib/aasm/event.rb +28 -17
  15. data/lib/aasm/instance_base.rb +9 -4
  16. data/lib/aasm/localizer.rb +1 -1
  17. data/lib/aasm/persistence/active_record_persistence.rb +65 -16
  18. data/lib/aasm/persistence/base.rb +10 -14
  19. data/lib/aasm/persistence/mongoid_persistence.rb +10 -8
  20. data/lib/aasm/persistence/sequel_persistence.rb +108 -0
  21. data/lib/aasm/persistence.rb +3 -0
  22. data/lib/aasm/state.rb +4 -3
  23. data/lib/aasm/state_machine.rb +18 -10
  24. data/lib/aasm/transition.rb +13 -6
  25. data/lib/aasm/version.rb +1 -1
  26. data/lib/aasm.rb +0 -3
  27. data/spec/database.rb +33 -0
  28. data/spec/models/double_definer.rb +21 -0
  29. data/spec/models/foo.rb +2 -1
  30. data/spec/models/guardian.rb +48 -0
  31. data/spec/models/mongoid/no_scope_mongoid.rb +1 -1
  32. data/spec/models/mongoid/simple_mongoid.rb +5 -4
  33. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +1 -1
  34. data/spec/models/not_auto_loaded/process.rb +10 -8
  35. data/spec/models/persistence.rb +5 -13
  36. data/spec/spec_helper.rb +1 -1
  37. data/spec/unit/api_spec.rb +12 -12
  38. data/spec/unit/callbacks_spec.rb +29 -45
  39. data/spec/unit/complex_example_spec.rb +24 -15
  40. data/spec/unit/event_naming_spec.rb +24 -0
  41. data/spec/unit/event_spec.rb +124 -76
  42. data/spec/unit/guard_spec.rb +60 -0
  43. data/spec/unit/initial_state_spec.rb +4 -5
  44. data/spec/unit/inspection_spec.rb +42 -53
  45. data/spec/unit/localizer_spec.rb +22 -18
  46. data/spec/unit/memory_leak_spec.rb +2 -2
  47. data/spec/unit/new_dsl_spec.rb +2 -2
  48. data/spec/unit/persistence/active_record_persistence_spec.rb +357 -89
  49. data/spec/unit/persistence/mongoid_persistance_spec.rb +102 -81
  50. data/spec/unit/persistence/sequel_persistence_spec.rb +103 -0
  51. data/spec/unit/reloading_spec.rb +15 -0
  52. data/spec/unit/simple_example_spec.rb +20 -21
  53. data/spec/unit/state_spec.rb +16 -16
  54. data/spec/unit/subclassing_spec.rb +8 -8
  55. data/spec/unit/transition_spec.rb +59 -44
  56. metadata +38 -96
  57. data/lib/aasm/deprecated/aasm.rb +0 -15
  58. data/spec/models/callback_old_dsl.rb +0 -41
  59. data/spec/schema.rb +0 -35
@@ -0,0 +1,108 @@
1
+ module AASM
2
+ module Persistence
3
+ module SequelPersistence
4
+ def self.included(base)
5
+ base.send(:include, AASM::Persistence::Base)
6
+ base.send(:include, AASM::Persistence::SequelPersistence::InstanceMethods)
7
+ end
8
+
9
+ module InstanceMethods
10
+ def before_validation
11
+ aasm_ensure_initial_state
12
+ super
13
+ end
14
+
15
+ def before_create
16
+ aasm_ensure_initial_state
17
+ super
18
+ end
19
+
20
+ # Returns the value of the aasm_column - called from <tt>aasm.current_state</tt>
21
+ #
22
+ # If it's a new record, and the aasm state column is blank it returns the initial state
23
+ #
24
+ # class Foo < Sequel::Model
25
+ # include AASM
26
+ # aasm :column => :status do
27
+ # state :opened
28
+ # state :closed
29
+ # end
30
+ # end
31
+ #
32
+ # foo = Foo.new
33
+ # foo.current_state # => :opened
34
+ # foo.close
35
+ # foo.current_state # => :closed
36
+ #
37
+ # foo = Foo[1]
38
+ # foo.current_state # => :opened
39
+ # foo.aasm_state = nil
40
+ # foo.current_state # => nil
41
+ #
42
+ # NOTE: intended to be called from an event
43
+ #
44
+ # This allows for nil aasm states - be sure to add validation to your model
45
+ def aasm_read_state
46
+ state = send(self.class.aasm_column)
47
+ if new? && state.to_s.strip.empty?
48
+ aasm.determine_state_name(self.class.aasm.initial_state)
49
+ elsif state.nil?
50
+ nil
51
+ else
52
+ state.to_sym
53
+ end
54
+ end
55
+
56
+ # Ensures that if the aasm_state column is nil and the record is new
57
+ # that the initial state gets populated before validation on create
58
+ #
59
+ # foo = Foo.new
60
+ # foo.aasm_state # => nil
61
+ # foo.valid?
62
+ # foo.aasm_state # => "open" (where :open is the initial state)
63
+ #
64
+ #
65
+ # foo = Foo.find(:first)
66
+ # foo.aasm_state # => 1
67
+ # foo.aasm_state = nil
68
+ # foo.valid?
69
+ # foo.aasm_state # => nil
70
+ #
71
+ def aasm_ensure_initial_state
72
+ aasm.enter_initial_state if
73
+ send(self.class.aasm_column).to_s.strip.empty?
74
+ end
75
+
76
+ # Writes <tt>state</tt> to the state column and persists it to the database
77
+ #
78
+ # foo = Foo[1]
79
+ # foo.aasm.current_state # => :opened
80
+ # foo.close!
81
+ # foo.aasm.current_state # => :closed
82
+ # Foo[1].aasm.current_state # => :closed
83
+ #
84
+ # NOTE: intended to be called from an event
85
+ def aasm_write_state state
86
+ aasm_column = self.class.aasm_column
87
+ update_only({aasm_column => state.to_s}, aasm_column)
88
+ end
89
+
90
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
91
+ #
92
+ # foo = Foo[1]
93
+ # foo.aasm.current_state # => :opened
94
+ # foo.close
95
+ # foo.aasm.current_state # => :closed
96
+ # Foo[1].aasm.current_state # => :opened
97
+ # foo.save
98
+ # foo.aasm.current_state # => :closed
99
+ # Foo[1].aasm.current_state # => :closed
100
+ #
101
+ # NOTE: intended to be called from an event
102
+ def aasm_write_state_without_persistence state
103
+ send("#{self.class.aasm_column}=", state.to_s)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -12,6 +12,9 @@ module AASM
12
12
  elsif hierarchy.include?("Mongoid::Document")
13
13
  require_files_for(:mongoid)
14
14
  base.send(:include, AASM::Persistence::MongoidPersistence)
15
+ elsif hierarchy.include?("Sequel::Model")
16
+ require_files_for(:sequel)
17
+ base.send(:include, AASM::Persistence::SequelPersistence)
15
18
  end
16
19
  end
17
20
 
data/lib/aasm/state.rb CHANGED
@@ -2,9 +2,9 @@ module AASM
2
2
  class State
3
3
  attr_reader :name, :options
4
4
 
5
- def initialize(name, clazz, options={})
5
+ def initialize(name, klass, options={})
6
6
  @name = name
7
- @clazz = clazz
7
+ @klass = klass
8
8
  update(options)
9
9
  end
10
10
 
@@ -48,8 +48,9 @@ module AASM
48
48
  end
49
49
 
50
50
  def localized_name
51
- AASM::Localizer.new.human_state_name(@clazz, self)
51
+ AASM::Localizer.new.human_state_name(@klass, self)
52
52
  end
53
+ alias human_name localized_name
53
54
 
54
55
  def for_select
55
56
  [display_name, name.to_s]
@@ -2,20 +2,17 @@ module AASM
2
2
  class StateMachine
3
3
 
4
4
  # the following two methods provide the storage of all state machines
5
- def self.[](clazz)
6
- (@machines ||= {})[clazz.to_s]
5
+ def self.[](klass)
6
+ (@machines ||= {})[klass.to_s]
7
7
  end
8
8
 
9
- def self.[]=(clazz, machine)
10
- (@machines ||= {})[clazz.to_s] = machine
9
+ def self.[]=(klass, machine)
10
+ (@machines ||= {})[klass.to_s] = machine
11
11
  end
12
12
 
13
13
  attr_accessor :states, :events, :initial_state, :config
14
- attr_reader :name
15
14
 
16
- # QUESTION: what's the name for? [alto, 2012-11-28]
17
- def initialize(name)
18
- @name = name
15
+ def initialize
19
16
  @initial_state = nil
20
17
  @states = []
21
18
  @events = {}
@@ -29,8 +26,19 @@ module AASM
29
26
  @events = @events.dup
30
27
  end
31
28
 
32
- def add_state(name, clazz, options)
33
- @states << AASM::State.new(name, clazz, options) unless @states.include?(name)
29
+ def add_state(name, klass, options)
30
+ set_initial_state(name, options)
31
+
32
+ # allow reloading, extending or redefining a state
33
+ @states.delete(name) if @states.include?(name)
34
+
35
+ @states << AASM::State.new(name, klass, options)
36
+ end
37
+
38
+ private
39
+
40
+ def set_initial_state(name, options)
41
+ @initial_state = name if options[:initial] || !initial_state
34
42
  end
35
43
 
36
44
  end # StateMachine
@@ -4,20 +4,24 @@ module AASM
4
4
  alias_method :options, :opts
5
5
 
6
6
  def initialize(opts)
7
- @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
7
+ @from = opts[:from]
8
+ @to = opts[:to]
9
+ @guards = Array(opts[:guard] || opts[:guards])
10
+ @on_transition = opts[:on_transition]
8
11
  @opts = opts
9
12
  end
10
13
 
11
14
  # TODO: should be named allowed? or similar
12
15
  def perform(obj, *args)
13
- case @guard
16
+ @guards.each do |guard|
17
+ case guard
14
18
  when Symbol, String
15
- obj.send(@guard, *args)
19
+ return false unless obj.send(guard, *args)
16
20
  when Proc
17
- @guard.call(obj, *args)
18
- else
19
- true
21
+ return false unless guard.call(obj, *args)
22
+ end
20
23
  end
24
+ true
21
25
  end
22
26
 
23
27
  def execute(obj, *args)
@@ -37,6 +41,9 @@ module AASM
37
41
  private
38
42
 
39
43
  def _execute(obj, on_transition, *args)
44
+ obj.aasm.from_state = @from if obj.aasm.respond_to?(:from_state=)
45
+ obj.aasm.to_state = @to if obj.aasm.respond_to?(:to_state=)
46
+
40
47
  case on_transition
41
48
  when Proc
42
49
  on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
data/lib/aasm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "3.0.24"
2
+ VERSION = "3.4.0"
3
3
  end
data/lib/aasm.rb CHANGED
@@ -13,6 +13,3 @@ require 'ostruct'
13
13
  persistence
14
14
  aasm
15
15
  ).each { |file| require File.join(File.dirname(__FILE__), 'aasm', file) }
16
-
17
- # load the deprecated methods and modules
18
- Dir[File.join(File.dirname(__FILE__), 'aasm', 'deprecated', '*.rb')].sort.each { |f| require File.expand_path(f) }
data/spec/database.rb ADDED
@@ -0,0 +1,33 @@
1
+ ActiveRecord::Migration.suppress_messages do
2
+ %w{gates readers writers transients simples simple_new_dsls no_scopes thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
3
+ ActiveRecord::Migration.create_table table_name, :force => true do |t|
4
+ t.string "aasm_state"
5
+ end
6
+ end
7
+
8
+ ActiveRecord::Migration.create_table "validators", :force => true do |t|
9
+ t.string "name"
10
+ t.string "status"
11
+ end
12
+
13
+ ActiveRecord::Migration.create_table "transactors", :force => true do |t|
14
+ t.string "name"
15
+ t.string "status"
16
+ t.integer "worker_id"
17
+ end
18
+
19
+ ActiveRecord::Migration.create_table "workers", :force => true do |t|
20
+ t.string "name"
21
+ t.string "status"
22
+ end
23
+
24
+ ActiveRecord::Migration.create_table "invalid_persistors", :force => true do |t|
25
+ t.string "name"
26
+ t.string "status"
27
+ end
28
+
29
+ ActiveRecord::Migration.create_table "fathers", :force => true do |t|
30
+ t.string "aasm_state"
31
+ t.string "type"
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ class DoubleDefiner
2
+ include AASM
3
+
4
+ aasm do
5
+ state :started
6
+ state :finished
7
+
8
+ event :finish do
9
+ transitions :from => :started, :to => :finished
10
+ end
11
+
12
+ # simulating a reload
13
+ state :finished, :enter => :do_enter
14
+ event :finish do
15
+ transitions :from => :started, :to => :finished, :on_transition => :do_on_transition
16
+ end
17
+ end
18
+
19
+ def do_enter; end
20
+ def do_on_transition; end
21
+ end
data/spec/models/foo.rb CHANGED
@@ -3,13 +3,14 @@ class Foo
3
3
  aasm do
4
4
  state :open, :initial => true, :exit => :exit
5
5
  state :closed, :enter => :enter
6
+ state :final
6
7
 
7
8
  event :close, :success => :success_callback do
8
9
  transitions :from => [:open], :to => [:closed]
9
10
  end
10
11
 
11
12
  event :null do
12
- transitions :from => [:open], :to => :closed, :guard => :always_false
13
+ transitions :from => [:open], :to => [:closed, :final], :guard => :always_false
13
14
  end
14
15
  end
15
16
 
@@ -0,0 +1,48 @@
1
+ class Guardian
2
+ include AASM
3
+
4
+ aasm do
5
+ state :alpha, :initial => true
6
+ state :beta
7
+
8
+ event :use_one_guard_that_succeeds do
9
+ transitions :from => :alpha, :to => :beta, :guard => :succeed
10
+ end
11
+ event :use_one_guard_that_fails do
12
+ transitions :from => :alpha, :to => :beta, :guard => :fail
13
+ end
14
+
15
+ event :use_guards_that_succeed do
16
+ transitions :from => :alpha, :to => :beta, :guards => [:succeed, :another_succeed]
17
+ end
18
+ event :use_guards_where_the_first_fails do
19
+ transitions :from => :alpha, :to => :beta, :guards => [:succeed, :fail]
20
+ end
21
+ event :use_guards_where_the_second_fails do
22
+ transitions :from => :alpha, :to => :beta, :guards => [:fail, :succeed]
23
+ end
24
+
25
+ event :use_event_guards_that_succeed, :guards => [:succeed, :another_succeed] do
26
+ transitions :from => :alpha, :to => :beta
27
+ end
28
+ event :use_event_and_transition_guards_that_succeed, :guards => [:succeed, :another_succeed] do
29
+ transitions :from => :alpha, :to => :beta, :guards => [:more_succeed]
30
+ end
31
+ event :use_event_guards_where_the_first_fails, :guards => [:succeed, :fail] do
32
+ transitions :from => :alpha, :to => :beta
33
+ end
34
+ event :use_event_guards_where_the_second_fails, :guards => [:fail, :succeed] do
35
+ transitions :from => :alpha, :to => :beta, :guard => :another_succeed
36
+ end
37
+ event :use_event_and_transition_guards_where_third_fails, :guards => [:succeed, :another_succeed] do
38
+ transitions :from => :alpha, :to => :beta, :guards => [:fail]
39
+ end
40
+ end
41
+
42
+ def fail; false; end
43
+
44
+ def succeed; true; end
45
+ def another_succeed; true; end
46
+ def more_succeed; true; end
47
+
48
+ end
@@ -2,7 +2,7 @@ class NoScopeMongoid
2
2
  include Mongoid::Document
3
3
  include AASM
4
4
 
5
- field :status, type: String
5
+ field :status, :type => String
6
6
 
7
7
  aasm :create_scopes => false, :column => :status do
8
8
  state :ignored_scope
@@ -2,9 +2,10 @@ class SimpleMongoid
2
2
  include Mongoid::Document
3
3
  include AASM
4
4
 
5
- field :status, type: String
5
+ field :status, :type => String
6
6
 
7
- aasm_column :status
8
- aasm_state :unknown_scope
9
- aasm_state :new
7
+ aasm column: :status do
8
+ state :unknown_scope
9
+ state :new
10
+ end
10
11
  end
@@ -2,7 +2,7 @@ class SimpleNewDslMongoid
2
2
  include Mongoid::Document
3
3
  include AASM
4
4
 
5
- field :status, type: String
5
+ field :status, :type => String
6
6
 
7
7
  aasm :column => :status
8
8
  aasm do
@@ -2,16 +2,18 @@ module Models
2
2
  class Process
3
3
  include AASM
4
4
 
5
- aasm_state :sleeping
6
- aasm_state :running
7
- aasm_state :suspended
5
+ aasm do
6
+ state :sleeping
7
+ state :running
8
+ state :suspended
8
9
 
9
- aasm_event :start do
10
- transitions :from => :sleeping, :to => :running
11
- end
10
+ event :start do
11
+ transitions :from => :sleeping, :to => :running
12
+ end
12
13
 
13
- aasm_event :stop do
14
- transitions :from => :running, :to => :suspended
14
+ event :stop do
15
+ transitions :from => :running, :to => :suspended
16
+ end
15
17
  end
16
18
 
17
19
  end
@@ -36,13 +36,6 @@ class Transient < ActiveRecord::Base
36
36
  include AASM
37
37
  end
38
38
 
39
- class Simple < ActiveRecord::Base
40
- include AASM
41
- aasm_column :status
42
- aasm_state :unknown_scope
43
- aasm_state :new
44
- end
45
-
46
39
  class SimpleNewDsl < ActiveRecord::Base
47
40
  include AASM
48
41
  aasm :column => :status
@@ -59,9 +52,6 @@ class NoScope < ActiveRecord::Base
59
52
  end
60
53
  end
61
54
 
62
- class Derivate < Simple
63
- end
64
-
65
55
  class DerivateNewDsl < SimpleNewDsl
66
56
  end
67
57
 
@@ -72,8 +62,10 @@ class Thief < ActiveRecord::Base
72
62
  set_table_name "thieves"
73
63
  end
74
64
  include AASM
75
- aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
76
- aasm_state :rich
77
- aasm_state :jailed
65
+ aasm do
66
+ state :rich
67
+ state :jailed
68
+ initial_state Proc.new {|thief| thief.skilled ? :rich : :jailed }
69
+ end
78
70
  attr_accessor :skilled, :aasm_state
79
71
  end
data/spec/spec_helper.rb CHANGED
@@ -16,7 +16,7 @@ def load_schema
16
16
  config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
17
17
  ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
18
18
  ActiveRecord::Base.establish_connection(config['sqlite3'])
19
- load(File.dirname(__FILE__) + "/schema.rb")
19
+ require File.dirname(__FILE__) + "/database.rb"
20
20
  end
21
21
 
22
22
  # custom spec helpers
@@ -3,19 +3,19 @@ require 'models/active_record/api.rb'
3
3
 
4
4
  describe "reading the current state" do
5
5
  it "uses the AASM default" do
6
- DefaultState.new.aasm.current_state.should eql :alpha
6
+ expect(DefaultState.new.aasm.current_state).to eql :alpha
7
7
  end
8
8
 
9
9
  it "uses the provided method" do
10
- ProvidedState.new.aasm.current_state.should eql :beta
10
+ expect(ProvidedState.new.aasm.current_state).to eql :beta
11
11
  end
12
12
 
13
13
  it "uses the persistence storage" do
14
- PersistedState.new.aasm.current_state.should eql :alpha
14
+ expect(PersistedState.new.aasm.current_state).to eql :alpha
15
15
  end
16
16
 
17
17
  it "uses the provided method even if persisted" do
18
- ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma
18
+ expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
19
19
  end
20
20
  end
21
21
 
@@ -23,25 +23,25 @@ describe "writing and persisting the current state" do
23
23
  it "uses the AASM default" do
24
24
  o = DefaultState.new
25
25
  o.release!
26
- o.persisted_store.should be_nil
26
+ expect(o.persisted_store).to be_nil
27
27
  end
28
28
 
29
29
  it "uses the provided method" do
30
30
  o = ProvidedState.new
31
31
  o.release!
32
- o.persisted_store.should eql :beta
32
+ expect(o.persisted_store).to eql :beta
33
33
  end
34
34
 
35
35
  it "uses the persistence storage" do
36
36
  o = PersistedState.new
37
37
  o.release!
38
- o.persisted_store.should be_nil
38
+ expect(o.persisted_store).to be_nil
39
39
  end
40
40
 
41
41
  it "uses the provided method even if persisted" do
42
42
  o = ProvidedAndPersistedState.new
43
43
  o.release!
44
- o.persisted_store.should eql :beta
44
+ expect(o.persisted_store).to eql :beta
45
45
  end
46
46
  end
47
47
 
@@ -49,24 +49,24 @@ describe "writing the current state without persisting it" do
49
49
  it "uses the AASM default" do
50
50
  o = DefaultState.new
51
51
  o.release
52
- o.transient_store.should be_nil
52
+ expect(o.transient_store).to be_nil
53
53
  end
54
54
 
55
55
  it "uses the provided method" do
56
56
  o = ProvidedState.new
57
57
  o.release
58
- o.transient_store.should eql :beta
58
+ expect(o.transient_store).to eql :beta
59
59
  end
60
60
 
61
61
  it "uses the persistence storage" do
62
62
  o = PersistedState.new
63
63
  o.release
64
- o.transient_store.should be_nil
64
+ expect(o.transient_store).to be_nil
65
65
  end
66
66
 
67
67
  it "uses the provided method even if persisted" do
68
68
  o = ProvidedAndPersistedState.new
69
69
  o.release
70
- o.transient_store.should eql :beta
70
+ expect(o.transient_store).to eql :beta
71
71
  end
72
72
  end