Chat Utilizando Streaming y Server Sent Events (SSE)

Los fuentes de este ejemplo se encuentran en la rama original del repositorio sinatra-streaming-example-chat. La rama simple en https://github.com/crguezl/sinatra-streaming-example-chat/tree/simple contiene una versión equivalente pero con los templates separados.

[~/srcSTW/streaming/chat_with_streaming(master)]$ cat -n chat.rb 
 1  # coding: utf-8
 2  require 'sinatra'
 3  set server: 'thin', connections: []
 4  
 5  get '/' do
 6    halt erb(:login) unless params[:user]
 7    erb :chat, locals: { user: params[:user].gsub(/\W/, '') }
 8  end
Cuando se visita la raíz la primera vez params[:user] es nil y se muestra el formulario definido en login.erb que obtiene un alias para el usuario:
[~/sinatra/sinatra-streaming/chat_with_streaming(simple)]$ cat views/login.erb 
<form action='/' method='GET'>
  <label for='user'>User Name:</label>
  <input name='user' value='' autofocus/>
  <input type='submit' value="GO!" />
</form>
El atributo for='user' de label indica que esta etiqueta está asociada con el campo input cuyo atributo name es user.

Una vez que el <input name='user'> es rellenado el formulario es procesado por la misma ruta / que ahora muestra el resultado de la plantilla chat.

10  get '/stream', provides: 'text/event-stream' do
11    stream :keep_open do |out|
12      settings.connections << out
13      out.callback { settings.connections.delete(out) }
14    end
15  end
Routes may include a variety of matching conditions, such as the user agent:, :host_name and :provides:
get '/', :provides => ['rss', 'atom', 'xml'] do
  builder :feed
end
Sending an event stream from the source is a matter of constructing a plaintext response, served with a text/event-stream Content-Type, that follows the Server Sent Event (SSE) format.

Sigamos:

16  
17  post '/' do
18    settings.connections.each { |out| out << "data: #{params[:msg]}\n\n" }
19    204 # response without entity body
20  end

204 No Content

The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation.

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.

The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.

21  
22  __END__
23  
24  @@ layout
25  <html>
26    <head> 
27      <title>Super Simple Chat with Sinatra</title> 
28      <meta charset="utf-8" />
29      <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> 
30    </head> 
31    <body><%= yield %></body>
32  </html>

The <script> tag is used to define a client-side script, such as a JavaScript.

The <script> element either contains scripting statements, or it points to an external script file through the src attribute.

Common uses for JavaScript are image manipulation, form validation, and dynamic changes of content.

The Google Hosted Libraries is a content distribution network for the most popular, open-source JavaScript libraries. To add a library to your site, simply use <script> tags to include the library. See https://developers.google.com/speed/libraries/devguide:

jQuery
snippet: <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
site: http://jquery.com/
versions: 1.8.3, 1.8.2, 1.8.1, 1.8.0, ...
note: 1.2.5 and 1.2.4 are not hosted due to their short and unstable lives in the wild.

jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development.

jquery.min.js is a minified version of the JQuery JavaScript library, which provides a number of basic functions for websites.

34  @@ login
35  <form action='/'>
36    <label for='user'>User Name:</label>
37    <input name='user' value='' />
38    <input type='submit' value="GO!" />
39  </form>
40  
41  @@ chat
42  <pre id='chat'></pre>
43  
44  <script>
45    // reading
46    var es = new EventSource('/stream');
47    es.onmessage = function(e) { $('#chat').append(e.data + "\n") };
48  
49    // writing
50    $("form").live("submit", function(e) {
51      $.post('/', {msg: "<%= user %>: " + $('#msg').val()});
52      $('#msg').val(''); $('#msg').focus();
53      e.preventDefault();
54    });
55  </script>
56  
57  <form>
58    <input id='msg' placeholder='type message here...' />
59  </form>

Casiano Rodriguez León 2015-06-18