golem_statemachine 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the golem_statemachine plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the golem_statemachine plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'GolemStatemachine'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
@@ -0,0 +1,44 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../lib")
2
+ require 'golem'
3
+
4
+ class Document
5
+
6
+ def initialize
7
+ @has_signature = false
8
+ end
9
+
10
+ def sign!
11
+ @has_signature = true
12
+ end
13
+
14
+ def has_signature?
15
+ @has_signature
16
+ end
17
+
18
+ include Golem
19
+ define_statemachine do
20
+ initial_state :NEW
21
+
22
+ state :NEW do
23
+ on :submit, :to => :SUBMITTED
24
+ end
25
+
26
+ state :SUBMITTED do
27
+ on :review do
28
+ transition :to => :APPROVED, :if => :has_signature?
29
+ transition :to => :REJECTED
30
+ end
31
+ end
32
+
33
+ state :REJECTED do
34
+ on :revise, :to => :REVISED
35
+ end
36
+
37
+ state :REVISED do
38
+ on :submit, :to => :SUBMITTED
39
+ end
40
+
41
+ state :APPROVED
42
+ end
43
+ end
44
+
@@ -0,0 +1,100 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../lib")
2
+ require 'golem'
3
+
4
+ class Monster
5
+ include Golem
6
+
7
+ attr_accessor :deeds
8
+ attr_accessor :journal
9
+
10
+ def initialize
11
+ @deeds = []
12
+ @journal = []
13
+ end
14
+
15
+ def stretch
16
+ @deeds << :stretched
17
+ end
18
+
19
+ def grumble
20
+ @deeds << :grumbled
21
+ end
22
+
23
+ def yawn
24
+ @deeds << :yawned
25
+ end
26
+
27
+ def barf
28
+ @deeds << :barfed
29
+ end
30
+
31
+ def likes_food?(food)
32
+ food == :hamburger
33
+ end
34
+
35
+ def hates_food?(food)
36
+ food == :tofu
37
+ end
38
+
39
+ def is_tired?
40
+ deeds.last == :yawned
41
+ end
42
+
43
+ def mouth_is_open?
44
+ mouth_state == :open
45
+ end
46
+
47
+ def write_in_journal(event, transition, *args)
48
+ @journal << event.name if event.kind_of?(Golem::Model::Event) && transition.kind_of?(Golem::Model::Transition)
49
+ end
50
+
51
+ # statemachine representing the Monster's affect (i.e. how it is feeling)
52
+ define_statemachine :affect do
53
+ initial_state :sleeping
54
+ on_all_transitions :write_in_journal
55
+
56
+ state :sleeping do
57
+ on :wake_up, :to => :hungry
58
+ exit :stretch
59
+ end
60
+
61
+ state :hungry do
62
+ enter :grumble
63
+ on :feed, :if => :mouth_is_open?, :guard_options => {:failure_message => "its mouth is not open"} do
64
+ transition :to => :satiated do
65
+ guard :likes_food?, :failure_message => "it does not like the food"
66
+ action {|monster,food| monster.deeds << "ate_tasty_#{food}".to_sym}
67
+ end
68
+ transition :to => :self do
69
+ guard :hates_food?, :failure_message => "it does not hate the food"
70
+ action :barf
71
+ end
72
+ transition :to => :self do
73
+ action {|monster,food| monster.deeds << "ate_#{food}".to_sym}
74
+ end
75
+ end
76
+ on :lullaby, :action => :yawn
77
+ on :tickle do
78
+ action {|monster| monster.deeds << :giggled }
79
+ end
80
+ end
81
+
82
+ state :satiated do
83
+ on :lullaby do
84
+ transition :to => :sleeping, :if => :is_tired?
85
+ transition :to => :self, :action => :yawn
86
+ end
87
+ end
88
+ end
89
+
90
+ # secondary statemachine representing the Monster's mouth
91
+ define_statemachine :mouth do
92
+ initial_state :closed
93
+ state :open do
94
+ on :lullaby, :to => :closed
95
+ end
96
+ state :closed do
97
+ on :tickle, :to => :open
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,138 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../lib")
2
+ require 'golem'
3
+
4
+
5
+ class Seminar
6
+ attr_accessor :status
7
+ attr_accessor :students
8
+ attr_accessor :waiting_list
9
+ attr_accessor :max_class_size
10
+ attr_accessor :notifications_sent
11
+
12
+ @@out = STDOUT
13
+
14
+ def self.output=(output)
15
+ @@out = output
16
+ end
17
+
18
+ def initialize
19
+ @students = [] # list of students enrolled in the course
20
+ @max_class_size = 5
21
+ @notifications_sent = []
22
+ end
23
+
24
+ def seats_available
25
+ @max_class_size - @students.size
26
+ end
27
+
28
+ def waiting_list_is_empty?
29
+ @waiting_list.empty?
30
+ end
31
+
32
+ def student_is_enrolled?(student)
33
+ @students.include? student
34
+ end
35
+
36
+ def add_student_to_waiting_list(student)
37
+ @waiting_list << student
38
+ end
39
+
40
+ def create_waiting_list
41
+ @waiting_list = []
42
+ end
43
+
44
+ def notify_waiting_list_that_enrollment_is_closed
45
+ @waiting_list.each{|student| self.notifications_sent << "#{student}: waiting list is closed"}
46
+ end
47
+
48
+ def notify_students_that_the_seminar_is_cancelled
49
+ (@students + @waiting_list).each{|student| self.notifications_sent << "#{student}: the seminar has been cancelled"}
50
+ end
51
+
52
+
53
+ include Golem
54
+
55
+ define_statemachine do
56
+ initial_state :proposed
57
+ state_attribute :status
58
+
59
+ state :proposed do
60
+ on :schedule, :to => :scheduled
61
+ end
62
+
63
+ state :scheduled do
64
+ on :open, :to => :open_for_enrollment
65
+ end
66
+
67
+ state :open_for_enrollment do
68
+ on :close, :to => :closed_to_enrollment
69
+ on :enroll_student do
70
+ transition do
71
+ guard {|seminar, student| !seminar.student_is_enrolled?(student) && seminar.seats_available > 1 }
72
+ action {|seminar, student| seminar.students << student}
73
+ end
74
+ transition :to => :full do
75
+ guard {|seminar, student| !seminar.student_is_enrolled?(student) }
76
+ action do |seminar, student|
77
+ seminar.create_waiting_list
78
+ if seminar.seats_available == 1
79
+ seminar.students << student
80
+ else
81
+ seminar.add_student_to_waiting_list(student)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ on :drop_student do
87
+ transition :if => :student_is_enrolled? do
88
+ action {|seminar, student| seminar.students.delete student}
89
+ end
90
+ end
91
+ end
92
+
93
+ state :full do
94
+ on :move_to_bigger_classroom, :to => :open_for_enrollment,
95
+ :action => Proc.new{|seminar, additional_seats| seminar.max_class_size += additional_seats}
96
+ # Note that this :if condition applies to all transitions inside the event, in addition to each
97
+ # transaction's own :if/guard statement.
98
+ on :drop_student, :if => :student_is_enrolled? do
99
+ transition :to => :open_for_enrollment, :if => :waiting_list_is_empty? do
100
+ action {|seminar, student| seminar.students.delete student}
101
+ end
102
+ transition do
103
+ action do |seminar, student|
104
+ seminar.students.delete student
105
+ seminar.enroll_student seminar.waiting_list.shift
106
+ end
107
+ end
108
+ end
109
+ on :enroll_student, :if => Proc.new{|seminar, student| !seminar.student_is_enrolled?(student)} do
110
+ transition do
111
+ guard {|seminar, student| seminar.seats_available > 0}
112
+ action {|seminar, student| seminar.students << student}
113
+ end
114
+ transition :action => :add_student_to_waiting_list
115
+ end
116
+ on :close, :to => :closed_to_enrollment
117
+ end
118
+
119
+ state :closed_to_enrollment do
120
+ enter :notify_waiting_list_that_enrollment_is_closed
121
+ end
122
+
123
+ state :cancelled do
124
+ enter :notify_students_that_the_seminar_is_cancelled
125
+ end
126
+
127
+ # The 'cancel' event can occur in all states.
128
+ all_states.each do |state|
129
+ state.on :cancel, :to => :cancelled
130
+ end
131
+
132
+ on_all_transitions do |seminar, event, transition, *event_args|
133
+ @@out.puts "==[#{event.name}(#{event_args.collect{|arg| arg.inspect}.join(",")})]==> #{transition.from.name} --> #{transition.to.name}"
134
+ @@out.puts " ENROLLED: #{seminar.students.inspect}"
135
+ @@out.puts " WAITING: #{seminar.waiting_list.inspect}"
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,141 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../lib")
2
+ require 'golem'
3
+
4
+
5
+ class Seminar
6
+ attr_accessor :status
7
+ attr_accessor :students
8
+ attr_accessor :waiting_list
9
+ attr_accessor :max_size
10
+
11
+ def initialize
12
+ @students = [] # list of students enrolled in the course
13
+ @max_class_size = 5
14
+ end
15
+
16
+ def seats_available?
17
+ @students.size < @max_class_size
18
+ end
19
+
20
+ def waiting_list_is_empty?
21
+ @waiting_list.empty?
22
+ end
23
+
24
+ def student_is_enrolled?(student)
25
+ @students.include? student
26
+ end
27
+
28
+ def add_student_to_waiting_list(student)
29
+ @waiting_list << student
30
+ end
31
+
32
+ def create_waiting_list
33
+ @waiting_list = []
34
+ end
35
+
36
+ def notify_waiting_list_that_enrollment_is_closed
37
+ @waiting_list.each{|student| puts "#{student}: waiting list is closed!"}
38
+ end
39
+
40
+ def notify_students_that_the_seminar_is_cancelled
41
+ (@students + @waiting_list).each{|student| puts "#{student}: the seminar has been cancelled!"}
42
+ end
43
+
44
+
45
+ include Golem
46
+
47
+ define_statemachine do
48
+ state :proposed do
49
+ on :schedule, :to => :scheduled
50
+ end
51
+
52
+ state :scheduled do
53
+ on :open, :to => :open_for_enrollment
54
+ end
55
+
56
+ state :open_for_enrollment do
57
+ on :close, :to => :closed_to_enrollment
58
+ on :enroll_student do
59
+ transition :if => :seats_available? do |seminar, student|
60
+ seminar.students << student
61
+ end
62
+ transition :to => :full, :if => Proc.new{|seminar, student| not seminar.student_is_enrolled?} do |seminar, student|
63
+ seminar.add_student_to_waiting_list(student)
64
+ end
65
+ end
66
+ end
67
+
68
+ state :full do
69
+ on :move_to_bigger_classroom, :to => :open_for_enrollment
70
+ on :drop_student do
71
+ transition :to => :open_for_enrollment,
72
+ :if => Proc.new{|seminar, student| seminar.student_is_enrolled?(student) && seminar.waiting_list_is_empty?} do
73
+ seminar.students.delete student
74
+ end
75
+ transition :if => :student_is_enrolled? do |seminar, student|
76
+ seminar.students.delete student
77
+ seminar.enroll_student! seminar.waiting_list.shift
78
+ end
79
+ end
80
+ on :enroll_student do
81
+ transition :if => :seats_available? do |seminar, student|
82
+ seminar.students << student
83
+ end
84
+ transition :action => :add_student_to_waiting_list
85
+ end
86
+ on :close, :to => :closed_to_enrollment
87
+ enter :create_waiting_list
88
+ end
89
+
90
+ state :closed_to_enrollment do
91
+ enter :notify_waiting_list_that_enrollment_is_closed
92
+ end
93
+
94
+ state :cancelled do
95
+ enter :notify_students_that_the_seminar_is_cancelled
96
+ end
97
+
98
+ # The 'cancel' event can occur in all states.
99
+ all_states.each do |state|
100
+ state.on :cancel, :to => :cancelled
101
+ end
102
+
103
+ initial_state :proposed
104
+ current_state_from :status
105
+
106
+ on_all_transitions Proc.new{|obj, event, transition, event_args| puts "Transitioning from #{transition.from.name.inspect} to #{transition.to.name.inspect}"}
107
+ end
108
+ end
109
+
110
+
111
+ s = Seminar.new
112
+ s.schedule!
113
+ s.open!
114
+ s.enroll_student! "bobby"
115
+ puts s.inspect
116
+ s.enroll_student! "eva"
117
+ puts s.inspect
118
+ s.enroll_student! "sally"
119
+ puts s.inspect
120
+ s.enroll_student! "matt"
121
+ puts s.inspect
122
+ s.enroll_student! "karina"
123
+ puts s.inspect
124
+ s.enroll_student! "tony"
125
+ puts s.inspect
126
+ s.enroll_student! "rich"
127
+ puts s.inspect
128
+ s.enroll_student! "suzie"
129
+ puts s.inspect
130
+ s.enroll_student! "fred"
131
+ puts s.inspect
132
+ s.drop_student! "sally"
133
+ puts s.inspect
134
+ s.drop_student! "bobby"
135
+ puts s.inspect
136
+ s.drop_student! "tony"
137
+ puts s.inspect
138
+ s.drop_student! "rich"
139
+ puts s.inspect
140
+ s.drop_student! "eva"
141
+ puts s.inspect