event_state¶ ↑
<img src=“https://secure.travis-ci.org/jdleesmiller/event_state.png”/>
SYNOPSIS¶ ↑
A small embedded DSL for implementing stateful protocols in EventMachine using finite state machines. The protocol is specified in terms of states and messages. The processing happens in the states, and the messages that can be sent or received from each state are declared by name using the DSL.
Here’s everyone’s favorite example: an echo server. It starts in the :listening
state, in which it can receive a Noise
message. It then transitions to the :speaking
state. After a short delay (EM.add_timer
), it sends the noise back to the client, which causes it to transition back to the listening state.
class MessageEchoServer < EventState::ObjectMachine Noise = Struct.new(:content) protocol do state :listening do on_recv Noise, :speaking end state :speaking do on_send Noise, :listening on_enter do |noise| EM.add_timer 0.5 do send_message Noise.new(noise.content) end end end end end
In a picture (generated from the code above using {EventState::Machine.print_state_machine_dot}):
Here the start state is indicated by a double circle, a blue arrow is a message that can be received, and a red arrow is a message that can be sent.
The {EventState::ObjectMachine} base class extends {EventState::Machine}, which in turn extends EventMachine::Connection
. ObjectMachine
handles serializing and deserializing the ruby objects using EventMachine::ObjectProtocol
, and (by default) it uses the class of the message object as the message name. In this example, the message name is Noise
. Machine
provides the state machine DSL and the primitives for handling arbitrary kinds of messages.
Here is the corresponding client and a demo showing how to run it:
class MessageEchoClient < EventState::ObjectMachine Noise = MessageEchoServer::Noise def initialize noises super @noises = noises end protocol do state :speaking do on_send Noise, :listening on_enter do if @noises.empty? EM.stop else send_message MessageEchoServer::Noise.new(@noises.shift) end end end state :listening do on_recv Noise, :speaking on_enter do |noise| puts "heard: #{noise.content}" end end end def self.demo EM.run do EM.start_server('localhost', 14159, MessageEchoServer) EM.connect('localhost', 14159, MessageEchoClient, %w(foo bar baz)) end end end
Output:
heard: foo heard: bar heard: baz
INSTALLATION¶ ↑
gem install event_state
RELATED PROJECTS¶ ↑
This library was inspired by slagyr.github.com/statemachine which provides a nice DSL for defining state machines but doesn’t integrate directly with EventMachine. See also the www.complang.org/ragel state machine compiler and Zed Shaw’s zedshaw.com/essays/ragel_state_charts.html blog post about it.
LICENSE¶ ↑
(The MIT License)
Copyright © 2011 John Lees-Miller
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.