barebone-fsm 0.0.1

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.
Files changed (2) hide show
  1. data/lib/barebone-fsm.rb +172 -0
  2. metadata +47 -0
@@ -0,0 +1,172 @@
1
+ ##
2
+ # == Overview
3
+ # ---
4
+ # This module implements a basic finite-state machine (FSM).
5
+ # An FSM consists of a finite number of states,
6
+ # with one of them being the current state of the FSM.
7
+ # Transitions are defined between states, which are triggered on events.
8
+ # For details on FSM, see this wiki page: {FSM}[http://en.wikipedia.org/wiki/Finite-state_machine].
9
+ #
10
+ # The motivation behind the module was to implement a very basic barebone FSM in Ruby.
11
+ # Features are kept at minimum as well as the code.
12
+ # Only two classes for the FSM are defined as the following:
13
+ # * FSM -> the finite state machine class
14
+ # * FSMState -> the state class
15
+ #
16
+ # Author:: Md. Imrul Hassan (mailto:mihassan@gmail.com)
17
+ # Copyright:: Copyright: Md. Imrul Hassan, 2013
18
+ #
19
+ # == Status
20
+ # The module works, but further testing and documentation is needed.
21
+ # The api is not stable yet, I may decide to change the way FSM is used.
22
+ #
23
+ # == Features
24
+ # Apart from having support for states and events, this module offers the following features:
25
+ # 1. Default state
26
+ # 2. Default event for each state
27
+ # 3. Entry and exit events for each state
28
+ #
29
+ # == Usage
30
+ # ---
31
+ # A simple example:
32
+ #
33
+ # fsm = FSM::FSM.new
34
+ # state = fsm[:state_name]
35
+ # state.event(:event_name) do |st, ev|
36
+ # puts "#{ev} triggered on state #{st}"
37
+ # :new_state}
38
+ # end
39
+ # fsm.event :event_name
40
+ #
41
+ # A complete example:
42
+ #
43
+ # fsm = FSM::FSM.new(:stopped)
44
+ #
45
+ # fsm[:stopped].event(:open) { puts "[Stopped]: Door Opened"; :open}
46
+ # fsm[:stopped].event(:start) { puts "[Stopped]: Started"; :started}
47
+ # fsm[:stopped].event(:enter) {|st, ev| puts "[Stopped]: Eneter into state #{st}"}
48
+ # fsm[:stopped].event(:exit) {|st, ev| puts "[Stopped]: Exit from state #{st}"}
49
+ #
50
+ # fsm[:open].event(:close) { puts "[Open]: Door Closed"; :stopped}
51
+ # fsm[:open].event(:default) {|st, ev| puts "[Open]: Event [#{ev}] not defined in state #{st}"; :open}
52
+ # fsm[:open].event(:enter) {|st, ev| puts "[Open]: Eneter into state #{st}"}
53
+ # fsm[:open].event(:exit) {|st, ev| puts "[Open]: Exit from state #{st}"}
54
+ #
55
+ # fsm[:started].event(:open) { puts "[Started]: Door Opened"; :open}
56
+ # fsm[:started].event(:stop) { puts "[Started]: Stopped"; :stopped}
57
+ # fsm[:started].event(:enter) {|st, ev| puts "[Started]: Eneter into state #{st}"}
58
+ # fsm[:started].event(:exit) {|st, ev| puts "[Started]: Exit from state #{st}"}
59
+ #
60
+ # puts fsm
61
+ #
62
+ # fsm.event :open
63
+ # fsm.event :start
64
+ # fsm.event :close
65
+ # fsm.event :start
66
+ # fsm.event :stop
67
+ # fsm.event :start
68
+ #
69
+ # puts sm
70
+ #
71
+ module FSM
72
+
73
+ # == Overview
74
+ # ---
75
+ # FSMState class represents a state of the finite state machine.
76
+ #
77
+ # == Usage
78
+ # ---
79
+ # state = FSMState.new :state_name
80
+ # state.event(:event_name) {|st, ev| puts "#{ev} triggered on state #{st}"; :new_state} # creates the event when block is given
81
+ # puts state
82
+ # state.event :event_name # triggers the event when block is absent
83
+ #
84
+ class FSMState
85
+
86
+ attr_reader :name
87
+
88
+ def initialize(state_name)
89
+ @name = state_name
90
+ @events = {}
91
+ end
92
+
93
+ def to_s()
94
+ @name.to_s +
95
+ ": [" +
96
+ @events.keys.map(&:to_s).join(', ') +
97
+ "]"
98
+ end
99
+
100
+ def event(event_name, &event_block)
101
+ if block_given? then
102
+ @events[event_name] = event_block
103
+ else
104
+ if @events.has_key? event_name then
105
+ @events[event_name].call @name, event_name
106
+ elsif @events.has_key? :default then
107
+ @events[:default].call @name, event_name
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ # == Overview
115
+ # ---
116
+ # This class implements the finite-state machine.
117
+ # FSM class exposes the states it contains and can trigger an event.
118
+ # States are created on the fly first time it's referenced through index operator [].
119
+ #
120
+ # The FSM state transits to the default state if the latest event does not define the next state.
121
+ # If default state is not set, then state does not change on undefined state.
122
+ # The initial state of the FSM is the first state mentioned.
123
+ # This can be either the default state if defined or the first referenced state.
124
+ #
125
+ # == Usage
126
+ # ---
127
+ # fsm = FSM.new :default_state
128
+ # fsm[:default_state].event(:first_event) do |st, ev| # the state is defined and referenced at the same time
129
+ # puts "The first transition from the default_state to state_name"
130
+ # :state_name # the next state is defined here
131
+ # end
132
+ #
133
+ class FSM
134
+ attr_reader :state
135
+
136
+ def initialize(default_state=nil)
137
+ @states = {}
138
+ if default_state then
139
+ @state = @default = default_state
140
+ @states[@state] = FSMState.new(@state)
141
+ end
142
+ end
143
+
144
+ def to_s()
145
+ "FSM" +
146
+ ": {" +
147
+ @states.values.map{ |st|
148
+ (st.name==@state ? ">" : "") + st.to_s
149
+ }.join(', ') +
150
+ "}"
151
+ end
152
+
153
+ def [](state_name)
154
+ unless @states.has_key? state_name then
155
+ @states[state_name] = FSMState.new(state_name)
156
+ @state ||= state_name
157
+ end
158
+ @states[state_name]
159
+ end
160
+
161
+ def event(event_name)
162
+ @states[@state].event :exit
163
+ new_state = @states[@state].event event_name
164
+ new_state = nil if not @states.has_key? new_state
165
+ new_state ||= @default_state
166
+ new_state ||= @state
167
+ @state = new_state
168
+ @states[@state].event :enter
169
+ end
170
+
171
+ end
172
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barebone-fsm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Md. Imrul Hassan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A barebone implementation of finite state machine keeping simplicity
15
+ in mind.
16
+ email: mihassan@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/barebone-fsm.rb
22
+ homepage: http://rubygems.org/gems/barebone-fsm
23
+ licenses: []
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.24
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: A barebone finite state machine(FSM).
46
+ test_files: []
47
+ has_rdoc: