Rails, REST e i18n

Ahora que estamos utilizando a tope Rails 2.0 y toda la parafernalia REST en nuestras aplicaciones, nos hemos dado de bruces con un pequeño problema de rutas.

La solución pre-REST

Digamos que nuestras aplicaciones están internacionalizadas y que tenemos la costumbre de poner el código de idioma en la URL, utilizando un filtro para forzar que este código aparezca en la misma mediante una redirección. Una técnica bastante habitual y extensamente documentada.

# application.rb
before_filter :set_language

def set_language     
  unless (locale = params[:locale]).blank? && Languages.supported?(locale)
    set_locale(@locale = locale)        
  else
    redirect_to params.merge(:locale => Languages.default)
    return false
  end
end

# routes.rb
map.connect ':locale/:controller/:action/:id' 
map.connect ':controller/:action/:id'

De este modo, la primera petición a la aplicación que no lleve el idioma en la URL recibirá como respuesta una redirección a la misma URL con el añadido del idioma.

El problema con REST

Si añadimos un recurso, las rutas generadas y los helpers para crear paths y urls no incluirán el código de idioma, por lo tanto, si no hacemos nada, al pasar por el filtro cada petición recibirá como respuesta un redirect. Mal asunto.

# routes.rb
ActionController::Routing::Routes.draw do |map|   
  map.resources :customers
end

La solución

En primer lugar necesitamos modificar ligeramente las rutas para que estas incluyan el código de idioma.

ActionController::Routing::Routes.draw do |map|   
  map.with_options(:path_prefix => ':locale') do |localized_map|
    localized_map.resources :order
  end
end

Por último debemos lidiar con la generación de rutas. Como quedaría feo (y no muy DRY) tener que andar pasando el código de idioma a cada helper (léase order_path(@current_locale, @order)) lo que necesitamos es modificar el comportamiento de Rails para que lo haga él solito. Y qué suerte la nuestra, ya existe un plugin para eso: localize_url_helpers, que de forma transparente incluye el idioma y nos permite usar los helpers como de costumbre.

C’est voila.

Kilos y kilos de refactoring

Las últimas semanas de desarrollo con Ruby on Rails en Trabe Soluciones se han dividido a partes iguales entre el desarrollo de una nueva aplicación y la modificación y mejora de otras dos. Trabajar con código antiguo (entiéndase por antiguo: con al menos un mes de vida) es siempre una invitación para la mejora y el refactoring. Es indiscutible que a lo largo de un mes un programador evoluciona de manera tal que todo lo que ha hecho hasta el momento le parece malo (o al menos peor que lo que hace actualmente). Demos paso al refactoring.

Podemos enfocar esta tarea de un modo sistemático y enfermizo, en busca de un código excelso cuya sola contemplación nos haga entrar en una suerte de experiencia mística (perdiendo tiempo y salud mental) o bien podemos dejar que fluya sin más.

Un ejemplo: vamos a simplificar la definición de las típicas acciones index que sólo delegan. Cuando te encuentras por segunda o tercera vez con algo como esto:

def index
  list
  render :action => list
end

... decides cambiarlo por algo como …

index_as :list

# Refactoring ohh yeah!!!
def self.index_as(method)
  define_method :index do
    send(method)
    render :action => method
  end      
end

10 minutos después la aplicación ha perdido peso y todos lo agradecemos (y más ahora que llega el verano y la playa).

Parece poca cosa, pero cualquier refactoring, por pequeño que sea, es bueno. El principio de no repetirse no es una idea feliz de un loco cualquiera; es un concepto fundamental en el desarrollo de aplicaciones de calidad, y si aún no lo practicas ya es momento de empezar.

Conclusiones tras el seminario introductorio de Ruby on Rails

Ya ha terminado el seminario (bueno, hace un par de horas) y estamos muy satisfechos con el resultado final. La gente ha participado, parece que no se ha aburrido (demasiado) y han aguantado como campeones las casi cuatro horas de Ruby y Rails. Tanto Asís como yo y el resto de gente en Trabe Soluciones queremos agradecer a todo el mundo su asistencia. También queremos darles las gracias a Fernando Bellas y Víctor Carneiro por su ayuda y colaboración para montar el seminario.

Por cierto, ya hemos dejado para descargar las trasparencias en PDF de la charla.

Nos vemos en la próxima.

POGPUD: RESTful CRUD

Digo yo que si en el mundo REST un POst te permite crear (Create) un recurso; un Get te permite obtener una representación de un recurso (leer, Read); un PUt te permite modificar (Update) un recurso; y un Delete te permite borrar (Delete), un recurso… POGPUD será la versión RESTful de CRUD.

Vamos, digo yo xD

Jugando con JRuby

Asís y yo hemos decidido hablar un poco sobre JRuby en el seminario introductorio de Rails del jueves, ya que esperamos que la mayoría de los asistententes vengan del mundo Java.

Para preparar el material he vuelto a revisitar la página del proyecto que tenía un poco abandonada. Me ha sorprendido lo mucho que han avanzado y como han mejorado la integración con Java. Es bastante sencillo:

include Java
import 'java.util.ArrayList'

list = ArrayList.new
list.add "uno"
list.add "dos"
list.add "tres"
list.each do |n|
  puts "num: #{n}"
end

list.class.name # => "Java::JavaUtil::ArrayList
list.java_class.name # => "java.util.ArrayList"

Esto y mucho más, en el wiki. La verdad, crear ventanas swing desde jirb hace que un escalofrio te recorra toda la espalda. Curioso cuanto menos.

NOTA: Un día de estos pruebo a desplegar alguna de nuestras aplicaciones con JRuby en un Tomcat utilizando Goldspike y os cuento.

Ruby, Rails, el código declarativo y la expresividad

Ayer, mientras estabamos introduciendo a un profano al mundo de Ruby on Rails surgió una breve pero interesante discusión, acerca del uso de métodos como delegate_method frente a su contrapartida “procedural”. Esto es:

delegate_method :country_name, :to => :country, :as => :name, :default => 'Unknown'

frente a:

def country_name
  country.name || 'Unknown'
end

que viene siendo, más o menos, el código que genera dinámicamente delegate_method.

Sin entrar en temas acerca de reutilización de código, mejor gestión del cambio, etc. etc. (que me parecen bastante obvios), la discusión se centraba en que nuestro “profano” (y lo digo con todo el cariño, porque confío en que será un fervoroso converso en el futuro) consideraba que el segundo pedazo de código era más claro y sencillo. Asís, por el contrario, indicaba que la versión declarativa era mejor, más expresiva y autodocumentada, ya que, por su propia naturaleza, declara lo que se está haciendo y no cómo se está haciendo. Yo no puedo estar más de acuerdo con Asís. El segundo código “pide a gritos” un comentario para “declarar” qué hace y no obligar a un posible lector a discernirlo en función de cómo lo hace.

Quizás el ejemplo es demasiado simple para convencerse, así que propongo aquí otro código como ejercicio filosófico-mental, utilizando la librería has_finder, cuya funcionalidad va a ser introducida en el core de Rails. Echadle un ojo.

class Order
  has_finder :unpaid, :conditions => {:paid => false}
end

Un pequeño 'Rails idiom'

Un idiom, para aquel que no lo sepa, es [...] a means of expressing a recurring construct in one or more programming languages (o eso dice la wikipedia, así que yo me lo creo).

Ruby es un lenguaje que permite hacer cualquier cosa de muy diversas maneras, por lo que no es de extrañar que existan multitud de idioms para hacer multitud de cosas. El caso concreto que nos ocupa tiene que ver con la asignación condicional o inicialización… ¿perezosa? Nunca recuerdo cómo se traduze (lazy initialization). Vamos, el operador ||=

my_var = nil
my_var ||= 5 #=> 5
my_var ||= 7 #=> 5

que viene a ser el forma idiomática de expresar en Ruby la construcción más tradicional

if (my_var != nil)
  my_var = value
end

(para un ejemplo de uso y alguna información adicional importante, ver este post de Jay Fields).

Al grano… al idiom

Con el advenimiento de REST, los controladores Rails han vuelto a la estructura clásica de una acción GET para mostrar un formulario, una acción POST para procesarlo. A la hora de hacer CRUD, lo normal es tener algo similar a esto (para la C en CRUD, por ejemplo):

# GET
def new
  @user = User.new
  @countries = Country.available_countries
  @jobs = Job.available_jobs
end

#POST
def create
  @user = User.create(params[:user])
  if @user.save
    flash[:notice] = 'Yeah!'
    redirect_to user_path
  else
    @countries = Country.available_countries # <= RM (Repeat Myself)
    @jobs = Job.available_jobs
    render :action => 'new'
  end
end

Evidentemente, el código anterior no es muy DRY. Estamos duplicando la “preparación” de los datos para renderizar la acción de ‘new’. Un primer impulso nos puede llevar a refactorizar por ahí y extraer un nuevo método que podría ser algo similar a

def prepare_data_for_new_and_edit # Porque para edit también lo necesitaríamos :D
  @countries = Country.available_countries
  @jobs = Job.available_jobs
end

y utilizar este método para cargar las colecciones necesarias tanto en la acción de ‘new’ como en la de ‘create’. Pero la asignación condicional nos permite hacer algo que, a mi juicio, es más directo y más cómodo (aunque también puede resultar un poco más oscuro): es suficiente con hacer que la asignación de la variable de instancia @user sea condicional en ‘new’ y podremos llamar al método ‘new’ directamente desde ‘create’. En código:

# GET
def new
  @user ||= User.new
  @countries = Country.available_countries
  @jobs = Job.available_jobs
end

#POST
def create
  @user = User.create(params[:user])
  if @user.save
    flash[:notice] = 'Yeah!'
    redirect_to user_path
  else
    new
    render :action => 'new'
  end
end

En este caso, como en muchos otros, el idiom Ruby conduce a un idiom Rails.

delegate_method versión 2

Hace tiempo propusimos desde este blog el método Module::delegate_method como sustituto del método delegate de Rails.

El tiempo ha pasado y después de usarlo mucho hemos decidido simplificarlo, cambiar su sintaxis y, como entonces, ponerlo a disposición de todo el que lo quiera usar.

class Module

  # 
  # delegate_method :do_something, 
  #     :to => :other_object
  #
  # delegate_method :do_other_thing,
  #     :to => :another_object, 
  #     :as => :do_something,  
  #     :default => "Can't do it"
  #
  def delegate_method(method, options)    

     to = options[:to]
     as = options[:as] || method
     default = options[:default] 

     raise ArgumentError(':to param is mandatory') if to.blank?

     module_eval %{
       def #{method}(*args, &block)                
         #{to}.__send__(:#{as}, *args, &block) || #{default.inspect}        
       end
     }
  end
end

Soporte de TimeZones en Rails 2.1... ¡Por fin!

Mmmm, adoro el sabor de las buenas noticias. En Rails 2.1, habrá soporte para timezones. No es todo lo que me gustaría ver en Rails acerca de internacionalización, pero es algo. Ya existe un pequeño tutorial sobre el uso del soporte en nuestras aplicaciones, que además explica como migrar si vuestra aplicación usa la clásica combinación tzinfo + tzinfo_timezone.

¿Quieres trabajar en Trabe Soluciones?

Trabe Soluciones está creciendo. Ahora que nos vamos a mudar a unas oficinas más amplias tenemos una vacante. La oferta de trabajo que estamos distribuyendo es la siguiente.

Buscamos personas para trabajar en A Coruña.Trabe te necesita

Nos gustaría que supieses desarrollar aplicaciones web MVC con J2EE y Ruby on Rails, que controlases de HTML, CSS y Javascript, que tuvieses conocimientos de diseño gráfico, que administrases servidores, que te sintieses cómodo en Linux, que te gustase usar software libre,...

Si esto te describe ven corriendo. En caso contrario, no te preocupes, valoramos la experiencia que tengas, pero también nos interesa tu pasión y tus ganas de aprender, de trabajar de una manera diferente, de asumir responsabilidades y de crecer con nosotros.

Te ofrecemos unas excelentes condiciones. Todo lo que te ofrecen el resto de ofertas de trabajo y mucho más: horario flexible, oficina céntrica(nada de polígonos), plantilla joven, café a media manaña, no llevamos corbata ni traje... Nos importa tu calidad de vida.

Si quieres saber más de nosotros, puedes echar un vistazo a nuestra web (http://www.trabesoluciones.com) o a nuestro blog (http://4trabes.com), o directamente contactar con nosotros para plantearnos cualquier duda (contacto@trabesoluciones.com).

Esperamos tu curriculum en rrhh@trabesoluciones.com.

Ya han llegado los primeros CVs. ¿A qué esperáis para enviar el vuestro?

The Overlooked Power of Javascript

Siempre he pensado que el lenguaje Javascript es uno de esos pequeños infiernos con los que uno aprende a convivir. Debo confesar que mi relación con este pequeño gran lenguaje de programación ha cambiado de manera brutal desde que conozco Prototype. Desde esta posición, me ha llamado mucho la atención la charla de Glenn Vandeburg The overlooked power of Javascript (que puede verse y leerse en InfoQ).

Building tigth enforced encapsulations and APIs ands contracts that must be follow, placing restrictions, enforcing rules, drawing boundaries, and working with C++ and then Java trained us to think in terms of this kind of power, and I’m not saying it’s bad, but I’m saying that if that’s yours perspective of power, Javascript is going to look like a toy. Everything is open. You practically can’t build any walls around anything in Javascript.

Creo que en esta reflexión en voz alta (que no curso o tutorial), Glenn ha reflejado a la perfección la relación que la mayor parte de los programadores criados en el mundo Java tenemos con Javascript y por qué deberíamos cambiarla. Una charla muy recomendable para críticos, defensores, fanáticos, indecisos, conversos, pasotas y toda clase de moderados y extremistas.

Faltan 15 días para el Seminario de Ruby on Rails en la FIC

Os recuerdo que el próximo día 24 de Abril impartimo el seminario de Ruby on Rails en la Facultad de Informática de A coruña. Los interesados pueden acercarse al aula 3.4 a las 16:00 de la tarde.

Os puedo adelantar que tendrá dos partes: un breve curso de choque sobre Ruby (utilizando trozos de código) y una introducción al framework Rails a través de un ejemplo de aplicación.

Nos vemos el 24.

Feeds RSS con Struts y RomeRSS

Sí amigos, Struts no está muerto, quizás superado, pero no muerto. Y como las cosas son así, hoy vamos a aprender (a la velocidad del galgo) como “montárnoslo” para servir feeds RSS desde nuestras acciones Struts utilizando la librería RomeRSS.

¿Para qué qerríamos hacer esto en lugar de servir los RSS a través de un Servlet específico? Sencillo. Para reaprovechar toda la infraestructura que tengamos montada para nuestra aplicación: superclases Action con comportamiento común, una cadena de filtros de preprocesado, etc, etc.

Mejor con un ejemplo. Vamos a procurar hacer las cosas de manera correcta, así que vamos a encapsular el uso de RomeRSS en una clase RSSFeeder para desacoplar nuestra aplicación de la librería. Algo así…

public class RSSFeeder {

    private HttpServletRequest request = null; 
    private HttpServletResponse response = null
    private SyndFeed feed = null;

    public RSSFeeder(HttpServletRequest request, 
            HttpServletResponse response) {

        this.request = request;
        this.response = response;      

        this.feed = new SyndFeedImpl();
        this.feed.setFeedType("rss_2.0");
        // ... más inicialización ...
    }

    public void setTitle(String title) {
        feed.setTitle(title);    
    }     

    // ... Y toda una "pléyade" the métodos set para establecer 
    // el contenido del RSS

    public void writeFeed() throws RSSFeederException {
        try { 
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("application/xml;charset=utf-8");

            Writer writer = response.getWriter();                    
            SyndFeedOutput output = new SyndFeedOutput();
            output.output(feed, writer);    
        } catch(Exception e) {
            throw new RSSFeederException(e);
        }
    }
}

No me paro en la generación del feed. Quién tenga interés por saber como se trabaja con RomeRSS puede echarle un ojo a su wiki.

Ahora nuestra acción de Struts…

public abstract class ExampleRSSAction extends BaseAction {

    public ActionForward doExecute(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException, RSFeederException {

        RSSFeeder rssFeeder = new RSSFeeder(request, response);            
        rssFeeder.setTitle("Example feed");
        // ... Más y más construcción del feed ...
        rssFeeder.writeFeed();

        return null;
    }
}

BaseAction es una clase plantilla que contiene código centralizado para la gestión de errores y otras cosas (el comportamiento común, vamos). Sus hijas sólo implementan el método doExecute que contiene la “chicha” de la acción.

Por último, el mapping en el satánico struts-config.xml.

<action path="/rss/example" 
    type="com.trabesoluciones.app.controller.actions.rss.ExampleRSSAction"
    scope="request"/>

Listo. Tras el churro de código, la explicación. El secreto del éxito de esta empresa es romper el ciclo normal de Struts de petición, acción y forward/redirect (las acciones suelen devolver un objeto ActionForward). Para ello, nuestro feeder escribe directamente en el Writer de salida del servlet y nuestra acción devuelve un mágico null.

Y no lo olvidéis: si esto fuera un código de verdad: ¡¡COMENTARIOS!!

Pretty URLs en Java: Outbound rules con URLRewriteFilter

Hace ya algunos meses os hablamos de un interesante modo de conseguir URLs bonitas sin depender de Apache. Como se comenta en ese post, URLRewriteFilter puede ayudarnos a conseguir ese propósito.

Hoy nos hemos encontrado con un problema con las outbound rules: siguiendo escrupulosamente la documentación proporcionada en la página de URLRewriteFilter, no era posible hacer que las outbound rules con parámetros funcionasen correctamente. Según la documentación del filtro, una outbound rule se escribe tal que así:

<outbound-rule>
    <from>^/world.jsp?country=([a-z]+)&city=([a-z]+)$</from>
    <to>/world/$1/$2</to>
</outbound-rule>

Tras un buen rato tirándonos de los pelos y haciendo todo tipo de experimentos se nos encendió una lucecita. "¿Acaso el '?' no tiene un significado específico en una expresión regular?¿No habría que escaparlo? Y escribimos la expresión escapando el '?':

<outbound-rule>
    <from>^/world.jsp\?country=([a-z]+)&city=([a-z]+)$</from>
    <to>/world/$1/$2</to>
</outbound-rule>

Y todo comenzó a funcionar ,con las limitaciones inherentes a este tipo de solución (que veremos en algún post futuro) pero correctamente.

Google Chart API + ruby = gchartrb

En nuestro último proyecto necesitamos incluir varios tipos de gráficas (líneas, barras y alguna tarta). De entre las múltiples posibilidades existentes hemos decidido usar el API de gráficas de Google (Google Chart API) que nos ofrece un montón de posibiliades de manera sencilla, fiable, gratuita e ilimitada. La única peculiaridad de su uso es que los datos a representar deben ser codificados antes de enviarlos al servidor de gráficas. Esto no representa mayor problema, menos aún cuando existen varios wrappers distribuidos como gemas o plugins que hacen este trabajo por nosotros, como son: gchartrb, Googlecharts o Google Charts on Rails.

Nosotros estamos usando gchartrb, porque entendemos es la más completa y documentada y porque su API nos agrada más. Por ejemplo, para pintar una sencilla gráfica como la que sigue…

... necesitamos un código como este:

PIE_COLORS = %w(9bd500 a4d520 b0d54b bbd575 c7d5a0)

data = Post.count(:group => :user_name, :order => 'count_all desc')

pie_chart = GoogleChart::PieChart.new('300x100') do |chart|
  data.each_with_index do |row, i| 
     chart.data "#{row[0]} (#{row[1]})", row[1], PIE_COLORS[i]
  end 
end 

image_tag pie_chart.to_url