fcoury-aasm 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gemspec
2
+ *.sw?
3
+ *~
4
+ .DS_Store
5
+ .idea
6
+ coverage
7
+ pkg
8
+ rdoc
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Scott Barron
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,129 @@
1
+ = AASM - Ruby state machines
2
+
3
+ This package contains AASM, a library for adding finite state machines to Ruby classes.
4
+
5
+ AASM started as the acts_as_state_machine plugin but has evolved into a more generic library that no longer targets only ActiveRecord models.
6
+
7
+ AASM has the following features:
8
+
9
+ * States
10
+ * Machines
11
+ * Events
12
+ * Transitions
13
+
14
+ == New Callbacks
15
+
16
+ The callback chain & order on a successful event looks like:
17
+
18
+ oldstate:exit*
19
+ event:before
20
+ __find transition, if possible__
21
+ transition:on_transition*
22
+ oldstate:before_exit
23
+ newstate:before_enter
24
+ newstate:enter*
25
+ __update state__
26
+ event:success*
27
+ oldstate:after_exit
28
+ newstate:after_enter
29
+ event:after
30
+ obj:aasm_event_fired*
31
+
32
+ (*) marks old callbacks
33
+
34
+
35
+ == Download
36
+
37
+ The latest AASM can currently be pulled from the git repository on github.
38
+
39
+ * http://github.com/rubyist/aasm/tree/master
40
+
41
+
42
+ == Installation
43
+
44
+ === From gemcutter
45
+
46
+ % sudo gem install gemcutter
47
+ % sudo gem tumble
48
+ % sudo gem install aasm
49
+
50
+ === From GitHub hosted gems (only older releases are available)
51
+
52
+ % sudo gem sources -a http://gems.github.com # (you only need to do this once)
53
+ % sudo gem install rubyist-aasm
54
+
55
+ === Building your own gems
56
+
57
+ % rake gemspec
58
+ % rake build
59
+ % sudo gem install pkg/aasm-2.1.gem
60
+
61
+
62
+ == Simple Example
63
+
64
+ Here's a quick example highlighting some of the features.
65
+
66
+ class Conversation
67
+ include AASM
68
+
69
+ aasm_initial_state :unread
70
+
71
+ aasm_state :unread
72
+ aasm_state :read
73
+ aasm_state :closed
74
+
75
+
76
+ aasm_event :view do
77
+ transitions :to => :read, :from => [:unread]
78
+ end
79
+
80
+ aasm_event :close do
81
+ transitions :to => :closed, :from => [:read, :unread]
82
+ end
83
+ end
84
+
85
+ == A Slightly More Complex Example
86
+
87
+ This example uses a few of the more complex features available.
88
+
89
+ class Relationship
90
+ include AASM
91
+
92
+ aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
93
+
94
+ aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
95
+ aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
96
+ aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
97
+
98
+ aasm_event :get_intimate do
99
+ transitions :to => :intimate, :from => [:dating], :guard => :drunk?
100
+ end
101
+
102
+ aasm_event :get_married do
103
+ transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
104
+ end
105
+
106
+ def strictly_for_fun?; end
107
+ def drunk?; end
108
+ def willing_to_give_up_manhood?; end
109
+ def make_happy; end
110
+ def make_depressed; end
111
+ def make_very_happy; end
112
+ def never_speak_again; end
113
+ def give_up_intimacy; end
114
+ def buy_exotic_car_and_wear_a_combover; end
115
+ end
116
+
117
+ = Other Stuff
118
+
119
+ Author:: Scott Barron <scott at elitists dot net>
120
+ License:: Original code Copyright 2006, 2007, 2008 by Scott Barron.
121
+ Released under an MIT-style license. See the LICENSE file
122
+ included in the distribution.
123
+
124
+ == Warranty
125
+
126
+ This software is provided "as is" and without any express or
127
+ implied warranties, including, without limitation, the implied
128
+ warranties of merchantibility and fitness for a particular
129
+ purpose.
data/Rakefile ADDED
@@ -0,0 +1,108 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "fcoury-aasm"
8
+ gem.summary = %Q{State machine mixin for Ruby objects}
9
+ gem.description = %Q{AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.}
10
+ gem.homepage = "http://rubyist.github.com/aasm/"
11
+ gem.authors = ["Scott Barron", "Scott Petersen", "Travis Tilley", "FelipeCoury"]
12
+ gem.email = "scott@elitists.net, ttilley@gmail.com, felipe@webbynode.com"
13
+ gem.add_development_dependency "rspec"
14
+ gem.add_development_dependency "shoulda"
15
+ gem.add_development_dependency 'sdoc'
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/*_test.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new(:rcov_shoulda) do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/*_test.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+ Spec::Rake::SpecTask.new(:spec) do |spec|
46
+ spec.libs << 'lib' << 'spec'
47
+ spec.spec_files = FileList['spec/**/*_spec.rb']
48
+ spec.spec_opts = ['-cfs']
49
+ end
50
+
51
+ Spec::Rake::SpecTask.new(:rcov_rspec) do |spec|
52
+ spec.libs << 'lib' << 'spec'
53
+ spec.pattern = 'spec/**/*_spec.rb'
54
+ spec.rcov = true
55
+ end
56
+
57
+ task :test => :check_dependencies
58
+ task :spec => :check_dependencies
59
+
60
+ begin
61
+ require 'reek/rake_task'
62
+ Reek::RakeTask.new do |t|
63
+ t.fail_on_error = true
64
+ t.verbose = false
65
+ t.source_files = 'lib/**/*.rb'
66
+ end
67
+ rescue LoadError
68
+ task :reek do
69
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
70
+ end
71
+ end
72
+
73
+ begin
74
+ require 'roodi'
75
+ require 'roodi_task'
76
+ RoodiTask.new do |t|
77
+ t.verbose = false
78
+ end
79
+ rescue LoadError
80
+ task :roodi do
81
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
82
+ end
83
+ end
84
+
85
+ task :default => :test
86
+
87
+ begin
88
+ require 'rake/rdoctask'
89
+ require 'sdoc'
90
+ Rake::RDocTask.new do |rdoc|
91
+ if File.exist?('VERSION')
92
+ version = File.read('VERSION')
93
+ else
94
+ version = ""
95
+ end
96
+
97
+ rdoc.rdoc_dir = 'rdoc'
98
+ rdoc.title = "aasm #{version}"
99
+ rdoc.rdoc_files.include('README*')
100
+ rdoc.rdoc_files.include('lib/**/*.rb')
101
+
102
+ rdoc.options << '--fmt' << 'shtml'
103
+ rdoc.template = 'direct'
104
+ end
105
+ rescue LoadError
106
+ puts "aasm makes use of the sdoc gem. Install it with: sudo gem install sdoc"
107
+ end
108
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.1.3
data/lib/aasm.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'aasm', 'aasm')
data/lib/aasm/aasm.rb ADDED
@@ -0,0 +1,195 @@
1
+ require File.join(File.dirname(__FILE__), 'event')
2
+ require File.join(File.dirname(__FILE__), 'state')
3
+ require File.join(File.dirname(__FILE__), 'state_machine')
4
+ require File.join(File.dirname(__FILE__), 'persistence')
5
+
6
+ module AASM
7
+ class InvalidTransition < RuntimeError
8
+ end
9
+
10
+ class UndefinedState < RuntimeError
11
+ end
12
+
13
+ def self.included(base) #:nodoc:
14
+ base.extend AASM::ClassMethods
15
+ AASM::Persistence.set_persistence(base)
16
+ unless AASM::StateMachine[base]
17
+ AASM::StateMachine[base] = AASM::StateMachine.new('')
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def inherited(klass)
23
+ AASM::StateMachine[klass] = AASM::StateMachine[self].clone
24
+ super
25
+ end
26
+
27
+ def aasm_initial_state(set_state=nil)
28
+ if set_state
29
+ AASM::StateMachine[self].initial_state = set_state
30
+ else
31
+ AASM::StateMachine[self].initial_state
32
+ end
33
+ end
34
+
35
+ def aasm_initial_state=(state)
36
+ AASM::StateMachine[self].initial_state = state
37
+ end
38
+
39
+ def aasm_state(name, options={})
40
+ sm = AASM::StateMachine[self]
41
+ sm.create_state(name, options)
42
+ sm.initial_state = name unless sm.initial_state
43
+
44
+ define_method("#{name.to_s}?") do
45
+ aasm_current_state == name
46
+ end
47
+ end
48
+
49
+ def aasm_event(name, options = {}, &block)
50
+ sm = AASM::StateMachine[self]
51
+
52
+ unless sm.events.has_key?(name)
53
+ sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
54
+ end
55
+
56
+ define_method("#{name.to_s}!") do |*args|
57
+ aasm_fire_event(name, true, *args)
58
+ end
59
+
60
+ define_method("#{name.to_s}") do |*args|
61
+ aasm_fire_event(name, false, *args)
62
+ end
63
+ end
64
+
65
+ def aasm_states
66
+ AASM::StateMachine[self].states
67
+ end
68
+
69
+ def aasm_events
70
+ AASM::StateMachine[self].events
71
+ end
72
+
73
+ def aasm_states_for_select
74
+ AASM::StateMachine[self].states.map { |state| state.for_select }
75
+ end
76
+
77
+ end
78
+
79
+ # Instance methods
80
+ def aasm_current_state
81
+ return @aasm_current_state if @aasm_current_state
82
+
83
+ if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
84
+ @aasm_current_state = aasm_read_state
85
+ end
86
+ return @aasm_current_state if @aasm_current_state
87
+
88
+ aasm_enter_initial_state
89
+ end
90
+
91
+ def aasm_enter_initial_state
92
+ state_name = aasm_determine_state_name(self.class.aasm_initial_state)
93
+ state = aasm_state_object_for_state(state_name)
94
+
95
+ state.call_action(:before_enter, self)
96
+ state.call_action(:enter, self)
97
+ self.aasm_current_state = state_name
98
+ state.call_action(:after_enter, self)
99
+
100
+ state_name
101
+ end
102
+
103
+ def aasm_events_for_current_state
104
+ aasm_events_for_state(aasm_current_state)
105
+ end
106
+
107
+ def aasm_events_for_state(state)
108
+ events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
109
+ events.map {|event| event.name}
110
+ end
111
+
112
+ private
113
+
114
+ def set_aasm_current_state_with_persistence(state)
115
+ save_success = true
116
+ if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
117
+ save_success = aasm_write_state(state)
118
+ end
119
+ self.aasm_current_state = state if save_success
120
+
121
+ save_success
122
+ end
123
+
124
+ def aasm_current_state=(state)
125
+ if self.respond_to?(:aasm_write_state_without_persistence) || self.private_methods.include?('aasm_write_state_without_persistence')
126
+ aasm_write_state_without_persistence(state)
127
+ end
128
+ @aasm_current_state = state
129
+ end
130
+
131
+ def aasm_determine_state_name(state)
132
+ case state
133
+ when Symbol, String
134
+ state
135
+ when Proc
136
+ state.call(self)
137
+ else
138
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
139
+ end
140
+ end
141
+
142
+ def aasm_state_object_for_state(name)
143
+ obj = self.class.aasm_states.find {|s| s == name}
144
+ raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
145
+ obj
146
+ end
147
+
148
+ def aasm_fire_event(name, persist, *args)
149
+ old_state = aasm_state_object_for_state(aasm_current_state)
150
+ event = self.class.aasm_events[name]
151
+
152
+ old_state.call_action(:exit, self)
153
+
154
+ # new event before callback
155
+ event.call_action(:before, self)
156
+
157
+ new_state_name = event.fire(self, *args)
158
+
159
+ unless new_state_name.nil?
160
+ new_state = aasm_state_object_for_state(new_state_name)
161
+
162
+ # new before_ callbacks
163
+ old_state.call_action(:before_exit, self)
164
+ new_state.call_action(:before_enter, self)
165
+
166
+ new_state.call_action(:enter, self)
167
+
168
+ persist_successful = true
169
+ if persist
170
+ persist_successful = set_aasm_current_state_with_persistence(new_state_name)
171
+ event.execute_success_callback(self) if persist_successful
172
+ else
173
+ self.aasm_current_state = new_state_name
174
+ end
175
+
176
+ if persist_successful
177
+ old_state.call_action(:after_exit, self)
178
+ new_state.call_action(:after_enter, self)
179
+ event.call_action(:after, self)
180
+
181
+ self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
182
+ else
183
+ self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
184
+ end
185
+
186
+ persist_successful
187
+ else
188
+ if self.respond_to?(:aasm_event_failed)
189
+ self.aasm_event_failed(name, old_state.name)
190
+ end
191
+
192
+ false
193
+ end
194
+ end
195
+ end