rubyist-aasm 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,15 @@
1
+ * In AR persistence, move state column from class level variables into the StateMachine object for the class
2
+
3
+ * allowed :to array and :on_transition callback [Kevin Triplett]
4
+
5
+ * Support enter and exit actions on states
6
+
7
+ * Use named_scope in AR persistence layer, if available [Jan De Poorter]
8
+
1
9
  * Incremented version number
10
+
2
11
  * Cleaned up aasm_states_for_select to return the value as a string
12
+
3
13
  * Specs and bug fixes for the ActiveRecordPersistence, keeping persistence columns in sync
4
14
  Allowing for nil values in states for active record
5
15
  Only set state to default state before_validation_on_create
data/README.rdoc CHANGED
@@ -63,6 +63,8 @@ Author:: Scott Barron <scott at elitists dot net>
63
63
  License:: Copyright 2006, 2007, 2008 by Scott Barron.
64
64
  Released under an MIT-style license. See the LICENSE file
65
65
  included in the distribution.
66
+ Bugs:: http://rubyist.lighthouseapp.com/projects/13207-aasm/
67
+ GitHub:: http://github.com/rubyist/aasm/tree/master
66
68
 
67
69
  == Warranty
68
70
 
data/Rakefile CHANGED
@@ -71,7 +71,7 @@ else
71
71
  Spec::Rake::SpecTask.new('cruise') do |t|
72
72
  t.spec_files = FileList['spec/**/*.rb']
73
73
  t.rcov = true
74
- t.rcov_opts = ['--exclude', 'spec']
74
+ t.rcov_opts = ['--exclude', 'spec', '--exclude', 'Library']
75
75
  end
76
76
 
77
77
  desc "Run all examples"
data/TODO CHANGED
@@ -1,10 +1,8 @@
1
1
  Before Next Release:
2
2
 
3
- * Add state actions (enter, exit, after)
4
3
  * Add #aasm_next_state_for_event
5
4
  * Add #aasm_next_states_for_event
6
5
 
7
-
8
6
  Cool ideas from users:
9
7
 
10
8
  * Support multiple state machines on one object (Chris Nelson)
data/lib/aasm.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), 'event')
2
2
  require File.join(File.dirname(__FILE__), 'state')
3
+ require File.join(File.dirname(__FILE__), 'state_machine')
3
4
  require File.join(File.dirname(__FILE__), 'persistence')
4
5
 
5
6
  module AASM
@@ -7,26 +8,37 @@ module AASM
7
8
  end
8
9
 
9
10
  def self.included(base) #:nodoc:
11
+ # TODO - need to ensure that a machine is being created because
12
+ # AASM was either included or arrived at via inheritance. It
13
+ # cannot be both.
10
14
  base.extend AASM::ClassMethods
11
15
  AASM::Persistence.set_persistence(base)
16
+ AASM::StateMachine[base] = AASM::StateMachine.new('')
17
+
18
+ base.class_eval do
19
+ def base.inherited(klass)
20
+ AASM::StateMachine[klass] = AASM::StateMachine[self].dup
21
+ end
22
+ end
12
23
  end
13
24
 
14
25
  module ClassMethods
15
26
  def aasm_initial_state(set_state=nil)
16
27
  if set_state
17
- aasm_initial_state = set_state
28
+ AASM::StateMachine[self].initial_state = set_state
18
29
  else
19
- @aasm_initial_state
30
+ AASM::StateMachine[self].initial_state
20
31
  end
21
32
  end
22
33
 
23
34
  def aasm_initial_state=(state)
24
- @aasm_initial_state = state
35
+ AASM::StateMachine[self].initial_state = state
25
36
  end
26
37
 
27
38
  def aasm_state(name, options={})
28
- aasm_states << name unless aasm_states.include?(name)
29
- self.aasm_initial_state = name unless self.aasm_initial_state
39
+ sm = AASM::StateMachine[self]
40
+ sm.create_state(name, options)
41
+ sm.initial_state = name unless sm.initial_state
30
42
 
31
43
  define_method("#{name.to_s}?") do
32
44
  aasm_current_state == name
@@ -34,61 +46,31 @@ module AASM
34
46
  end
35
47
 
36
48
  def aasm_event(name, options = {}, &block)
37
- unless aasm_events.has_key?(name)
38
- aasm_events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
49
+ sm = AASM::StateMachine[self]
50
+
51
+ unless sm.events.has_key?(name)
52
+ sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
39
53
  end
40
54
 
41
- define_method("#{name.to_s}!") do
42
- new_state = self.class.aasm_events[name].fire(self)
43
- unless new_state.nil?
44
- if self.respond_to?(:aasm_event_fired)
45
- self.aasm_event_fired(self.aasm_current_state, new_state)
46
- end
47
-
48
- self.aasm_current_state_with_persistence = new_state
49
-
50
- self.send(self.class.aasm_events[name].success) if self.class.aasm_events[name].success
51
-
52
- true
53
- else
54
- if self.respond_to?(:aasm_event_failed)
55
- self.aasm_event_failed(name)
56
- end
57
-
58
- false
59
- end
55
+ define_method("#{name.to_s}!") do |*args|
56
+ aasm_fire_event(name, true, *args)
60
57
  end
61
58
 
62
- define_method("#{name.to_s}") do
63
- new_state = self.class.aasm_events[name].fire(self)
64
- unless new_state.nil?
65
- if self.respond_to?(:aasm_event_fired)
66
- self.aasm_event_fired(self.aasm_current_state, new_state)
67
- end
68
-
69
- self.aasm_current_state = new_state
70
- true
71
- else
72
- if self.respond_to?(:aasm_event_failed)
73
- self.aasm_event_failed(name)
74
- end
75
-
76
- false
77
- end
59
+ define_method("#{name.to_s}") do |*args|
60
+ aasm_fire_event(name, false, *args)
78
61
  end
79
-
80
62
  end
81
63
 
82
64
  def aasm_states
83
- @aasm_states ||= []
65
+ AASM::StateMachine[self].states
84
66
  end
85
67
 
86
68
  def aasm_events
87
- @aasm_events ||= {}
69
+ AASM::StateMachine[self].events
88
70
  end
89
71
 
90
72
  def aasm_states_for_select
91
- aasm_states.collect { |state| [state.to_s.gsub(/_/, ' ').capitalize, state.to_s] }
73
+ AASM::StateMachine[self].states.map { |state| state.for_select }
92
74
  end
93
75
 
94
76
  end
@@ -128,4 +110,36 @@ module AASM
128
110
  @aasm_current_state = state
129
111
  end
130
112
 
113
+ def aasm_state_object_for_state(name)
114
+ self.class.aasm_states.find {|s| s == name}
115
+ end
116
+
117
+ def aasm_fire_event(name, persist, *args)
118
+ aasm_state_object_for_state(aasm_current_state).call_action(:exit, self)
119
+
120
+ new_state = self.class.aasm_events[name].fire(self, *args)
121
+
122
+ unless new_state.nil?
123
+ aasm_state_object_for_state(new_state).call_action(:enter, self)
124
+
125
+ if self.respond_to?(:aasm_event_fired)
126
+ self.aasm_event_fired(self.aasm_current_state, new_state)
127
+ end
128
+
129
+ if persist
130
+ self.aasm_current_state_with_persistence = new_state
131
+ self.send(self.class.aasm_events[name].success) if self.class.aasm_events[name].success
132
+ else
133
+ self.aasm_current_state = new_state
134
+ end
135
+
136
+ true
137
+ else
138
+ if self.respond_to?(:aasm_event_failed)
139
+ self.aasm_event_failed(name)
140
+ end
141
+
142
+ false
143
+ end
144
+ end
131
145
  end
data/lib/event.rb CHANGED
@@ -12,14 +12,16 @@ module AASM
12
12
  instance_eval(&block) if block
13
13
  end
14
14
 
15
- def fire(obj)
15
+ def fire(obj, to_state=nil, *args)
16
16
  transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
17
17
  raise AASM::InvalidTransition if transitions.size == 0
18
18
 
19
19
  next_state = nil
20
20
  transitions.each do |transition|
21
+ next if to_state and !Array(transition.to).include?(to_state)
21
22
  if transition.perform(obj)
22
- next_state = transition.to
23
+ next_state = to_state || Array(transition.to).first
24
+ transition.execute(obj, *args)
23
25
  break
24
26
  end
25
27
  end
@@ -36,7 +36,19 @@ module AASM
36
36
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
37
37
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::ReadState) unless base.method_defined?(:aasm_read_state)
38
38
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteState) unless base.method_defined?(:aasm_write_state)
39
- base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence)
39
+ base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence)
40
+
41
+ if base.respond_to?(:named_scope)
42
+ base.extend(AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods)
43
+
44
+ base.class_eval do
45
+ class << self
46
+ alias_method :aasm_state_without_named_scope, :aasm_state
47
+ alias_method :aasm_state, :aasm_state_with_named_scope
48
+ end
49
+ end
50
+ end
51
+
40
52
  base.before_validation_on_create :aasm_ensure_initial_state
41
53
  end
42
54
 
@@ -67,13 +79,40 @@ module AASM
67
79
  # This method is both a getter and a setter
68
80
  def aasm_column(column_name=nil)
69
81
  if column_name
70
- @aasm_column = column_name.to_sym
82
+ AASM::StateMachine[self].config.column = column_name.to_sym
83
+ # @aasm_column = column_name.to_sym
71
84
  else
72
- @aasm_column ||= :aasm_state
85
+ AASM::StateMachine[self].config.column ||= :aasm_state
86
+ # @aasm_column ||= :aasm_state
73
87
  end
74
- @aasm_column
88
+ # @aasm_column
89
+ AASM::StateMachine[self].config.column
75
90
  end
76
91
 
92
+ def find_in_state(number, state, *args)
93
+ with_state_scope state do
94
+ find(number, *args)
95
+ end
96
+ end
97
+
98
+ def count_in_state(state, *args)
99
+ with_state_scope state do
100
+ count(*args)
101
+ end
102
+ end
103
+
104
+ def calculate_in_state(state, *args)
105
+ with_state_scope state do
106
+ calculate(*args)
107
+ end
108
+ end
109
+
110
+ protected
111
+ def with_state_scope(state)
112
+ with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
113
+ yield if block_given?
114
+ end
115
+ end
77
116
  end
78
117
 
79
118
  module InstanceMethods
@@ -187,6 +226,13 @@ module AASM
187
226
  end
188
227
  end
189
228
  end
229
+
230
+ module NamedScopeMethods
231
+ def aasm_state_with_named_scope name, options = {}
232
+ aasm_state_without_named_scope name, options
233
+ self.named_scope name, :conditions => {self.aasm_column => name.to_s} unless self.scopes.include?(name)
234
+ end
235
+ end
190
236
  end
191
237
  end
192
238
  end
data/lib/state.rb CHANGED
@@ -7,22 +7,26 @@ module AASM
7
7
  @name, @options = name, options
8
8
  end
9
9
 
10
- def entering(record)
11
- enteract = @options[:enter]
12
- record.send(:run_transition_action, enteract) if enteract
10
+ def ==(state)
11
+ if state.is_a? Symbol
12
+ name == state
13
+ else
14
+ name == state.name
15
+ end
13
16
  end
14
17
 
15
- def entered(record)
16
- afteractions = @options[:after]
17
- return unless afteractions
18
- Array(afteractions).each do |afteract|
19
- record.send(:run_transition_action, afteract)
18
+ def call_action(action, record)
19
+ action = @options[action]
20
+ case action
21
+ when Symbol, String
22
+ record.send(action)
23
+ when Proc
24
+ action.call(record)
20
25
  end
21
26
  end
22
27
 
23
- def exited(record)
24
- exitact = @options[:exit]
25
- record.send(:run_transition_action, exitact) if exitact
28
+ def for_select
29
+ [name.to_s.gsub(/_/, ' ').capitalize, name.to_s]
26
30
  end
27
31
  end
28
32
  end
@@ -0,0 +1,29 @@
1
+ require 'ostruct'
2
+
3
+ module AASM
4
+ class StateMachine
5
+ def self.[](*args)
6
+ (@machines ||= {})[args]
7
+ end
8
+
9
+ def self.[]=(*args)
10
+ val = args.pop
11
+ (@machines ||= {})[args] = val
12
+ end
13
+
14
+ attr_accessor :states, :events, :initial_state, :config
15
+ attr_reader :name
16
+
17
+ def initialize(name)
18
+ @name = name
19
+ @initial_state = nil
20
+ @states = []
21
+ @events = {}
22
+ @config = OpenStruct.new
23
+ end
24
+
25
+ def create_state(name, options)
26
+ @states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
27
+ end
28
+ end
29
+ end
@@ -4,7 +4,7 @@ module AASM
4
4
  attr_reader :from, :to, :opts
5
5
 
6
6
  def initialize(opts)
7
- @from, @to, @guard = opts[:from], opts[:to], opts[:guard]
7
+ @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
8
8
  @opts = opts
9
9
  end
10
10
 
@@ -19,6 +19,15 @@ module AASM
19
19
  end
20
20
  end
21
21
 
22
+ def execute(obj, *args)
23
+ case @on_transition
24
+ when Symbol, String
25
+ obj.send(@on_transition, *args)
26
+ when Proc
27
+ @on_transition.call(obj, *args)
28
+ end
29
+ end
30
+
22
31
  def ==(obj)
23
32
  @from == obj.from && @to == obj.to
24
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyist-aasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Barron
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-14 00:00:00 -07:00
12
+ date: 2008-07-08 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -35,6 +35,7 @@ files:
35
35
  - lib/persistence/active_record_persistence.rb
36
36
  - lib/persistence.rb
37
37
  - lib/state.rb
38
+ - lib/state_machine.rb
38
39
  - lib/state_transition.rb
39
40
  - lib/version.rb
40
41
  - doc/jamis.rb
@@ -65,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
66
  requirements: []
66
67
 
67
68
  rubyforge_project:
68
- rubygems_version: 1.0.1
69
+ rubygems_version: 1.2.0
69
70
  signing_key:
70
71
  specification_version: 2
71
72
  summary: State machine mixin for Ruby objects