La Coctelera

Categoría: Ruby on Rails

Tres de mis helpers para Ruby on Rails

Un par de helpers para tener las vistas más limpias

A veces, al trabajar con layouts, queremos que determinadas partes de nuestras vistas dependan de si estamos (o no) en una acción o controlador determinados.

Uno de los inconvenientes de este tipo de condiciones (para mi gusto) es luego ver mucho código como este:

 <% if params[:action] != 'index' %>
   [...]
 <% end %>
 <% if params[:action] == 'show' or params[:action] == 'edit' %>
   [...]
 <% end %>
 

Para mi gusto ese tipo de condiciones son demasiado ruidosas para lo que hacen, así que tengo dos helpers: in_action? e in_controller?. Los argumentos son los mismos para ambas, y funcionarían así:

 <% if in_action? :except => 'index' %>
   [...]
 <% end %>
 <% if  in_action? :only => ['show', 'edit'] %>
   [...]
 <% end %>
 

A mí me gusta más, si a ti también, aquí viene el código de los helpers ^_^

   def check_in(element, list)
     raise ArgumentError, "Wrong parameter, #{list} is not a Hash" unless list.class == Hash
     if list[:only]
       list[:only]   = [list[:only]].flatten
       return (list[:only].include?(element))
     end
     
     if list[:except]
       list[:except] = [list[:except]].flatten 
       return (not list[:except].include?(element))
     end
     raise ArgumentError, "Required :only or :except"
   end
 
   def in_action?(actions={})
     check_in(params[:action],actions)
   end
 
   def in_controller?(controllers={})
     check_in(params[:controller],controllers)
   end
 

Si te prefieres, en vez de utilizar :only y :except, tener: in_action?, not_in_action?, in_controller? y not_in_controller?. Cambiar el código de antes debería ser trivial ;)

Un tercer helper: tab_link_to

A veces queremos usar menús de "tabs" (pestañas) en los que todos los enlaces tienen un estilo menos el de la página en la que nos encontramos que tiene un estilo distinto. Aquí en la coctelera, sin ir más lejos, tenemos los siguientes tabs:

Para evitarme más condiciones de las necesarias en las vistas hice el helper tab_link_to que, apoyándose en in_action? e in_controller?, añade class="selected" al enlace en el que nos encontramos.

   def tab_link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
     options[:controller] ||= params[:controller]
     options[:controller]   = options[:controller].to_s
     options[:action] = options[:action].to_s
     raise ArgumentError, "Action not specified" unless options[:action]
     
     html_options ||= {}
     html_options[:class] ||= ""
     if in_action?(:only => options[:action]) and in_controller?(:only => options[:controller])
       html_options[:class] = "#{html_options[:class]} selected".strip
     end
     
     return(link_to name, options, html_options, *parameters_for_method_reference)
   end
 

Los parámetros del helper son exactamente los mismos que los de link_to, pero hay que especificar siempre la acción a la que apunta el enlace con :action. Si no especificamos controlador se supondrá que es el mismo que en la petición actual.

Fernando Blat lo decía: TESTEA TUS FIXTURES! (y se DRY)

Fernando Blat (al que no tengo el placer de conocer :) lo decía: hay que testear las fixtures. Al ponerme con el TDD y encontrarme con algunas fixtures inválidas me acordé de su consejo.

Una vez tenía mi test_your_fixtures en el modelo que tenía fixtures inválidas me dije:

  1. Fernando lo comenta como algo obligatorio
  2. Yo estoy de acuerdo
  3. Estar definiendo este método en todas las clases de tests unitarios no es DRY

Así que he aquí mi propuesta para testear tus fixtures de forma DRY y bastante transparente:

 $ cat test/test_your_fixtures.rb
 module TestYourFixtures
   def test_fixtures
     begin
       mod = Object.const_get(self.class.to_s.sub(/Test$/,''))
       if mod.public_methods.include? 'find'
         mod.find(:all).each do |fixture|
           assert fixture.valid?, "Invalid fixture #{mod}: #{fixture.to_param}\n#{fixture.to_yaml}"
         end
       end
     rescue NameError
     end
   end
 end
 Test::Unit::TestCase.send :include, TestYourFixtures
 


 $ cat test/test_helper.rb | grep your_fixtures
 require File.dirname(__FILE__) + '/test_your_fixtures'
 

Si seguimos la convención de nombres de rails, las clases de tests unitarios de nuestros modelos tendrán un nombre tal que ModeloTest. Si en cada una de estas clases hemos cargado las fixtures de ese modelo:

 class UserTest < AbstractModelTestCase
   fixtures :users
 [...]
 end
 

Al ejecutar los tests de UserTest se ejecutará también el test heredado text_fixtures, que cargará la clase del modelo User, comprobará que tiene un método find (para asegurarnos de que es una clase de modelo), y comprobará que todas las fixtures son válidas.

Para mi gusto esto es más DRY y transparente. No obstante, se admiten sugerencias :)

[an error occurred while processing the directive]