<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Negonation Blog &#187; Ruby</title>
	<atom:link href="http://blog.negonation.com/es/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.negonation.com/es</link>
	<description>Justice is ripe for disruption</description>
	<lastBuildDate>Mon, 06 Feb 2012 17:27:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Gestión de eventos en Rails: Observers</title>
		<link>http://blog.negonation.com/es/gestion-de-eventos-en-rails-observers/</link>
		<comments>http://blog.negonation.com/es/gestion-de-eventos-en-rails-observers/#comments</comments>
		<pubDate>Sun, 02 Mar 2008 23:06:28 +0000</pubDate>
		<dc:creator>Ernesto Jiménez</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.negonation.com/es/gestion-de-eventos-en-rails-observers/</guid>
		<description><![CDATA[Todos los desarrolladores Rails utilizamos los callbacks de ActiveRecord. El uso de estos callbacks da mucho juego a nuestros modelos: validaciones, tratamiento de datos, operaciones sobre modelos relacionados, envío de e-mails automáticos&#8230; Al principio, cuando uno empieza a desarrollar en Rails, todas esas operaciones que comentaba solemos realizarlas en el controllador. Sin embargo, a medida [...]]]></description>
			<content:encoded><![CDATA[<p>Todos los desarrolladores Rails utilizamos los <a href="http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html" title="callbacks en ActiveRecord"><em>callbacks</em> de ActiveRecord</a>. El uso de estos callbacks da mucho juego a nuestros modelos: validaciones, tratamiento de datos, operaciones sobre modelos relacionados, envío de e-mails automáticos&#8230;</p>
<p>Al principio, cuando uno empieza a desarrollar en Rails, todas esas operaciones que comentaba solemos realizarlas en el controllador. Sin embargo, a medida que vamos aligerando nuestros controladores y <a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model" title="Skinny Controller, Fat Model">engordando nuestros modelos</a> la cantidad de callbacks que empleamos en nuestros modelos va aumentando. Esto es algo bueno, pero en ocasiones acabamos con un montón de callbacks en el modelo que no tienen que ver con el modelo en si.</p>
<p>Para extraer de los modelos todo ese código disparado por callbacks que no tiene que ver directamente con el modelo Rails proporciona <a href="http://api.rubyonrails.org/classes/ActiveRecord/Observer.html" title="Documentación de la clase Observer">los observers</a>.</p>
<h4 id="los_observers_por_dentro">Los observers por dentro</h4>
<p>En este post vamos a ver cómo funcionan los observers de rails por dentro. Para ello es necesario saber cómo se emplean así que, si no los has usado nunca, deberías echarle un vistazo a <a href="http://api.rubyonrails.org/classes/ActiveRecord/Observer.html" title="Documentación de la clase Observer">la documentación</a> <img src='http://blog.negonation.com/es/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h5 id="el_mdulo_observable_en_ruby">El módulo <em>Observable</em> en Ruby</h5>
<p>Ruby implementa su librería estándar algunos módulos sobre patrones de diseño habituales. Uno de estos módulos es <em>Observable</em>.</p>
<p>El módulo <em>Observable</em> implementa el <a href="http://en.wikipedia.org/wiki/Observer_pattern" title="Observer pattern">patrón Observer</a> también llamado <em>Publicador-Sucriptor</em>. En este patrón un objeto (el <em>publicador</em> o la fuente) informa a un conjunto de objetos interesados (los <em>suscriptores</em>) cuando su estado cambia. Para ello nos proporciona una serie de métodos para registrar suscriptores y para notificarles los cambios de estado.</p>
<p>Veamos un ejemplo de uso del módulo Observable:</p>
<pre class="ruby"><span class="ident">require</span> <span class="punct">'</span><span class="string">observer</span><span class="punct">'</span>

<span class="keyword">class </span><span class="class">MonitorSistema</span>
  <span class="ident">include</span> <span class="constant">Observable</span>

  <span class="keyword">def </span><span class="method">run</span>
    <span class="ident">ultimo_espacio_libre</span> <span class="punct">=</span> <span class="constant">nil</span>
    <span class="ident">loop</span> <span class="keyword">do</span>
      <span class="ident">espacio_libre</span> <span class="punct">=</span> <span class="constant">Disco</span><span class="punct">.</span><span class="ident">espacio_libre</span>
      <span class="ident">puts</span> <span class="punct">&quot;</span><span class="string">Espacio libre en disco: <span class="expr">#{espacio_libre}</span>MB</span><span class="punct">&quot;</span>
      <span class="keyword">if</span> <span class="ident">espacio_libre</span> <span class="punct">!=</span> <span class="ident">ultimo_espacio_libre</span>
        <span class="ident">changed</span>
        <span class="ident">notify_observers</span><span class="punct">(</span><span class="ident">espacio_libre</span><span class="punct">)</span>
        <span class="ident">ultimo_espacio_libre</span> <span class="punct">=</span> <span class="ident">espacio_libre</span>
      <span class="keyword">end</span>
      <span class="ident">sleep</span><span class="punct">(</span><span class="number">60</span><span class="punct">)</span>
    <span class="keyword">end</span>
  <span class="keyword">end</span>
<span class="keyword">end</span>

<span class="keyword">class </span><span class="class">MantenimientoDisco</span>
  <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">limite</span><span class="punct">)</span>
    <span class="attribute">@limite</span> <span class="punct">=</span> <span class="ident">limite</span>
  <span class="keyword">end</span>

  <span class="keyword">def </span><span class="method">update</span><span class="punct">(</span><span class="ident">espacio_libre</span><span class="punct">)</span>
    <span class="keyword">if</span> <span class="ident">espacio_libre</span> <span class="punct">&lt;</span> <span class="attribute">@limite</span>
      <span class="ident">puts</span> <span class="punct">&quot;</span><span class="string">-- Limpiando archivos temporales. <span class="expr">#{espacio_libre}</span>MB de espacio libre</span><span class="punct">&quot;</span>
    <span class="keyword">end</span>
  <span class="keyword">end</span>
<span class="keyword">end</span>

<span class="keyword">class </span><span class="class">AlertaPocoEspacio</span>
  <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">limite</span><span class="punct">,</span> <span class="ident">email</span><span class="punct">)</span>
    <span class="attribute">@limite</span> <span class="punct">=</span> <span class="ident">limite</span>
    <span class="attribute">@email</span> <span class="punct">=</span> <span class="ident">email</span>
  <span class="keyword">end</span>

  <span class="keyword">def </span><span class="method">update</span><span class="punct">(</span><span class="ident">espacio_libre</span><span class="punct">)</span>
    <span class="keyword">if</span> <span class="ident">espacio_libre</span> <span class="punct">&lt;</span> <span class="attribute">@limite</span>
      <span class="ident">puts</span> <span class="punct">&quot;</span><span class="string">-- Notificando a <span class="expr">#{@email}</span>. <span class="expr">#{espacio_libre}</span>MB de espacio libre</span><span class="punct">&quot;</span>
    <span class="keyword">end</span>
  <span class="keyword">end</span>
<span class="keyword">end</span>

<span class="ident">monitor</span> <span class="punct">=</span> <span class="constant">MonitorSistema</span><span class="punct">.</span><span class="ident">new</span>
<span class="ident">monitor</span><span class="punct">.</span><span class="ident">add_observer</span><span class="punct">(</span><span class="constant">MantenimientoDisco</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="number">700</span><span class="punct">))</span>
<span class="ident">monitor</span><span class="punct">.</span><span class="ident">add_observer</span><span class="punct">(</span><span class="constant">AlertaPocoEspacio</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="number">700</span><span class="punct">,</span> <span class="punct">'</span><span class="string">pedro@ejemplo.com</span><span class="punct">'))</span>
<span class="ident">monitor</span><span class="punct">.</span><span class="ident">add_observer</span><span class="punct">(</span><span class="constant">AlertaPocoEspacio</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="number">600</span><span class="punct">,</span> <span class="punct">'</span><span class="string">antonio@ejemplo.com</span><span class="punct">'))</span>
<span class="ident">monitor</span><span class="punct">.</span><span class="ident">run</span></pre>
<p>El ejemplo me parece que es bastante ilustrativo, pero mejor aclararlo algunos detalles <img src='http://blog.negonation.com/es/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>El módulo <em>Observable</em> define una serie de métodos que podéis consultar en <a href="http://ruby-doc.org/core/classes/Observable.html" title="módulo Observable">la documentación del módulo</a>. En nuestro ejemplo usamos tres de ellos:</p>
<ul>
<li><strong>changed</strong><em>(state=true)</em>: cambia el estado del objeto.</li>
<li><strong>notify_observers</strong><em>(*args)</em>: <strong>si el estado es true</strong>, invoca al método <em>update</em> de cada suscriptor con los mismos argumentos.</li>
<li><strong>add_observer</strong><em>(observer)</em>: registra un nuevo suscriptor.</li>
</ul>
<p>Veamos una ejecución de ejemplo del script anterior:</p>
<pre class="ruby"><span class="comment">Espacio libre en disco: 1011MB</span>
<span class="comment">Espacio libre en disco: 821MB</span>
<span class="comment">Espacio libre en disco: 880MB</span>
<span class="comment">Espacio libre en disco: 625MB</span>
<span class="comment">-- Limpiando archivos temporales. 625MB de espacio libre</span>
<span class="comment">-- Notificando a pedro@ejemplo.com. 625MB de espacio libre</span>
<span class="comment">Espacio libre en disco: 730MB</span>
<span class="comment">Espacio libre en disco: 570MB</span>
<span class="comment">-- Limpiando archivos temporales. 570MB de espacio libre</span>
<span class="comment">-- Notificando a pedro@ejemplo.com. 570MB de espacio libre</span>
<span class="comment">-- Notificando a antonio@ejemplo.com. 570MB de espacio libre</span>
<span class="comment">Espacio libre en disco: 716MB</span>
<span class="comment">Espacio libre en disco: 841MB</span>
<span class="comment">Espacio libre en disco: 1016MB</span></pre>
<h5 id="activerecord_publicador_y_suscriptor">ActiveRecord: Publicador y suscriptor</h5>
<p>ActiveRecord tiene dos clases involucradas en la gestión de eventos:</p>
<ul>
<li><em>ActiveRecord::Base</em>: incluye el módulo <em>Observable</em> de Ruby y notifica a los suscriptores todos los eventos a los que se pueden asociar callbacks.</li>
<li><em>ActiveRecord::Observer</em>: es la clase base para todos los observers y define el método <em>update</em>.</li>
</ul>
<p>Cuando un modelo (como sabemos, subclase de <em>ActiveRecord::Base</em> <img src='http://blog.negonation.com/es/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ) cambia de estado invoca a, además de los callbacks dentro de la propia clase, el método <em>notify_observers</em> con los siguientes argumentos:</p>
<ul>
<li>El nombre del evento: before_validation, after_save, after_destroy&#8230;</li>
<li>El objeto ActiveRecord.</li>
</ul>
<p>Tal y como hemos visto, al invocar <em>notify_observers</em> en el modelo se invoca <em>update</em> en todos los observers suscritos a dicho modelo. En los observers de rails el método update invocará a su vez, en caso de que exista, al método de la clase que tenga el nombre del evento notificado y le pasará como argumento el objeto que ha cambiado de estado.</p>
<pre class="ruby">    <span class="comment"># Send observed_method(object) if the method exists.</span>
    <span class="keyword">def </span><span class="method">update</span><span class="punct">(</span><span class="ident">observed_method</span><span class="punct">,</span> <span class="ident">object</span><span class="punct">)</span> <span class="comment">#:nodoc:</span>
      <span class="ident">send</span><span class="punct">(</span><span class="ident">observed_method</span><span class="punct">,</span> <span class="ident">object</span><span class="punct">)</span> <span class="keyword">if</span> <span class="ident">respond_to?</span><span class="punct">(</span><span class="ident">observed_method</span><span class="punct">)</span>
    <span class="keyword">end</span></pre>
<h5 id="inicializacin_de_observers_en_rails">Inicialización de observers en Rails</h5>
<p>Ya hemos visto cómo notifica <em>ActiveRecord::Base</em> los eventos a los observers. No obstante, no hemos visto cómo se suscriben los observers (ya sabéis, la llamada al método <em>add_observer</em> del módulo Observable).</p>
<p>En el caso de los observers de rails la llamada a <em>add_observer</em> se realiza en el constructor de <em>ActiveRecord::Observer</em>:</p>
<pre class="ruby">    <span class="comment"># Start observing the declared classes and their subclasses.</span>
    <span class="keyword">def </span><span class="method">initialize</span>
      <span class="constant">Set</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="ident">observed_classes</span> <span class="punct">+</span> <span class="ident">observed_subclasses</span><span class="punct">).</span><span class="ident">each</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">klass</span><span class="punct">|</span> <span class="ident">add_observer!</span> <span class="ident">klass</span> <span class="punct">}</span>
    <span class="keyword">end</span></pre>
<p>Sin embargo, tal y como dice <a href="http://api.rubyonrails.org/classes/ActiveRecord/Observer.html" title="Documentación de la clase Observer">la documentación</a>, para utilizar los observers no instanciamos objetos, sino que los configuramos en el archivo <em>environment.rb</em> (<em>config.active</em>record.observers_). Esto es así para delegar en Rails la responsabilidad sobre cuándo inicializar los observers.</p>
<p>No vamos a entrar en cuándo los inicializa, sin embargo sí que diremos cómo lo hace.</p>
<p>La clase <em>ActiveRecord::Observer</em> sigue un <a href="http://en.wikipedia.org/wiki/Singleton_pattern" title="Singleton pattern">patrón Singleton</a>. Este patrón garantiza que como máximo habrá una instancia de una clase. Para ello se cambia la visibilidad del constructor para que sea privado y se define un método que cree devuelva el único objeto que instancia la clase.</p>
<p>Este patrón también viene en la librería estándar de Ruby con el <a href="http://ruby-doc.org/core/classes/Singleton.html" title="módulo Singleton">módulo Singleton</a>. Este módulo cambia la visibilidad del constructor y define el método <em>instance</em>.</p>
<p>Por lo tanto, Rails emplea el método <em>instance</em> para suscribir los observers a los modelos correspondientes.</p>
<h5 id="sabiendo_esto8230">Sabiendo esto&#8230;</h5>
<p>Sabiendo estos detalles sobre la implementación del patrón Publicador-Suscriptor en ActiveRecord y su inicialización de Rails tenemos los conocimientos técnicos necesarios para desarrollar nuestros propios observers.</p>
<p>Nosotros tuvimos la necesidad de desarrollar nuestros propios observers al querer tener una única clase que se suscribiese varios modelos, pero atendiendo a eventos distintos según el modelo.</p>
<p>En un post próximo, aprovecharemos lo que hemos explicado aquí para ver los detalles de nuestros observers.</p>
<p>Happy Hacking! <img src='http://blog.negonation.com/es/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.negonation.com/es/gestion-de-eventos-en-rails-observers/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

