Snippets
- Non-snippet Snippets
- Small client Example
- Small server Example
- EventMachine, Telnet Style
- Safe run in any circumstances
- Writing out to clients before Exit
- Start a server on an unassigned port
- determine client's port
- More complicated example
- EventMachine over a Serial Port
- Using EventMachine with GTK+
Code Snippets
Put your interesting code snippets here. Please include a description of what is special about said snippet.
You can add code highlighting as follows:
{{{ #!ruby def hello_world puts "hi" end }}}
results in
def hello_world puts "hi" end
Non-snippet Snippets
Feel free to turn code found here into additional snippets
http://rubyeventmachine.com/browser/version_0
Small client Example
class Echo < EventMachine::Connection def initialize(*args) super # stuff here... end def receive_data(data) p data end end EventMachine::run { EventMachine::connect '127.0.0.1', 8081, Echo }
Small server Example
require 'eventmachine' module EchoServer def post_init puts "-- someone connected to the echo server!" end def receive_data data send_data ">>>you sent: #{data}" end end EventMachine::run { EventMachine::start_server "127.0.0.1", 8081, EchoServer puts 'running echo server on 8081' }
EventMachine, Telnet Style
EventMachine (EM) can respond to keyboard events. This gives your event-driven programs the ability to respond to input from local users.
Programming EM to handle keyboard input in Ruby is simplicity itself. Just use EventMachine#open_keyboard, and supply the name of a Ruby module or class that will receive the input:
require 'rubygems' require 'eventmachine' module MyKeyboardHandler def receive_data keystrokes puts "I received the following data from the keyboard: #{keystrokes}" end end EM.run { EM.open_keyboard(MyKeyboardHandler) }
If you want EM to send line-buffered keyboard input to your program, just include the LineText?2 protocol module in your handler class or module:
require 'rubygems' require 'eventmachine' module MyKeyboardHandler include EM::Protocols::LineText2 def receive_line data puts "I received the following line from the keyboard: #{data}" end end EM.run { EM.open_keyboard(MyKeyboardHandler) }
As we said, simplicity itself. You can call EventMachine#open_keyboard at any time while the EM reactor loop is running. In other words, the method invocation may appear anywhere in an EventMachine#run block, or in any code invoked in the #run block.
Safe run in any circumstances
N.B. this is no longer required for a lot of apps, as of EM 0.12 you can now stack run blocks, i.e. EM::run{ EM::run {} } does not raise. For threaded apps there may still be some requirement, however a good pattern for managable code is to push the call to #run to the top of your application stack, and do your threaded work using EM::defer where possible.
module EventMachine def EventMachine::safe_run(background = nil, &block) if EM::reactor_running? # Attention: here we loose the ability to catch # immediate connection errors. EM::next_tick(&block) sleep unless background # this blocks the thread as it was inside a reactor else if background $em_reactor_thread = Thread.new do EM::run(&block) end else EM::run(&block) end end end end
Usage:
EM::safe_run(:background) do EM::start_server(host, port, server_klass) EM::connect(host, port, client_klass) end
Use this snippet if you'd like to design your library in a way compatible with different apps: both event-driven and threaded.
Writing out to clients before Exit
Let connections finish their work before stopping EM for good.
class Server attr_accessor :connections def initialize @connections = [] # ... end def start @signature = EventMachine.start_server('0.0.0.0', 3000, Connection) do |con| con.server = self end end def stop EventMachine.stop_server(@signature) unless wait_for_connections_and_stop # Still some connections running, schedule a check later EventMachine.add_periodic_timer(1) { wait_for_connections_and_stop } end end def wait_for_connections_and_stop if @connections.empty? EventMachine.stop true else puts "Waiting for #{@connections.size} connection(s) to finish ..." false end end end class Connection < EventMachine::Connection attr_accessor :server # ... def unbind server.connections.delete(self) end end EventMachine::run { s = Server.new s.start puts "New server listening" }
Start a server on an unassigned port
require ‘socket’ EM.run { srvr = EM.start_server “0.0.0.0”, 0 p Socket.unpack_sockaddr_in( EM.get_sockname( srvr )) }
determine client's port
port, *ip_parts = get_peername[2,6].unpack "nC4" ip = ip_parts.join('.')
or
require 'socket' port, ip = Socket.unpack_sockaddr_in(get_peername)
More complicated example
class Echo < EventMachine::Connection def initialize(*args) super # stuff here... end def receive_data(data) p data send_data data close_connection_after_writing end def unbind p ' connection totally closed' end end EventMachine::connect '127.0.0.1', 22, Echo {|conn| }
EventMachine over a Serial Port
It is actually possible to use Guilliame Perionnet's ruby-serialport library with (the pure Ruby) EventMachine. The following code works with EventMachine 0.12.2 and ruby-serialport 0.7.0:
$eventmachine_library = :pure_ruby # need to force pure ruby require 'eventmachine' gem_original_require 'serialport' require 'smsrelay/gsmpdu' module EventMachine class EvmaSerialPort < StreamObject def self.open(dev, baud, databits, stopbits, parity) io = SerialPort.new(dev, baud, databits, stopbits, parity) return(EvmaSerialPort.new(io)) end def initialize(io) super end ## # Monkeypatched version of EventMachine::StreamObject#eventable_read so # that EOFErrors from the SerialPort object (which the ruby-serialport # library uses to signal the fact that there is no more data available # for reading) do not cause the connection to unbind. def eventable_read @last_activity = Reactor.instance.current_loop_time begin if io.respond_to?(:read_nonblock) 10.times { data = io.read_nonblock(4096) EventMachine::event_callback uuid, ConnectionData, data } else data = io.sysread(4096) EventMachine::event_callback uuid, ConnectionData, data end rescue Errno::EAGAIN, Errno::EWOULDBLOCK, EOFError # no-op rescue Errno::ECONNRESET, Errno::ECONNREFUSED @close_scheduled = true EventMachine::event_callback uuid, ConnectionUnbound, nil end end end class << self def connect_serial(dev, baud, databits, stopbits, parity) EvmaSerialPort.open(dev, baud, databits, stopbits, parity).uuid end end def EventMachine::open_serial(dev, baud, databits, stopbits, parity, handler=nil) klass = if (handler and handler.is_a?(Class)) handler else Class.new( Connection ) {handler and include handler} end s = connect_serial(dev, baud, databits, stopbits, parity) c = klass.new s @conns[s] = c block_given? and yield c c end class Connection # This seems to be necessary with EventMachine 0.12.x def associate_callback_target(sig) return(nil) end end end
Using EventMachine with GTK+
GTK+ applications generally make use of a blocking event loop ( gtk_main / Gtk::main ) to do their work, which interacts poorly with EventMachine. The way around it seems to be to explicitly "give it a tick", like so:
require 'gtk2' require 'eventmachine' class MyApp def initialize EM::connect ... end end EM::run do client = MyApp.new give_tick = proc { Gtk::main_iteration; EM.next_tick(give_tick); } give_tick.call end
This avoids the blocking event loop (which stops EM from working) by running one iteration of its main loop every tick of EventMachine. This ensures GTK events are processed in a timely fashion without blocking EM from doing its work.
There might be some way to use gtk_idle_add() to run EM inside GTK's event loop - but I've not had any success with that.