Permalink
Browse files

Merge branch 'master' of github.com:defaude/asciicasts.com-translations

  • Loading branch information...
2 parents c381758 + 40eb8f8 commit 94ea3aa344ab8e513c09bf74004e17cf1e4b9deb @eifion committed Mar 8, 2012
@@ -0,0 +1,193 @@
+<p>En el &lt;an href=&quot;http://es.asciicasts.com/episodes/260-mensajeria-asincrona-con-faye&quot;&gt;episodio 260</a> vimos como se pueden actualizar los contenidos del navegador del usuario en tiempo real utilizando <a href="http://faye.jcoglan.com/">Faye</a>. Pero aunque Faye es una soluci&oacute;n muy buena su uso puede acarrear bastante trabajo, especialmente si tenemos requisitos complejos de seguridad. Esto es lo que llev&oacute; a Ryan Bates a crear una gema llamada <a href="https://github.com/ryanb/private_pub">Private Pub</a> que funciona sobre Faye, con el prop&oacute;sito de hacer que fuese m&aacute;s f&aacute;cil publicar y suscribirse a eventos en tiempo real en una aplicaci&oacute;n Rails, y en este episodio veremos c&oacute;mo funciona.</p>
+
+<p>La aplicaci&oacute;n con la que vamos a trabajar es un sencillo <em>chat</em>. Si introducimos un mensaje en el campo de texto del fondo de la p&aacute;gina y hacemos clic en &ldquo;Send&rdquo; el navegador enviar&aacute; una petici&oacute;n AJAX para guardar el mensaje y luego actualizar&aacute; el navegador para que dicho mensaje aparezca en la ventana principal.</p>
+
+<div class="imageWrapper">
+ <img src="http://asciicasts.com/system/photos/943/original/E316I01.png" width="800" height="399" alt="Nuestra aplicación de chat."/>
+</div>
+
+<p>Pero hay un problema con nuestra aplicaci&oacute;n. Si hay m&uacute;ltiples clientes de chat abiertos no se actualizar&aacute;n todos en tiempo real cuando se env&iacute;e el mensaje. S&oacute;lo el cliente que envi&oacute; el mensaje lo ver&aacute; aparecer; el resto tendr&aacute; que recargar la p&aacute;gina para verlo. Hay varias soluciones a este problema, una de ellas es hacer que los clientes realicen sondeos peri&oacute;dicos consultando si hay nuevos mensajes cada par de segundos, lo cual no es la mejor idea. Otra soluci&oacute;n es mantener una conexi&oacute;n por <em>socket</em> con el servidor en cada cliente de forma que el servidor pueda avisar a los clientes cuando lleguen nuevos mensajes para que estos aparezcan en tiempo real. El problema con esta soluci&oacute;n es que la arquitectura de Rails no est&aacute; pensada para peticiones de larga duraci&oacute;n.</p>
+
+<p>Faye y Private Pub pueden sernos de ayuda en este escenario pero antes de empezar nuestro repaso veremos c&oacute;mo funciona nuestra aplicaci&oacute;n de <em>chat</em>. Empezaremos con la plantilla de la vista de la p&aacute;gina de <em>chat</em>.</p>
+
+``` /app/views/messages/index.html.erb
+<h1>Chat</h1>
+
+<ul id="chat">
+ <%= render @messages %>
+</ul>
+
+<%= form_for Message.new, remote: true do |f| %>
+ <%= f.text_field :content %>
+ <%= f.submit "Send" %>
+<% end %>
+```
+
+<p>En esta plantilla se usa un parcial llamado <code>messages</code> para mostrar el listado de mensajes y se incluye un formulario para crear nuevos mensajes. Este formulario utiliza la opci&oacute;n <code>remote:true</code> para que la petici&oacute;n se env&iacute;e v&iacute;a AJAX hacia la acci&oacute;n <code>create</code> de <code>MessagesController</code>, que tiene el siguiente aspecto:</p>
+
+
+``` /app/controllers/messages_controller.rb
+class MessagesController < ApplicationController
+ def index
+ @messages = Message.all
+ end
+
+ def create
+ @message = Message.create!(params[:message])
+ end
+end
+```
+
+<p>La acci&oacute;n <code>create</code> es muy sencilla, tan s&oacute;lo crea un nuevo <code>Message</code>. Veamos la plantilla de JavaScript que se devolver&aacute; como respuesta a la llamada AJAX:</p>
+
+``` /app/views/messages/create.js.erb
+$("#chat").append("<%= j render(@message) %>");
+$("#new_message")[0].reset();
+```
+
+<p>Este fragmento de JavaScript a&ntilde;ade el mensaje que se acaba de crear al listado del <em>chat</em> y borra el campo del formulario de nuevo mensaje. Lo que queremos que se haga aqu&iacute; es enviar el mensaje a todos los usuarios del <em>chat</em> para que lo muestren, no s&oacute;lo al que lo envi&oacute; y es para esto para lo que vamos a usar Private Pub.</p>
+
+<h3>Instalaci&oacute;n de Private Pub</h3>
+
+<p>Private Pub se instala de la forma habitual a&ntilde;adiendo la gema al fichero <code>Gemfile</code> de la aplicaci&oacute;n y ejecutando a continuaci&oacute;n <code>bundle</code>. Esto har&aacute; que se instale tambi&eacute;n Faye porque est&aacute; declarado como dependencia.</p>
+
+``` /Gemfile
+source 'http://rubygems.org'
+
+gem 'rails', '3.1.3'
+gem 'sqlite3'
+
+# Gems used only for assets and not required
+# in production environments by default.
+group :assets do
+ gem 'sass-rails', '~> 3.1.5'
+ gem 'coffee-rails', '~> 3.1.1'
+ gem 'uglifier', '>= 1.0.3'
+end
+
+gem 'jquery-rails'
+gem 'private_pub'
+```
+
+<p>A continuaci&oacute;n ejecutamos el generador para crear el fichero de configuraci&oacute;n y un fichero Rackup para lanzar el servidor Faye.</p>
+
+``` terminal
+$ rails g private_pub:install
+ create config/private_pub.yml
+ create private_pub.ru
+```
+
+<p>El servidor Rack se arranca con esta orden:</p>
+
+``` terminal
+$ rackup private_pub.ru -s thin -E production
+>> Thin web server (v1.3.1 codename Triple Espresso)
+>> Maximum connections set to 1024
+>> Listening on 0.0.0.0:9292, CTRL+C to stop
+```
+
+<p>Esta orden arranca Faye usando el servidor Thin en el entorno de producci&oacute;n (lo cual es necesario para que arranque). El &uacute;ltimo paso es poner <code>private_pub</code> en fichero de manifiesto de JavaScript de nuestra aplicaci&oacute;n.</p>
+
+``` /app/assets/javascripts/application.js
+// This is a manifest file that'll be compiled into including all the files listed below.
+// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// be included in the compiled file accessible from http://example.com/assets/application.js
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// the compiled file.
+//
+//= require jquery
+//= require jquery_ujs
+//= require private_pub
+//= require_tree .
+```
+
+<p>Podemos incluir este JavaScript en el <em>layout</em> de la aplicaci&oacute;n si estamos con versiones de Rails anteriores a la 3.1.</p>
+
+<h3>Uso de Private Pub en nuestras aplicaciones</h3>
+
+<p>Una vez configurada la carga de Private Pub es f&aacute;cil suscribirse a un canal y publicar notificaciones de actualizaci&oacute;n sobre &eacute;l. Lo &uacute;nico que hay que hacer es suscribirse a un canal en una plantilla de la vista, por ejemplo en nuestra p&aacute;gina de mensajes, invocando <code>subscribe_to</code> y pasando el nombre del canal. El formato de un canal en Faye es como el de una ruta de archivo, nosotros vamos a usar <code>/messages/new</code>.</p>
+
+``` /app/views/messages/index.html.erb
+<h1>Chat</h1>
+
+<ul id="chat">
+ <%= render @messages %>
+</ul>
+
+<%= form_for Message.new, remote: true do |f| %>
+ <%= f.text_field :content %>
+ <%= f.submit "Send" %>
+<% end %>
+
+<%= subscribe_to "/messages/new" %>
+```
+
+<p>Lo siguiente que tenemos que hacer es ir a una plantilla JavaScript que se muestre como resultado de una petici&oacute;n AJAX (como la que tenemos que muestra los nuevos mensajes) y envolver el c&oacute;digo en un bloque <code>publish_to</code>.</p>
+
+``` /app/views/messages/create.js.erb
+<% publish_to "/messages/new" do %>
+ $("#chat").append("<%= j render(@message) %>");
+ $("#new_message")[0].reset();
+<% end %>
+```
+
+<p>El hacer esto quiere decir que este JavaScript no s&oacute;lo se devolver&aacute; al cliente que hizo la petici&oacute;n, sino que ser&aacute; publicada a todos los suscriptores del canal <code>/messages/new</code>. Podemos hacer la prueba ahora mismo. Si abrimos la aplicaci&oacute;n en dos navegadores distintos y enviamos un mensaje en uno de ellos, dicho mensaje aparecer&aacute; instant&aacute;neamente en el otro.</p>
+
+<div class="imageWrapper">
+ <img src="http://asciicasts.com/system/photos/944/original/E316I02.png" width="851" height="418" alt="Los mensajes aparecen inmediatamente en todos los clientes."/>
+</div>
+
+<p>Puede ser que no queramos publicar sobre un canal directamente en la respuesta AJAX. Por ejemplo podr&iacute;amos querer publicar desde una acci&oacute;n del controlador. Esto se puede hacer desde Ruby utilizando <code>PrivatePub.publish_to</code>, y pasando el nombre del canal en el que queremos publicar y el c&oacute;digo JavaScript que queremos que se ejecute en todos los clientes suscritos. Demostremos esta funcionalidad devolviendo un <code>alert</code>.</p>
+
+``` /app/controllers/messages_controller.rb
+def create
+ @message = Message.create!(params[:message])
+ PrivatePub.publish_to("/messages/new", "alert('#{@message.content}');")
+end
+```
+
+<p>El <code>alert</code> aparecer&aacute; en ambos navegadores cuando se escriba un mensaje en cualquiera de ellos.</p>
+
+<div class="imageWrapper">
+ <img src="http://asciicasts.com/system/photos/945/original/E316I03.png" width="852" height="417" alt="La alerta aparece en ambos navegadores."/>
+</div>
+
+<h3>Manipulaci&oacute;n de datos JSON</h3>
+
+<p>Tambi&eacute;n se puede si lo preferimos trabajar con datos en JSON en lugar de c&oacute;digo JavaScript. Lo &uacute;nico que hace falta hacer es pasar a <code>publish_to</code> un objeto (como un <em>hash</em>) en lugar de una cadena y entonces ser&aacute; convertido a JSON.</p>
+
+``` /app/controllers/messages_controller.rb
+def create
+ @message = Message.create!(params[:message])
+ PrivatePub.publish_to("/messages/new", message: @message)
+end
+```
+
+<p>Con este enfoque tendremos que escribir JavaScript para gestionar el JSON que estamos emitiendo. Lo haremos en el fichero CoffeeScript <code>messages</code>.</p>
+
+``` /app/assets/javascripts/messages.js.coffee
+PrivatePub.subscribe "/messages/new", (data, channel) ->
+ alert data.message.content
+```
+
+<p>Aqu&iacute; llamamos a <code>PrivatePub.subscribe</code>, que recibe un nombre de canal y una funci&oacute;n as&iacute;ncrona que recibe dos argumentos, <code>data</code> y <code>channel</code>. La funci&oacute;n ser&aacute; invocada cada vez que haya una actualizaci&oacute;n con nuevos datos JSON en el canal. En nuestro c&oacute;digo tan s&oacute;lo hacemos <code>alert</code> mostrando el campo <code>message.content</code> de los datos recibidos. Si recargamos las p&aacute;ginas y enviamos otro mensaje veremos la alerta mostrando los datos JSON enviados desde el servidor.</p>
+
+
+<div class="imageWrapper">
+ <img src="http://asciicasts.com/system/photos/946/original/E316I04.png" width="800" height="415" alt="La alerta mostrando JSON."/>
+</div>
+
+<p>Una ventaja de Private Pub es que los canales por defecto son privados. Esto quiere decir que no tenemos que preocuparnos de que haya usuarios escuchando en canales a los que no hayan sido expl&iacute;citamente suscritos. Con esto es muy f&aacute;cil crear, por ejemplo, una sala privada de conversaci&oacute;n donde s&oacute;lo pueden escribir y leer los usuarios que se hayan suscrito. Tambi&eacute;n podemos expirar las suscripciones pasado alg&uacute;n tiempo de forma que los usuarios no puedan volver a escuchar mensajes despu&eacute;s de haber terminado la sesi&oacute;n. Todo esto se configura en el fichero <code>private_pub.yml</code>.</p>
+
+``` /config/private_pub.yml
+development:
+ server: "http://localhost:9292/faye"
+ secret_token: "secret"
+test:
+ server: "http://localhost:9292/faye"
+ secret_token: "secret"
+production:
+ server: "http://example.com/faye"
+ secret_token: "210eb617b6ce1c351d986a3185d34025cf42e5091a37502f18f595f7e8773853"
+ signature_expiration: 3600 # one hour
+```
Oops, something went wrong.

0 comments on commit 94ea3aa

Please sign in to comment.