4Trabes Historias de una empresa en 100 metros cuadrados

El blog de Trabe Soluciones

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 ||=

1
2
3
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

1
2
3
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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 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

1
2
3
4
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 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.

Lo sentimos, pero los comentarios están cerrados