rescue true

4 marzo, 2011

Escribir en el layout desde una vista: content_for

Archivado en: Ruby on Rails — xurdekio @ 12:51 pm
Tags: , , ,

En algunas ocasiones puntuales, por requerimientos de funcionalidad o de diseño, necesitaremos poder escribir desde una vista o un helper en algún lugar del layout que lo pinta (o desde un partial en la vista, etc…), esto podemos hacerlo en rails gracias al método content_for

Una de las maravillas de Rails es que las vistas se procesan al revés de como lo intuimos, se empieza por lo más pequeño hasta llegar al layout. Así es que primero se interpretan los partials, despues las vistas y por último el layout. Esto es lo que nos permite poder escribir desde la vista en el layout.

Vamos a ponernos en el caso de un layout con dos columnas (col_left, col_right), en el cual col_right no siempre va a tener contenido puesto que dependerá de la vista en la que estemos. En col_left tendremo la ya conocida instrucción yield, para pintar dentro la vista, y en col_right usaremos content_for con la etiqueta :col_right.

<body>
 <div class="col_left">
   <%=yield%>
 </div>
 <div class="col_right">
  <%=content_for :col_right%>
 </div>
</body>

Ahora, desde nuestra vista, necesitamos escribir el código que queremos que aparezca dentro de la columna derecha, para ello volvemos a recurrir a content_for, pero esta vez con un bloque que sera lo que se pinte en col_right.

<p>Esto se pinta en col_left</p>
<%content_for :col_right do%>
 <p>Sin embargo esto se pinta dentro de col_right</p>
<%end%>

Cómo nota extra vamos a suponer que no somos muy buenos con las css y queremos que nuestra col_left crezca cuando no existe contenido para col_right. Para ello no queremos que se pinte ese div y además le añadiremos un class al body usando el helper content_for?

<body <%= 'class="no-col-right"' unless content_for? :col_right %>>
 <div class="col_left">
   <%=yield%>
 </div>
<%if content_for? :col_right%>
 <div class="col_right">
  <%=content_for :col_right%>
 </div>
<%end%>
</body>

Podemos usar content_for tambien dentro de un helper, y por supuesto podemos usar tantas etiquetas y tantos content_for como queramos a lo largo de nuestros layouts y vistas.

21 noviembre, 2010

Ruby: Diferencias entre “and” y “&&”, “or” y “||”

Archivado en: Ruby,Tips&Tricks — xurdekio @ 12:43 pm
Tags: , ,

Hace unos días me preguntaban por la diferencia en ruby sobre los operadores en sus diferentes formas, esto es and Vs. && y or Vs. ||.
Mucha gente piensa que uno de los dos no sigue los atajos de los circuitos lógicos, sin embargo esto es falso. Las dos formas son igual de eficientes a la hora de evaluarse, es decir, en caso de and y && si la primera condición es falsa no se evalua la segunda, y en caso de or y || si la primera condición es verdadera no se evalua la segunda.
Ahora bien, la diferencia se encuentra en la preferencia de los operadores a la hora de evaluarse. && y || tienen mas preferencia que and y or. Para verlo claramente:

 a = false or true #=> true
 a #=> false

 a = false || true #=> true
 a #=> true

En el primer caso se evalua antes el operador =, con lo que a la variable a se le asigna el valor false. Sin embargo el resultado de la operación es true.
En el segundo caso se evalua primero el operador ||. La expresion devuelve true y a la variable a se le asigna el valor true.

Espero que haya quedado claro, para cualquier cosa.. los comentarios!

28 septiembre, 2010

Salvando hashs, arrays y otros objetos no mapeables en columnas de texto

Archivado en: ActiveRecord,Ruby on Rails — xurdekio @ 12:00 pm
Tags: , , , , ,

Para hoy algo realmente interesante, que cuando lo ves te acuerdas de aquel proyecto en el que te hubiese venido genial saberlo.
En muchas ocasiones tenemos un hash de opciones, o un array con varios valores que nos gustaría salvar junto con el objeto, pero no hay ninguna estructura de base de datos que se acople a el.
Por ejemplo un usuario que tiene configuraciones sobre cuando quiere recibir emails. Lo ideal seria tener un hash de la siguiente manera:

 alert_me_on = {:new_message => true, :new_event => false, :new_board_message => true}

Para guardar esto en base de datos necesitaríamos 3 columnas en la tabla del usuario o bien una nueva tabla con las 3 columnas y la foreign key del usuario.
Sin embargo Rails nos permite guardar el hash tal cual en una columna de texto, utilizando para ello serialize:

 class User < ActiveRecord::Base
   serialize :alert_me_on, Hash
 end
 u = User.new
 u.alert_me_on = {:new_message => true, :new_event => false, :new_board_message => true}
 u.save
 u.alert_me_on[:new_message] #=> true

Realmente cómodo, y sirve también para arrays, structs… etc. Espero que no lo conocierais!

Edito: La solución es cómoda cuando NO vamos a realizar búsquedas por ese campo. Es decir si necesitamos traer todos los usuarios que requieren una notificación en concreto estamos ralentizando la búsqueda respecto a un campo booleano.

22 septiembre, 2010

Detectando cambios en los atributos: self.changes

Archivado en: Proyectos,Ruby on Rails,Tips&Tricks — xurdekio @ 9:25 am
Tags: , ,

Hoy se nos planteaba el problema de enviar un e-mail a los usuarios inscritos a un evento cuando cuando las fechas cambiasen, para así mantenerlos informados.
Como siempre Ruby y Rails al rescate con el metodo changes, que nos devuelve un completo hash con los cambios en el objeto, y va más allá incluso devolviéndonos el cambio exacto, es decir, estado anterior y estado actual del valor.

  evento = Evento.create(:titulo => "Prueba", :fecha_inicio => Date.today) #Creamos el evento con la fecha de hoy
  evento.fecha_inicio = Date.tomorrow #Cambiamos la fecha..
  evento.changes # Nos devuelve {"fecha_inicio" =>[Wed, 22 Sep 2010, Thu, 23 Sep 2010] }

Como veis, las keys del hash son los nombre de los atributos, y los values el array [valor_anterior, valor_actual].

En nuestro caso solo necesitábamos avisar a los usuarios si el evento estaba pendiente de fecha y ahora ya la tenía, así que aprovechando los callbacks nos queda algo así:

  def after_save
    #Si cambia la fecha de inicio (antes no la tenia y ahora si.. hay que mandar un mail a los inscritos)
    if self.changes.has_key?("fecha_inicio") and self.changes["fecha_inicio"][0].nil?
        self.inscripciones.all(:conditions => ["estado != ?",Inscripcion::ESTADO_CANCELADA]).each do |inscripcion|
          InscripcionMailer.deliver_nueva_fecha(inscripcion)
        end
    end
  end

No será un método desconocido para muchos de vosotros pero tampoco está de más recordar de vez en cuando su funcionamiento!

P.D.: Una vez que el objeto se ha salvado podeis recurrir al método previous_changes

9 septiembre, 2010

Curiosidades de Ruby y de Rails

Archivado en: Ruby on Rails,Tips&Tricks — xurdekio @ 3:47 pm
Tags: , ,

Algunas notas curiosas sobre Ruby y Rails

  • El id de false es 0 y su clase FalseClass
  • El id de true es 2 y su clase TrueClass
  • El id de nil es 4 (especialmente util cuando algo referencia constantemente al id 4 y no sabemos porqué)
  • 1/0 es una excepción, pero 1/0.0 o 1.0/0 son Infinity
  • 0/0 es una excepcion, pero 0/0.0 y 0.0/0 son NaN
  • Infinity y NaN son de tipo float
  • Cualquier operacion con NaN es NaN
  • Cualquier cosa dividido entre Infinity es 0
  • Cualquier cosa menos Inifity es -Inifinity

Otro dia pongo alguna más… pero estas son cuando menos, interesantes.

31 agosto, 2010

Trabajando en Rails con tablas de otros esquemas

Archivado en: Ruby on Rails,Tips&Tricks — xurdekio @ 11:19 am
Tags: , , , ,

Cuando trabajamos en proyectos grandes, en múltiples ocasiones podemos necesitar acceder a diferentes tablas en diferentes esquemas. En algunos lenguajes esto puede ser un caos de configuraciones, sin embargo en Rails podemos configurar la tabla para el modelo en otro esquema con una sola línea en nuestro modelo:

class Customer < ActiveRecord::Base
  set_table_name 'other_schema.customers'
end

Puede parecer una tontería pero es muy útil para hacer cargas de contenidos desde viejos proyectos, probar inconsistencias en base de datos respecto a la base de datos de producción y de desarrollo sin falta de tener varios servidores arrancados, etc.

25 agosto, 2010

Mejorando el rendimiento: find_in_batches y find_each

Archivado en: Ruby on Rails,Tips&Tricks — xurdekio @ 8:09 am
Tags: , ,

Ruby on Rails no deja de sorprenderme con la cantidad de métodos útiles que te puedes encontrar por su API un día aburrido. En esta ocasión os traigo dos métodos para tratar grandes cantidades de registros sin saturar la memoria del servidor.

El primero es find_in_batches, que nos permite traer los registros de la base de datos en lotes, pudiendo pasarle condiciones de búsqueda, tamaño de los lotes, offset…

El segundo es find_each, que es un iterador aprovechando el método anterior.

Estos dos métodos nos permiten procesar simulatenamente en dos (o más) hilos todos los registros de la base de datos, pudiendo estar un worker con los registros del 0 al 10000, otro del 10000 al 20000… etc.

No hay que olvidarse de la nota al pie en la API, estos métodos estan orientados a grandes cantidades de datos, si sólo vas a tratar unos pocos usar el find normal!

24 mayo, 2010

Ejecutar tareas no periodicas en segundo plano

Archivado en: Linux,Ruby on Rails,Tips&Tricks — xurdekio @ 7:51 am
Tags: , , , ,

En muchas ocasiones nuestras aplicaciones necesitaran hacer tareas que tarden bastante tiempo en dar respuesta y esto provocará que el servidor nos devuelva un timeout y veamos la página de error de la aplicación.
Una solución para ejecutar estas tareas es realizarlas en segundo plano, de forma que la aplicación se olvide del proceso y conteste al navegador rapidamente evitando así el dichoso timeout.
Para esto Ruby on Rails combinado con la potencia de Linux nos proporciona una magnífica herramienta que es el script/runner, que nos permite ejecutar una función cargando todo el entorno de la aplicación.

Vamos a suponer que tenemos una función Boletin.enviar, que envía boletines a todos los usuarios registrados en la web (y nuestra web es impresionante y tiene miles de usuarios). La función completa envia por lotes, tiene una cola, etcetera, con lo cual tarda bastante en ejecutarse, no podemos dejar al usuario que envía el boletín esperando respuesta.
Así pues nuestro action para enviar el boletín sera algo asi:

def enviar_boletin
  system("ruby script/runner \"Boletin.enviar\"&")
end

Con el & de unix le indicamos que se ejecute en segundo plano. Los lectores más avispados ya os habreis dado cuenta de que si el proceso padre, en este caso el hilo de ruby que está ejecutando el servidor, se muere pues tambien morirá su hijo y nunca llegaremos a terminar la tarea. Para ello otroa solucion de sistemas usando nohup

def enviar_boletin
  system("nohup ruby script/runner \"Boletin.enviar\"&")
end

Y por ultimo, ¿qué ocurre si queremos pasarle argumentos a la función? pues se los pasamos como si fuese una funcion normal:

def enviar_boletin
  from = "nuestro_email@nuestrodominio.es"
  system("nohup ruby script/runner \"Boletin.enviar('#{from}')\" &")
end

Espero que os sea tan util como me ha sido a mi en varios proyectos de gran envergadura.

13 mayo, 2010

Zerofill en Rails

Archivado en: Ruby on Rails,Tips&Tricks — xurdekio @ 12:54 pm
Tags: , , ,

Para hoy, uno rápido, pero que siempre te hace ir a buscar a google… Mostrar un número en una vista con un formato zerofill, es decir, 14 => 00014.
Para ello vamos a seguir la convención de Rails DRY y hacerlo en un helper, así que en el application_helper.rb de tu aplicación

  def zerofill(dato,tam=8)
    "%0#{tam}d" % dato
  end

Una vez lo tenemos ya solo nos queda llamarlo desde la vista con el número de dígitos que nos interesen como segundo parámetro.

 <%=zerofill(14, 5)%>

Prometo no ser tan vago para el próximo post!

10 mayo, 2010

Exportar SQL a CSV desde Rails

Archivado en: Ruby on Rails,Tips&Tricks — xurdekio @ 7:47 am
Tags: , , , ,

En muchas ocasiones nos encontramos con la problemática de tener que exportar muchos datos rapidamente de la base de datos SQL a un fichero CSV.

La forma mas cómoda es delegar el trabajo a la base de datos, que para eso ya lo tiene implementado. Por ejemplo vamos a exportar el archivo de libros de una biblioteca a un CSV desde nuestra aplicación rails.
En el modelo:

CSV_PATH = "#{RAILS_ROOT}/private/arhivo_biblioteca.csv"
def self.all_to_csv(path=CSV_PATH,separator=',',delimitator='"')
  #Esta es la consulta sql
  query = "SELECT l.id, l.autor, l.titulo, l.descripcion,  l.tema, l.editorial, l.paginas, l.localizacion, l.isbn  FROM `libros` l INTO OUTFILE '#{path}' FIELDS TERMINATED BY '#{separator}' ENCLOSED BY '#{delimitator}' LINES TERMINATED BY '\\n'" 
  ActiveRecord::Base.connection.execute(query) #Ejecutamos la consultar
  path #devolvemos la ruta del path 
end

Como podeis ver el método recibe 3 argumentos, que tienen valores por defecto:

      El path donde dejar el fichero
      El separador de los campos
      Y por último el delimitador

Si queremos usarlo desde el controlador nos valdrá algo como

  def exportar
    path = Libro.all_to_csv
    send_file(path)
  end

Ahora toca experimentar… podeis pasarle más parametros, como unas conditions para no sacar todos los libros, el orden, etc…

Página siguiente »

Tema Rubric. Blog de WordPress.com.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.