<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>http://dagi3d.net/blog/</id>
  <title>dagi3d - ruby on rails</title>
  <link type="text/xml" rel="self" href="http://dagi3d.net/blog/atom/"/>
  <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/"/>
  <updated>2009-08-16T22:43:44Z</updated>
  <author>
    <name>dagi3d</name>
  </author>
  <entry>
    <title>Actualizaci&#243;n de Acts As Flying Saucer</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2009/8/16/actualizacion-de-acts-as-flying-saucer"/>
    <id>http://dagi3d.net/blog/archive/2009/8/16/actualizacion-de-acts-as-flying-saucer</id>
    <published>2009-08-16T22:43:44Z</published>
    <updated>2009-08-16T22:43:44Z</updated>
    <content type="html">
      <![CDATA[<p>Hace poco me enviaron un correo con un par de bugs que habían encontrado en el plugin que escribí para <a href="/blog/archive/2008/12/06/acts_as_flying_saucer-plugin-para-generar-documentos-pdf-con-rails">generar pdfs desde Rails</a> y y­a están solucionados:</p>
  <p>­Antes se añadía por defecto la opción x_sendfile a la hora de enviar el pdf al cliente, pero en según qué entornos puede generar un fichero vacio(por ejemplo cuando estemos en desarrollo con mongrel), por lo que ahora se ha eliminado, pero se puede pasar como parámetro de send_file cuando sea necesario:</p>
<p><code></code><br />
  <pre><br />
class FooController &lt; ApplicationController<br />
  <br />
  acts_as_flying_saucer<br />
  <br />
  def index<br />
    render_pdf :send_file =&gt; { :filename =&gt; &#8216;bar.pdf&#8217;, :x_sendfile =&gt; true}<br />
  end<br />
end­<br />
</pre><br />
  <p></p>
<p>La librería Flying Saucer necesita que las llamadas a los recursos(imágenes y hojas de estilo) sean llamadas de manera absoluta. Antes el plugin convertía las llamadas en rutas locales pero entonces no se podían usar los assets hosts, por lo que ahora se convierten en llamadas remotas y en caso de no especificar ningún asset host, se utiliza el mismo desde el que se este solicitando el documento:</p><br />
  <pre><code></p>
<ol>
	<li><span class="caps">HTML</span> <br />
&lt;%= stylesheet_link_tag(&#8220;styles.css&#8221;) %&gt;<br />
&lt;link href=&#8220;/stylesheets/styles.css?1228586784&#8221; media=&#8220;screen&#8221; rel=&#8220;stylesheet&#8221; type=&#8220;text/css&#8221;&gt;</li>
</ol>
<p>&lt;%= image_tag(&#8220;rails.png&#8221;) %&gt;<br />
&lt;img alt=&#8220;Rails&#8221; src=&#8220;/images/rails.png?1228433051&#8221;&gt;</p>
<ol>
	<li><span class="caps">PDF</span><br />
&lt;%= stylesheet_link_tag(&#8220;styles.css&#8221;) %&gt;<br />
&lt;link href=&#8220;http://localhost:3000/stylesheets/styles.css&#8221; media=&#8220;print&#8221; rel=&#8220;stylesheet&#8221; type=&#8220;text/css&#8221;&gt;</li>
</ol>
<p>&lt;%= image_tag(&#8220;rails.png&#8221;) %&gt;<br />
&lt;img alt=&#8220;Rails&#8221; src=&#8220;http://localhost:3000/images/rails.png&#8221;&gt;<br />
</code><br />
</pre><br />
  <p><a href="http://github.com/dagi3d/acts_as_flying_saucer/commit/52e3c71cc92bd36622232adb4608a1aa872ab087">Commit</a></p><br />
  <p>Gracias a <a href="http://webcrisps.wordpress.com/">Max Williams</a> por el feedback<br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br /></p></p>]]>
    </content>
  </entry>
  <entry>
    <title>Cambiando la locale de una clase en Globalize2</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2009/4/10/cambiando-la-locale-de-una-clase-en-globalize2"/>
    <id>http://dagi3d.net/blog/archive/2009/4/10/cambiando-la-locale-de-una-clase-en-globalize2</id>
    <published>2009-04-10T12:07:18Z</published>
    <updated>2009-04-10T12:07:18Z</updated>
    <content type="html">
      <![CDATA[<p>En el último proyecto que estuve realizando con RoR necesitaba ofrecer soporte multiidioma para los modelos así que opté por utilizar Globalize2 ya que ya había estado trasteando con esta librería y me parecía muy cómoda de usar(aunque todavía le quedan algunas cosas por pulir).</p>
  <p>El caso es que desde hace relativamente poco permite cambiar la locale de una clase en concreto sin necesidad de cambiar el idioma de toda la aplicación. Así se podría tener nuestra aplicación en un idioma determinado<br />
    y trabajar con una instancia de un modelo en otro totalmente distinto.</p>
  <p>El problema era que con la implementación actual, si se cambiaba la locale de una clase en concreto, se cambiaba automáticamente en <strong>todas</strong> las demás clases que tuvieran campos traducibles con Globalize2:­&nbsp;<br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br /></p>
<pre><code>­I18n.locale = 'es'
<p>Post.locale = &#8216;en&#8217;<br />
Post.locale &nbsp;       # en<br />
Category.locale     # en &lt;- <span class="caps">WTF</span>?!<br />
</code></pre><br />
  <p>Esto sucede por la utilización de variables de clase y la implementación de éstas en Ruby­. Con Ruby, si se modifica el valor de una variable de clase que ha heredado de otra, cambiará en todas las demás clases que hereden de la misma:</p><br />
  <pre><code>
class Polygon
  @@sides = 0</p>
def self.sides
@@sides
end
<p>end</p>
<p>class Triangle &lt; Polygon<br />
  @@sides = 3<br />
end</p>
<p>class Square &lt; Polygon<br />
­  @@sides = 4<br />
end</p>
<p>Triangle.sides # 4 &lt;- <span class="caps">WTF</span>?!­<br />
</code></pre><br />
Rails provee un mecanismo para solucionar esto a través de los métodos write_inheritable_attribute y read_inheritable_attribute, así que escribí un mini parche para este plugin de Rails.&nbsp;<a href="http://github.com/dagi3d/globalize2/commit/5026a20fcb975655d5f0568ca3b6b82d449c0fe7">Aquí el commit del fork en Github</a><br />
  <br /></p>]]>
    </content>
  </entry>
  <entry>
    <title>Probando Globalize2</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2008/11/10/probando-globalize2"/>
    <id>http://dagi3d.net/blog/archive/2008/11/10/probando-globalize2</id>
    <published>2008-11-10T00:53:13Z</published>
    <updated>2008-11-10T00:53:13Z</updated>
    <content type="html">
      <![CDATA[<p>
<p>Vi que se había liberado hace poco una <a href="http://github.com/joshmh/globalize2/tree/master">nueva versión de Globalize</a>, el plugin para Rails que permite tener traducciones de nuestros modelos, así que me animé a probarlo. Ahora es compatible con Rails 2.2 y hace uso de la nueva api para la internacionalización de nuestras aplicaciones. Además, ahora en lugar de tener una única tabla con todas las traducciones, hay que crear una tabla adicional para cada modelo con los campos que queramos que sean traducibles(tal como hace el plugin <a href="http://www.samlown.com/en/page/RailsTranslateColumnsPluginReadme">translate_columns</a> de Samuel Lown). Por ahora hay que escribir la migración a mano, pero se planea hacer un generator para automatizar este paso(si saco un momentillo igual me animo a escribirlo yo, que nunca hice uno). <br />
    <br />
    <br />
    <br />
    <br />De momento parece que funciona bastante bien, salvo un detalle que me tuvo entretenido un buen rato y que comento por si alguien se encuentra en la misma situación.</p><br />
  <p>En todos los ejemplos de la documentación aparece que podemos indicar la nueva locale usando un símbolo, pero resulta que si lo hacemos así, no se puede recuperar luego el campo traducido(aunque sí que se guarda correctamente en la base de datos) y lo que hay que hacer es utilizar cadenas en su lugar.<br />
   <br /></p><br />
  <p>Supongamos que tenemos la clase Post con la siguiente migración:</p><code></code><br />
  <pre># Post<br />
class Post &lt; ActiveRecord::Base<br />
  translates :title<br />
end­<br />
­­</pre><br />
  <pre># CreatePosts<br />
class CreatePosts &lt; ActiveRecord::Migration<br />
  def self.up<br />
    create_table :posts do |t|<br />
      t.timestamps<br />
    end<br />
    create_table :post_translations, :force =&gt; true do |t|<br />
      t.references :post<br />
      t.string :locale<br />
      t.string :title<br />
      t.timestamps<br />
    end<br />
  end</p>
def self.down
drop_table :post_translations
drop_table :posts
end
<p>end<br />
</pre><br />
  <p></p>
<p>Podemos ver que si usa­­mos un símbolo para nuestra locale, el campo nos devuelve un nil, pero si usamos una cadena, devuelve el valor correcto:</p><br />
  <pre>gambitero:rails-test dagi3d$ ./script/console <br />
Loading development environment (Rails 2.2.0)<br />
&gt;&gt; I18n.locale = :es<br />
=&gt; :es<br />
&gt;&gt; post = Post.new(:title =&gt; &#8216;titulo&#8217;)<br />
=&gt; #&lt;post id:=&quot;&quot; created_at:=&quot;&quot; nil,=&quot;&quot; updated_at:=&quot;&quot; nil=&quot;&quot;&gt;<br />
&gt;&gt; post.title<br />
=&gt; nil<br />
&gt;&gt; I18n.locale = &#8220;es&#8221;<br />
=&gt; &#8220;es&#8221;<br />
&gt;&gt; post.title = &#8220;titulo&#8221;<br />
­=&gt; &#8220;titulo&#8221;<br />
&gt;&gt; post.title<br />
=&gt; &#8220;titulo&#8221;<br />
&gt;&gt; I18n.locale = &#8220;en&#8221;<br />
=&gt; &#8220;en&#8221;<br />
&gt;&gt; post.title = &#8220;title&#8221;<br />
=&gt; &#8220;title&#8221;<br />
&gt;&gt; I18n.locale = &#8220;es&#8221;<br />
=&gt; &#8220;es&#8221;<br />
&gt;&gt; post.title<br />
=&gt; &#8220;titulo&#8221;</p>
<p></post></pre><br />
  <p></p>
<p>También­ comentar que si tenemos traducido un campo en la locale por defecto(inicialmente ésta es &#8216;en-US&#8217;) e intentamos acceder a un atributo de una que todavía no tiene ningún valor asignado, se devolverá el valor de la locale por defecto. Si queremos evitar este comportamiento basta con comentar la línea 26 del fichero vendor/plugins/globalize2/lib/globalize/locale/fallbacks.rb para que no añada ésta a la lista de fallbacks(otra opción sería redefinir el constructor de la clase Globalize::Locale::FallBacks)</p><br />
  <p>Por último decir que de momento la asociación que se crea entre nuestra clase y la generada por el plugin con las traducciones no se carga con &#8216;eager loading&#8217; pero es una cosa que tienen prevista hacer.</p>
</p>]]>
    </content>
  </entry>
  <entry>
    <title>Validando las asociaciones de ActiveRecord con RSpec</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2008/9/6/validando-las-asociaciones-de-activerecord-con-rspec"/>
    <id>http://dagi3d.net/blog/archive/2008/9/6/validando-las-asociaciones-de-activerecord-con-rspec</id>
    <published>2008-09-06T01:35:58Z</published>
    <updated>2008-09-06T01:35:58Z</updated>
    <content type="html">
      <![CDATA[<p>
<p>Después de estar un tiempo sin tocar Rails para nada, me puse el otro día a cacharrerar un poco con RSpec y se me ocurrió escribir un <em>matcher </em>para validar las asociaciones de los modelos, ya que consideraba que un simple <em><code>objeto.should respond_to(:metodo)&lt;/em&gt; realmente tampoco garantiza nada. 
    
    
    
    
    
    
    
    
    
    
    &lt;br /&gt;Lo único 'interesante' que puede aportar este código es que a la hora de escribir nuestros specs, basta con poner directamente el nombre de la relación, ya que la clase asociada se obtiene de manera automática(bendita sea la convención sobre la configuración :)), al contrario que el resto de ejemplos que pude encontrar por ahí, donde se indica la clase y si se desea, se indica aparte el nombre de la relación(y creo que así se aporta algo de legibilidad a los specs)
    :&lt;/p&gt;
  &lt;pre&gt;</code>record.should have_many(:songs) # utiliza la clase Song</p>
<p>@record.should belong_to(:artist) # utiliza la clase Artist­</p>
<p><code>record.should have_one(:cover) # utiliza la clase Cover
&lt;/pre&gt;
  &lt;p&gt;Si fuese necesario también se puede indicar de manera manual la clase del modelo relacionado: 
    
    
    
    
    
    
    &lt;br /&gt;&lt;/p&gt;
  &lt;pre&gt;</code>record.should have_many(:favorite_songs).from_class(Song)</pre><br />
  <pre>module ARAssociationsMatchers<br />
  <br />
  # ARAssociationMatcher<br />
  #<br />
  class ARAssociationMatcher<br />
  <br />
    def initialize(expected, macro)<br />
      <code>expected_association = expected
      @expected_macro = macro
    end
    
    def matches?(target)
      @target = target
      
      unless @expected_class.nil?
        expected_class = @expected_class
      else
        expected_class_name = @expected_association.to_s.singularize.camelize
        expected_class = Kernel.const_get(expected_class_name)
      end
      
      reflection = target.class.reflect_on_association(</code>expected_association)<br />
      <br />
      !reflection.nil? &amp;&amp; (reflection.macro == <code>expected_macro) &amp;amp;&amp;amp; (reflection.klass == expected_class)
    end
    
    def from_class(expected_class)
      @expected_class = expected_class
      self
    end
    
    def failure_message
      "expected #{</code>target.inspect} to #{@expected_macro} #{@expected_association.inspect}, but it didn&#8217;t&quot;<br />
    end<br />
    <br />
    def negative_failure_message<br />
      &#8220;expected #{@target.inspect} not to #{@expected_macro} #{@expected_association.inspect}, but it didn&#8217;t&#8221;<br />
    end<br />
    <br />
  end<br />
  <br />
  # matchers functions<br />
  #<br />
  def have_many(expected)<br />
    ARAssociationMatcher.new(expected, :has_many)<br />
  end</p>
def have_one(expected)
ARAssociationMatcher.new(expected, :has_one)
end

def belong_to(expected)
ARAssociationMatcher.new(expected, :belongs_to)
end

<p>end<br />
</pre></p>
<p>La idea inicial está tomada de ­<a href="http://smartic.us/2007/11/26/rspec-matcher-for-active-record-associations">este enlace</a></p>]]>
    </content>
  </entry>
  <entry>
    <title>Back to the world</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2008/8/21/back-to-the-world"/>
    <id>http://dagi3d.net/blog/archive/2008/8/21/back-to-the-world</id>
    <published>2008-08-21T22:44:02Z</published>
    <updated>2008-08-21T22:44:02Z</updated>
    <content type="html">
      <![CDATA[<p>Bueno, después de bastante tiempo sin actualizar voy a ver si vuelvo a retomar el tema del blog. Últimamente han cambiado muchas cosas, tanto en el aspecto laboral/escolar como en el personal(eso sí, todas para bien), así que espero volver a darle caña al tema de Rails que lo tenía bastante abandonado. Me acaban de llegar de Amazon los libros <a href="http://www.mypearsonstore.com/title/0672328844">The Ruby Way</a>, <a href="http://www.mypearsonstore.com/title/0321445619">The Rails Way</a>(que ya había catado en su versión en pdf y me pareció muy bueno) y <a href="http://www.mypearsonstore.com/bookstore/product.asp?isbn=0130676349">Agile Software Development with Scrum</a>, así que ahora toca <a href="http://es.wikipedia.org/wiki/Procrastinaci%C3%B3n">procrastinar</a> a tope :)<br />
    <br />
    <br />
    <br />
    <br /></p>]]>
    </content>
  </entry>
  <entry>
    <title>A&#241;adiendo nuevos tipos en las migraciones de Rails</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2008/1/4/anadiendo-nuevos-tipos-en-las-migraciones-de-rails"/>
    <id>http://dagi3d.net/blog/archive/2008/1/4/anadiendo-nuevos-tipos-en-las-migraciones-de-rails</id>
    <published>2008-01-04T00:50:12Z</published>
    <updated>2008-01-04T00:50:12Z</updated>
    <content type="html">
      <![CDATA[<p>
<p>En el proyecto con el que ando liado en mis r­atos libres, necesitaba añadir a varios modelos atributos que almaceneran decimales. En principio era tan simple como crear en cada migración las columnas con su tipo de dato correspondiente:</p><br />
  <pre>t.column :price, :precision =&gt; 6, :scale =&gt; 2, :default =&gt; nil<br />
</pre><br />
  <p>El caso es ­que se&nbsp; me hacía un tanto repetitivo estar añadiendo la misma línea en todas las migraciones donde me hacía falta(sé que no es para tanto, pero a veces la vagancia me puede) y además no estaba usando el estilo de los <a href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#M001222">nuevos atajos que trae Rails 2.0</a>, así que la solución era tan sencilla como reabrir la clase <em>ActiveRecord::ConnectionAdapters::TableDefinition</em>(bendito Ruby) y crear el método necesario:</p>
</p>
<pre>class ActiveRecord::ConnectionAdapters::TableDefinition

def currency(*columns)
columns.each do |column|
self.column column, :decimal, :precision =&gt; 6, :scale =&gt; 2, :default =&gt; nil
end
end

<p>end­<br />
</pre><br />
­<br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <br />
  <p>Y ya podía usar en todas mis migraciones el método t.price teniendo que indicar únicamente el nombre de la columna(o columnas) que quería crear:<br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br /></p><br />
  <pre>class CreateLineItems &lt; ActiveRecord::Migration<br />
  def self.up<br />
    create_table :line_items do |t|<br />
      t.references :order<br />
      t.references :item, :polymorphic =&gt; true<br />
      <strong>t.currency :price</strong><br />
      t.timestamps<br />
    end<br />
  end</p>
def self.down
drop_table :line_items
end
<p>end<br />
</pre></p>]]>
    </content>
  </entry>
  <entry>
    <title>Charla/Taller de introducci&#243;n a Ruby On Rails</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2007/11/6/charla-taller-de-introduccion-a-ruby-on-rails"/>
    <id>http://dagi3d.net/blog/archive/2007/11/6/charla-taller-de-introduccion-a-ruby-on-rails</id>
    <published>2007-11-06T21:42:07Z</published>
    <updated>2007-11-06T21:42:07Z</updated>
    <content type="html">
      <![CDATA[<p><img align="right" src="http://www.dagi3d.net/temp/rails.png" />Bajo el marco de las <span class="caps">III</span> Jornadas de Informática organizadas en la Universidad Europea de Madrid que se celebran los días 15 y 16 de noviembre, <a href="http://raul.murciano.net/">Raúl Murciano</a> y yo impartiremos una charla-taller de introducción a Ruby On Rails. El taller tendrá lugar el jueves 15 y comenzará a las 12:00 y finalizará sobre las 14:00.<br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />
    <br />El acceso es totalmente libre y gratuito(al igual que el resto de charlas y eventos de las jornadas) y animo a que se acerque a todo aquel que quiera iniciarse en este estupendo framework de desarrollo web.</p>
  <p><a href="http://www.gluem.net/jornadas/programa.html">Programa de las jornadas</a><br />
    <br /><a href="http://corporativo.uem.es/es/como-llegar">Cómo llegar a la <span class="caps">UEM</span></a><br />
    <br /></p>]]>
    </content>
  </entry>
  <entry>
    <title>Programa de facturaci&#243;n en Ruby On Rails</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2007/8/15/programa-de-facturacion-en-ruby-on-rails"/>
    <id>http://dagi3d.net/blog/archive/2007/8/15/programa-de-facturacion-en-ruby-on-rails</id>
    <published>2007-08-15T21:47:33Z</published>
    <updated>2007-08-15T21:47:33Z</updated>
    <content type="html">
      <![CDATA[<p>Hace poco para optar a un trabajo tuve que realizar como prueba una<br />
aplicación en Rails que gestionase un listado de facturas junto a sus<br />
clientes. El caso es que decidí añadirle alguna cosilla más y hacerle<br />
una interfaz más aceptable y liberar el código por si a alguien le podía<br />
interesar.</p>
<br />La aplicación puede exportar a pdf(aunque hace falta java para
<p>esto) y el diseño del pdf es totalmente personalizable a partir de un<br />
documento xhtml y css.</p>
<br />De momento no tiene mucha cosa, pero la idea es utilizarlo e irlo
<p>ampliando conforme lo vaya necesitando ahora que empiezo con el tema<br />
del freelanceo.</p>
<br />
<br />
<p>Se puede ver en funcionamiento en <a target="_blank" href="http://facturails.dagi3d.net/">http://facturails.dagi3d.net/</a> y se puede descargar directamente desde el repositorio subversion en <a target="_blank" href="http://svn.dagi3d.net/rails/facturails/trunk">http://svn.dagi3d.net/rails/facturails/trunk</a> (bajo licencia <span class="caps">MIT</span>)</p>]]>
    </content>
  </entry>
  <entry>
    <title>Sumando varios elementos de un array</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2007/8/12/sumando-varios-elementos-de-un-array"/>
    <id>http://dagi3d.net/blog/archive/2007/8/12/sumando-varios-elementos-de-un-array</id>
    <published>2007-08-12T16:54:49Z</published>
    <updated>2007-08-12T16:54:49Z</updated>
    <content type="html">
      <![CDATA[<p>
<p>Realizando una aplicación en Rails necesitaba<br />
  sumar varios atributos de los objetos almacenados en un array. El problema es que cuando se trata de sumar un único atributo, se suele utilizar el método <em>inject</em>:</p><br />
  <pre>total = mi_array.inject { |sum, obj| sum + obj.value }</pre><br />
  <p><br />
pero en este caso no me terminaba de convecer estar llamando al método <em>inject </em>tantas veces como atributos quisiera sumar, así que en teoría la solución pasaría por iterar sobre el array e ir sumando:</p><br />
  <pre>var1 = 0<br />
var2 = 0<br />
var3 = 0<br />
&#8230;<br />
mi_array.each do |obj|<br />
  var1 += obj.value1<br />
  var2 += obj.value2<br />
  var3 += obj.value3<br />
  &#8230;<br />
end<br />
</pre><br />
  <p>Funcionar, funcionaba, pero digamos que el código quedaba algo feo, así que para seguir trasteando, intenté encontrar una solución más &#8216;ruby&#8217; y esto fue lo que hice:</p><br />
  <pre>class Array<br />
  <br />
  def accumulate(fields)<br />
    <br />
    results = fields.dup<br />
    <br />
    self.each do |obj|<br />
      fields.each_key do |key|<br />
        results[key] += obj.send(key) if obj.respond_to?(key)<br />
      end<br />
    end<br />
    <br />
    results<br />
  end<br />
end<br />
</pre><br />
  <p>Ahora bastaba con llamar al método <em>accumulate </em>sobre el array de objetos e indicar qué atributos quería sumar para obtener un hash con los resultados:</p><br />
  <pre># la lista de objetos<br />
foos = [<br />
  OpenStruct.new(:foo =&gt; 1, :bar =&gt; 2, :foobar =&gt; 3),<br />
  OpenStruct.new(:foo =&gt; 3, :bar =&gt; 4, :foobar =&gt; 5),<br />
  OpenStruct.new(:foo =&gt; 5, :bar =&gt; 6, :foobar =&gt; 7)<br />
]</p>
<p>foos.accumulate(:foo =&gt;­; 0, :bar =&gt; 0, :foobar =&gt; 0)</p>
<ol>
	<li>{:foo=&gt;9, :bar=&gt;12, :foobar=&gt;15}­<br />
</pre></li>
</ol>]]>
    </content>
  </entry>
  <entry>
    <title>Comparar gemas instaladas en desarrollo y producci&#243;n</title>
    <link type="text/html" rel="alternate" href="http://dagi3d.net/blog/archive/2007/7/21/comparar-gemas-instaladas-en-desarrollo-y-produccion"/>
    <id>http://dagi3d.net/blog/archive/2007/7/21/comparar-gemas-instaladas-en-desarrollo-y-produccion</id>
    <published>2007-07-21T05:42:10Z</published>
    <updated>2007-07-21T05:42:10Z</updated>
    <content type="html">
      <![CDATA[<p>
<p>A raíz de un <a href="http://www.norellana.com/2007/07/17/cuidado-con-lo-que-instalas-en-desarrollo/">post</a> en el blog de <a href="http://www.norellana.com/">Nicolás Orellana</a>, donde mencionaba el problema de no tener sincronizadas las gemas instaladas en la máquina de desarrollo con las que deberían estar también en la máquina de producción, me animé a escribir una pequeña tarea para Capistrano que intentase resolver este problema. La idea en principio era sencilla, tan sólo habría que obtener la lista de gemas instaladas en local utilizando el comando &#8216;gem list&#8217; tal como mencionaba Nicolás, hacer lo mismo en remoto, compararlas para ver las diferencias y luego ofrecer la posibilidad de instalar aquellas que no estuviesen en la máquina de producción.</p><br />
  <p>Y esta fue la solución a la que llegué (de momento no hace ningún control sobre las versiones, pero si tengo un rato intentaré completarla):</p><br />
  <p></p><br />
  <pre>task :compare_gems do<br />
  <br />
  def parse_gem_line(line, gems)<br />
    if line =~ /^([[:alnum:]]+)\s\((.*)\)/<br />
      gem = $1<br />
      gems[gem] = []<br />
      versions = $2.gsub(/\s/, &#8217;&#8217;).split(&#8220;,&#8221;)<br />
      gems[gem] &lt;&lt; versions<br />
    end<br />
  end<br />
  <br />
  local_gems = {}<br />
  remote_gems = {}<br />
  <br />
  local_gem_list = %x{gem list}<br />
  local_gem_list.each_line { |line| parse_gem_line(line, local_gems)}</p>
run &#8220;gem list&#8221; do |channel, stream, data|
parse_gem_line(data, remote_gems)
end

not_installed_gems = local_gems.dup

not_installed_gems.delete_if {|key, value| remote_gems.has_key? key }

puts &#8220;== Not installed gems ==&#8221;

not_installed_gems.each_key do |gem|

puts &#8220;* #{gem}&#8221;
puts &#8220;Install? [yN]&#8221;
answer = <span class="caps">STDIN</span>.gets.chomp

if answer.downcase == &#8220;y&#8221;
run &#8220;gem install #{gem} -y&#8221; do |channel, stream, data|
puts data
if data =~ /^&gt;/
install_option = <span class="caps">STDIN</span>.gets.chomp
channel.send_data &#8220;#{install_option}\n&#8221;
end
end
end
end

puts &#8220;========&#8221;
<p>end<br />
</pre><br />
  <p></p></p>]]>
    </content>
  </entry>
</feed>
