flojo 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -0
- data/MIT_LICENSE +11 -0
- data/Manifest +11 -0
- data/README.rdoc +109 -0
- data/Rakefile +20 -0
- data/flojo.gemspec +31 -0
- data/lib/flojo.rb +91 -0
- data/test/test_active_record.rb +115 -0
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +124 -0
- data/test/test_poro.rb +67 -0
- metadata +87 -0
data/CHANGELOG
ADDED
File without changes
|
data/MIT_LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
LICENSE:
|
2
|
+
|
3
|
+
(The MIT License)
|
4
|
+
|
5
|
+
Copyright © 2010 Joey Adarkwah
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
10
|
+
|
11
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
= Flojo
|
2
|
+
|
3
|
+
Flojo is a simple ActiveRecord aware state machine module, but it will also work with any plain old ruby object.
|
4
|
+
When used within an ActiveRecord subclass, flojo events can automatically save a record after a transition.
|
5
|
+
|
6
|
+
After including the module in your class and configuring it with an event _event_, and a state _state_,
|
7
|
+
you can interact with instances of that class using the dynamically generated methods of the following form:
|
8
|
+
|
9
|
+
object.wf_event - Triggers event and invokes any applicable transitions
|
10
|
+
object.wf_event! - Behaves just like object.wf_event but will also persist object.
|
11
|
+
object.wf_state? - Returns true if the current workflow state is _state_.
|
12
|
+
object.wf_current_state - Returns the objects current state.
|
13
|
+
|
14
|
+
To avoid method name collisions with your classes, Flojo methods are usually prefixed with "wf_".
|
15
|
+
|
16
|
+
See test.rb, test_active_record.rb and test_helper.rb for concrete examples.
|
17
|
+
|
18
|
+
== Install
|
19
|
+
|
20
|
+
Simply require flojo.rb.
|
21
|
+
If you are using rails, copy flojo.rb to your project's <b>lib</b> folder.
|
22
|
+
Alternatively, install the gem with gem install flojo.
|
23
|
+
|
24
|
+
== Usage
|
25
|
+
|
26
|
+
require 'flojo'
|
27
|
+
|
28
|
+
class Baby
|
29
|
+
# Include the module
|
30
|
+
include Flojo
|
31
|
+
|
32
|
+
# configure the states. The first element represents the initial state
|
33
|
+
workflow_states [:asleep, :crying, :pooping, :chilling, :puking]
|
34
|
+
|
35
|
+
# configure your transitions. Using: transition :begin_state, :end_state
|
36
|
+
event :feed do
|
37
|
+
transition :crying, :chilling
|
38
|
+
transition :chilling, :pooping
|
39
|
+
end
|
40
|
+
|
41
|
+
event :rock do
|
42
|
+
transition :crying, :chilling
|
43
|
+
transition :chilling, :asleep
|
44
|
+
transition :asleep, :pooping
|
45
|
+
end
|
46
|
+
|
47
|
+
event :burp do
|
48
|
+
transition :crying, :chilling
|
49
|
+
transition :chilling, :puking
|
50
|
+
end
|
51
|
+
|
52
|
+
event :wake do
|
53
|
+
transition :asleep, :crying
|
54
|
+
end
|
55
|
+
|
56
|
+
event :spank do
|
57
|
+
# :any is a wildcard state and is only valid as a begin state.
|
58
|
+
transition :any, :crying
|
59
|
+
end
|
60
|
+
|
61
|
+
# event, enter state and exit state callbacks should take the form wf_on_event,
|
62
|
+
wf_on_enter_state and wf_on_exit_state and are only called if they are defined
|
63
|
+
def wf_on_spank
|
64
|
+
puts "Never spank a baby! Somebody dial 911!"
|
65
|
+
end
|
66
|
+
|
67
|
+
def wf_on_enter_chilling
|
68
|
+
puts "drool and fart"
|
69
|
+
end
|
70
|
+
|
71
|
+
def wf_on_exit_asleep
|
72
|
+
puts "waaaaah waaaaah waaaaaah"
|
73
|
+
end
|
74
|
+
|
75
|
+
# called only after an event yields a transition.
|
76
|
+
def wf_after_transition
|
77
|
+
puts "Send a tweet baby is alive and kicking"
|
78
|
+
end
|
79
|
+
|
80
|
+
# called only after an event triggers a save.
|
81
|
+
def wf_after_save
|
82
|
+
puts "Send a tweet saying baby was saved"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
baby = Baby.new
|
89
|
+
baby.wf_asleep? #Should return true
|
90
|
+
|
91
|
+
baby.wf_wake
|
92
|
+
baby.wf_current_state #Should return :awake
|
93
|
+
|
94
|
+
== ActiveRecord specific details
|
95
|
+
If you want the current workflow state persisted, your table needs to have a string column named _wf_state_.
|
96
|
+
If your table has a _wf_last_event_ column, that column will store the most recent event triggered on the object.
|
97
|
+
The module will still work if the wf_state is absent but the workflow state will be transient.
|
98
|
+
When used within an ActiveRecord subclass, the "!" versions of the wf_event methods will -
|
99
|
+
trigger a record save immediately after transition.
|
100
|
+
|
101
|
+
eg:
|
102
|
+
baby.wf_feed - Will *not* trigger a save.
|
103
|
+
baby.wf_feed! - *Will* trigger a save.
|
104
|
+
|
105
|
+
== Copyright
|
106
|
+
|
107
|
+
Copyright (c) 2010 Joey Adarkwah
|
108
|
+
|
109
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('flojo', '0.5.0') do |p|
|
6
|
+
p.description = "ActiveRecord aware workflow (state machine) module that will also work with any plain old ruby object."
|
7
|
+
p.summary = "When used within an ActiveRecord subclass, flojo events can automatically save a record after a transition.
|
8
|
+
|
9
|
+
After including the module in your class and configuring it with an event _event_, and a state _state_, you can interact with instances of that class using the dynamically generated methods of the following form:
|
10
|
+
|
11
|
+
+object.wf_event+ - Triggers event and invokes any applicable transitions
|
12
|
+
+object.wf_event!+ - Behaves just like +object.wf_event+ but will also persist object.
|
13
|
+
+object.wf_state?+ - Returns true if the current workflow state is _state_.
|
14
|
+
+object.wf_current_state+ - Returns the objects current state."
|
15
|
+
|
16
|
+
p.url = "http://github.com/alternegro/flojo"
|
17
|
+
p.author = "Joey Adarkwah"
|
18
|
+
p.email = "alternegro @nospam@ me.com"
|
19
|
+
p.development_dependencies = []
|
20
|
+
end
|
data/flojo.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{flojo}
|
5
|
+
s.version = "0.5.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Joey Adarkwah"]
|
9
|
+
s.date = %q{2010-12-07}
|
10
|
+
s.description = %q{ActiveRecord aware workflow (state machine) module that will also work with any plain old ruby object.}
|
11
|
+
s.email = %q{alternegro @nospam@ me.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "README.rdoc", "lib/flojo.rb"]
|
13
|
+
s.files = ["CHANGELOG", "MIT_LICENSE", "README.rdoc", "Rakefile", "flojo.gemspec", "lib/flojo.rb", "test/test_active_record.rb", "test/test_db.sqlite3", "test/test_helper.rb", "test/test_poro.rb", "Manifest"]
|
14
|
+
s.homepage = %q{http://github.com/alternegro/flojo}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Flojo", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{flojo}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{When used within an ActiveRecord subclass, flojo events can automatically save a record after a transition. After including the module in your class and configuring it with an event _event_, and a state _state_, you can interact with instances of that class using the dynamically generated methods of the following form: +object.wf_event+ - Triggers event and invokes any applicable transitions +object.wf_event!+ - Behaves just like +object.wf_event+ but will also persist object. +object.wf_state?+ - Returns true if the current workflow state is _state_. +object.wf_current_state+ - Returns the objects current state.}
|
20
|
+
s.test_files = ["test/test_active_record.rb", "test/test_helper.rb", "test/test_poro.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
else
|
28
|
+
end
|
29
|
+
else
|
30
|
+
end
|
31
|
+
end
|
data/lib/flojo.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Flojo
|
2
|
+
def Flojo.included(host)
|
3
|
+
|
4
|
+
host.class_eval do
|
5
|
+
attr_writer :wf_current_state
|
6
|
+
attr_accessor :_wf_current_event_transition_map, :wf_previous_state
|
7
|
+
protected :wf_current_state=, :_set_workflow_state
|
8
|
+
|
9
|
+
def wf_current_state
|
10
|
+
wf_current_state = @wf_current_state.nil? ? wf_initial_state : @wf_current_state
|
11
|
+
end
|
12
|
+
|
13
|
+
def wf_initial_state
|
14
|
+
st = self.respond_to?(:wf_state) ? (wf_state || self.class.wf_states[0]) : self.class.wf_states[0]
|
15
|
+
st.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.workflow_states(s)
|
19
|
+
@workflow_states = s.uniq
|
20
|
+
self.synthesize_state_query_methods
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.wf_states
|
24
|
+
@workflow_states
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.synthesize_state_query_methods
|
28
|
+
@workflow_states.each {|st| define_method("wf_#{st}?") { st.eql?(wf_current_state)}}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.valid_state(*states)
|
32
|
+
states.each {|st| return false if !@workflow_states.include?(st) && (st != :any)}
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
host.class_eval("def self.transition(start_state, end_state); raise 'Invalid Transition State' unless self.valid_state(start_state, end_state); @wf_current_event_transition_map[start_state]=end_state; end")
|
39
|
+
|
40
|
+
host.class_eval do
|
41
|
+
def self.event(e, &actions)
|
42
|
+
event_token = "wf_#{e}"
|
43
|
+
map_token = "#{event_token}_transition_map"
|
44
|
+
|
45
|
+
#setup a transition map class instance variable and corresponding accessors for each event.
|
46
|
+
self.class_eval("@#{map_token}={}")
|
47
|
+
self.class_eval("def self.#{map_token}; return @#{map_token}; end")
|
48
|
+
self.class_eval("def self.wf_current_event_transition_map=(cem); @wf_current_event_transition_map=cem; end" )
|
49
|
+
self.class_eval("@wf_current_event_transition_map = @#{map_token}={}")
|
50
|
+
self.class_eval("def self.wf_current_event_transition_map; @wf_current_event_transition_map; end" )
|
51
|
+
self.class_eval("def self.wf_current_event_transition_map=(cem); @wf_current_event_transition_map=cem; end" )
|
52
|
+
|
53
|
+
#instance event_methods
|
54
|
+
self.class_eval("def #{event_token}; self._wf_current_event_transition_map=self.class.#{map_token}; _set_workflow_state \"#{e}\"; end\n")
|
55
|
+
self.class_eval("def #{event_token}!; #{event_token}; _wf_after_transition_save!; wf_after_save if self.class.method_defined? :wf_after_save; end\n" )
|
56
|
+
|
57
|
+
#Calls transitions in event block
|
58
|
+
actions.call
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def _set_workflow_state(event=nil)
|
67
|
+
self.wf_previous_state = self.wf_current_state
|
68
|
+
self.wf_last_event = event if self.respond_to?(:wf_last_event)
|
69
|
+
|
70
|
+
state = self._wf_current_event_transition_map[self.wf_current_state] || self._wf_current_event_transition_map[:any]
|
71
|
+
|
72
|
+
if (state != self.wf_current_state) && (!state.nil?)
|
73
|
+
self.wf_current_state=state
|
74
|
+
|
75
|
+
eval("wf_on_#{event}") if event && (self.class.method_defined? "wf_on_#{event}")
|
76
|
+
eval("wf_on_exit_#{wf_current_state}") if self.class.method_defined? "wf_on_exit_#{wf_current_state}"
|
77
|
+
eval("wf_on_enter_#{state}") if self.class.method_defined? "wf_on_enter_#{state}"
|
78
|
+
|
79
|
+
wf_after_transition if self.class.method_defined? :wf_after_transition
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def _wf_after_transition_save!
|
84
|
+
self.wf_state = self.wf_current_state if self.respond_to?(:wf_state=)
|
85
|
+
save! if self.respond_to?(:save!)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'flojo'
|
5
|
+
require 'test_helper'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
:adapter => "sqlite3",
|
9
|
+
:database => "test/test_db.sqlite3"
|
10
|
+
)
|
11
|
+
|
12
|
+
class Counter < ActiveRecord::Base
|
13
|
+
include Flojo
|
14
|
+
|
15
|
+
workflow_states [:one, :two, :three, :four]
|
16
|
+
|
17
|
+
event :up do
|
18
|
+
transition :one, :two
|
19
|
+
transition :two, :three
|
20
|
+
transition :three, :four
|
21
|
+
end
|
22
|
+
|
23
|
+
event :reset do
|
24
|
+
transition :two, :one
|
25
|
+
transition :three, :one
|
26
|
+
transition :four, :one
|
27
|
+
end
|
28
|
+
|
29
|
+
event :down do
|
30
|
+
transition :one, :three
|
31
|
+
transition :two, :one
|
32
|
+
transition :three, :two
|
33
|
+
end
|
34
|
+
|
35
|
+
def wf_on_enter_one
|
36
|
+
"on_enter_one"
|
37
|
+
end
|
38
|
+
|
39
|
+
def wf_on_exit_one
|
40
|
+
"on_exit_one"
|
41
|
+
end
|
42
|
+
|
43
|
+
def wf_on_down
|
44
|
+
"on_down"
|
45
|
+
end
|
46
|
+
|
47
|
+
def wf_on_up
|
48
|
+
"on_up"
|
49
|
+
end
|
50
|
+
|
51
|
+
def wf_after_transition
|
52
|
+
"wf_after_transition"
|
53
|
+
end
|
54
|
+
|
55
|
+
def wf_after_save
|
56
|
+
"wf_after_save"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class TestWorkflow < Test::Unit::TestCase
|
63
|
+
include CommonTests
|
64
|
+
def setup
|
65
|
+
Counter.delete_all
|
66
|
+
@b = Baby.new
|
67
|
+
@c = Counter.new
|
68
|
+
@c.name = "First"
|
69
|
+
@c.save
|
70
|
+
end
|
71
|
+
|
72
|
+
def teardown
|
73
|
+
Counter.delete_all
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_stored_counter
|
77
|
+
stored_counter = Counter.first
|
78
|
+
assert_equal(:one, stored_counter.wf_current_state, "Initial state must be :one")
|
79
|
+
|
80
|
+
stored_counter.wf_up!
|
81
|
+
assert_equal(:two, stored_counter.wf_current_state, "Current state must be :two")
|
82
|
+
|
83
|
+
stored_counter.reload
|
84
|
+
assert_equal("two", stored_counter.wf_state, "Stored state must be two")
|
85
|
+
assert_equal(:two, stored_counter.wf_current_state, "Current state must be :two")
|
86
|
+
|
87
|
+
stored_counter.wf_state = "three"
|
88
|
+
stored_counter.save!
|
89
|
+
|
90
|
+
assert_equal("three", stored_counter.wf_state, "Stored state must be three")
|
91
|
+
|
92
|
+
stored_counter = Counter.first
|
93
|
+
assert_equal(:three, stored_counter.wf_current_state, "Current state must be :three")
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_new_counter
|
97
|
+
new_counter = Counter.new
|
98
|
+
assert_equal(nil, new_counter.wf_state, "Stored state must be nil")
|
99
|
+
assert_equal(:one, new_counter.wf_current_state, "Initial state must be :one")
|
100
|
+
assert_equal(1, Counter.count)
|
101
|
+
|
102
|
+
new_counter.wf_up!
|
103
|
+
assert_equal(2, Counter.count)
|
104
|
+
assert_equal("up", new_counter.wf_last_event)
|
105
|
+
|
106
|
+
new_counter = Counter.last
|
107
|
+
assert_equal("two", new_counter.wf_state, "Stored state must be nil")
|
108
|
+
assert_equal(:two, new_counter.wf_current_state, "Initial state must be :two")
|
109
|
+
assert_equal("up", new_counter.wf_last_event)
|
110
|
+
new_counter.wf_down
|
111
|
+
assert_equal("down", new_counter.wf_last_event)
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module CommonTests
|
2
|
+
def test_events
|
3
|
+
assert_equal(:one, @c.wf_current_state, "Initial state must be :one")
|
4
|
+
assert_equal(@c.wf_up!, "wf_after_save", "Last method called should be wf_after_save hook")
|
5
|
+
assert_equal(:one, @c.wf_previous_state)
|
6
|
+
assert_equal(:two, @c.wf_current_state)
|
7
|
+
|
8
|
+
@c.wf_down!
|
9
|
+
|
10
|
+
assert_equal(:one, @c.wf_current_state)
|
11
|
+
|
12
|
+
assert_respond_to(@c, :wf_up!)
|
13
|
+
assert_respond_to(@c, :wf_down!)
|
14
|
+
assert_respond_to(@c, :wf_reset!)
|
15
|
+
|
16
|
+
@c.wf_up #to :two
|
17
|
+
@c.wf_up #to :three
|
18
|
+
|
19
|
+
@c.wf_up #to :four
|
20
|
+
|
21
|
+
@c.wf_up #to :four
|
22
|
+
|
23
|
+
assert_nil(@c.wf_up, "No transition expected")
|
24
|
+
|
25
|
+
@c.wf_up! #to :four
|
26
|
+
|
27
|
+
assert_nil(@c.wf_up, "No transition expected")
|
28
|
+
|
29
|
+
assert(@c.wf_four?, "Should still be at :four")
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_states
|
34
|
+
assert_equal(Counter.wf_states, [:one, :two, :three, :four])
|
35
|
+
assert_equal(Baby.wf_states, [:asleep, :crying, :pooping, :chilling, :puking] )
|
36
|
+
assert_respond_to(@c, :wf_one?)
|
37
|
+
assert_respond_to(@c, :wf_two?)
|
38
|
+
assert_respond_to(@c, :wf_three?)
|
39
|
+
assert_respond_to(@c, :wf_four?)
|
40
|
+
|
41
|
+
#states are mutually exclusive and there is always one state
|
42
|
+
assert(@c.wf_one? || @c.wf_two? || @c.wf_three? || @c.wf_four?)
|
43
|
+
assert(!(@c.wf_one? && @c.wf_two? && @c.wf_three? && @c.wf_four?))
|
44
|
+
|
45
|
+
@c.wf_up! #to :two
|
46
|
+
assert(@c.wf_two?)
|
47
|
+
assert(!(@c.wf_one? || @c.wf_three? || @c.wf_four?))
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_wildcard
|
52
|
+
assert_equal(@b.wf_current_state, :asleep)
|
53
|
+
assert(@b.wf_asleep?, "Initial state is asleep")
|
54
|
+
@b.wf_spank; assert(@b.wf_crying?, "Spanking must always lead to crying")
|
55
|
+
@b.wf_feed; assert(@b.wf_chilling?, "Feed must move from crying to chilling")
|
56
|
+
@b.wf_burp; assert(@b.wf_puking?, "Burp must move from chilling to puking")
|
57
|
+
@b.wf_spank; assert(@b.wf_crying?, "Spanking must always lead to crying")
|
58
|
+
@b.wf_rock; assert(@b.wf_chilling?, "Rock must move from crying to chilling")
|
59
|
+
@b.wf_spank; assert(@b.wf_crying?, "Spanking must always lead to crying")
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_inheritance
|
63
|
+
oc = OtherCounter.new
|
64
|
+
assert(oc.wf_a?)
|
65
|
+
oc.wf_up; assert(oc.wf_b?, "State must be :b after wf_up")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Baby
|
70
|
+
include Flojo
|
71
|
+
|
72
|
+
workflow_states [:asleep, :crying, :pooping, :chilling, :puking]
|
73
|
+
|
74
|
+
event :feed do
|
75
|
+
transition :crying, :chilling
|
76
|
+
transition :chilling, :pooping
|
77
|
+
end
|
78
|
+
|
79
|
+
event :rock do
|
80
|
+
transition :crying, :chilling
|
81
|
+
transition :chilling, :asleep
|
82
|
+
transition :asleep, :pooping
|
83
|
+
end
|
84
|
+
|
85
|
+
event :burp do
|
86
|
+
transition :crying, :chilling
|
87
|
+
transition :chilling, :puking
|
88
|
+
end
|
89
|
+
|
90
|
+
event :wake do
|
91
|
+
transition :asleep, :crying
|
92
|
+
end
|
93
|
+
|
94
|
+
event :spank do
|
95
|
+
transition :any, :crying
|
96
|
+
end
|
97
|
+
|
98
|
+
#enter and exit event handlers should take the form on_enter_xxx, on_exit_xxx where xxx is the state
|
99
|
+
def wf_on_enter_asleep
|
100
|
+
puts "I peed all over the bed"
|
101
|
+
end
|
102
|
+
|
103
|
+
def wf_on_enter_chilling
|
104
|
+
puts "drool and fart"
|
105
|
+
end
|
106
|
+
|
107
|
+
def wf_on_exit_asleep
|
108
|
+
puts "waaaaah waaaaah waaaaaah"
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
class OtherCounter < Baby
|
115
|
+
include Flojo
|
116
|
+
|
117
|
+
workflow_states [:a, :b, :c, :d]
|
118
|
+
|
119
|
+
event :up do
|
120
|
+
transition :a, :b
|
121
|
+
transition :b, :c
|
122
|
+
transition :c, :d
|
123
|
+
end
|
124
|
+
end
|
data/test/test_poro.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'flojo'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class Counter
|
6
|
+
include Flojo
|
7
|
+
|
8
|
+
workflow_states [:one, :two, :three, :four]
|
9
|
+
|
10
|
+
event :up do
|
11
|
+
transition :one, :two
|
12
|
+
transition :two, :three
|
13
|
+
transition :three, :four
|
14
|
+
end
|
15
|
+
|
16
|
+
event :reset do
|
17
|
+
transition :two, :one
|
18
|
+
transition :three, :one
|
19
|
+
transition :four, :one
|
20
|
+
end
|
21
|
+
|
22
|
+
event :down do
|
23
|
+
transition :one, :three
|
24
|
+
transition :two, :one
|
25
|
+
transition :three, :two
|
26
|
+
end
|
27
|
+
|
28
|
+
#enter and exit event handlers should take the form on_enter_xxx, on_exit_xxx where xxx is the state
|
29
|
+
def wf_on_enter_one
|
30
|
+
"on_enter_one"
|
31
|
+
end
|
32
|
+
|
33
|
+
def wf_on_exit_one
|
34
|
+
"on_exit_one"
|
35
|
+
end
|
36
|
+
|
37
|
+
def wf_on_down
|
38
|
+
"on_down"
|
39
|
+
end
|
40
|
+
|
41
|
+
def wf_on_up
|
42
|
+
"on_up"
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def wf_after_transition
|
47
|
+
"wf_after_transition"
|
48
|
+
end
|
49
|
+
|
50
|
+
def wf_after_save
|
51
|
+
"wf_after_save"
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
class TestState < Test::Unit::TestCase
|
59
|
+
include CommonTests
|
60
|
+
|
61
|
+
def setup
|
62
|
+
@c = Counter.new
|
63
|
+
@b = Baby.new
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flojo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Joey Adarkwah
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-07 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: ActiveRecord aware workflow (state machine) module that will also work with any plain old ruby object.
|
23
|
+
email: alternegro @nospam@ me.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- CHANGELOG
|
30
|
+
- README.rdoc
|
31
|
+
- lib/flojo.rb
|
32
|
+
files:
|
33
|
+
- CHANGELOG
|
34
|
+
- MIT_LICENSE
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- flojo.gemspec
|
38
|
+
- lib/flojo.rb
|
39
|
+
- test/test_active_record.rb
|
40
|
+
- test/test_db.sqlite3
|
41
|
+
- test/test_helper.rb
|
42
|
+
- test/test_poro.rb
|
43
|
+
- Manifest
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/alternegro/flojo
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --line-numbers
|
51
|
+
- --inline-source
|
52
|
+
- --title
|
53
|
+
- Flojo
|
54
|
+
- --main
|
55
|
+
- README.rdoc
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 11
|
73
|
+
segments:
|
74
|
+
- 1
|
75
|
+
- 2
|
76
|
+
version: "1.2"
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project: flojo
|
80
|
+
rubygems_version: 1.3.7
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: "When used within an ActiveRecord subclass, flojo events can automatically save a record after a transition. After including the module in your class and configuring it with an event _event_, and a state _state_, you can interact with instances of that class using the dynamically generated methods of the following form: +object.wf_event+ - Triggers event and invokes any applicable transitions +object.wf_event!+ - Behaves just like +object.wf_event+ but will also persist object. +object.wf_state?+ - Returns true if the current workflow state is _state_. +object.wf_current_state+ - Returns the objects current state."
|
84
|
+
test_files:
|
85
|
+
- test/test_active_record.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
- test/test_poro.rb
|