celluloid/reel

View on GitHub
examples/roundtrip.rb

Summary

Maintainability
A
30 mins
Test Coverage
require 'rubygems'
require 'bundler/setup'
require 'reel'
require 'celluloid/autostart'


class RoundtripServer 
  include Celluloid
  include Celluloid::Notifications

  def initialize
    async.run
  end

  def run
    now = Time.now.to_f
    sleep now.ceil - now + 0.001
    every(1) do 
      publish 'read_message'
      end
  end
end

class Writer
  include Celluloid
  include Celluloid::Notifications
  include Celluloid::Logger

  def initialize(websocket)
    info "Writing to socket"
    @socket = websocket
    subscribe('write_message', :new_message)
  end

  def new_message(topic, new_time)
    @socket << new_time.inspect
  rescue Reel::SocketError
    info "WS client disconnected"
    terminate
  end

end

class Reader
  include Celluloid
  include Celluloid::Notifications
  include Celluloid::Logger

  def initialize(websocket)
    info "Reading socket"
    @socket = websocket
    subscribe('read_message', :new_message)
  end

   def new_message(topic)
    msg = @socket.read
    publish 'write_message', msg 
  rescue Reel::SocketError, EOFError
    info "WS client disconnected"
    terminate
  end
end

class WebServer < Reel::Server::HTTP
  include Celluloid::Logger

  def initialize(host = "0.0.0.0", port = 9000)
    info "Roundtrip example starting on #{host}:#{port}"
    super(host, port, &method(:on_connection))
  end

  def on_connection(connection)
    while request = connection.request
      if request.websocket?
        info "Received a WebSocket connection"

        # We're going to hand off this connection to another actor (Writer/Reader)
        # However, initially Reel::Connections are "attached" to the
        # Reel::Server::HTTP actor, meaning that the server manages the connection
        # lifecycle (e.g. error handling) for us.
        #
        # If we want to hand this connection off to another actor, we first
        # need to detach it from the Reel::Server (in this case, Reel::Server::HTTP)
        connection.detach

        route_websocket request.websocket
        return
      else
        route_request connection, request
      end
    end
  end

  def route_request(connection, request)
    if request.url == "/"
      return render_index(connection)
    end

    info "404 Not Found: #{request.path}"
    connection.respond :not_found, "Not found"
  end

  def route_websocket(socket)
    if socket.url == "/ws"
      Writer.new(socket)
      Reader.new(socket)
    else
      info "Received invalid WebSocket request for: #{socket.url}"
      socket.close
    end
  end

  def render_index(connection)
    info "200 OK: /"
    connection.respond :ok, <<-HTML
      <!doctype html>
      <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Reel WebSockets roundtrip example</title>
        <style>
          body {
            font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
            font-weight: 300;
            text-align: center;
          }

          #content {
            width: 800px;
            margin: 0 auto;
            background: #EEEEEE;
            padding: 1em;
          }
        </style>
      </head>
      <body>
        <div id="content">
          <h1>Roundtrip communication with websockets</h1>
          <div>
           <input id="text_input" type="text" name="q" value="" autocomplete="off"/>
           Latest message is: <span id="current-time">...</span></div>
        </div>
      </body>
      <script>
        var SocketKlass = "MozWebSocket" in window ? MozWebSocket : WebSocket;
        var ws = new SocketKlass('ws://' + window.location.host + '/ws');
        ws.onmessage = function(msg){
          document.getElementById('current-time').innerHTML = msg.data;
        };
        var input = document.getElementById("text_input");
        input.focus();     
        input.onkeydown = function(evt) {       
          var evt = evt || window.event;
          if (evt.keyCode === 13) {
            ws.send(input.value);console.log(input.value); 
            input.value = "";  
          }
        };
      </script>
      </html>
    HTML
  end
end

RoundtripServer.supervise_as :roundtrip_server
WebServer.supervise_as :reel

sleep