state_machine 0.1.1 → 0.2.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.
- data/{CHANGELOG → CHANGELOG.rdoc} +13 -46
- data/{MIT-LICENSE → LICENSE} +0 -0
- data/{README → README.rdoc} +7 -7
- data/Rakefile +43 -35
- data/lib/state_machine.rb +3 -1
- data/lib/state_machine/event.rb +69 -29
- data/lib/state_machine/machine.rb +17 -17
- data/lib/state_machine/transition.rb +37 -9
- data/test/app_root/app/models/switch.rb +8 -0
- data/test/factory.rb +27 -21
- data/test/functional/state_machine_test.rb +87 -83
- data/test/unit/event_test.rb +115 -17
- data/test/unit/invalid_transition_test.rb +7 -0
- data/test/unit/machine_test.rb +19 -11
- data/test/unit/state_machine_test.rb +22 -4
- data/test/unit/transition_test.rb +150 -12
- metadata +9 -7
|
@@ -1,93 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
== master
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
== 0.2.0 / 2008-06-29
|
|
4
|
+
|
|
5
|
+
* Add a non-bang version of events (e.g. park) that will return a boolean value for success
|
|
6
|
+
* Raise an exception if the bang version of events are used (e.g. park!) and no transition is successful
|
|
7
|
+
* Change callbacks to act a little more like ActiveRecord
|
|
8
|
+
* Avoid using string evaluation for dynamic methods
|
|
9
|
+
|
|
10
|
+
== 0.1.1 / 2008-06-22
|
|
4
11
|
|
|
5
12
|
* Remove log files from gems
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
== 0.1.0 / 2008-05-05
|
|
8
15
|
|
|
9
16
|
* Completely rewritten from scratch
|
|
10
|
-
|
|
11
17
|
* Renamed to state_machine
|
|
12
|
-
|
|
13
18
|
* Removed database dependencies
|
|
14
|
-
|
|
15
19
|
* Removed models in favor of an attribute-agnostic design
|
|
16
|
-
|
|
17
20
|
* Use ActiveSupport::Callbacks instead of eval_call
|
|
18
|
-
|
|
19
21
|
* Remove dry_transaction_rollbacks dependencies
|
|
20
|
-
|
|
21
22
|
* Added functional tests
|
|
22
|
-
|
|
23
23
|
* Updated documentation
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
== 0.0.1 / 2007-09-26
|
|
26
26
|
|
|
27
27
|
* Add dependency on custom_callbacks
|
|
28
|
-
|
|
29
28
|
* Move test fixtures out of the test application root directory
|
|
30
|
-
|
|
31
29
|
* Improve documentation
|
|
32
|
-
|
|
33
30
|
* Remove the StateExtension module in favor of adding singleton methods to the stateful class
|
|
34
|
-
|
|
35
31
|
* Convert dos newlines to unix newlines
|
|
36
|
-
|
|
37
32
|
* Fix error message when a given event can't be found in the database
|
|
38
|
-
|
|
39
33
|
* Add before_#{action} and #{action} callbacks when an event is performed
|
|
40
|
-
|
|
41
34
|
* All state and event callbacks can now explicitly return false in order to cancel the action
|
|
42
|
-
|
|
43
35
|
* Refactor ActiveState callback creation
|
|
44
|
-
|
|
45
36
|
* Refactor unit tests so that they use mock classes instead of themselves
|
|
46
|
-
|
|
47
37
|
* Allow force_reload option to be set in the state association
|
|
48
|
-
|
|
49
38
|
* Don't save the entire model when updating the state_id
|
|
50
|
-
|
|
51
39
|
* Raise exception if a class tries to define a state more than once
|
|
52
|
-
|
|
53
40
|
* Add tests for PluginAWeek::Has::States::ActiveState
|
|
54
|
-
|
|
55
41
|
* Refactor active state/active event creation
|
|
56
|
-
|
|
57
42
|
* Fix owner_type not being set correctly in active states/events of subclasses
|
|
58
|
-
|
|
59
43
|
* Allow subclasses to override the initial state
|
|
60
|
-
|
|
61
44
|
* Fix problem with migrations using default null when column cannot be null
|
|
62
|
-
|
|
63
45
|
* Moved deadline support into a separate plugin (has_state_deadlines).
|
|
64
|
-
|
|
65
46
|
* Added many more unit tests.
|
|
66
|
-
|
|
67
47
|
* Simplified many of the interfaces for maintainability.
|
|
68
|
-
|
|
69
48
|
* Added support for turning off recording state changes.
|
|
70
|
-
|
|
71
49
|
* Removed the short_description and long_description columns, in favor of an optional human_name column.
|
|
72
|
-
|
|
73
50
|
* Fixed not overriding the correct equality methods in the StateTransition class.
|
|
74
|
-
|
|
75
51
|
* Added to_sym to State and Event.
|
|
76
|
-
|
|
77
52
|
* State#name and Event#name now return the string version of the name instead of the symbol version.
|
|
78
|
-
|
|
79
53
|
* Added State#human_name and Event#human_name to automatically figure out what the human name is if it isn't specified in the table.
|
|
80
|
-
|
|
81
54
|
* Updated manual rollbacks to use the new Rails edge api (ActiveRecord::Rollback exception).
|
|
82
|
-
|
|
83
55
|
* Moved StateExtension class into a separate file in order to help keep the has_state files clean.
|
|
84
|
-
|
|
85
56
|
* Renamed InvalidState and InvalidEvent exceptions to StateNotFound and EventNotFound in order to follow the ActiveRecord convention (i.e. RecordNotFound).
|
|
86
|
-
|
|
87
57
|
* Added StateNotActive and EventNotActive exceptions to help differentiate between states which don't exist and states which weren't defined in the class.
|
|
88
|
-
|
|
89
58
|
* Added support for defining callbacks like so:
|
|
90
|
-
|
|
59
|
+
|
|
91
60
|
def before_exit_parked
|
|
92
61
|
end
|
|
93
62
|
|
|
@@ -95,11 +64,9 @@
|
|
|
95
64
|
end
|
|
96
65
|
|
|
97
66
|
* Added support for defining callbacks using class methods:
|
|
98
|
-
|
|
67
|
+
|
|
99
68
|
before_exit_parked :fasten_seatbelt
|
|
100
69
|
|
|
101
70
|
* Added event callbacks after the transition has occurred (e.g. after_park)
|
|
102
|
-
|
|
103
71
|
* State callbacks no longer receive any of the arguments that were provided in the event action
|
|
104
|
-
|
|
105
72
|
* Updated license to include our names.
|
data/{MIT-LICENSE → LICENSE}
RENAMED
|
File without changes
|
data/{README → README.rdoc}
RENAMED
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
== Resources
|
|
6
6
|
|
|
7
|
-
Wiki
|
|
8
|
-
|
|
9
|
-
* http://wiki.pluginaweek.org/State_machine
|
|
10
|
-
|
|
11
7
|
API
|
|
12
8
|
|
|
13
9
|
* http://api.pluginaweek.org/state_machine
|
|
14
10
|
|
|
11
|
+
Bugs
|
|
12
|
+
|
|
13
|
+
* http://pluginaweek.lighthouseapp.com/projects/13288-state_machine
|
|
14
|
+
|
|
15
15
|
Development
|
|
16
16
|
|
|
17
|
-
* http://
|
|
17
|
+
* http://github.com/pluginaweek/state_machine
|
|
18
18
|
|
|
19
19
|
Source
|
|
20
20
|
|
|
21
|
-
*
|
|
21
|
+
* git://github.com/pluginaweek/state_machine.git
|
|
22
22
|
|
|
23
23
|
== Description
|
|
24
24
|
|
|
@@ -86,7 +86,7 @@ events for your models. It is cross-platform, written in Java.
|
|
|
86
86
|
== Testing
|
|
87
87
|
|
|
88
88
|
Before you can run any tests, the following gem must be installed:
|
|
89
|
-
* plugin_test_helper[http://
|
|
89
|
+
* plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
|
|
90
90
|
|
|
91
91
|
To run against a specific version of Rails:
|
|
92
92
|
|
data/Rakefile
CHANGED
|
@@ -3,46 +3,54 @@ require 'rake/rdoctask'
|
|
|
3
3
|
require 'rake/gempackagetask'
|
|
4
4
|
require 'rake/contrib/sshpublisher'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
spec = Gem::Specification.new do |s|
|
|
7
|
+
s.name = 'state_machine'
|
|
8
|
+
s.version = '0.2.0'
|
|
9
|
+
s.platform = Gem::Platform::RUBY
|
|
10
|
+
s.summary = 'Adds support for creating state machines for attributes within a model'
|
|
11
|
+
|
|
12
|
+
s.files = FileList['{lib,test}/**/*'].to_a - FileList['test/app_root/log/*'].to_a + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc)
|
|
13
|
+
s.require_path = 'lib'
|
|
14
|
+
s.has_rdoc = true
|
|
15
|
+
s.test_files = Dir['test/**/*_test.rb']
|
|
16
|
+
|
|
17
|
+
s.author = 'Aaron Pfeifer'
|
|
18
|
+
s.email = 'aaron@pluginaweek.org'
|
|
19
|
+
s.homepage = 'http://www.pluginaweek.org'
|
|
20
|
+
s.rubyforge_project = 'pluginaweek'
|
|
21
|
+
end
|
|
10
22
|
|
|
11
|
-
desc 'Default: run
|
|
23
|
+
desc 'Default: run all tests.'
|
|
12
24
|
task :default => :test
|
|
13
25
|
|
|
14
|
-
desc
|
|
26
|
+
desc "Test the #{spec.name} plugin."
|
|
15
27
|
Rake::TestTask.new(:test) do |t|
|
|
16
28
|
t.libs << 'lib'
|
|
17
|
-
t.
|
|
29
|
+
t.test_files = spec.test_files
|
|
18
30
|
t.verbose = true
|
|
19
31
|
end
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
begin
|
|
34
|
+
require 'rcov/rcovtask'
|
|
35
|
+
namespace :test do
|
|
36
|
+
desc "Test the #{spec.name} plugin with Rcov."
|
|
37
|
+
Rcov::RcovTask.new(:rcov) do |t|
|
|
38
|
+
t.libs << 'lib'
|
|
39
|
+
t.test_files = spec.test_files
|
|
40
|
+
t.rcov_opts << '--exclude="^(?!lib/)"'
|
|
41
|
+
t.verbose = true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
rescue LoadError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc "Generate documentation for the #{spec.name} plugin."
|
|
22
48
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
23
49
|
rdoc.rdoc_dir = 'rdoc'
|
|
24
|
-
rdoc.title =
|
|
50
|
+
rdoc.title = spec.name
|
|
25
51
|
rdoc.template = '../rdoc_template.rb'
|
|
26
52
|
rdoc.options << '--line-numbers' << '--inline-source'
|
|
27
|
-
rdoc.rdoc_files.include('README')
|
|
28
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
spec = Gem::Specification.new do |s|
|
|
32
|
-
s.name = PKG_NAME
|
|
33
|
-
s.version = PKG_VERSION
|
|
34
|
-
s.platform = Gem::Platform::RUBY
|
|
35
|
-
s.summary = 'Adds support for creating state machines for attributes within a model'
|
|
36
|
-
|
|
37
|
-
s.files = FileList['{lib,test}/**/*'].to_a - FileList['test/app_root/log/*'].to_a + %w(CHANGELOG init.rb MIT-LICENSE Rakefile README)
|
|
38
|
-
s.require_path = 'lib'
|
|
39
|
-
s.autorequire = 'state_machine'
|
|
40
|
-
s.has_rdoc = true
|
|
41
|
-
s.test_files = Dir['test/**/*_test.rb']
|
|
42
|
-
|
|
43
|
-
s.author = 'Aaron Pfeifer'
|
|
44
|
-
s.email = 'aaron@pluginaweek.org'
|
|
45
|
-
s.homepage = 'http://www.pluginaweek.org'
|
|
53
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb')
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
Rake::GemPackageTask.new(spec) do |p|
|
|
@@ -51,14 +59,14 @@ Rake::GemPackageTask.new(spec) do |p|
|
|
|
51
59
|
p.need_zip = true
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
desc 'Publish the beta gem'
|
|
62
|
+
desc 'Publish the beta gem.'
|
|
55
63
|
task :pgem => [:package] do
|
|
56
|
-
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{
|
|
64
|
+
Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{spec.name}-#{spec.version}.gem").upload
|
|
57
65
|
end
|
|
58
66
|
|
|
59
|
-
desc 'Publish the API documentation'
|
|
67
|
+
desc 'Publish the API documentation.'
|
|
60
68
|
task :pdoc => [:rdoc] do
|
|
61
|
-
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{
|
|
69
|
+
Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{spec.name}", 'rdoc').upload
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
desc 'Publish the API docs and gem'
|
|
@@ -71,10 +79,10 @@ task :release => [:gem, :package] do
|
|
|
71
79
|
ruby_forge = RubyForge.new.configure
|
|
72
80
|
ruby_forge.login
|
|
73
81
|
|
|
74
|
-
%w(
|
|
75
|
-
file = "pkg/#{
|
|
82
|
+
%w(gem tgz zip).each do |ext|
|
|
83
|
+
file = "pkg/#{spec.name}-#{spec.version}.#{ext}"
|
|
76
84
|
puts "Releasing #{File.basename(file)}..."
|
|
77
85
|
|
|
78
|
-
ruby_forge.add_release(
|
|
86
|
+
ruby_forge.add_release(spec.rubyforge_project, spec.name, spec.version, file)
|
|
79
87
|
end
|
|
80
88
|
end
|
data/lib/state_machine.rb
CHANGED
|
@@ -67,6 +67,8 @@ module PluginAWeek #:nodoc:
|
|
|
67
67
|
|
|
68
68
|
attribute_keys = (attributes || {}).keys.map!(&:to_s)
|
|
69
69
|
|
|
70
|
+
# Set the initial value of each state machine as long as the value wasn't
|
|
71
|
+
# included in the attribute hash passed in
|
|
70
72
|
self.class.state_machines.each do |attribute, machine|
|
|
71
73
|
unless attribute_keys.include?(attribute)
|
|
72
74
|
send("#{attribute}=", machine.initial_state(self))
|
|
@@ -80,7 +82,7 @@ module PluginAWeek #:nodoc:
|
|
|
80
82
|
def run_initial_state_machine_actions
|
|
81
83
|
self.class.state_machines.each do |attribute, machine|
|
|
82
84
|
callback = "after_enter_#{attribute}_#{self[attribute]}"
|
|
83
|
-
run_callbacks(callback) if self.class.respond_to?(callback)
|
|
85
|
+
run_callbacks(callback) if self[attribute] && self.class.respond_to?(callback)
|
|
84
86
|
end
|
|
85
87
|
end
|
|
86
88
|
end
|
data/lib/state_machine/event.rb
CHANGED
|
@@ -22,7 +22,7 @@ module PluginAWeek #:nodoc:
|
|
|
22
22
|
@name = name
|
|
23
23
|
@options = options.stringify_keys
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
add_transition_actions
|
|
26
26
|
add_transition_callbacks
|
|
27
27
|
add_event_callbacks
|
|
28
28
|
end
|
|
@@ -46,49 +46,66 @@ module PluginAWeek #:nodoc:
|
|
|
46
46
|
options.assert_valid_keys(:to, :from, :if, :unless)
|
|
47
47
|
raise ArgumentError, ':to state must be specified' unless options.include?(:to)
|
|
48
48
|
|
|
49
|
+
# Get the states involved in the transition
|
|
49
50
|
to_state = options.delete(:to)
|
|
50
51
|
from_states = Array(options.delete(:from))
|
|
52
|
+
|
|
51
53
|
from_states.collect do |from_state|
|
|
52
|
-
# Create the actual transition that will update records when
|
|
54
|
+
# Create the actual transition that will update records when performed
|
|
53
55
|
transition = Transition.new(self, from_state, to_state)
|
|
54
56
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
callback = Proc.new do |record, *args|
|
|
59
|
-
transition.can_perform_on?(record) &&
|
|
60
|
-
invoke_event_callbacks(:before, record, *args) != false &&
|
|
61
|
-
transition.perform(record, *args) &&
|
|
62
|
-
invoke_event_callbacks(:after, record, *args) != false
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Add the callback to the model
|
|
57
|
+
# Add the callback to the model. If the callback fails, then the next
|
|
58
|
+
# available callback for the event will run until one is successful.
|
|
59
|
+
callback = Proc.new {|record, *args| try_transition(transition, false, record, *args)}
|
|
66
60
|
owner_class.send("transition_on_#{name}", callback, options)
|
|
67
61
|
|
|
62
|
+
# Add the callback! to the model similar to above
|
|
63
|
+
callback = Proc.new {|record, *args| try_transition(transition, true, record, *args)}
|
|
64
|
+
owner_class.send("transition_bang_on_#{name}", callback, options)
|
|
65
|
+
|
|
68
66
|
transition
|
|
69
67
|
end
|
|
70
68
|
end
|
|
71
69
|
|
|
72
|
-
# Attempts to
|
|
70
|
+
# Attempts to perform one of the event's transitions for the given record
|
|
71
|
+
def fire(record, *args)
|
|
72
|
+
record.class.transaction {invoke_transition_callbacks(record, false, *args) || raise(ActiveRecord::Rollback)} || false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Attempts to perform one of the event's transitions for the given record.
|
|
76
|
+
# If the transition cannot be made, then a PluginAWeek::StateMachine::InvalidTransition
|
|
77
|
+
# error will be raised.
|
|
73
78
|
def fire!(record, *args)
|
|
74
|
-
|
|
75
|
-
record.class.transaction {success = invoke_transition_callbacks(record, *args) == true || raise(ActiveRecord::Rollback)}
|
|
76
|
-
success
|
|
79
|
+
record.class.transaction {invoke_transition_callbacks(record, true, *args) || raise(ActiveRecord::Rollback)} || raise(PluginAWeek::StateMachine::InvalidTransition)
|
|
77
80
|
end
|
|
78
81
|
|
|
79
82
|
private
|
|
80
|
-
# Add
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
# Add the various instance methods that can transition the record using
|
|
84
|
+
# the current event
|
|
85
|
+
def add_transition_actions
|
|
86
|
+
name = self.name
|
|
87
|
+
owner_class = self.owner_class
|
|
88
|
+
machine = self.machine
|
|
89
|
+
|
|
90
|
+
owner_class.class_eval do
|
|
91
|
+
# Fires the event, returning true/false
|
|
92
|
+
define_method(name) do |*args|
|
|
93
|
+
owner_class.state_machines[machine.attribute].events[name].fire(self, *args)
|
|
85
94
|
end
|
|
86
|
-
|
|
95
|
+
|
|
96
|
+
# Fires the event, raising an exception if it fails
|
|
97
|
+
define_method("#{name}!") do |*args|
|
|
98
|
+
owner_class.state_machines[machine.attribute].events[name].fire!(self, *args)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
87
101
|
end
|
|
88
102
|
|
|
89
103
|
# Defines callbacks for invoking transitions when this event is performed
|
|
90
104
|
def add_transition_callbacks
|
|
91
|
-
|
|
105
|
+
%W(transition transition_bang).each do |callback_name|
|
|
106
|
+
callback_name = "#{callback_name}_on_#{name}"
|
|
107
|
+
owner_class.define_callbacks(callback_name)
|
|
108
|
+
end
|
|
92
109
|
end
|
|
93
110
|
|
|
94
111
|
# Adds the before/after callbacks for when the event is performed
|
|
@@ -102,7 +119,25 @@ module PluginAWeek #:nodoc:
|
|
|
102
119
|
end
|
|
103
120
|
end
|
|
104
121
|
|
|
105
|
-
#
|
|
122
|
+
# Attempts to perform the given transition. If it can't be performed based
|
|
123
|
+
# on the state of the given record, then the transition will be skipped
|
|
124
|
+
# and the next available one will be tried.
|
|
125
|
+
#
|
|
126
|
+
# If +bang+ is specified, then perform! will be called on the transition.
|
|
127
|
+
# Otherwise, the default +perform+ will be invoked.
|
|
128
|
+
def try_transition(transition, bang, record, *args)
|
|
129
|
+
if transition.can_perform_on?(record)
|
|
130
|
+
return false if invoke_event_callbacks(:before, record, *args) == false
|
|
131
|
+
result = bang ? transition.perform!(record, *args) : transition.perform(record, *args)
|
|
132
|
+
invoke_event_callbacks(:after, record, *args)
|
|
133
|
+
result
|
|
134
|
+
else
|
|
135
|
+
# Indicate that the transition cannot be performed
|
|
136
|
+
:skip
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Invokes a particulary type of callback for the event
|
|
106
141
|
def invoke_event_callbacks(type, record, *args)
|
|
107
142
|
args = [record] + args
|
|
108
143
|
|
|
@@ -113,14 +148,19 @@ module PluginAWeek #:nodoc:
|
|
|
113
148
|
end
|
|
114
149
|
|
|
115
150
|
# Invokes the callbacks for each transition in order to find one that
|
|
116
|
-
# completes successfully
|
|
117
|
-
|
|
151
|
+
# completes successfully.
|
|
152
|
+
#
|
|
153
|
+
# +bang+ indicates whether perform or perform! will be invoked on the
|
|
154
|
+
# transitions in the callback chain
|
|
155
|
+
def invoke_transition_callbacks(record, bang, *args)
|
|
118
156
|
args = [record] + args
|
|
157
|
+
callback_chain = "transition#{'_bang' if bang}_on_#{name}_callback_chain"
|
|
119
158
|
|
|
120
|
-
record.class.send(
|
|
159
|
+
result = record.class.send(callback_chain).each do |callback|
|
|
121
160
|
result = callback.call(*args)
|
|
122
|
-
break result if result
|
|
161
|
+
break result if [true, false].include?(result)
|
|
123
162
|
end
|
|
163
|
+
result == true
|
|
124
164
|
end
|
|
125
165
|
end
|
|
126
166
|
end
|
|
@@ -77,7 +77,8 @@ module PluginAWeek #:nodoc:
|
|
|
77
77
|
#
|
|
78
78
|
# The following instance methods are generated when a new event is defined
|
|
79
79
|
# (the "park" event is used as an example):
|
|
80
|
-
# * <tt>park
|
|
80
|
+
# * <tt>park(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional +args+ list which is passed to the event callbacks.
|
|
81
|
+
# * <tt>park!(*args)</tt> - Fires the "park" event, transitioning from the current state to the next valid state. This takes an optional +args+ list which is passed to the event callbacks. If the transition cannot happen (for validation, database, etc. reasons), then an error will be raised
|
|
81
82
|
#
|
|
82
83
|
# == Defining transitions
|
|
83
84
|
#
|
|
@@ -113,26 +114,25 @@ module PluginAWeek #:nodoc:
|
|
|
113
114
|
end
|
|
114
115
|
|
|
115
116
|
# Define state callbacks
|
|
116
|
-
%w(before_exit before_enter after_exit after_enter).each do |
|
|
117
|
-
|
|
118
|
-
def #{callback}(state, callback)
|
|
119
|
-
callback_name = "#{callback}_\#{attribute}_\#{state}"
|
|
120
|
-
owner_class.define_callbacks(callback_name)
|
|
121
|
-
owner_class.send(callback_name, callback)
|
|
122
|
-
end
|
|
123
|
-
end_eval
|
|
117
|
+
%w(before_exit before_enter after_exit after_enter).each do |callback_type|
|
|
118
|
+
define_method(callback_type) {|state, callback| add_callback(callback_type, state, callback)}
|
|
124
119
|
end
|
|
125
120
|
|
|
126
121
|
private
|
|
122
|
+
# Adds the given callback to the callback chain during a state transition
|
|
123
|
+
def add_callback(type, state, callback)
|
|
124
|
+
callback_name = "#{type}_#{attribute}_#{state}"
|
|
125
|
+
owner_class.define_callbacks(callback_name)
|
|
126
|
+
owner_class.send(callback_name, callback)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Add named scopes for finding records with a particular value or values
|
|
130
|
+
# for the attribute
|
|
127
131
|
def add_named_scopes
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
owner_class.
|
|
132
|
-
named_scope :#{scope_name}, Proc.new {|*values| {
|
|
133
|
-
:conditions => {:#{attribute} => values.flatten}
|
|
134
|
-
}}
|
|
135
|
-
end_eos
|
|
132
|
+
[attribute, attribute.pluralize].each do |name|
|
|
133
|
+
unless owner_class.respond_to?("with_#{name}")
|
|
134
|
+
name = "with_#{name}"
|
|
135
|
+
owner_class.named_scope name, Proc.new {|*values| {:conditions => {attribute => values.flatten}}}
|
|
136
136
|
end
|
|
137
137
|
end
|
|
138
138
|
end
|