Está en la página 1de 722

Tabla

de contenido
Introduccin 1.1
Empezar 1.2
I- Fundamentos de Active Record 1.3
1- Que es Active Record? 1.3.1
2- Convencin sobre Configuracin en Active Record 1.3.2
3- Creando Modelos Active Record 1.3.3
4- Sobre-escribiendo las Convenciones de Nombres 1.3.4
5- CRUD: Leyendo y Escribiendo Datos 1.3.5
6- Validaciones 1.3.6
7- Callbacks 1.3.7
8- Migraciones 1.3.8
II- Migraciones en Active Record 1.4
1- Resumen de las Migraciones 1.4.1
2- Creando una Migracin 1.4.2
3- Escribiendo una Migracin 1.4.3
4- Ejecutando Migraciones 1.4.4
5- Cambiando las Migraciones Existentes 1.4.5
6- Volcando el Esquema y T 1.4.6
7- Active Record y la Integridad Referencial 1.4.7
8- Migraciones y Semillas de Datos (Seeds) 1.4.8
III- Validaciones en Active Record 1.5
1- Resumen de las Validaciones 1.5.1
2- Helpers de Validacin 1.5.2
3- Opciones Comunes de Validacin 1.5.3
4- Validaciones Estrictas 1.5.4
5- Validaciones Condicionales 1.5.5
6- Realizacin de validaciones personalizadas 1.5.6
7- Trabajar con errores de validacin 1.5.7
8- Visualizacin de errores de validacin en vistas 1.5.8
IV- Callbacks en Active Record 1.6

1
1- El Ciclo de Vida del Objeto 1.6.1
2- Un vistazo a los Callbacks 1.6.2
3- Callbacks Disponibles 1.6.3
4- Ejecutando Callbacks 1.6.4
5- Saltando Callbacks 1.6.5
6- Parando la Ejecucin 1.6.6
7- Callbacks Relacionados 1.6.7
8- Callbacks Condicionales 1.6.8
9- Clases Callbacks 1.6.9
10- Callbacks de Transacciones 1.6.10
V- Asociaciones en Active Record 1.7
1- Por qu asociaciones? 1.7.1
2- Tipos de Asociaciones 1.7.2
3- Consejos, Trucos y Advertencias 1.7.3
4- Referencia detallada de la asociacin 1.7.4
4.1 Referencia de la asociacin belongs_to 1.7.4.1
4.2 Referencia de la asociacin has_one 1.7.4.2
4.3 Referencia de la asociacin has_many 1.7.4.3
4.4 Referencia de la asociacin has_and_belongs_to_many 1.7.4.4
4.5 Callbacks de la asociacin 1.7.4.5
4.6 Extensiones de la asociacin 1.7.4.6
5- Herencia de tabla nica 1.7.5
VI- Interfaz de Consulta de Active Record 1.8
1- Recuperacin de objetos desde la base de datos 1.8.1
2- Condiciones 1.8.2
3- Ordenando 1.8.3
4- Seleccionando campos especficos 1.8.4
5- Lmite y desplazamiento 1.8.5
6- Grupos 1.8.6
7- Having 1.8.7
8- Sobre-escribiendo condiciones 1.8.8
9- Relacin NULL 1.8.9
10- Objetos de slo lectura 1.8.10
11- Bloqueo de registros para actualizacin 1.8.11

2
12- Unir tablas 1.8.12
13- Asociaciones de carga impaciente 1.8.13
14- Ambitos 1.8.14
15- Buscadores dinmicos 1.8.15
16- Enums 1.8.16
17- Comprensin del mtodo Chaining 1.8.17
18- Buscar o construir un nuevo objeto 1.8.18
19- Bsqueda por SQL 1.8.19
20- Existencia de Objetos 1.8.20
21- Clculos 1.8.21
22- Ejecutando EXPLAIN 1.8.22
VII- Conceptos bsicos de Active Model 1.9
1- Introduccin 1.9.1
VIII- Conceptos bsicos de Action View 1.10
1- Qu es Action View? 1.10.1
2- Uso de Action View con Rails 1.10.2
3- Templates, Partials y Layouts 1.10.3
4- Partial Layouts 1.10.4
5- Rutas de las Vistas 1.10.5
6- Visin general de los helpers proporcionados por Action View 1.10.6
6.1 AssetTagHelper 1.10.6.1
6.2 AtomFeedHelper 1.10.6.2
6.3 BenchmarkHelper 1.10.6.3
6.4 CacheHelper 1.10.6.4
6.5 CaptureHelper 1.10.6.5
6.6 DateHelper 1.10.6.6
6.7 DebugHelper 1.10.6.7
6.8 FormHelper 1.10.6.8
6.9 FormOptionsHelper 1.10.6.9
6.10 FormTagHelper 1.10.6.10
6.11 JavaScriptHelper 1.10.6.11
6.12 NumberHelper 1.10.6.12
6.13 SanitizeHelper 1.10.6.13

3
6.14 CsrfHelper 1.10.6.14
7- Vistas con Locale 1.10.7
IX- Layouts y Rendering en Rails 1.11
1- Descripcin General: Cmo encajan las piezas 1.11.1
2- Creacin de respuestas 1.11.2
2.1 Rendering por defecto: Convencin sobre configuracin en Action
2.2 Uso del render 1.11.2.2 1.11.2.1
2.3 Uso de redirect_to 1.11.2.3
2.4 Uso de los headers para crear respuestas Header-Only 1.11.2.4
3 Estructuracin de los layouts 1.11.3
3.1 Asset Tag Helpers 1.11.3.1
3.2 Entendiendo Yield 1.11.3.2
3.3 Uso del mtodo content_for 1.11.3.3
3.4 Uso de Partials 1.11.3.4
3.5 Uso de layouts anidados 1.11.3.5
X- Helpers de Formulario de Action View 1.12
1- Tratamiento de formularios bsicos 1.12.1
2- Tratamiento de objetos del modelo 1.12.2
3- Hacer Select Boxes con facilidad 1.12.3
4- Uso de los helpers de formulario Fecha y Hora 1.12.4
5- Carga de archivos 1.12.5
6- Personalizacin de los constructores de formularios 1.12.6
7- Entendiendo las convenciones de nombramiento de parmetros 1.12.7
8- Formularios a Recursos Externos 1.12.8
9- Construccin de formularios complejos 1.12.9
XI- Descripcin general de Action Controller 1.13
1- Qu hace un controlador? 1.13.1
2- Convencin de nombres del controlador 1.13.2
3- Mtodos y acciones 1.13.3
4- Parmetros 1.13.4
5- Session 1.13.5
6- Cookies 1.13.6
7- Renderizando datos XML y JSON 1.13.7
8- Filters 1.13.8

4
9- Request Forgery Protection 1.13.9
10- Objetos de solicitud y respuesta 1.13.10
11- Autenticaciones HTTP 1.13.11
12- Streaming y descargas de archivos 1.13.12
13- Filtrado de logs 1.13.13
14- Rescue 1.13.14
15- Forzar el protocolo HTTPS 1.13.15
XII- Enrutamiento en Rails 1.14
1- El Propsito del Enrutador de Rails 1.14.1
2- Ruta resource: el valor predeterminado de Rails 1.14.2
3- Rutas sin resources 1.14.3
4- Personalizacin de rutas de recursos 1.14.4
5- Inspeccin y pruebas de las rutas 1.14.5
XIII - Active Support Extensiones Core 1.15
1- Cmo cargar las extensiones core 1.15.1
2- Extensiones a todos los objetos 1.15.2
XIV- API de Internacionalizacin de Rails (I18n) 1.16
1- Cmo funciona I18n en Ruby on Rails 1.16.1
2- Configuracin de una aplicacin de Rails para la internacionalizacin 1.16.2
3- Internacionalizacin y localizacin 1.16.3
4- Descripcin general de las funciones de la API I18n 1.16.4
5- Cmo almacenar sus traducciones personalizadas 1.16.5
6- Personaliza tu configuracin de I18n 1.16.6
XV- Fundamentos de Action Mailer 1.17
1- Introduccin 1.17.1
2- Envo de mensajes de correo electrnico 1.17.2
3- Recibir correos electrnicos 1.17.3
4- Action Mailer Callbacks 1.17.4
5- Uso de Helpers de Action Mailer 1.17.5
6- Configuracin de Action Mailer 1.17.6
7- Mailer Testing 1.17.7
8- Intercepcin de correos electrnicos 1.17.8
XVI - Conceptos bsicos de Active Jobs 1.18

5
1- Introduccin 1.18.1
2- El Propsito de Active Jobs 1.18.2
3- Creacin de un Job 1.18.3
4- Ejecucin del Job 1.18.4
5- Colas 1.18.5
6- Callbacks 1.18.6
7- Action Mailer 1.18.7
8- Internacionalizacin 1.18.8
9- GlobalID 1.18.9
10- Excepciones 1.18.10
11- Job Testing 1.18.11
XVII - Testing de aplicaciones Rails 1.19
1- Por qu escribir pruebas para sus aplicaciones Rails? 1.19.1
2- Introduccin a las pruebas 1.19.2
3- Base de datos del testing 1.19.3
4- Pruebas de modelos 1.19.4
5- Pruebas del sistema 1.19.5
6- Pruebas de integracin 1.19.6
7- Pruebas funcionales para sus controladores 1.19.7
8- Testeo de Rutas 1.19.8
9- Testeo de las Vistas 1.19.9
10- Testeo de los Helpers 1.19.10
11- Testeo de los Mailers 1.19.11
12- Testeo de los Jobs 1.19.12
13- Recursos Adicionales de Pruebas 1.19.13
XVIII - Seguridad de las aplicaciones de Rails 1.20
1- Introduccin 1.20.1
2- Sesiones 1.20.2
3- Falsificacin de solicitudes entre sitios (CSRF) 1.20.3
4- Redireccin y archivos 1.20.4
5- Intranet y seguridad del administrador 1.20.5
6- Gestin de usuarios 1.20.6
7- Injection 1.20.7
8- Generacin de consultas inseguras 1.20.8

6
9- Encabezados predeterminados 1.20.9
XIX- Debugging de aplicaciones Rails 1.21
1- Helpers de las Vistas para depurar 1.21.1
2- El Logger 1.21.2
3- Debugging con la gema byebug 1.21.3
4- Depuracin con la gema web-console 1.21.4
5- Depuracin de fugas de memoria 1.21.5
6- Plugins para Depurar 1.21.6
XX- Configuracin de aplicaciones de Rails 1.22
XXI- La lnea de comandos de Rails 1.23
1- Conceptos bsicos de la lnea de comandos 1.23.1
2- bin/rails 1.23.2
3- La Lnea de Comando Avanzada de Rails 1.23.3
XXII- La Canalizacin de Recursos 1.24
1- Qu es la Canalizacin de Recursos? 1.24.1
2- Cmo utilizar el canalizador de recursos 1.24.2
3- In Development 1.24.3
4- In Production 1.24.4
5- Personalizacin del Canalizador 1.24.5
6- Almacn de cach de recursos 1.24.6
7- Agregar recursos a sus gemas 1.24.7
8- Hacer de su libreria o gema un preprocesador 1.24.8
9- Actualizacin de versiones antiguas de Rails 1.24.9
XXIII - Trabajar con JavaScript en Rails 1.25
1- Una introduccin a Ajax 1.25.1
2- JavaScript no intrusivo. 1.25.2
3- Helpers integrados 1.25.3
4- Tratamiento de eventos Ajax 1.25.4
5- Problemas con el servidor 1.25.5
6- Turbolinks 1.25.6
7- Otros Recursos 1.25.7
XXIV- El proceso de inicializacin de Rails 1.26
XXV- Carga Automatica y recarga de Constantes 1.27

7
XXVI- Almacenamiento en cach con Rails: una visin general 1.28
XXVII- Instrumentacin de Active Support 1.29
XXVIII- Una gua para perfilar aplicaciones Rails 1.30
XXIX- Uso de Rails para aplicaciones API-only 1.31
1 Qu es una aplicacin API? 1.31.1
2 Por qu utilizar Rails para las API de JSON? 1.31.2
3- La configuracin bsica 1.31.3
4- Eleccin de middleware 1.31.4
5- Seleccin de los mdulos del controlador 1.31.5
XXX- Descripcin de Action Cable 1.32

8
Introduccin

Guas de Rails en Espaol (Rails 5)


Estas son las guas de Rails 5 en Espaol (Guas Completas, con todos los Captulos).
Estas guas estn diseadas para que tengas una productividad inmediata con Rails, y para
ayudarte a entender como encajan las piezas en Rails.

Estas Guas son la traduccin de la documentacin oficial de Rails, y no tiene ninguna


interpretacin, modificacin o explicacin adicional y subjetiva realizada por el
traductor.

Porque realizo este trabajo?


Mi nombre es Daniel Morales, este es mi perfil de GitHub, y soy apasionado de Rails. Inici
la traduccin de estas guas para comprender mejor el funcionamiento de Rails, por tanto
era un deseo tener la documentacin traducida para mi, pero de paso pens "Nadie ha
realizado una traduccin COMPLETA al espaol, porqu no hacerlo?". La publico para que
todos los programadores de habla hispana puedan aprovecharse de este material.

Si deseas hacer alguna correccin o aporte (ya que seguro podra tener errores de
traduccin o interpretacin), puedes hacer un fork al repo principal y solicitar un pull-
request, apreciar dicha ayuda!

Empezamos desde Active Record


Ya hay en internet traducciones de la primera parte de las Guas (la cual es un Blog
explicado paso a paso). Aqu empezamos desde Active Record.

9
Empezar

Comenzando con Rails


Ya hay en internet traducciones de la primera parte de las Guas (la cual es un Blog
explicado paso a paso). Aqu empezamos desde Active Record.

10
I- Fundamentos de Active Record

I- Fundamentos de Active Record


Esta gua es una introduccin a Active Record, (Registro Activo). Despus de leer esta gua,
conocers:

Qu son el Mapeo de Objetos Relacionales y Active Record y como se utilizan en Rails.

Cmo entra Active Record en el paradigma Modelo-Vista-Controlador.

Cmo se utilizan los modelos Active Record para manipular datos buardados en una
base de datos relacional.

Las convenciones sobre el esquema de nombres en Active Record.

Los conceptos de migraciones de bases de datos, validaciones y retrollamadas.

11
1- Que es Active Record?

1. Que es Active Record?


Active Record es la M en MVC - el modelo - el cual es la capa del sistema responsable de
representar los datos y la lgica de negocio para manipularlos. Active Record facilita la
creacin y manipulacin de objetos de negocio quienes requieren ser almacenados
persistentemente en una base de datos. Esta es una implementacin del patrn de Active
Record el cual en si mismo es una descripcin de un sistema de Mapeo de Objetos
Relacionales.

1.1 El Patrn Active Record


Active Record fue descrito por Martin Fowler en su libro Patterns of Enterprise Application
Architecture. En Active Record, los objetos soportan tanto la persistencia y el
comportamiento que opera con los datos. Active Record toma la opinion que al asegurar el
acceso lgico a los datos como parte del objeto educar a los usuarios de ese objeto sobre
como escribir y leer en la base de datos.

1.2 Mapeo de Objetos Relacionales


El Mapeo de Objetos Relacionales (Object-Relational Mapping), comunmente nombrado por
sus siglas ORM, es una tcnica que conecta la riqueza de los objetos de una aplicacin con
las tablas de un sistema de base de datos relacional. Utilizando ORM, las propiedades y las
relaciones de los objetos en una aplicacin pueden ser fcilmente guardadas y recuperadas
desde la base de datos sin escribir sentencias SQL directamente y con el mnimo cdigo en
general de acceso a la base de datos.

1.3 Active Record como un Framework ORM


Active Record brinda varios mecanismos, los ms importantes nos dan la capacidad para:

Representar modelos y sus datos.


Representar asociaciones entre esos modelos.
Representar jerarquas de herencia a travs de modelos relacionados.
Validar modelos antes de que sean guardados o cambiados en la base de datos.
Mantener las operaciones de la base de datos orientadas a objetos.

12
2- Convencin sobre Configuracin en Active Record

2. Convencin sobre Configuracin en


Active Record
Cuando escribimos aplicaciones usando otros lenguajes de programacin o frameworks, es
necesario escribir una gran cantidad de cdigo de configuracin. Esto es particularmente
verdad para los frameworks ORM en general. Sin embargo, si sigues las convenciones
adoptadas por Rails, necesitars escribir muy poca configuracin (en algunos casos
ninguna) cuando creas modelos Active Record. La idea es que si configuras tus
aplicaciones de la misma manera la mayora de las veces, entonces ese debera ser la
manera por defecto. Por consiguiente, podramos necesitar configuracin explcita solo en
los casos donde no podemos seguir la convencin standart.

2.1 Convenciones sobre Nombres


Por defecto, Active Record utiliza algunas convenciones para encontrar y conocer con
detalle el mapeo entre los modelos y las tablas de la base de datos y como debera crearse.
Rails convertir al plural los nombres de tus clases para encontrar la respectiva tabla en la
base de datos. Entonces, para una clase Book , deberas tener una tabla de base de datos
llamada books. Los mecanismos de pluralizar en Rails son muy potentes, y tienen la
capacidad de pluralizarar (y singularizar) ambos en palabras regulares e irregulares.
Cuando usamos nombres de clases compuestos de dos o ms palabras, el nombre de la
clase del modelo debe debera seguir las convenciones Ruby, usando la forma CamelCase,
mientras que el nombre de la tabla debe contener las palabras separadas por guiones
bajos. Ejemplos:

Tabla de Base de Datos - Plural con guiones bajos separando las palabras (ej:
book_clubs ).

Clase del Modelo - Singular con la primera letra de cada palabra en maysculas (ej:
BookClub ).

Modelo / Clase Tabla / Esquema

Article articles
LineItem line_items

Deer deers
Mouse mice

Person people

13
2- Convencin sobre Configuracin en Active Record

2.2 Convenciones del Esquema


Active Record utiliza convenciones de nombres para las columnas en las tablas de la base
de datos, dependiendo de los propsitos de esas columnas.

Claves forneas - Estos campos deberan ser nombrados siguiendo el patrn


nombre_de_tabla_en_singular_id (ej: item_id , order_id ). Estos son los campos que
Active Record buscar cuando crees asociaciones entre tus modelos.
Claves primarias - Por defecto, Active Record utilizar una columna entera llamada id
como la clave primaria de la tabla. Cuando utilizas Active Record Migrations para crear
tus tablas, esta columna ser creada automticamente.

Tambin hay nombres de columnas opcionales que podrn dar caractersticas adicionales a
las instancias de Active Record:

created_at - Automticamente guarda el dia y la hora actual al momento en que se

crea el objeto.
updated_at - Automticamente guarda el da y la hora actual al momento en que se

actualiza un registro.
lock_version - Aade bloqueo optimista a un modelo.

type - Especifica el modo de utilizar el modelo La Herencia Simple de Tables (Simple

Table Inheritance - STI)


(association_name)_type - Graba el tipo para asociaciones polimrficas.

(table_name)_count - Utilizado para cachear el nmero de objetos que pertenecientes

en una asociacin.

Por ejemplo, una columna comments_count en una clase Articles que tiene muchas
instancias de Comment guardar en cache el nmero de los comentarios existentes de cada
artculo.

Mientras estos nombres de columnas son opcionales, estn en realidad reservados por
Active Record. Evita el uso de las palabras reservadas si no quieres funcionalidades extra.
Por ejemplo, type es una palabra reservada utilizada para definir que una tabla est
utilizando Herencia Simple de Tabla (STI). Si no ests utilizando STI, intenta con una
palabra anloga como context , que puede an as mantener la descripcin de los datos
que ests modelando.

14
3- Creando Modelos Active Record

3. Creando Modelos Active Record


Es muy fcil crear modelos Active Record. Todo lo que necesitas hacer es una subclase de
la clase ActiveRecord::Base y listo:

class Product < ActiveRecord::Base


end

Esto crear una clase modelo Product, mapeada a la tabla products de la base de datos.
Para hacer esto tambin tendrs que tener la posibilidad de mapear columnas de cada fila
con los atributos de cada instancia del modelo.

Supn que la tabla products fue creada utilizando una sentencia SQL como:

CREATE TABLE products (


id int(11) NOT NULL auto_increment,
name varchar(255),
PRIMARY KEY (id)
);

Siguiendo el esquema de arriba, tendras la capacidad de escribir cdigo como el que sigue:

p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"

15
4- Sobre-escribiendo las Convenciones de Nombres

4. Sobre-escribiendo las Convenciones de


Nombres
Que sucede si quieres seguir diferentes convenciones de nombres o necesitas utilizar una
aplicacin Rails con una base de datos heredada? No hay problema, puedes fcilmente
sobre-escribir las convenciones por defecto. Utilizando el mtodo
ActiveRecord::Base.table_name= para especificar el nombre de la tabla que debera ser

utilizada:

class Product < ApplicationRecord


self.table_name = "my_products"
end

Si tu haces esto, tendrs que definir manualmente el nombre de la clase a contiene los
fixtures ( class_name.yml ) utilizando el mtodo the set_fixture_class en la definicin de los
test.

class FunnyJoke < ActiveSupport::TestCase


set_fixture_class funny_jokes: Joke
fixtures :funny_jokes
...
end

Tambin es posible cambiar la columna clave primaria de la tabla con el mtodo


ActiveRecord::Base.primary_key= :

class Product < ActiveRecord::Base


self.primary_key = "product_id"
end

16
5- CRUD: Leyendo y Escribiendo Datos

5. CRUD: Leyendo y Escribiendo Datos


CRUD es el acrnimo para las cuatro verbos que utilizamos para operar con los datos:
Create, Read, Update and Delete. Active Record automticamente crea mtodos que
permiten a una aplicacin leer y manipular los datos guardados dentro de las tablas.

5.1 Create
Los objetos Active Record pueden crearse desde un hash, un bloque o configurar sus
atributos manualmente antes de la creacin. El mtodo new retornar un nuevo objeto
mientras create retornar el objeto y lo guardar en la base de datos.

Por ejemplo, dado un modelo User con los atributos name y occupation , el mtodo
llamado create crear y guardar un nuevo registro en la base de datos:

user = User.create(name: "David", occupation: "Code Artist")

Utilizando el mtodo new , un objeto puede ser instanciado sin haber sido guardado:

user = User.new
user.name = "David"
user.occupation = "Code Artist"

Una llamada a user.save guardar el registro en la base de datos.

Finalmente, si un bloque es provedo, ambos create y new producir el nuevo objeto


inicializado dentro de un bloque:

user = User.new do |u|


u.name = "David"
u.occupation = "Code Artist"
end

5.2 Read
Active Record provee una rica API para acceder a los datos dentro de una base de datos.
Debajo hay algunos ejemplos de diferentes mtodos provistos por Active Record.

17
5- CRUD: Leyendo y Escribiendo Datos

# devuelve una coleccin de usuarios


users = User.all
# devuelve el primer usuario
user = User.first
# devuelve el primer usuario llamado David
david = User.find_by(name: 'David')
# encontrar todos los usuarios llamados David que tienen de ocupacin Code Artists y o
rdenado por created_at en sentido cronolgicamente inverso
users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')

Puedes aprender ms acerca de consultar un modelo Active Record en la gua Interface de


Consultas Active Record.

5.3 Update
Una vez que un objeto Active Record ha sido recuperado, sus atributos pueden ser
modificados y volver a ser guardados en la base de datos.

user = User.find_by(name: 'David')


user.name = 'Dave'
user.save

Un camino corto para este uso es mapear un hash con los nombres de los atributos que se
desean modificar y su valor, como por ejemplo:

user = User.find_by(name: 'David')


user.update(name: 'Dave')

Esta es la forma ms poderosa de actualizar varios atributos a la vez. Si, por otro lado,
quieres actualizar varios registros a la vez, encontrars muy til el mtodo de clase
update_all :

User.update_all "max_login_attempts = 3, must_change_password = 'true'"

5.4 Delete
Asimismo, una vez que se recupera el objeto Active Record tambin puede ser destrudo, lo
cual lo borrar de la base de datos.

user = User.find_by(name: 'David')


user.destroy

18
5- CRUD: Leyendo y Escribiendo Datos

19
6- Validaciones

6. Validaciones
Active Record te permite validar el estado del modelo antes que se escriba en la base de
datos. Hay varios mtodos que puedes utilizar para comprobar tu modelo y validar que un
atributo no est vaco, es nico y no est an en la base de datos, que siga un formato
especfico, y muchos ms. La validacin es una tarea muy importante a considerar cuando
se guardan datos en una base de datos, entonces los mtodos save y update toman esto
en cuenta cuando se ejecutan: ellos retornan false cuando una validacin falla y no
mantuvieron ninguna operacin en la base de datos. Todos estos tienen una contrapartida
bang (esta es , save! y update! ), las cuales son estrictas y arrojan una excepcinn
ActiveRecord::RecordInvalid si la validacin falla. Un rpido ejemplo para ilustralo:

class User < ActiveRecord::Base


validates :name, presence: true
end

user = User.new
user.save # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

Puedes aprender ms acerca de validaciones en la Gua de Validaciones Active Record.

20
7- Callbacks

7. Callbacks
Las retrollamadas Active Record (o callbacks) te permiten adjuntar ciertos eventos en el
ciclo de vida de tus modelos. Esto te faculta a aadir comportamintos a tus modelos de
forma transparente en la ejecucin cuando estos eventos ocurren. Como cuando tu quieres
crear un nuevo registro, actualizarlo, destrurlo, etc. Puedes aprender ms de retrollamadas
en la Gua de Active Record Callbacks.

21
8- Migraciones

8. Migraciones
Rails provee un lenguaje de dominio especfico para manejar un esquema de base de datos
llamado migraciones (migrations). Las migraciones son ficheros guardados que se ejecutan
contra cualquier base de datos que Active Record soporte utilizando rake. Aqu hay una
migracin que crea una tabla:

class CreatePublications < ActiveRecord::Migration


def change
create_table :publications do |t|
t.string :title
t.text :description
t.references :publication_type
t.integer :publisher_id
t.string :publisher_type
t.boolean :single_issue

t.timestamps null: false


end
add_index :publications, :publication_type_id
end
end

Rails mantiene el historial sobre que fichero fue actualizado en la base de datos y provee
caractersticas para deshacer los cambios. Para realmente crear la tabla, deberas ejecutar
rake db:migrate y para deshacerlo rake db:rollback .

Nota que el cdigo de arriba es database-agnostic: se puede ejectuar en MySQL,


PostgreSQL, Oracle y others. Puedes aprender ms acerca de las migraciones en la
Gua de Migraciones Active Record.

22
II- Migraciones en Active Record

II- Migraciones Active Record


Las migraciones son la caracterstica de Active Record que permite mantener tu esquema
de base de datos a travs del tiempo. En lugar de escribir las modificaciones del esquema
en SQL puro, las migraciones te permiten utilizar un lenguaje de definicin fcil en Ruby
DSL, para describir cambios para tus tablas. Despus de leer esta gua sabrs:

Los comandos generadores que puedes utilizar para crearlas.


Los mtodos que Active Record provee para manipular tu base de datos.
Las tareas Rake para manipular las migraciones y el esquema.
Cmo las migraciones afectan al fichero schema.rb.

Schema Migration
No debe confundirse con la migracin de datos. En la ingeniera de software, la migracin
de esquemas (tambin migracin de base de datos, gestin de cambios de base de datos)
se refiere a la gestin incremental, cambios reversibles a los esquemas de base de datos
relacional. Se realiza una migracin de esquema en una base de datos siempre que sea
necesario actualizar o revertir el esquema de dicha base de datos a alguna versin ms
reciente o anterior.

Las migraciones se realizan mediante programacin utilizando una herramienta de


migracin de esquema. Cuando se invoca con una versin de esquema deseada
especificada, la herramienta automatiza la aplicacin sucesiva o inversin de una secuencia
apropiada de cambios de esquema hasta que se lleva al estado deseado.

La mayora de las herramientas de migracin de esquemas tienen como objetivo minimizar


el impacto de los cambios de esquema en cualquier dato existente en la base de datos. A
pesar de esto, la preservacin de datos en general no est garantizada porque los cambios
de esquema como la eliminacin de una columna de base de datos pueden destruir datos
(es decir, todos los valores almacenados en esa columna para todas las filas de esa tabla
se eliminan). En su lugar, las herramientas ayudan a preservar el significado de los datos o
a reorganizar los datos existentes para satisfacer nuevos requisitos. Dado que el significado
de los datos a menudo no puede codificarse, la configuracin de las herramientas suele
necesitar intervencin manual.

Riesgos y beneficios

23
II- Migraciones en Active Record

La migracin de esquemas permite corregir errores y adaptar los datos a medida que
cambian los requisitos. Son una parte esencial de la evolucin del software, especialmente
en entornos giles (ver ms abajo).

La aplicacin de una migracin de esquema a una base de datos de produccin siempre es


un riesgo. Las bases de datos de desarrollo y pruebas tienden a ser ms pequeas y
limpias. Los datos en ellos se entienden mejor o, si todo falla, la cantidad de datos es lo
suficientemente pequea como para que un humano pueda procesar. Las bases de datos
de produccin son generalmente enormes, viejas y llenas de sorpresas. Las sorpresas
pueden provenir de muchas fuentes:

Los datos daados que fueron escritos por las versiones viejas del software y que no
fueron limpiadas correctamente
Dependencias implcitas en los datos que nadie ms conoce
Personas que cambian directamente la base de datos sin utilizar las herramientas
designadas
Errores en las herramientas de migracin del esquema
Errores en los supuestos de cmo se deben migrar los datos

Por estas razones, el proceso de migracin necesita un alto nivel de disciplina, pruebas
exhaustivas y una estrategia de copia de seguridad.

24
1- Resumen de las Migraciones

1. Resumen de las Migraciones


Las migraciones son el modo conveniente de cambiar el esquema de tu base de datos a
travs del tiempo de una manera consistente y fcil. Ellas utilizan un lenguaje de Definicin
de Esquemas (DSL) en Ruby por lo que no tienes que escribir SQL a mano, permitindole
al esquema y a los cambios en la base de datos ser independientes. Puedes pensar cada
migracin como una nueva 'versin' de la base de datos. Un esquema comienza sin nada
dentro, y cada migracin lo modifica para aadir o remover tablas, columnas, o registros.
Active Record conoce como actualizar tu esquema a lo largo de su vida, trayendo desde
cualquier punto de su historia hasta la ltima versin. Active Record actualizar tambin el
fichero db/schema.rb para emparejar la estructura modificada de tu base de datos.

Aqu un ejemplo de migracin:

class CreateProducts < ActiveRecord::Migration


def change
create_table :products do |t|
t.string :name
t.text :description

t.timestamps null: false


end
end
end

Esta migracin aade una tabla llamada products con una columna de cadena de
caractres llamada name y una columna de texto llamada description . Una columna de
clave primaria llamada id ser tambin aadida implcitamente, como la clave primaria
por defecto para todos los modelos Active Record. Los macro timestamps aaden dos
columnas, created_at y updated_at . Estas columnas especiales son automaticamemente
administradas por Active Record si existen.

Nota que nosotros definimos el cambio que queremos que ocurra a travs del tiempo. Antes
de que esta migracin se ejecute, no exista la tabla, despus de la migracin la tabla
existir. Active Record, tambien sabe como revertir esta migracin. Si ejecutamos la
migracin hacia atrs, borrar la tabla. En las bases de datos que soportan transacciones
con declaraciones que cambian el esquema, las migraciones son envueltas en una
transaccin. Si la base de datos no soporta esto, cuando una migracin falla la parte de
esta que ha tenido xito no se vuelve atrs. Tendrs que hacer el retroceso de los cambios
que fueron hechos a mano.

25
1- Resumen de las Migraciones

Hay ciertas consultas que no pueden ejecutarse en una transaccin. Si tu adaptador


soporta transacciones DSL puedes utilizar disable_ddl_transaction! para deshabilitarlas
para una simple migracin.

Si tu deseas en una migracin hacer algo que Active Record no sabe como revertir, puedes
utilizar reversible :

class ChangeProductsPrice < ActiveRecord::Migration


def change
reversible do |dir|
change_table :products do |t|
dir.up { t.change :price, :string }
dir.down { t.change :price, :integer }
end
end
end
end

Alternativamente, puedes utilizar up y down en lugar de change :

class ChangeProductsPrice < ActiveRecord::Migration


def up
change_table :products do |t|
t.change :price, :string
end
end

def down
change_table :products do |t|
t.change :price, :integer
end
end
end

26
2- Creando una Migracin

2. Creando una Migracin


2.1 Creando una Migracin Independiente
Las migraciones son grabadas como ficheros en el directorio db/migrate , una por una para
cada clase migration . El nombre del fichero es de la forma
YYYYMMDDHHMMSS_create_products.rb , que es como decir un instante del tiempo UTC para

identificar la migracin, seguida por un guin bajo, seguido de un nombre de migracin. El


nombre de la clase migracin (versin CamelCased) debera emparejar con la parte
posterior del nombre del fichero. Por ejemplo en el fichero
20080906120000_create_products.rb se deberia definir la clase CreateProducts y en el

fichero 20080906120001_add_details_to_products.rb se debera definir


AddDetailsToProducts . Rails utiliza esta marca de tiempo para determinar cual migracin

debera ejecutarse en su correspondiente orden, entonces si tu ests copiando desde otra


aplicacin o generas el fichero por ti mismo, se consciente de sus posiciones en el orden.

Por supuesto, calcular los instantes no es divertido, entonces Active Record provee un
generador que hace esto por ti:

$ bin/rails generate migration AddPartNumberToProducts

Esto crear una migracin vaca pero con el nombre apropiado:

class AddPartNumberToProducts < ActiveRecord::Migration


def change
end
end

Si el nombre de la migracin es de la forma "AddXXXToYYY" o "RemoveXXXFromYYY" y


es seguido por una lista de nombres de columnas y tipos, ser creada una migracin
conteniendo las declaraciones apropiadas add_column y remove_column

$ bin/rails generate migration AddPartNumberToProducts part_number:string

generar

27
2- Creando una Migracin

class AddPartNumberToProducts < ActiveRecord::Migration


def change
add_column :products, :part_number, :string
end
end

De modo similar, puedes generar una migracin para remover una columna desde la lnea
de comandos:

$ bin/rails generate migration RemovePartNumberFromProducts part_number:string

generar

class RemovePartNumberFromProducts < ActiveRecord::Migration


def change
remove_column :products, :part_number, :string
end
end

Si el nombre de la migracin es de la forma "CreateXXX" y es seguida por una lista de


nombres de columnas y tipos entonces ser generada una migracin para crear la tabla
XXX con las columnas listadas. Por ejemplo:

$ bin/rails generate migration CreateProducts name:string part_number:string

generar

class CreateProducts < ActiveRecord::Migration


def change
create_table :products do |t|
t.string :name
t.string :part_number
end
end
end

Como siempre, lo que ha sido recientemente generado es un punto de partida. Puedes


aadir o remover lo que sea adecuado editando el fichero
db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb . Tambin, el generador acepta tipos

de columna como references (tambin disponible como belongs_to ). Como ejemplo:

$ bin/rails generate migration AddUserRefToProducts user:references

28
2- Creando una Migracin

generar

class AddUserRefToProducts < ActiveRecord::Migration


def change
add_reference :products, :user, index: true
end
end

Esta migracin crear una columna user_id y un ndice apropiado. Hay tambin un
generador que produce tablas de interseccin, si JoinTable es parte del nombre:

$ bin/rails g migration CreateJoinTableCustomerProduct customer product

producir la siguiente migracin:

class CreateJoinTableCustomerProduct < ActiveRecord::Migration


def change
create_join_table :customers, :products do |t|
# t.index [:customer_id, :product_id]
# t.index [:product_id, :customer_id]
end
end
end

2.2 Generadores del Modelo


Los generadores de modelos y de andamiaje (Scaffold) crearn las migraciones apropiadas
para aadir un nuevo modelo. Estas migraciones ya contendrn las instrucciones para la
creacin de la correspondiente tabla. Si le dices a Rails que columnas quieres, las
declaraciones para aadir esas columnas sern tambin creadas. Por ejemplo, ejecutando:

$ bin/rails generate model Product name:string description:text

crear una migracin que se ve de la siguiente manera:

29
2- Creando una Migracin

class CreateProducts < ActiveRecord::Migration


def change
create_table :products do |t|
t.string :name
t.text :description

t.timestamps null: false


end
end
end

Puedes aadir la cantidad de columnas "nombre/tipo" que quieras.

2.3 Modificadores de Paso


Algo comnmente utilizado es un tipo de modificador que puede ser pasados directamente
en la lnea de comandos. Estn encerrados por llaves y despus por el tipo de campo: Por
ejemplo, ejecutando:

$ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:refe


rences{polymorphic}

producir una migracin que luce como sta:

class AddDetailsToProducts < ActiveRecord::Migration


def change
add_column :products, :price, :decimal, precision: 5, scale: 2
add_reference :products, :supplier, polymorphic: true, index: true
end
end

Echa un vistazo a las ayudas de los generadores para mayor detalle.

30
3- Escribiendo una Migracin

3. Escribiendo una Migracin


Una vez que has creado tu migracin utilizando uno de los generadores es tiempo de
comenzar el trabajo!

3.1 Creando una Tabla


El mtodo create_table es uno de los fundamentales, pero la mayora del tiempo, los
generars utilizando el generador de modelo o de andamiaje (Scaffold). Un tpico uso
podra ser:

create_table :products do |t|


t.string :name
end

El cual crea una tabla products con una columna llamada name (y como se discutir a
continuacin, una columna implcita id ).

Por defecto, create_table crear una clave primaria llamada id . Puedes cambiar el
nombre de la clave primaria con la opcin :primary_key (no te olvides de actualizar el
modelo correspondiente) o, si no quieres ninguna clave primaria, puedes escribir la opcin
id: false . Si necesitas pasarle a la base de datos opciones especficas puedes escribir un

fragmento de SQ en la opcin :options . Por ejemplo:

create_table :products, options: "ENGINE=BLACKHOLE" do |t|


t.string :name, null: false
end

Esto aadir ENGINE=BLACKHOLE con la declaracin SQL utilizada para crear una tabla
(cuando utilizamos MySQL, por defecto es ENGINE=InnoDB ).

3.2 Crear una Tabla de Interseccin


El mtodo de migracin create_join_table crea una tabla de interseccin HABTM (Has
And Belongs To Many). Una tpica podra ser:

create_join_table :products, :categories

31
3- Escribiendo una Migracin

La cual crear una tabla categories_products con dos columnas llamadas category_id y
product_id . Esas columnas tienen la opcin :null configurada a false por defecto.

Esto ser sobrescrito especificando la opcin :column_options .

create_join_table :products, :categories, column_options: {null: true}

Crear product_id y category_id con la opcin :null a true .

Puedes pasar la opcin :table_name cuando quieras para adaptar el nombre de la tabla a
tus necesidades. Por ejemplo:

create_join_table :products, :categories, table_name: :categorization

crear una tabla categorization .

create_join_table tambin acepta un bloque, el cual puedes utilizar para aadir ndices

(que no son creados por defecto) o columnas adicionales:

create_join_table :products, :categories do |t|


t.index :product_id
t.index :category_id
end

3.3 Cambiando Tablas


La prima hermana de create_table es change_table , utilizada para modificar tablas
existentes. Esto es utilizado de forma similar a create_table pero el objeto que se cede al
bloque conoce ms trucos. Por ejemplo:

change_table :products do |t|


t.remove :description, :name
t.string :part_number
t.index :part_number
t.rename :upccode, :upc_code
end

Al igual que remove_column y add_column , Rails proporciona el mtodo de migracin


change_column .

change_column :products, :part_number, :text

32
3- Escribiendo una Migracin

Esto cambia la columna part_number sobre la tabla products para convertirla en un


campo de texto .

Adems de change_column , los mtodos change_column_null y change_column_default son


utilizados especficamente para cambiar el nulo y el valor por defecto de una columna.

change_column_null :products, :name, false


change_column_default :products, :approved, false

Esto establece el campo :name de products a una columna NOT NULL y el valor por
defecto del campo :approved a false .

A diferencia de change_column (y change_column_default ), change_column_null es


reversible.

3.5 Modificadores de Columnas


Los modificadores de columnas pueden ser aplicados cuando se crea o se cambia una
columna:

limit Establece el mximo tamao de un campo string/text/binary/integer.

precision Define la precisin de los campos decimal, representando el nmero de

dgitos total del nmero.


scale Define la escala para los campos decimal, representando el numero de dgitos

despus del punto decimal.


polymorphic Aade una columna type para las asociaciones belongs_to .

null Aade o rechaza valores NULL en la columna.

default Permite establecer un valor por defecto de la columna. Nota que si utilizas un

valor dinmico (como una fecha), el valor por defecto solo ser calculado la primera vez
(ej: en la fecha y hora que la migracin es aplicada).
index Aade un ndice para la columna.

required Aade required: true para las asociaciones belongs_to y null: false

para la columna de la migracin.

Algunos adaptadores pueden soportar opciones adicionales; ver el adaptador especfico en


los documentos API para ms informacin.

3.6 Claves forneas


Mientras que no es requerida podras querer aadir una restriccin de clave fornea para
garantizar la integridad referencial.

33
3- Escribiendo una Migracin

add_foreign_key :articles, :authors

Esto aade una nueva clave fornea a la columna author_id en la tabla articles . La
clave hace referencia a la columna id de la tabla authors . Si los nombres de las
columnas no pueden ser derivados de los nombres de las tablas, puedes utilizar ls opciones
:column y :primary_key .

Rails generar un nombre para cada clave fornea empezando por fk_rails_ seguido por
10 caracteres aleatorios. Hay una opcin :name para especificar un nombre diferente si
fuera necesario.

Active Record solo soporta una columna simple como clave fornea. execute y
structure.sql son requeridas para utilizar claves forneas compuestas.

Remover una clave fornea es tan fcil como:

# decirle a Active Record que la averigue por los modelos involucrados


remove_foreign_key :accounts, :branches

# borrar la clave foranea de una columna especfica


remove_foreign_key :accounts, column: :owner_id

# borrar la clave foranea por su nombre

3.7 Cuando los Helpers no son Suficientes


Si los helpers provistos por Active Record no son suficientes puedes utilizar el mtodo
execute para ejecutar cualquier SQL:

Product.connection.execute('UPDATE `products` SET `price`=`free` WHERE 1')

Para ms detalles y ejemplos de mtodos individuales, leer la documentacin API. En


particular la documentacin para

ActiveRecord::ConnectionAdapters::SchemaStatements (la cual provee los mtodos

disponibles en los mtodos change , up y down ),


ActiveRecord::ConnectionAdapters::TableDefinition (el cual provee los mtodos

disponibles en el objeto cedido por create_table ) y


ActiveRecord::ConnectionAdapters::Table (el cual provee los mtodos disponibles en el

objeto cedido por change_table ).

3.8 Utilizando el Mtodo change

34
3- Escribiendo una Migracin

El mtodo change es la principal manera de escribir migraciones. Este funciona para la


mayora de los casos, donde Active Record conoce como revertir la migracin
automticamente. Actualmente, el mtodo change soporta solo estas definiciones de
migracin:

add_column
add_index
add_reference
add_timestamps
add_foreign_key
create_table
create_join_table
drop_table (must supply a block)
drop_join_table (must supply a block)
remove_timestamps
rename_column
rename_index
remove_reference
rename_table

change_table es tambin reversible, siempre y cuando el bloque no llame a change ,

change_default o remove . Si necesitas utilizar algn otro mtodo, deberas usar

reversible o escribir los mtodos up y down en lugar de utilizar el mtodo change .

3.9 Usando reversible


Las migraciones complejas pueden requerir procesamiento que Active Record no sabe
como revertir. Puedes utilizar reversible para especificar que hacer cuando ests
ejecutando una migracin y ser lo que se haga cuando se revierta. Por ejemplo:

35
3- Escribiendo una Migracin

class ExampleMigration < ActiveRecord::Migration


def change
create_table :distributors do |t|
t.string :zipcode
end

reversible do |dir|
dir.up do
# aadir una restriccin CHECK
execute <<-SQL
ALTER TABLE distributors
ADD CONSTRAINT zipchk
CHECK (char_length(zipcode) = 5) NO INHERIT;
SQL
end
dir.down do
execute <<-SQL
ALTER TABLE distributors
DROP CONSTRAINT zipchk
SQL
end
end

add_column :users, :home_page_url, :string


rename_column :users, :email, :email_address
end
end

Utilizando reversible nos aseguraremos que las instrucciones son ejecutadas en el orden
correcto tambin. Si el ejemplo de la anterior migracin es revertido, el bloque down ser
ejecutado despus de que la columna home_page_url sea borrada y justo antes de que la
tabla distributors se borre.

Algunas veces tu migracin har algo que es simplemente irreversible, por ejemplo, puede
borrar algunos datos, En tales casos, puedes lanzar una excepcin
ActiveRecord::IrreversibleMigration en tu bloque down . Si alguien intenta revertir tu

migracin, se le mostrar un mensaje de error diciendo que no se pude hacer.

3.10 Utilizando los Mtodos up/down


Tambin puedes utilizar los viejos mtodos up y down en lugar del mtodo change . El
mtodo up debera describir la transformacin que deseas hacer a tu esquema, y el
mtodo down de tu migracin debera revertir las transformaciones hechas por el mtodo
up . En otras palabras, el esquema de la base de datos permanecer sin cambios si

ejecutas un up seguido por un down . Por ejemplo, si creas una tabla en el mtodo up ,

36
3- Escribiendo una Migracin

deberas borrarla en el mtodo down . Esto es para revertir las transformaciones


precisamente en el orden reverso en el que fueron hechas en el mtodo up . El ejemplo en
la seccin reversible es equivalente a:

class ExampleMigration < ActiveRecord::Migration


def up
create_table :distributors do |t|
t.string :zipcode
end

# aadir una restriccin CHECK


execute <<-SQL
ALTER TABLE distributors
ADD CONSTRAINT zipchk
CHECK (char_length(zipcode) = 5);
SQL

add_column :users, :home_page_url, :string


rename_column :users, :email, :email_address
end
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
execute <<-SQL
ALTER TABLE distributors
DROP CONSTRAINT zipchk
SQL

drop_table :distributors
end
end

Si tu migracin es irreversible, deberas lanzar una ActiveRecord::IrreversibleMigration


desde tu mtodo down . Si alguin trata de revertir la migracin, un mensaje de error ser
mostrado diciendo que esto no se puede hacer.

3.11 Revertir Migraciones Anteriores


Puedes utilizar las posibilidades de Active Record para revertir migraciones utilizando el
mtodo revert:

37
3- Escribiendo una Migracin

require_relative '2012121212_example_migration'

class FixupExampleMigration < ActiveRecord::Migration


def change
revert ExampleMigration

create_table(:apples) do |t|
t.string :variety
end
end
end

El mtodo revert tambin acepta un bloque de instrucciones para revertir. Este puede ser
utilizado para revertir partes seleccionadas de las migraciones. Por ejemplo, vamos a
imaginar que la ExampleMigration es "commiteada" y ms tarde se decide que lo mejor
sera utilizar validaciones Active Record, en lugar de la restriccin CHECK, para verificar el
cdigo postal (zipcode).

class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration


def change
revert do
# cdigo cortado y pedado de ExampleMigration
reversible do |dir|
dir.up do
# add a CHECK constraint
execute <<-SQL
ALTER TABLE distributors
ADD CONSTRAINT zipchk
CHECK (char_length(zipcode) = 5);
SQL
end
dir.down do
execute <<-SQL
ALTER TABLE distributors
DROP CONSTRAINT zipchk
SQL
end
end

# El resto de la migracin fue bien


end
end
end

La misma migracin podra tambin haberse escrito sin utilizar revert pero esto podra
haber llevado unos pocos pasos ms: revertiendo el orden de create_table y reversible ,
reemplazando create_table por drop_table , y finalmente reemplazando up por down y
vice-versa. Todo esto est a cargo de revert.

38
3- Escribiendo una Migracin

39
4- Ejecutando Migraciones

4. Ejecutando Migraciones
Rails provee un conjunto de tareas Rake para ejecutar cierto conjunto de migraciones.

La primera tarea de migracin que utilizars ser probablemente rake db:migrate . En su


forma ms bsica ejecuta un mtodo change o up para todas las migraciones que an no
se han ejecutado. Si no hay tales migraciones, finaliza. Ejecutar esas migraciones en
orden basado en la fecha de creacin de cada migracin.

Nota que ejecutar la tarea db:migrate tambin se invoca la tarea db:schema:dump , el


cual actualizar tu fichero db/schema.rb para emparejarlo a la estructura de tu base de
datos.

Si especificas una versin de destino, Active Record ejecturar las migraciones requeridas
( change , up , down ) hasta alcanzar la versin especfica. La versin es el prefijo numrico
en el nombre de fichero de la migracin. Por ejemplo, para migrar a la versin
20080906120000 ejectuta:

$ bin/rake db:migrate VERSION=20080906120000

Si la versin 20080906120000 es mayor que la versin actual, y se est migrando hacia


arriba, se ejectutar el mtodo change (o up ) en todas las migraciones hacia arriba e
incluir la 20080906120000, y no ejecutar ninguna migracin posterior. Si se est
migrando hacia abajo, esto ejecutar los metodos down de todas las migraciones hacia
abajo, paro no incluir la 20080906120000.

4.1 Deshaciendo Migraciones


Una tarea comn es deshacer la ltima migracin. Por ejemplo, si cometes un error en esta
y quieres corregirlo. En lugar de rastrear la versin anterior asociada puedes ejecutar:

$ bin/rake db:rollback

Esto ejecutar hacia atrs la ltima migracin, ya sea por revertir el mtodo change o por
ejecucin del mtodo down . Si necesitas deshacer varias migraciones puedes proveer un
parmetro STEP:

$ bin/rake db:rollback STEP=3

40
4- Ejecutando Migraciones

revertir las ltimas 3 migraciones.

La tarea db:migrate:redo es un atajo para deshacer y luego migrar hacia arriba otra vez.
Con la tarea db:rollback , tambin puedes utilizar el parmetro STEP .

Si necesitas retroceder ms de una versin haces lo siguiente, por ejemplo:

$ bin/rake db:migrate:redo STEP=3

Ninguna de estas tareas Rake hace algo que no se pueda hacer con db:migrate . Son
simplemente ms convenientes, por que no necesitas escribir la versin especfica desde la
cual hay deshacer y volver a ejecutar las migraciones.

4.2 Configurar la Base de Datos


La tarea rake db:setup crear la base de datos, carga el esquema con los datos iniciales.

4.3 Recomponer la Base de Datos


La tarea rake db:reset borrar la base de datos y la configurar nuevamente. Esto es
funcionalmente equivalente a rake db:drop db:setup .

Esto no es lo mismo que ejecutar todas las migraciones. Esto utilizar nicamente el fichero
schema.rb actual. Si una migracin no puede ser revertida, rake db:reset no te podr

ayudar. Para descubrir ms acerca del volcado del esquema ver la seccin El Volcado del
Esquema y T.

4.4 Ejecutar Migraciones Especficas


Si necesitas ejecutar una migracin especfica hacia arriba o abajo, las tareas
db:migrate:up y db:migrate:down harn esto. Slo especifica la versin adecuada y la

migracin correspondiente tendr su mtodo change , up o down invocado, por ejemplo:

$ bin/rake db:migrate:up VERSION=20080906120000

ejecutara la migracin 20080906120000 por ejecucin del mtodo change (o del mtodo
up ). Esta tarea primero comprobar si la migracin est ya realizada y no har nada si

Active Record cree que esta ya se ha ejecutado antes.

4.5 Ejecutando Migraciones en Diferentes Entornos

41
4- Ejecutando Migraciones

Por defecto cuando ejecutamos rake db:migrate se ejecutar en el entorno de desarrollo o


development. Si quieres ejecutar las migraciones otra vez en otro entorno, puedes
especificarlo utilizando la variable de entorno RAILS_ENV cuando ejecutas el comando. Por
ejemplo para ejecutar las migraciones nuevamente en el entorno de pruebas, test, podras
ejecutar:

$ bin/rake db:migrate RAILS_ENV=test

4.6 Cambiando la Salida de la Ejecucin de Migraciones


Por defecto las migraciones te indican exactamente que han hecho y cuanto tiempo le ha
llevado. Una migracin que crea una tabla y aade un ndice puede producir una salida
como esta:

== CreateProducts: migrating =================================================


-- create_table(:products)
-> 0.0028s
== CreateProducts: migrated (0.0028s) ========================================

Varios mtodos son provistos en las migraciones que te permiten controlar todo esto:

Metodo Propsito
Toma un bloque como argumento y suprime cualquier salida
suppress_messages
generada por el bloque.
Toma como argumento un mensaje y emite tal cual es. Se le
say puede pasar un segundo argumento booleano para especificar
si se va a tabular o no.

Salida de texto junto con el tiempo que tom para ejecutar el


say_with_time bloque correspondiente. Si el bloque devuelve un entero que
asume que es el nmero de filas afectadas.

Por ejemplo, esta migracin:

42
4- Ejecutando Migraciones

class CreateProducts < ActiveRecord::Migration


def change
suppress_messages do
create_table :products do |t|
t.string :name
t.text :description
t.timestamps null: false
end
end

say "Created a table"


suppress_messages {add_index :products, :name}
say "and an index!", true

say_with_time 'Waiting for a while' do


sleep 10
250
end
end
end

genera la siguiente salida

== CreateProducts: migrating =================================================


-- Created a table
-> and an index!
-- Waiting for a while
-> 10.0013s
-> 250 rows
== CreateProducts: migrated (10.0054s) =======================================

Si quieres que Active Record no muestre ninguna salida, ejecutando rake db:migrate
VERBOSE=false se suprimirn toda las salidas.

43
5- Cambiando las Migraciones Existentes

5. Cambiando las Migraciones Existentes


Ocasionalmente podemos cometer un error cuando escribimos una migracin. Si has
ejecutado ya la migracin entonces no puedes editarla y ejecutarla otra vez: Rails pensar
que ya se ha ejecutado la migracin y no har nada cuando ejecutes otra vez rake
db:migrate . Por eso debes revertir la migracin (por ejemplo con rake db:rollback ), edita

la migracin y luego escribe rake db:migrate para ejecutar la versin corregida.

En general, editar una migracin existente no es una buena idea. Crears trabajo extra para
ti mismo y tus colaboradores y causar mayores dolores de cabeza si la versin existente
de una migracin ha sido ya ejecutada en los servidores de produccin. En su lugar,
deberas escribir una nueva migracin que realice los cambios que requieres. Editar una
migracin anterior con otra recin generada que no ha sido "commiteada" en la base de
datos, controlamos mejor el cdigo fuente (que adems en general no se ha propagado
ms all de tu mquina de desarrollo) y es relativamente inofensivo, y se convierte en una
mejor practica.

El mtodo revert puede ser de mucha ayuda cuando escribes una nueva migracin para
deshacer migraciones anteriores en conjunto o en parte (ver Revertir Migraciones
Anteriores ver arriba).

44
6- Volcando el Esquema y T

6. Volcando el Esquema y T
6.1 Para qu son los Ficheros de Esquema?
Las migraciones, con lo poderosas que pueden ser, no son la fuente fiel de tu esquema de
base de datos. Este rol cae sobre ya sea sobre el fichero db/schema.rb o un fichero SQL
generado por Active Record al examinar la base de datos. Estos no estn diseados para
ser editados, representan solo el estado actual del esquema de la base de datos.

No es necesario (y eso es un error repetido) desplegar una nueva instancia de una


aplicacin al volver a ejecutar todo el historial de migraciones. Es mucho ms simple y
rpido cargar en la base de datos la descripcin del esquema actual.

Por ejemplo, as es como la base de datos de test es creada: la actual base de datos de
desarrollo es volcada (ya sea desde db/schema.rb o desde db/structure.sql ) y luego
grabada en la base de datos de test .

El fichero de esquema tambin es til si quieres echar un vistazo a los atributos que un
objeto Active Record tiene. Esta informacin no est en el cdigo de los modelos y es
frecuentemente propagada a travs de varias migraciones, pero esta informacin est muy
bien resumida en el fichero de esquema.

La gema annotate_models (https://github.com/ctran/annotate\_models\) automticamente


aade y actualiza comentarios en la parte superior de cada modelo resumiendo el esquema
si deseas esa funcionalidad.

6.2 Tipos de Volcado del Esquema


Hay 2 maneras de volcar el esquema. Esto se configura en config/application.rb a travs
de la directiva config.active_record.schema_format , la cual puede ser o bien :sql o
:ruby . Si est seleccionado :ruby entonces el esquema es guardado en db/schema.rb .

Si miras este fichero encontrars que luce casi como una gran migracin:

45
6- Volcando el Esquema y T

ActiveRecord::Schema.define(version: 20080906171750) do
create_table "authors", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end

create_table "products", force: true do |t|


t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "part_number"
end
end

En muchos sentidos esto es exactamente lo que es. Este fichero fue creado inspeccionando
la base de datos y expresando su estructura utilizando create_table , add_index , etc.
Porque es independiente de la base de datos, podra ser cargado en cualquier base de
datos que Active Record soporte. Esto podra ser muy til si tienes que distribuir una
aplicacin que debe estar disponible para ejecutarse contra multiples bases de datos.

Sin embargo hay algunas funcionalidades que quedan fuera: db/schema.rb no puede
expresar items especficos de una base de datos como disparadores, o procedimientos
almacenados. Mientras que en una migracin tu puedes ejecutar definiciones SQL
personlizadas, el volcado de esquema no puede reconstituir aquellas definiciones desde la
base de datos. Si ests utilizando caractersticas como estas, entonces deberas configuar
el formato del esquema a :sql .

En lugar de utilizar el volcado del esquema de Active Record, la estructura de la base de


datos ser volcada utilizando una herramienta especfica de la base de datos dentro del
db/structure.sql (via la tarea Rake db:structure:dump ). Por ejemplo, para PostgreSQL,

se utiliza pg_dump . Para MySQL, este fichero contendr la salida SHOW CREATE TABL de te
varias tablas.

Cargar estos esquemas es sencillamente cuestin de ejecutar las definiciones SQL que
contienen. Por defincin, esto crear una copia perfecta de la estructura de la base de
datos. Utilizando el formato del esquema :sql sin embargo, vamos a asegurarnos la carga
del esquema en otro RDBMS(Relational Data Base Manager System - Sistema de gestin
de bases de datos relacionales ) distinto al que fue utilizado para crearlo.

6.3 Volcados del Esquema y Control de la Fuente


Los volcados del esquema son el cdigo fuente que manda a tu esquema de base de datos.

46
6- Volcando el Esquema y T

db/schema.rb contiene la versin actual de la base de datos. Esto asegura que ocurrirn

conflictos en el caso de que se mezclen dos ramas que han tocado el esquema. Cuando
esto ocurre, resuelve los conflictos manualmente, manteniendo el nmero de versin ms
alto entre los dos.

47
7- Active Record y la Integridad Referencial

7. Active Record y la Integridad


Referencial
La metodologa de Active Record dice que la inteligencia pertenece al modelo, no a la base
de datos. As bien, caractersticas tales como disparadores o restricciones, las cuales
otorgan algo de inteligencia a la base de datos, no son fuertemente utilizadas.

Las validaciones tal como validates :foreign_key, uniqueness: true son un modo por el
cual los modelos pueden forzar la integridad de los datos. La opcin :dependent en las
asociaciones permite a los modelos destruir los objetos que son hijos cuando el padre es
destruido. Como cualquier cosa que opere a nivel de aplicacin, eso no garantiza integridad
referencial, entonces algunas personas se aseguran esta con restricciones sobre las claves
forneas en la base de datos.

Sin embargo Active Record no provee todas las herramientas para trabajar directamente
con algunas caractersticas, el mtodo execute puede ser utilizado para ejecutar un SQL
arbitrario.

48
8- Migraciones y Semillas de Datos (Seeds)

8. Migraciones y Semillas de Datos


(Seeds)
Algunas personas utilizan las migraciones para aadir datos a la base de datos:

class AddInitialProducts < ActiveRecord::Migration


def up
5.times do |i|
Product.create(name: "Product ##{i}", description: "A product.")
end
end

def down
Product.delete_all
end
end

Sin embargo, Rails tiene una caraterstica 'seeds' que puede ser utilizada para crear las
semillas en una base de datos con datos iniciales. Esta es una caracterstica realmente
simple: solo rellena db/seeds.rb con algn codigo Ruby, y ejecuta rake db:seed:

5.times do |i|
Product.create(name: "Product ##{i}", description: "A product.")
end

Esto es generalmente el camino ms limpio para configurar la base de datos de una


aplicacin en blanco.

49
III- Validaciones en Active Record

III- Validaciones Active Record


Esta gua te ensear como validar el estado de los objetos antes de guardarse en la base
de datos utilizando las caractersticas de validacin de Active Record. Despus de leer esta
gua, conocers:

Cmo utilizar los mtodos para construir mtodos de ayuda a las validaciones Active
Record.
Cmo crear tus propios mtodos de validacin a medida.
Cmo trabajar con los mensajes de error generados por el proceso de validacin.

50
1- Resumen de las Validaciones

1. Resumen de las Validaciones


Aqu hay un ejemplo de una validacin muy simple:

class Person < ActiveRecord::Base


validates :name, presence: true
end

Person.create(name: "John Doe").valid? # => true


Person.create(name: nil).valid? # => false

Como puedes ver, nuestra validacin nos hace saber que nuestro objeto Person no es
vlido sin un atributo name . La segunda instancia de Person no ser guardada en la base
de datos.

Antes de entrar en ms detalles, hablaremos de como las validaciones quedan en el cuadro


general de nuestra aplicacin.

1.1 Por qu Utilizamos Validaciones?


Las validaciones son utilizadas para asegurarse que solo datos vlidos son guardados
dentro de la base de datos. Por ejemplo, esto puede ser muy importante para que tu
aplicacin se asegure que todos los usuarios proveen una direccin de correo electrnico y
una direccin postal vlidas. Las validaciones del nivel del Modelo, son la mejor manera de
asegurar que solo los datos vlidos son guardados en la base de datos. Son independientes
del motor de base de datos, no los pueden sobrepasar los usuarios finales, y son
recomendables para probar y mantener. Rails los hace fcil de utilizar, provee mtodos
ayudantes construdos previamente para las necesidades ms comunes, y te permite crear
tus propias validaciones tambin.

Hay varias otras maneras de validar los datos antes de guardarlos en tu base de datos,
incluyendo una restricciones nativas de la base de datos, validaciones del lado del cliente y
validaciones en al nivel del controlador. Aqu un resumen de las mismas:

Restricciones de la base de datos, y/o procedimientos almacenados que hacen el


mecanismo de validacin dependiente del motor de la base de datos y pueden hacer
las pruebas y el mantenimiento ms dificil. Sin embargo, si tu base de datos es utilizada
por otras aplicaciones, puede ser una buena idea utilizar algunas restricciones en el
nivel de base de datos. Adicionalmente, las validaciones en el nivel de base de datos,
pueden mantener la seguridad en algunas cosas (por ejemplo la unicidad en tablas
muy grandes) que son difciles de implementar de otra manera.

51
1- Resumen de las Validaciones

Las validaciones del lado del cliente suelen ser utilizadas, pero son generalmente poco
confiables si se utilizan solas. Si son implementadas utilizando JavaScript, pueden ser
sobrepasadas si el JavaScript est quitado en el navegador del usuario. Sin embargo,
si se combinan con otras tcnicas, las validaciones del lado del cliente pueden ser una
manera conveniente de proporcionar una respuesta inmediata a quienes utilicen tu
pgina.
Las validaciones en el nivel del controlador pueden ser temporalmente utilizadas, pero
frecuentemente se vuelven incomprensibles y difciles de probar y mantener. Siempre
que sea posible, es una buena idea mantener los controladores limpios, esto har que
sea un placer trabajar en tu aplicacin a largo plazo.

Elgelas con certeza, en casos especficos. Esta es la opinin del equipo de Rails que las
validaciones al nivel del modelo son las ms apropiadas en la mayora de las
circunstancias.

1.2 Cuando Ocurren las Validaciones?


Hay dos clases de objetos Active Record: aquellos que se corresponden a un registro en la
base de datos y los que no. Cuando creas un objeto nuevo, por ejemplo usando el mtodo
new , ese objeto no pertenece a la base de datos an. Una vez que llamas al mtodo save

el objeto ser guardado en la tabla apropiada de la base de datos. Active Record utiliza el
mtodo de instancia new_record? para determinar si un objeto est ya en la base de datos
o no. Considera la siguiente clase Active Record:

class Person < ActiveRecord::Base


end

Podemos ver como trabaja mirando algunas salidas en rails console:

$ bin/rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false

Al crear y guardar un nuevo registro se enviar una operacin SQL INSERT a la base de
datos. En cambio al actualizar un registro existente enviaremos una operacin SQL UPDATE .
Las validaciones tpicamente son ejecutadas antes que estos comandos se enven a la
base de datos. Si alguna validacin falla, el objeto ser marcado como invlido y Active

52
1- Resumen de las Validaciones

Record no crear las operaciones INSERT o UPDATE . De lo contrario se guardara un objeto


invlido en la base de datos. Puedes elegir tener validaciones especficas ejecutndose
cuando un objeto es creado, guardado o actualizado.

Hay muchas maneras para cambiar el estado de un objeto en la base de datos. Algunos
mtodos dispararn validaciones, pero otros no lo harn. Esto significa que es posible
guardar un objeto en la base de datos en un estado invlido si no tienes cuidado.

Los siguientes mtodos disparan validaciones, y guardarn un objeto en la base de datos


solo si el objeto es vlido:

create
create!
save
save!
update
update!

Las versiones bang (ej: save! ) lanzan una excepcin si el registro es invlido. Las
versiones no-bang como save y update retornan false , y create solo retorna el objeto.

1.3 Saltando las Validaciones


Los siguientes mtodos saltan validaciones, y guardarn el objeto en la base de datos sin
tener en cuenta su validez. Deben ser utilizados con precaucin.

decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters

Nota que save tambin tiene la posibilidad de saltar validaciones si se le pasa de argumento
validate: false . Esta tcnica debe ser utilizada con precaucin.

save(validate: false)

1.4 valid? e invalid?

53
1- Resumen de las Validaciones

Para verificar si un objeto es o no vlido, Rails utiliza el mtodo valid? . Tambin puedes
utilizar este mtodo por ti mismo. valid? dispara tus validaciones y retorna true si no
fueron encontrados errores en el objeto, y false de lo contrario. Como puedes ver aqu:

class Person < ActiveRecord::Base


validates :name, presence: true
end

Person.create(name: "John Doe").valid? # => true


Person.create(name: nil).valid? # => false

Despus de que Active Record ha ejecutado las validaciones, cualquier error encontrado
puede ser conocido a travs de la instancia del mtodo errors.messages , el cual retorna
una coleccin de errores. Por definicin, un objeto es vlido si la coleccin est vaca
despus de ejecutar las validaciones.

Nota que un objeto instanciado con new no reportar errores mientras solo sea
instanciado aunque sea tcnicamente invlido, porque las validaciones no sern
ejecutadas cuando utilizas new.

class Person < ActiveRecord::Base


validates :name, presence: true
end

>> p = Person.new
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {}

>> p.valid?
# => false
>> p.errors.messages
# => {name:["can't be blank"]}

>> p = Person.create
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {name:["can't be blank"]}

>> p.save
# => false

>> p.save!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

>> Person.create!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

54
1- Resumen de las Validaciones

invalid? es simplemente la inversa de valid? . Dispara las validaciones, retornando

true si cualquier error es encontrado en el objeto, y false en caso contrario.

1.5 errors[]
Para verificar si un atributo en particular de un objeto es vlido, lo puedes verificar utilizando
errors[:attribute] . Esto retornar un un array de todos los errores por :attribute . Si no

hay errores en un atributo especfico, es devuelto un array vaco.

Este mtodo es solo utilizado despus de que las validaciones se han ejectuado, porque
solo inspecciona la coleccin de errores y no disparan validaciones por si mismos. Esto es
diferente desde el mtodo ActiveRecord::Base#invalid? que explicamos antes porque este
no verifica la validez del objeto no tiene ese objetivo. Solo comprueba para ver si hay
errores encontrados en un atributo individual del objeto.

55
2- Helpers de Validacin

2. Helpers de Validacin
Active Record ofrece muchos helpers de validacin predefinidos que puedes utilizar
directamente dentro de las definiciones de tu clase. Estos helpers proveen las reglas de
validacin comunes. Cada vez que una validacin falla, un mensaje de error es aadido a la
coleccin de errors del objeto, y este mensaje es asociado con el atributo que est siendo
validado.

Cada ayudante acepta un nmero arbitrario de nombres de atributos, entonces con una
simple lnea de cdigo puedes aadir algn tipo de validadacin a varios atributos.

Todos ellos aceptan las opciones :on y :message , las cuales definen cuando la validacin
se ejecuta y el mensaje que debera ser aadido a la coleccin errors si falla,
respectivamente. La opcin :on toma uno de los valores :create o :update . Hay un
mensaje de error por defecto para cada uno de los helpers de validacin. Estos mensajes
son utilizados cuando la opcin :message no est especificada. Vamos a repasar cada uno
de los helpers disponibles.

2.1 acceptance
Este mtodo valida si un checkbox en la interfaz de usuarios es chequeado al enviar un
formulario. Esto es tpicamente utilizado cuando el usuario necesita dar su conformidad
sobre los trminos y servicios de tu aplicacin, confirmndolo despus de leer algn texto o
algo por el estilo. Esta validacin es muy especfica para aplicaciones web y este mtodo
acceptance no necesita guardarse en ningn sitio de tu base de datos (si no tienes un

campo para esto, el helper crear un atributo virtual).

class Person < ActiveRecord::Base


validates :terms_of_service, acceptance: true
end

El mensaje de error por defecto de este helper es "must be accepted".

Puede recibir una opcin :accept , la cual determina que valor ser considerado aceptable.
Por defecto es "1" y puede ser cambiado fcilmente.

class Person < ActiveRecord::Base


validates :terms_of_service, acceptance: { accept: 'yes' }
end

56
2- Helpers de Validacin

2.2 validates_associated
Deberas utilizar este helper cuando tu modelo tiene asociaciones con otros modelos y
tambin necesiten ser validados. Cuando intentas guardar tu objeto valid? ser llamado
por cada uno de los objetos asociados.

class Library < ActiveRecord::Base


has_many :books
validates_associated :books
end

Esta validacin trabajar con todos los tipos de asociaciones.

No utilices validates_associated en ambas puntas de tus asociaciones, porque se llamarn


la una a la otra en un ciclo infinito.

El mensaje de error por defecto para validates_associated es "is invalid". Nota que cada
objeto asociado contendr sus propias colecciones errors ; los errores no escalan hacia el
modelo llamado.

2.3 confirmation
Puedes utilizar este helper cuando tengas dos campos de texto que deban recibir
exactamente el mismo contenido. Por ejemplo, puedes querer confirmar una direccin de
email y un password. Esta validacin crea un atributo virtual cuyo nombre es el campo que
tiene que ser confirmado con la _confirmation aadida.

class Person < ActiveRecord::Base


validates :email, confirmation: true
end

En la plantilla de la vista podras utilizar algo como

<%= text_field :person, :email %>


<%= text_field :person, :email_confirmation %>

Esta comprobacin se lleva a cabo solo si email_confirmation no es nil . Para requerir


confirmacin, asegrate de aadir una comprobacin presence al atributo de confirmacin
(veremos presence ms adelante en esta gua):

57
2- Helpers de Validacin

class Person < ActiveRecord::Base


validates :email, confirmation: true
validates :email_confirmation, presence: true
end

El mensaje de error para este ayudante es "doesn't match confirmation".

2.4 exclusion
Este helper valida que los valores de los atributos no estn incluidos en una configuracin
dada. En realidad, esta configuracin puede ser cualquier objeto enumerable.

class Account < ActiveRecord::Base


validates :subdomain, exclusion: { in: %w(www us ca jp),
message: "%{value} is reserved." }
end

El helper exclusion tiene una opcin :in que recibe la configuracin de los valores que
no sern aceptados por los atributos validados. La opcin :in tiene un alias llamado
:within que puedes utilizar para el mismo propsito, si lo quieres. Este ejemplo utiliza la

opcin :message para mostrar como puedes incluir los valores de los atributos.

El mensaje de error por defecto es "is reserved".

2.5 format
Este helper valida los valores de los atributos por pruebas si coinciden o no con una
expresin regular dada, que se especifica utilizando la opcin :with .

class Product < ActiveRecord::Base


validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
end

Alternativamente, puedes requerir que el atributo especfico no responda a la expresin


regular utilizando la opcin :without .

El mensaje de error por defecto es "is invalid".

2.6 inclusion
Este helper valida que los valores de los atributos estn incluidos en una configuracin
dada. En realidad, esta configuracin puede ser un objeto enumerable.

58
2- Helpers de Validacin

class Coffee < ActiveRecord::Base


validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }
end

El helper inclusion tiene una opcin :in que recibe lo que debe ser incluido. La opcin
:in tiene un alias llamado :within que puedes utilizar para algunos propsitos, si lo

deseas. Los ejemplos anteriores utilizan la opcin :message para mostrar como puedes
incluir valores de los atributos.

El mensaje de error para este helper es "is not included in the list".

2.7 length
Este helper valida la longitud de los valores de los atributos. Provee una variedad de
opciones, entonces puedes especificar restricciones de longitud de diferentes maneras:

class Person < ActiveRecord::Base


validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :registration_number, length: { is: 6 }
end

Las opciones de restriccin de longitud son:

:minimum - El atributo no puede tener menos caracteres que una longitud especfica.

:maximum - El atributo no puede tener ms caracteres que una longitud especfica.

:in (o :within ) - La longitud del atributo debe estar contenida en un intervalo dado.

El valor para esta opcin de ser un rango.


:is - La longitud del atributo debe ser igual a un valor dado.

Los mensajes de error por defecto dependen del tipo de validacin de longitud que es
utilizada. Puedes personalizar estos mensajes utilizando las opciones :wrong_length ,
:too_long , :too_short y %{count} como referencia para mostrar el nmero que

corresponda para la restriccin que se est utilizando. Tambin puedes utilizar la opcin
:message para especificar un mensaje de error.

class Person < ActiveRecord::Base


validates :bio, length: { maximum: 1000,
too_long: "%{count} characters is the maximum allowed" }
end

59
2- Helpers de Validacin

Este helper cuenta los caracteres por defecto, pero puedes separar el valor de diferentes
maneras utilizanod la opcin :tokenizer option :

class Essay < ActiveRecord::Base


validates :content, length: {
minimum: 300,
maximum: 400,
tokenizer: lambda { |str| str.split(/\s+/) },
too_short: "must have at least %{count} words",
too_long: "must have at most %{count} words"
}
end

Nota que los mensajes de error por defecto estn en plural (ej: "is too short (minimum
is %{count} characters)"). Por esta razn, cuando :minimum es 1 debes indicar un
mensaje personalizado o utilizar en su lugar presence: true . Cuando :in o :within
tienen un un lmite mnimo de 1, puedes proveer tambin un mensaje personalizado o
llamar a presence antes que a length .

2.8 numericality
Este helper valida que tus atributos tienen solo valores numricos. Por defecto, esto se
comparar con una firma seguida por un entero o un nmero de punto flotante. Para
especificar que solo nmeros enteros estn permitidos configuras a true :only_integer .

Si configuras :only_integer a true , luego se utilizar la expresin regular /\A[+-]?


\d+\Z/ para validar el valor del atributo. De otro modo, intentar convertir el valor a un

nmero utilizando la expresin Float .

Nota que la expresin regular de arriba permite un caracter perdido de salto de lnea.

class Player < ActiveRecord::Base


validates :points, numericality: true
validates :games_played, numericality: { only_integer: true }
end

Adems :only_integer, este helper tambin acepta las siguientes opciones para aadir
restricciones a los valores acceptables:

:greater_than - Especifica que el valor del atributo debe debe ser mayor que un valor

determinado. El mensaje de error para esta opcin es "must be greater than %{count}".
:greater_than_or_equal_to - Especifica que el valor del atributo debe ser mayor o igual

a un valor determinado. El mensaje de error por defecto para esta opcin es "must be
greater than or equal to %{count}".

60
2- Helpers de Validacin

:equal_to - Especifica que el valor del atributo debe ser igual a un valor determinado.

El mensaje de error por defecto de esta opcin es "must be equal to %{count}".


:less_than - Especifica que el valor del atributo debe ser menor que un determinado

valor. El mensaje de error de esta opcin es "must be less than %{count}".


:less_than_or_equal_to - Especifica que el valor del atributo debe ser menor o igual de

un valor determinado. El mensaje de error por defecto es "must be less than or equal to
%{count}".
:odd - Especifica que el valor debe ser un nmero impar si es configurado a true . El

mensaje de error por defecto para esta opcin es "must be odd".


:even - Especifica que el valor del atributo debe ser un nmero par si es configurado a

true . El mensaje de error por defecto para esta opcin es "must be even".

Por defecto, numericality no permite valores nil . Puedes utilizar la opcin allow_nil:
true para permitirlo.

El mensaje de error por defecto en este caso es "is not a number".

2.9 presence
Este ayudante valida que los atributos especificados no estn vacos. Utiliza el mtodo
blank? para comprobar si el valor es nil o una cadena esta en blanco, es decir, una

cadena que est vaca o consta de espacios en blanco.

class Person < ActiveRecord::Base


validates :name, :login, :email, presence: true
end

Si desea estar seguro de que una asociacin est presente, deber probar si el objeto
asociado est presente y no la clave fornea utilizada para asignar la asociacin.

class LineItem < ActiveRecord::Base


belongs_to :order
validates :order, presence: true
end

En el modelo order , para validar los registros asociados cuya presencia es necesaria,
debe especificar la opcin :inverse_of para la asociacin:

class Order < ActiveRecord::Base


has_many :line_items, inverse_of: :order
end

61
2- Helpers de Validacin

Si valida la presencia de un objeto asociado a travs de una relacin has_one o has_many ,


comprobar que el objeto no est en blank? Ni marked_for_destruction?

Desde false.blank? Es true , si desea validar la presencia de un campo booleano debe


utilizar una de las siguientes validaciones:

validates :boolean_field_name, presence: true


validates :boolean_field_name, inclusion: { in: [true, false] }
validates :boolean_field_name, exclusion: { in: [nil] }

Mediante el uso de una de estas validaciones, se asegurar de que el valor NO ser nil
lo que resultara en un valor NULL en la mayora de los casos.

2.10 absence
Este ayudante valida que los atributos especificados estn ausentes. Utiliza el mtodo
present? para comprobar si el valor no es nil o una cadena en blanco, es decir, una

cadena que est vaca o consta de espacios en blanco.

class Person < ActiveRecord::Base


validates :name, :login, :email, absence: true
end

Si desea estar seguro de que una asociacin est ausente, deber probar si el objeto
asociado est ausente y no la clave fornea utilizada para asignar la asociacin.

class LineItem < ActiveRecord::Base


belongs_to :order
validates :order, absence: true
end

Para validar los registros asociados cuyo absence es necesario, debe especificar la opcin:
inverse_of para la asociacin:

class Order < ActiveRecord::Base


has_many :line_items, inverse_of: :order
end

Si valida la ausencia de un objeto asociado a travs de una relacin has_one o has_many ,


comprobar que el objeto no est present? Ni marked_for_destruction? .

Desde false.present? Es falso, si desea validar la ausencia de un campo booleano que


debe utilizar un validador :field_name , exclusion: { in: [true, false] } .

62
2- Helpers de Validacin

El mensaje de error predeterminado es "must be blank".

2.11 uniqueness
Este ayudante valida que el valor del atributo es nico justo antes de que el objeto sea
guardado. No crea una restriccin de unicidad en la base de datos, por lo que puede
suceder que dos conexiones de base de datos diferentes creen dos registros con el mismo
valor para una columna que desea ser nico. Para evitarlo, debe crear un ndice nico en
ambas columnas de la base de datos. Consulte el manual de MySQL para obtener ms
detalles sobre varios ndices de columnas.

class Account < ActiveRecord::Base


validates :email, uniqueness: true
end

La validacin ocurre realizando una consulta SQL en la tabla del modelo, buscando un
registro existente con el mismo valor en ese atributo.

Hay una opcin de :scope que puede utilizar para especificar otros atributos que se
utilizan para limitar la comprobacin de unicidad:

class Holiday < ActiveRecord::Base


validates :name, uniqueness: { scope: :year,
message: "should happen once per year" }
end

Tambin hay una opcin: case_sensitive que puede utilizar para definir si la restriccin de
unicidad ser sensible a maysculas o minsculas. Esta opcin predeterminada es true .

class Person < ActiveRecord::Base


validates :name, uniqueness: { case_sensitive: false }
end

Tenga en cuenta que algunas bases de datos estn configuradas para realizar bsquedas
sin distincin entre maysculas y minsculas.

El mensaje de error predeterminado es "has already been taken".

2.12 validates_with
Este ayudante pasa el registro a una clase separada para su validacin.

63
2- Helpers de Validacin

class GoodnessValidator < ActiveModel::Validator


def validate(record)
if record.first_name == "Evil"
record.errors[:base] << "This person is evil"
end
end
end

class Person < ActiveRecord::Base


validates_with GoodnessValidator
end

Los errores aadidos a record.errors [:base] se refieren al estado del registro como un
todo y no a un atributo especfico.

El ayudante validates_with toma una clase, o una lista de clases para utilizar para la
validacin. No hay un mensaje de error predeterminado para validates_with . Debe
agregar manualmente errores a la coleccin de errores del registro en la clase validator .

Para implementar el mtodo validate , debe tener definido un parmetro record , que es
el registro a validar.

Como todas las dems validaciones, validates_with toma las opciones :if , :unless y
:on . Si pasa otras opciones, enviar las opciones a la clase validator como option :

class GoodnessValidator < ActiveModel::Validator


def validate(record)
if options[:fields].any?{|field| record.send(field) == "Evil" }
record.errors[:base] << "This person is evil"
end
end
end

class Person < ActiveRecord::Base


validates_with GoodnessValidator, fields: [:first_name, :last_name]
end

Tenga en cuenta que el validador se inicializar slo una vez para todo el ciclo de vida de la
aplicacin, y no en cada ejecucin de validacin, as que tenga cuidado al usar variables de
instancia dentro de ella.

Si su validador es lo suficientemente complejo como para que desee variables de instancia,


puede utilizar fcilmente un objeto Ruby antiguo

2.13 validates_each

64
2- Helpers de Validacin

Este ayudante valida atributos contra un bloque. No tiene una funcin de validacin
predefinida. Deberas crear uno usando un bloque, y cada atributo pasado a
validates_each ser probado. En el ejemplo siguiente, no queremos que los nombres y

apellidos comiencen con minsculas.

class Person < ActiveRecord::Base


validates_each :name, :surname do |record, attr, value|
record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
end
end

El bloque recibe el registro, el nombre del atributo y el valor del atributo. Puede hacer
cualquier cosa que desee para buscar datos vlidos dentro del bloque. Si la validacin falla,
debe agregar un mensaje de error al modelo, por lo que no es vlido.

65
3- Opciones Comunes de Validacin

3. Opciones Comunes de Validacin


Estas son las opciones comunes de validacin:

3.1 :allow_nil
La opcin: allow_nil omite la validacin cuando el valor que se valida es nil .

class Coffee < ActiveRecord::Base


validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }, allow_nil: true
end

3.2 :allow_blank
La opcin: allow_blank es similar a la opcin: allow_nil . Esta opcin permitir que la
validacin pase si el valor del atributo est en blank? , como nil o una cadena vaca por
ejemplo.

class Topic < ActiveRecord::Base


validates :title, length: { is: 5 }, allow_blank: true
end

Topic.create(title: "").valid? # => true


Topic.create(title: nil).valid? # => true

3.3 :message
Como ya lo ha visto, la opcin: message le permite especificar el mensaje que se agregar
a la coleccin de errores cuando la validacin falle. Cuando no se utiliza esta opcin, Active
Record utilizar el mensaje de error predeterminado respectivo para cada ayudante de
validacin.

3.4 :on
La opcin :on permite especificar cundo debe ocurrir la validacin. El comportamiento
predeterminado de todos los ayudantes de validacin incorporados se ejecutar en la
funcin Guardar (tanto cuando se crea un nuevo registro como cuando se est

66
3- Opciones Comunes de Validacin

actualizando). Si desea cambiarlo, puede utilizar on::create para ejecutar la validacin


slo cuando se crea un nuevo registro o on::update para ejecutar la validacin slo
cuando se actualice un registro.

class Person < ActiveRecord::Base


# it will be possible to update email with a duplicated value
validates :email, uniqueness: true, on: :create

# it will be possible to create the record with a non-numerical age


validates :age, numericality: true, on: :update

# the default (validates on both create and update)


validates :name, presence: true
end

67
4- Validaciones Estrictas

4. Validaciones Estrictas
Tambin puede especificar validaciones para que sean estrictas y ejecutar
ActiveModel::StrictValidationFailed cuando el objeto no es vlido.

class Person < ActiveRecord::Base


validates :name, presence: { strict: true }
end

Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank

Tambin existe la posibilidad de pasar una excepcin personalizada a la opcin :strict .

class Person < ActiveRecord::Base


validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end

Person.new.valid? # => TokenGenerationException: Token can't be blank

68
5- Validaciones Condicionales

5. Validaciones Condicionales
A veces tendr sentido validar un objeto slo cuando se cumpla un predicado dado. Puede
hacerlo usando las opciones :if y :unless , que pueden tomar un smbolo, una cadena,
un Proc o un Array. Puede utilizar la opcin :if si desea especificar cundo debe ocurrir la
validacin. Si desea especificar cundo no debera ocurrir la validacin, puede utilizar la
opcin :unless .

5.1 Uso de un smbolo :if y :unless


Puede asociar las opciones :if y :unless en las opciones que tengan un smbolo que
corresponda al nombre de un mtodo que se llamar justo antes de que ocurra la
validacin. Esta es la opcin ms comnmente utilizada.

class Order < ActiveRecord::Base


validates :card_number, presence: true, if: :paid_with_card?

def paid_with_card?
payment_type == "card"
end
end

5.2 Uso de una cadena con :if y :unless


Tambin puede utilizar una cadena que se evaluar mediante eval y debe contener cdigo
Ruby vlido. Debe utilizar esta opcin slo cuando la cadena representa una condicin muy
corta.

class Person < ActiveRecord::Base


validates :surname, presence: true, if: "name.nil?"
end

5.3 Usando un Proc con :if y :unless


Por ltimo, es posible asociar :if y :unless que con un objeto Proc que se llamar. El
uso de un objeto Proc le da la posibilidad de escribir una condicin en lnea en lugar de un
mtodo separado. Esta opcin es la ms adecuada para los "one-liners".

69
5- Validaciones Condicionales

class Account < ActiveRecord::Base


validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
end

5.4 Agrupacin de Validaciones condicionales


A veces es til tener varias validaciones a usar en una condicin, se puede lograr fcilmente
usando with_options .

class User < ActiveRecord::Base


with_options if: :is_admin? do |admin|
admin.validates :password, length: { minimum: 10 }
admin.validates :email, presence: true
end
end

Todas las validaciones dentro del bloque with_options habrn pasado automticamente la
condicin if: :is_admin?

5.5 Combinacin de condiciones de validacin


Por otro lado, cuando mltiples condiciones definen si una validacin debe ocurrir o no,
puede utilizarse un Array. Adems, puede aplicar ambos :if y :unless a la misma
validacin.

class Computer < ActiveRecord::Base


validates :mouse, presence: true,
if: ["market.retail?", :desktop?],
unless: Proc.new { |c| c.trackpad.present? }
end

La validacin slo se ejecuta cuando todas las condiciones: if y ninguno de los :unless
se evalen a true .

70
6- Realizacin de validaciones personalizadas

6. Realizacin de validaciones
personalizadas
Cuando los ayudantes de validacin incorporados no son suficientes para sus necesidades,
puede escribir sus propios validadores o mtodos de validacin como prefiera.

6.1 Validadores personalizados


Los validadores personalizados son clases que amplan ActiveModel::Validator . Estas
clases deben implementar un mtodo de validacin que toma un registro como un
argumento y realiza la validacin en l. El validador personalizado se llama utilizando el
mtodo validates_with .

class MyValidator < ActiveModel::Validator


def validate(record)
unless record.name.starts_with? 'X'
record.errors[:name] << 'Need a name starting with X please!'
end
end
end

class Person
include ActiveModel::Validations
validates_with MyValidator
end

La manera ms fcil de agregar validadores personalizados para validar atributos


individuales es con el conveniente ActiveModel::EachValidator . En este caso, la clase
validator personalizada debe implementar un mtodo validate_each que toma tres

argumentos: record , attribute y value . stos corresponden a la instancia, el atributo a


validar y el valor del atributo en la instancia pasada.

71
6- Realizacin de validaciones personalizadas

class EmailValidator < ActiveModel::EachValidator


def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end

class Person < ActiveRecord::Base


validates :email, presence: true, email: true
end

Como se muestra en el ejemplo, tambin puede combinar validaciones estndar con sus
propios validadores personalizados.

6.2 Mtodos personalizados


Tambin puede crear mtodos que verifiquen el estado de sus modelos y agreguen
mensajes a la coleccin de errores cuando no sean vlidos. A continuacin, debe registrar
estos mtodos utilizando el mtodo validate class , pasando los smbolos para los
nombres de los mtodos de validacin. Puede pasar ms de un smbolo para cada mtodo
de clase y las respectivas validaciones se ejecutarn en el mismo orden en que se
registraron.

class Invoice < ActiveRecord::Base


validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value

def expiration_date_cannot_be_in_the_past
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "can't be in the past")
end
end

def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end

De forma predeterminada, dichas validaciones se ejecutarn cada vez que se llame


valid? . Tambin es posible controlar cundo ejecutar estas validaciones personalizadas si

se da una opcin :on al mtodo validate , con :create o :update

72
6- Realizacin de validaciones personalizadas

class Invoice < ActiveRecord::Base


validate :active_customer, on: :create

def active_customer
errors.add(:customer_id, "is not active") unless customer.active?
end
end

73
7- Trabajar con errores de validacin

7. Trabajar con errores de validacin


Adems de los Mtodos valid? e invalid? cubiertos anteriormente, Rails proporciona
una serie de mtodos para trabajar con la coleccin de errores y preguntar sobre la validez
de los objetos.

La siguiente es una lista de los mtodos ms utilizados. Consulte la documentacin de


ActiveModel::Errors para obtener una lista de todos los mtodos disponibles.

7.1 errors
Devuelve una instancia de la clase ActiveModel::Errors que contiene todos los errores.
Cada clave es el nombre del atributo y el valor es una matriz de cadenas con todos los
errores.

class Person < ActiveRecord::Base


validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors.messages
# => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}

person = Person.new(name: "John Doe")


person.valid? # => true
person.errors.messages # => {}

7.2 errors[ ]
errors[ ] se utiliza cuando se desea comprobar los mensajes de error de un atributo

especfico. Devuelve una matriz de cadenas con todos los mensajes de error para el
atributo dado, cada cadena con un mensaje de error. Si no hay errores relacionados con el
atributo, devuelve una matriz vaca.

74
7- Trabajar con errores de validacin

class Person < ActiveRecord::Base


validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new(name: "John Doe")


person.valid? # => true
person.errors[:name] # => []

person = Person.new(name: "JD")


person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]

person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]

7.3 errors.add
El mtodo add le permite agregar manualmente mensajes relacionados con atributos
particulares. Puede utilizar los mtodos errors.full_messages o errors.to_a para ver los
mensajes en la forma en que se pueden mostrar a un usuario. Esos mensajes particulares
obtienen el nombre de atributo prependido (y capitalizado). add recibe el nombre del
atributo al que desea agregar el mensaje y el propio mensaje.

class Person < ActiveRecord::Base


def a_method_used_for_validation_purposes
errors.add(:name, "cannot contain the characters !@#%*()_-+=")
end
end

person = Person.create(name: "!@#")

person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]

person.errors.full_messages
# => ["Name cannot contain the characters !@#%*()_-+="]

Otra forma de hacerlo es utilizando [] = setter

75
7- Trabajar con errores de validacin

class Person < ActiveRecord::Base


def a_method_used_for_validation_purposes
errors[:name] = "cannot contain the characters !@#%*()_-+="
end
end

person = Person.create(name: "!@#")

person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]

person.errors.to_a
# => ["Name cannot contain the characters !@#%*()_-+="]

7.4 errors[: base]


Puede agregar mensajes de error relacionados con el estado del objeto como un todo, en
lugar de estar relacionado con un atributo especfico. Puede utilizar este mtodo cuando
desea decir que el objeto no es vlido, independientemente de los valores de sus atributos.
Como errors[: base] es un array, simplemente puede agregar una cadena a ella y se
utilizar como un mensaje de error.

class Person < ActiveRecord::Base


def a_method_used_for_validation_purposes
errors[:base] << "This person is invalid because ..."
end
end

7.5 errors.clear
El mtodo clear se utiliza cuando intencionalmente desea borrar todos los mensajes de la
coleccin de errores. Por supuesto, llamar a errors.clear sobre un objeto no vlido en
realidad no lo har valid: la coleccin de errores ahora estar vaca, pero la prxima vez
que llame valid? O cualquier mtodo que intente guardar este objeto en la base de datos,
las validaciones se ejecutarn de nuevo. Si alguna de las validaciones falla, la coleccin de
errores se rellenar de nuevo.

76
7- Trabajar con errores de validacin

class Person < ActiveRecord::Base


validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]

person.errors.clear
person.errors.empty? # => true

p.save # => false

p.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]

7.6 errors.size
El mtodo size devuelve el nmero total de mensajes de error para el objeto.

class Person < ActiveRecord::Base


validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors.size # => 2

person = Person.new(name: "Andrea", email: "andrea@example.com")


person.valid? # => true
person.errors.size # => 0

77
8- Visualizacin de errores de validacin en vistas

8. Visualizacin de errores de validacin


en vistas
Una vez que haya creado un modelo y agregado validaciones, si ese modelo se crea
mediante un formulario web, probablemente desee mostrar un mensaje de error cuando una
de las validaciones falla.

Debido a que cada aplicacin maneja este tipo de cosas de manera diferente, Rails no
incluye ningn ayudante de vista que le ayude a generar estos mensajes directamente. Sin
embargo, debido al gran nmero de mtodos que Rails le ofrece para interactuar con las
validaciones en general, es bastante fcil de construir su propio helper. Adems, al generar
un scaffold, Rails colocar algn ERB en el _form.html.erb que muestra la lista completa
de errores en ese modelo.

Asumiendo que tenemos un modelo que se ha guardado en una variable de instancia


llamada @article , se ve as:

<% if @article.errors.any? %>


<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from
being saved:</h2>

<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

Adems, si utiliza los ayudantes de formulario de Rails para generar sus formularios,
cuando se produce un error de validacin en un campo, generar un <div> adicional
alrededor de la entrada.

<div class="field_with_errors">
<input id="article_title" name="article[title]" size="30" type="text" value="">
</div>

A continuacin, puede generar el estilo para este div como quiera. El scaffold que Rails
genera por defecto, por ejemplo, agrega esta regla CSS:

78
8- Visualizacin de errores de validacin en vistas

.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}

Esto significa que cualquier campo con un error termina con un borde rojo de 2 pxeles.

79
IV- Callbacks en Active Record

IV- Callbacks en Active Record


Esta gua te ensear como mantener el ciclo de vida de tus objetos Active Record.
Despus de leer esta gua conocers:

El ciclo de vida de los objetos Active Record.


Como crear mtodos callback que respondan a eventos en el ciclo de vida del objeto.
Como crear clases especiales para encapsular el comportamiento comn para tus
callbacks.

80
1- El Ciclo de Vida del Objeto

1. El Ciclo de Vida del Objeto


Durante la operativa normal de una aplicacin Rails, los objetos pueden ser creados,
actualizados, y destrudos. Active Record provee disparadores dentro del ciclo de vida del
objeto para que con ellos puedas controlar tu aplicacin y sus datos.

Los Callbacks o retro-llamadas, te posibilitan disparar lgica antes o despus de la


alteracin del estado de un objeto.

81
2- Un vistazo a los Callbacks

2. Un vistazo a los Callbacks


Los Callbacks son mtodos que son llamados en ciertos momentos del ciclo de vida de un
objeto. Con los callbacks es posible escribir cdigo que se ejecutar siempre que un objeto
Active Record es creado, guardado, actualizado, borrado, validado o cargado desde la base
de datos.

2.1 Registro de Callbacks


Con el objeto de utilizar algunos callbacks disponibles, necesitas registrarlos. Puedes
implementar los callbacks como cualquier mtodo normal y utilizar un mtodo de clase
macro-style para registrarlos como callbacks:

class User < ActiveRecord::Base


validates :login, :email, presence: true

before_validation :ensure_login_has_a_value

protected
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end

Los mtodos de clase macro-style pueden tambin recibir un bloque. Considera utilizar este
estilo si el cdigo dentro de tu bloque est utilizando es tan corto que llevar una sola lnea:

82
2- Un vistazo a los Callbacks

class User < ActiveRecord::Base


validates :login, :email, presence: true

before_create do
self.name = login.capitalize if name.blank?
end
end

class User < ActiveRecord::Base


before_validation :normalize_name, on: :create

# :on takes an array as well


after_validation :set_location, on: [ :create, :update ]

protected
def normalize_name
self.name = self.name.downcase.titleize
end

def set_location
self.location = LocationService.query(self)
end
end

Est considerada una buena prctica declarar los mtodos callback como privados o
protegidos. Si los dejas pblicos, pueden ser llamados desde fuera del modelo y violar el
principio de encapsular del objeto.

83
3- Callbacks Disponibles

3. Callbacks Disponibles
Aqu hay una lista con todos los callbacks Active Record, listadas en el mismo orden en el
cual sern llamados durante la ejecucin de las respectivas operaciones:

3.1 Creando un Objeto

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback

3.2 Actualizando un Objecto

before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback

3.3 Destruyendo un Objecto

before_destroy
around_destroy
after_destroy
after_commit/after_rollback

after_save se ejecuta tanto en la creacin como en la actualizacin, pero siempre after

los callbacks ms especficos after_create y after_update , no cambia el orden en el cual


las llamadas macro fueron ejecutadas.

84
3- Callbacks Disponibles

3.4 after_initialize y after_find


El callbak after_initialize ser llamado siempre que un objeto Active Record es
instanciado, tanto por el uso directo de new o cuando un objeto es cargado desde la base
de datos. Esto puede ser de utilidad cuando se tiene la necesidad de directamente
sobrescribir un mtodo Active Record initialize .

El callback after_find ser llamado siempre que Active Record cargue un registro desde
la base de datos. after_find es llamado antes de after_initialize si ambos estn
definidos.

Los callbacks after_initialize y after_find no tienen contrapartidas before_* , pero


ellos pueden ser registrados como cualquier otro callback Active Record.

class User < ActiveRecord::Base


after_initialize do |user|
puts "You have initialized an object!"
end

after_find do |user|
puts "You have found an object!"
end
end

>> User.new
You have initialized an object!
=> #<User id: nil>

>> User.first
You have found an object!
You have initialized an object!
=> #<User id: 1>

3.5 after_touch
El callback after_touch ser llamado si un objeto Active Record es tocado.

85
3- Callbacks Disponibles

class User < ActiveRecord::Base


after_touch do |user|
puts "Has tocado un objeto"
end
end

>> u = User.create(name: 'Kuldeep')


=> #<User id: 1, name: "Kuldeep", created_at: "2013-11-25 12:17:49", updated_at: "2013
-11-25 12:17:49">

>> u.touch
Has tocado un objeto
=> true

Este puede ser utilizado junto con belongs_to :

class Employee < ActiveRecord::Base


belongs_to :company, touch: true
after_touch do
puts 'Un empleado fue tocado'
end
end

class Company < ActiveRecord::Base


has_many :employees
after_touch :log_when_employees_or_company_touched
private
def log_when_employees_or_company_touched
puts 'Employee/Company fueron tocados'
end
end

>> @employee = Employee.last


=> #<Employee id: 1, company_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "20
13-11-25 17:05:05">

# triggers @employee.company.touch
>> @employee.touch
Employee/Company fueron tocados
Un empleado fue tocado
=> true

86
4- Ejecutando Callbacks

4. Ejecutando Callbacks
Los siguientes mtodos disparan callbacks:

create
create!
decrement!
destroy
destroy!
destroy_all
increment!
save
save!
save(validate: false)
toggle!
update_attribute
update
update!
valid?

Adicionalmente, el callback after_find es disparado por los siguientes mtodos de


bsqueda:

all
first
find
find_by
find_by_*
find_by_*!
find_by_sql
last

El callback after_initialize es lanzado cada vez que un nuevo objeto de la clase es


instanciado.

Los mtodos find_by_* y find_by_*! son finders dinmicos generados automticamente


para todos los atributos. Aprende ms acerca de ellos en la Seccin de finders dinmicos

87
5- Saltando Callbacks

5. Saltando Callbacks
As como con las validaciones, es tambin posible saltarse callbacks por el uso de los
siguientes mtodos:

decrement
decrement_counter
delete
delete_all
increment
increment_counter
toggle
touch
update_column
update_columns
update_all
update_counters

Estos mtodos deben ser utilizados con precaucin, porque importantes reglas de negocio y
lgica de la aplicacin pueden ser mantenidas en los callbacks. Pasar por encima de ellas
sin entender las implicaciones potenciales podran dejar invlidos los datos.

88
6- Parando la Ejecucin

6. Parando la Ejecucin
Como comienzas registrando nuevos callbaks para tus modelos, ellos sern encolados para
su ejecucin. Esta cola incluir toda la validacin de tu modelo, los callbaks registrados, y la
operacin en la base de datos que ser ejecutada.

La cadena entera de callbaks es envuelta en una transaccin. Si algn mtodo callback


before retorna exactamente false o lanza una excepcin, la cadena de ejecucin se

detiene y un hace ROLLBACK ; los callbacks after solo pueden llevar esto a cabo lanzando
una excepcin.

Alguna excepcin que no es ActiveRecord::Rollback ser relanzada por Rails despus de


la que la cadena de callbacks es detenida. Lanzando una excepcin como
ActiveRecord::Rollback puede romper el cdigo de manera que no espere que mtodos

como save y update_attributes (los cuales normalmente intentan retornar true o


false ) para lanzar una excepcin.

89
7- Callbacks Relacionados

7. Callbacks Relacionados
Los Callbacks trabajan a travs del modelo de relaciones, y pueden tambin ser definidos
por ellos. Supn un ejemplo donde un usuario tiene muchos artculos. Un artculo de
usuario debera ser borrado si el usuario es borrado. Vamos a aadir un callback
after_destroy al modelo User de esta relacin con el modelo Article :

class User < ActiveRecord::Base


has_many :articles, dependent: :destroy
end

class Article < ActiveRecord::Base


after_destroy :log_destroy_action

def log_destroy_action
puts 'Artculo borrado'
end
end

>> user = User.first


=> #<User id: 1>
>> user.articles.create!
=> #<Article id: 1, user_id: 1>
>> user.destroy
Artculo borrado
=> #<User id: 1>

90
8- Callbacks Condicionales

8. Callbacks Condicionales
As como las validaciones, podemos tambin crear la llamada de un mtodo condicional
sobre la satisfaccin de un predicado dado. Tambin podemos hacer esto utilizando las
opciones :if y :unless , las cuales pueden tomar un smbolo, un string, un Proc o un
Array. Puedes utilizar la opcin :if cuando quieras especificar bajo que condiciones el
callback debera ser llamado. Si quieres especificar las condiciones bajo las cuales el
callback no debera ser llamado, podras utilizar la opcin :unless .

8.1 Utilizando :if y :unless con un Smbolo


Puedes asociar la opciones :if y :unless con un smbolo correspondiendo con el
nombre del mtodo predicado que ser llamado correctamente antes del callback. Cuando
usamos la opcin :if , el callback no se ejecutar si el mtodo predicado retorna false ;
cuando utilizamos la opcin :unless , el callback no ser ejecutado si el mtodo predicado
retorna true . Esta es la opcin ms comn. Utilizando esta forma de registrarlos tambin
es posible registrar varios predicados diferentes que deberan ser llamados para comprobar
si el callback debera ser ejecutado.

class Order < ActiveRecord::Base


before_save :normalize_card_number, if: :paid_with_card?
end

8.2 Utilizando :if y :unless como un String


Tambin puedes utilizar un string que ser evaluado que ser evaluado utilizando eval y
por lo tanto necesita contener cdigo Ruby vlido. Deberas utilizar esta opcin solo cuando
el string representa una condicin realmente corta:

class Order < ActiveRecord::Base


before_save :normalize_card_number, if: "paid_with_card?"
end

8.3 Utilizando :if y :unless con un Proc


Finalmente, esto es posible para asociar :if y :unless con un objeto Proc. Esta opcin
es la mejor combinacin cuando escribimos mtodos cortos para validar, normalmente una
lnea:

91
8- Callbacks Condicionales

class Order < ActiveRecord::Base


before_save :normalize_card_number,
if: Proc.new { |order| order.paid_with_card? }
end

8.4 Condiciones Multiples para Callbacks


Cuando escribimos callbacks condicionales, podemos tambin mezclar ambos :if y
:unless en la misma declaracin callback:

class Comment < ActiveRecord::Base


after_create :send_email_to_author, if: :author_wants_emails?,
unless: Proc.new { |comment| comment.article.ignore_comments? }
end

92
9- Clases Callbacks

9. Clases Callbacks
Algunas veces los mtodos callback que escribirs sern lo suficientemente tiles como
para ser reutilizados por otros modelos. Active Record hace posible crear clases que
encapsulen mtodos callbacks, entonces se vuelve muy sencillo reutilizarlos.

Aqu un ejemplo donde creamos una clase con un callback after_destroy para un modelo
PictureFile :

class PictureFileCallbacks
def after_destroy(picture_file)
if File.exist?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end

Cuando declaramos dentro de una clase, como la de arriba, los mtodos callback recibirn
el objeto del modelo como un parmetro. Ahora podemos utilizar la clase callback en el
modelo:

class PictureFile < ActiveRecord::Base


after_destroy PictureFileCallbacks.new
end

Nota que necesitamos instanciar un nuevo objeto PictureFileCallbacks , por haber


declarado nuestro callback como un mtodo de instancia. Esto es particularmente til
si los callbacks hacen uso del estado del objeto instanciado. Frecuentemente, sin
embargo, tendr ms sentido para declarar los callbacks como mtodos de clase:

class PictureFileCallbacks
def self.after_destroy(picture_file)
if File.exist?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end

Si el mtodo callback es declarado de esta manera, no ser necesario instanciar un objeto


PictureFileCallbacks.

93
9- Clases Callbacks

class PictureFile < ActiveRecord::Base


after_destroy PictureFileCallbacks
end

Puedes declarar tantos callbacks como quieras dentro de tu clase callback.

94
10- Callbacks de Transacciones

10. Callbacks de Transacciones


Hay dos callbacks adicionales que sern disparados para completar una transaccin de
base de datos: after_commit y after_rollback . Estos callbacks son muy parecidos al
callback after_save excepto que estos no se ejecutan hasta despus de los cambios en la
base de datos hayan sido commiteados o revertidos. Son muy tiles cuando tus modelos
active record necesitan interactuar con sistemas externos los cuales no son parte de una
transaccin de la base de datos.

Considera, por ejemplo, los ejemplos previos donde el modelo PictureFile necesita borrar
un fichero despus de que el registro correspondiente es destrudo. Si por algo se lanzase
una excepcin despus del callback after_destroy y la transaccin se revirtiera, el fichero
se habr borrado y dejar el modelo en un estado inconsistente. Por ejemplo, suponiendo
que picture_file_2 en el cdigo de abajo no es vlido y el mtodo save! arroja un error.

PictureFile.transaction do
picture_file_1.destroy
picture_file_2.save!
end

Utilizando el callback after_commit podemos solucionar este caso.

class PictureFile < ActiveRecord::Base


after_commit :delete_picture_file_from_disk, on: [:destroy]

def delete_picture_file_from_disk
if File.exist?(filepath)
File.delete(filepath)
end
end
end

La opcin :on especifica cuando un callback ser disparado. Si tu no escribes la opcin


:on el callback ser disparado en todas las acciones.

Los callbacks after_commit y after_rollback nos garantizan que sern llamados por
todos los modelos creados, actualizados, o destudos, dentro de un bloque de transaccin.
Si alguna excepcin es lanzada dentro de estos callbacks, sern ignorados y entonces no
interfieren con otros callbacks. As como tambin, si tu cdigo callback puede lanzar una
excepcin, necesitars rescatarla y manejarla apropiadamente dentro del callback.

95
10- Callbacks de Transacciones

96
V- Asociaciones en Active Record

V- Asociaciones en Active Record


Esta gua cubre las caractersticas de las asociaciones en Active Record. Despus de leer
esta gua, sabr:

Cmo declarar asociaciones entre modelos Active Record.


Cmo entender los diversos tipos de asociaciones Active Record.
Cmo utilizar los mtodos agregados a sus modelos creando asociaciones.

97
1- Por qu asociaciones?

1. Por qu asociaciones?
En Rails, una asociacin es una conexin entre dos modelos Active Record. Por qu
necesitamos las asociaciones entre modelos? Porque hacen las operaciones comunes ms
simples y ms fciles en su cdigo. Por ejemplo, considere una sencilla aplicacin de Rails
que incluye un modelo para autores y un modelo para libros. Cada autor puede tener
muchos libros. Sin asociaciones, las declaraciones del modelo se veran as:

class Author < ApplicationRecord


end

class Book < ApplicationRecord


end

Ahora, supongamos que queremos agregar un nuevo libro para un autor existente.
Necesitamos hacer algo como esto:

@book = Book.create(published_at: Time.now, author_id: @author.id)

O considere eliminar un autor y asegurarse de que todos sus libros tambin se eliminen:

@books = Book.where(author_id: @author.id)


@books.each do |book|
book.destroy
end
@author.destroy

Con las asociaciones Active Record, podemos agilizar estas y otras operaciones declarando
a Rails que hay una conexin entre los dos modelos. Aqu est el cdigo revisado para la
creacin de autores y libros:

class Author < ApplicationRecord


has_many :books, dependent: :destroy
end

class Book < ApplicationRecord


belongs_to :author
end

Con este cambio, crear un nuevo libro para un autor en particular es ms fcil:

98
1- Por qu asociaciones?

@book = @author.books.create(published_at: Time.now)

Eliminar un autor y todos sus libros es mucho ms fcil:

@author.destroy

Para obtener ms informacin sobre los diferentes tipos de asociaciones, lea la siguiente
seccin de esta gua. A continuacin, algunos consejos y trucos para trabajar con
asociaciones y, a continuacin, una referencia completa a los mtodos y opciones para las
asociaciones en Rails.

99
2- Tipos de Asociaciones

2. Tipos de Asociaciones
Rails soporta seis tipos de asociaciones:

belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many

Las asociaciones se implementan mediante llamadas de macro-estilo, de modo que puede


aadir declarativamente caractersticas a sus modelos. Por ejemplo, al declarar que un
modelo pertenece a otro, instruye a Rails para que mantenga la informacin de la Primary
Key - Foreign Key entre instancias de los dos modelos, y tambin obtiene una serie de
mtodos de utilidad agregados a su modelo.

En el resto de esta gua, aprender a declarar y utilizar las diversas formas de asociaciones.
Pero primero, una introduccin rpida a las situaciones donde cada tipo de asociacin es
apropiado.

2.1 La asociacin belongs_to


Una asociacin belongs_to establece una conexin uno a uno con otro modelo, de modo
que cada instancia del modelo declarante "pertenece a" una instancia del otro modelo. Por
ejemplo, si su aplicacin incluye autores y libros, y cada libro se puede asignar a
exactamente un autor, declarara el modelo de libro de esta manera:

class Book < ApplicationRecord


belongs_to :author
end

Las asociaciones belongs_to deben utilizar el trmino singular. Si usa el formulario


pluralizado en el ejemplo anterior para la asociacin de autores en el modelo de Book, se le
dira que hay una constante no inicializada uninitialized constant Book::Authors . Esto se
debe a que Rails infiere automticamente el nombre de la clase del nombre de la
asociacin. Si el nombre de la asociacin se pluraliza errneamente, entonces la clase
inferida tambin se pluralizar errneamente.

La migracin correspondiente podra tener este aspecto:

100
2- Tipos de Asociaciones

class CreateBooks < ActiveRecord::Migration[5.0]


def change
create_table :authors do |t|
t.string :name
t.timestamps
end
create_table :books do |t|
t.belongs_to :author, index: true
t.datetime :published_at
t.timestamps
end
end
end

2.2 La asociacin has_one


Una asociacin has_one tambin establece una conexin uno a uno con otro modelo, pero
con una semntica algo diferente (y consecuencias diferentes). Esta asociacin indica que
cada instancia de un modelo contiene o posee una instancia de otro modelo. Por ejemplo, si
cada proveedor de su aplicacin tiene una sola cuenta, declarara el modelo de proveedor
de la siguiente manera:

class Supplier < ApplicationRecord


has_one :account
end

La migracin correspondiente podra tener este aspecto:

class CreateSuppliers < ActiveRecord::Migration[5.0]


def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end

create_table :accounts do |t|


t.belongs_to :supplier, index: true
t.string :account_number
t.timestamps
end
end
end

Dependiendo del caso de uso, tambin puede ser necesario crear un ndice nico y / o una
restriccin de clave externa en la columna proveedor para la tabla de cuentas. En este
caso, la definicin de columna podra tener este aspecto:

101
2- Tipos de Asociaciones

create_table :accounts do |t|


t.belongs_to :supplier, index: { unique: true }, foreign_key: true
# ...
end

2.3 La asociacin has_many


Una asociacin has_many indica una conexin uno-a-muchos con otro modelo. A menudo
encontrar esta asociacin en el "otro lado" de una asociacin belongs_to . Esta asociacin
indica que cada instancia del modelo tiene cero o ms instancias de otro modelo. Por
ejemplo, en una aplicacin que contiene autores y libros, el modelo de autor podra ser
declarado as:

class Author < ApplicationRecord


has_many :books
end

El nombre del otro modelo se pluraliza cuando se declara una asociacin has_many .

La migracin correspondiente podra tener este aspecto:

class CreateAuthors < ActiveRecord::Migration[5.0]


def change
create_table :authors do |t|
t.string :name
t.timestamps
end
create_table :books do |t|
t.belongs_to :author, index: true
t.datetime :published_at
t.timestamps
end
end
end

2.4 La asociacin has_many :through


La asociacin has_many :through se utiliza a menudo para establecer una conexin
muchos-a-muchos con otro modelo. Esta asociacin indica que el modelo de declaracin
puede coincidir con cero o ms instancias de otro modelo, procediendo a travs de un
tercer modelo. Por ejemplo, considere una prctica mdica donde los pacientes hacen citas
para ver a los mdicos. Las declaraciones de asociacin pertinentes podran tener este
aspecto:

102
2- Tipos de Asociaciones

class Physician < ApplicationRecord


has_many :appointments
has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord


belongs_to :physician
belongs_to :patient
end

class Patient < ApplicationRecord


has_many :appointments
has_many :physicians, through: :appointments
end

La migracin correspondiente podra tener este aspecto:

class CreateAppointments < ActiveRecord::Migration[5.0]


def change
create_table :physicians do |t|
t.string :name
t.timestamps
end

create_table :patients do |t|


t.string :name
t.timestamps
end

create_table :appointments do |t|


t.belongs_to :physician, index: true
t.belongs_to :patient, index: true
t.datetime :appointment_date
t.timestamps
end
end
end

La coleccin de modelos de unin (join) se puede gestionar a travs de los mtodos de


asociacin has_many . Por ejemplo, si asigna:

physician.patients = patients

A continuacin, los nuevos modelos "Join" se crean automticamente para los objetos
recin asociados. Si algunas de las que ya existan anteriormente faltan, sus filas "Join" se
eliminan automticamente.

103
2- Tipos de Asociaciones

La eliminacin automtica de los modelos de combinacin es directa, no destruye los


callbacks disparados

La asociacin has_many :through tambin es til para configurar "accesos directos -


shortcuts" a travs de asociaciones anidadas (nested) has_many . Por ejemplo, si un
documento tiene muchas secciones y una seccin tiene muchos prrafos, a veces puede
que desee obtener una coleccin simple de todos los prrafos del documento. Usted podra
configurarlo de esta manera:

class Document < ApplicationRecord


has_many :sections
has_many :paragraphs, through: :sections
end

class Section < ApplicationRecord


belongs_to :document
has_many :paragraphs
end

class Paragraph < ApplicationRecord


belongs_to :section
end

Con through: especificando :sections , Rails ahora entender:

@document.paragraphs

2.5 La asociacin has_one :through


Una asociacin has_one :through crea una conexin uno a uno con otro modelo. Esta
asociacin indica que el modelo de declaracin puede coincidir con una instancia de otro
modelo procediendo a travs de un tercer modelo. Por ejemplo, si cada proveedor tiene una
cuenta y cada cuenta est asociada con un historial de cuenta, el modelo de proveedor
podra tener este aspecto:

104
2- Tipos de Asociaciones

class Supplier < ApplicationRecord


has_one :account
has_one :account_history, through: :account
end

class Account < ApplicationRecord


belongs_to :supplier
has_one :account_history
end

class AccountHistory < ApplicationRecord


belongs_to :account
end

La migracin correspondiente podra tener este aspecto:

class CreateAccountHistories < ActiveRecord::Migration[5.0]


def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end

create_table :accounts do |t|


t.belongs_to :supplier, index: true
t.string :account_number
t.timestamps
end

create_table :account_histories do |t|


t.belongs_to :account, index: true
t.integer :credit_rating
t.timestamps
end
end
end

2.6 La asociacin has_and_belongs_to_many


Una asociacin has_and_belongs_to_many crea una conexin directa many-to-many con otro
modelo, sin modelo intermedio. Por ejemplo, si su aplicacin incluye ensamblajes y partes,
con cada ensamblaje teniendo muchas partes y cada parte apareciendo en muchos
ensamblajes, podra declarar los modelos de esta manera:

105
2- Tipos de Asociaciones

class Assembly < ApplicationRecord


has_and_belongs_to_many :parts
end

class Part < ApplicationRecord


has_and_belongs_to_many :assemblies
end

La migracin correspondiente podra tener este aspecto:

class CreateAssembliesAndParts < ActiveRecord::Migration[5.0]


def change
create_table :assemblies do |t|
t.string :name
t.timestamps
end
create_table :parts do |t|
t.string :part_number
t.timestamps
end

create_table :assemblies_parts, id: false do |t|


t.belongs_to :assembly, index: true
t.belongs_to :part, index: true
end
end
end

2.7 Elegir entre belongs_to y has_one


Si desea establecer una relacin de uno a uno entre dos modelos, deber agregar
belongs_to a uno y has_one al otro. Cmo sabes cul es cul?

La distincin est en donde se coloca la clave fornea (va en la tabla para la clase que
declara la asociacin belongs_to ), pero debe reflexionar tambin sobre el significado real
de los datos. La relacin has_one dice que uno de algo es tuyo, es decir, que algo te indica
algo. Por ejemplo, tiene ms sentido decir que un proveedor posee una cuenta que una
cuenta posee un proveedor. Esto sugiere que las relaciones correctas son as:

class Supplier < ApplicationRecord


has_one :account
end

class Account < ApplicationRecord


belongs_to :supplier
end

106
2- Tipos de Asociaciones

La migracin correspondiente podra tener este aspecto:

class CreateSuppliers < ActiveRecord::Migration[5.0]


def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.integer :supplier_id
t.string :account_number
t.timestamps
end

add_index :accounts, :supplier_id


end
end

El uso de t.integer :supplier_id hace que el nombre de clave externa sea obvio y
explcito. En las versiones actuales de Rails, puede abstraer este detalle de implementacin
utilizando t.references :supplier .

2.8 Elegir entre has_many: through y


has_and_belongs_to_many
Rails ofrece dos formas diferentes de declarar una relacin de muchos a muchos entre
modelos. La forma ms sencilla es usar has_and_belongs_to_many , lo que le permite hacer la
asociacin directamente:

class Assembly < ApplicationRecord


has_and_belongs_to_many :parts
end

class Part < ApplicationRecord


has_and_belongs_to_many :assemblies
end

La segunda forma de declarar una relacin muchos-a-muchos es usar has_many :through .


Esto hace la asociacin indirectamente, a travs de un modelo de unin:

107
2- Tipos de Asociaciones

class Assembly < ApplicationRecord


has_many :manifests
has_many :parts, through: :manifests
end

class Manifest < ApplicationRecord


belongs_to :assembly
belongs_to :part
end

class Part < ApplicationRecord


has_many :manifests
has_many :assemblies, through: :manifests
end

La regla ms sencilla es que debe establecer una relacin has_many :through si necesita
trabajar con el modelo de relacin como una entidad independiente. Si no necesita hacer
nada con el modelo de relacin, puede ser ms sencillo configurar una relacin
has_and_belongs_to_many (aunque deber recordar crear la "join table" en la base de datos).

Debe utilizar has_many: :through si necesita validaciones, callbacks o atributos adicionales


en el modelo de union.

2.9 Asociaciones polimrficas


Un giro un poco ms avanzado en las asociaciones es la asociacin polimrfica. Con
asociaciones polimrficas, un modelo puede pertenecer a ms de un modelo, en una sola
asociacin. Por ejemplo, es posible que tenga un modelo "imagen" que pertenezca a un
modelo de empleado o un modelo de producto. He aqu cmo se podra declarar esto:

class Picture < ApplicationRecord


belongs_to :imageable, polymorphic: true
end

class Employee < ApplicationRecord


has_many :pictures, as: :imageable
end

class Product < ApplicationRecord


has_many :pictures, as: :imageable
end

Puede pensar en una declaracin polimrfica de belongs_to como configurar una interfaz
que cualquier otro modelo puede usar. Desde una instancia del modelo Employee , puede
recuperar una coleccin de imgenes: @employee.pictures .

108
2- Tipos de Asociaciones

Del mismo modo, puede recuperar @product.pictures .

Si tiene una instancia del modelo de imagen, puede llegar a su padre a travs de
@picture.imageable . Para que esto funcione, debe declarar tanto una columna de clave

foranea como una columna "type" en el modelo que declare la interfaz polimrfica:

class CreatePictures < ActiveRecord::Migration[5.0]


def change
create_table :pictures do |t|
t.string :name
t.integer :imageable_id
t.string :imageable_type
t.timestamps
end

add_index :pictures, [:imageable_type, :imageable_id]


end
end

Esta migracin puede simplificarse mediante el uso de la forma t.references :

class CreatePictures < ActiveRecord::Migration[5.0]


def change
create_table :pictures do |t|
t.string :name
t.references :imageable, polymorphic: true, index: true
t.timestamps
end
end
end

2.10 Self Joins


En el diseo de un modelo de datos, a veces se encuentra un modelo que debe tener una
relacin con s mismo. Por ejemplo, es posible que desee almacenar todos los empleados
en un nico modelo de base de datos, pero pueda rastrear las relaciones, como por ejemplo
entre el administrador y los subordinados. Esta situacin puede ser modelada con
asociaciones Self Joins:

class Employee < ApplicationRecord


has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"

belongs_to :manager, class_name: "Employee"


end

109
2- Tipos de Asociaciones

Con esta configuracin, puede recuperar @employee.subordinates y @employee.manager .

En sus migraciones / esquema, agregar una columna de referencias al propio modelo.

class CreateEmployees < ActiveRecord::Migration[5.0]


def change
create_table :employees do |t|
t.references :manager, index: true
t.timestamps
end
end
end

110
3- Consejos, Trucos y Advertencias

3. Consejos, Trucos y Advertencias


Estas son algunas de las cosas que debe saber para hacer un uso eficiente de las
asociaciones Active Record en sus aplicaciones de Rails:

Control del almacenamiento en cach


Evitar colisiones de nombres
Actualizacin del esquema
Controlar el alcance de la asociacin
Asociaciones bidireccionales

3.1 Control del almacenamiento en cach


Todos los mtodos de asociacin se construyen alrededor del almacenamiento en cach, lo
que mantiene el resultado de la consulta ms reciente disponible para operaciones
adicionales. La cach se comparte incluso entre los mtodos. Por ejemplo:

author.books # recupera los libros de la base de datos


author.books.size # utiliza la copia en cach de libros
author.books.empty? # utiliza la copia en cach de libros

Pero qu pasa si desea volver a cargar la cach, porque los datos podran haber sido
cambiados por alguna otra parte de la aplicacin? Simplemente llame a "reload" en la
asociacin:

author.books # recupera los libros de la base de datos


author.books.size # utiliza la copia en cach de libros
author.books.reload.empty? # Descarta la copia en cach de libros
# Y vuelve a la base de datos

3.2 Evitar las colisiones de nombres


No eres libre de usar cualquier nombre para tus asociaciones. Debido a que crear una
asociacin agrega un mtodo con ese nombre al modelo, es una mala idea darle a una
asociacin un nombre que ya est utilizado para un mtodo de instancia de
ActiveRecord::Base . El mtodo de asociacin invalidara el mtodo base y rompera las

cosas. Por ejemplo, attributes o connection son malos nombres para las asociaciones.

3.3 Actualizacin del esquema

111
3- Consejos, Trucos y Advertencias

Las asociaciones son extremadamente tiles, pero no son mgicas. Usted es responsable
de mantener el esquema de la base de datos para que coincida con sus asociaciones. En la
prctica, esto significa dos cosas, dependiendo del tipo de asociaciones que est creando.
Para asociaciones belongs_to es necesario crear claves forneas y para asociaciones
has_and_belongs_to_many es necesario crear la tabla de unin (join table) adecuada.

3.3.1 Creacin de claves forneas para las asociaciones


belongs_to
Cuando declara una asociacin belongs_to , debe crear claves forneas segn
corresponda. Por ejemplo, considere este modelo:

class Book < ApplicationRecord


belongs_to :author
end

Esta declaracin debe ser respaldada por la declaracin de clave fornea adecuada en la
tabla de libros:

class CreateBooks < ActiveRecord::Migration[5.0]


def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end
end
end

Si crea una asociacin algn tiempo despus de crear el modelo subyacente, debe recordar
crear una migracin add_column para proporcionar la clave fornea necesaria.

Es una buena prctica agregar un ndice en la clave fornea para mejorar el rendimiento de
las consultas y una restriccin de clave fornea para garantizar la integridad de los datos
referenciales:

112
3- Consejos, Trucos y Advertencias

class CreateBooks < ActiveRecord::Migration[5.0]


def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end

add_index :books, :author_id


add_foreign_key :books, :authors
end
end

3.3.2 Creacin de tablas de unin (join tables) para las


asociaciones has_and_belongs_to_many
Si crea una asociacin has_and_belongs_to_many , debe crear explcitamente la tabla de
unin. A menos que el nombre de la tabla de unin se especifique explcitamente utilizando
la opcin: join_table , Active Record crea el nombre utilizando el lxico de los nombres de
clase. Por lo tanto, una combinacin entre los modelos autor y libros dar el nombre de
tabla de unin predeterminada de authors_books porque "a" supera a "b" en el orden
alfabtico.

La precedencia entre los nombres de modelo se calcula utilizando el operador <=> para
String. Esto significa que si las cadenas son de diferentes longitudes y las cadenas son
iguales en comparacin con la longitud ms corta, entonces la cadena ms larga se
considera de mayor precedencia lxica que la ms corta. Por ejemplo, se podra esperar
que las tablas paper_boxes y papers generen un nombre de tabla de unin de
papers_paper_boxes debido a la longitud del nombre paper_boxes , pero de hecho genera

un nombre de tabla de unin de paper_boxes_papers ( Porque el subrayado '_' es


lexicogrficamente menor que 's' en codificaciones comunes).

Independientemente del nombre, debe generar manualmente la tabla de unin con una
migracin adecuada. Por ejemplo, considere estas asociaciones:

class Assembly < ApplicationRecord


has_and_belongs_to_many :parts
end

class Part < ApplicationRecord


has_and_belongs_to_many :assemblies
end

113
3- Consejos, Trucos y Advertencias

Estos deben ser respaldados por una migracin para crear la tabla assemblies_parts . Esta
tabla debe crearse sin una clave primaria:

class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]


def change
create_table :assemblies_parts, id: false do |t|
t.integer :assembly_id
t.integer :part_id
end

add_index :assemblies_parts, :assembly_id


add_index :assemblies_parts, :part_id
end
end

Pasamos id :false a create_table porque esa tabla no representa un modelo. Eso es


necesario para que la asociacin funcione correctamente. Si observas cualquier
comportamiento extrao en una asociacin has_and_belongs_to_many como ID de modelo
mutilado, o excepciones sobre IDs en conflicto, es probable que te hayas olvidado de ese
detalle.

Tambin puede utilizar el mtodo create_join_table

class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]


def change
create_join_table :assemblies, :parts do |t|
t.index :assembly_id
t.index :part_id
end
end
end

3.4 Control del alcance de la asociacin


De forma predeterminada, las asociaciones buscan objetos slo dentro del mbito del
mdulo actual. Esto puede ser importante cuando declara los modelos Active Record dentro
de un mdulo. Por ejemplo:

114
3- Consejos, Trucos y Advertencias

module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end

class Account < ApplicationRecord


belongs_to :supplier
end
end
end

Esto funcionar bien, ya que tanto la clase proveedor como la clase cuenta se definen
dentro del mismo mbito. Pero lo siguiente no funcionar, ya que el proveedor y la cuenta
se definen en mbitos diferentes:

module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier
end
end
end

Para asociar un modelo con un modelo en un namespace diferente, debe especificar el


nombre completo de la clase en la declaracin de asociacin:

module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account,
class_name: "MyApplication::Billing::Account"
end
end

module Billing
class Account < ApplicationRecord
belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
end

115
3- Consejos, Trucos y Advertencias

3.5 Asociaciones bidireccionales


Es normal que las asociaciones trabajen en dos direcciones, requiriendo la declaracin en
dos modelos diferentes:

class Author < ApplicationRecord


has_many :books
end

class Book < ApplicationRecord


belongs_to :author
end

Active Record intentar identificar automticamente que estos dos modelos comparten una
asociacin bidireccional basada en el nombre de la asociacin. De esta manera, Active
Record slo cargar una copia del objeto Author , haciendo que su aplicacin sea ms
eficiente y evite datos incoherentes:

a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'David'
a.first_name == b.author.first_name # => true

Active Record admite la identificacin automtica para la mayora de las asociaciones con
nombres estndar. Sin embargo, Active Record no identificar automticamente
asociaciones bidireccionales que contengan cualquiera de las siguientes opciones:

:conditions
:through
:polymorphic
:class_name
:foreign_key

Por ejemplo, considere las siguientes declaraciones de modelo:

class Author < ApplicationRecord


has_many :books
end

class Book < ApplicationRecord


belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

Active Record ya no reconocer automticamente la asociacin bidireccional:

116
3- Consejos, Trucos y Advertencias

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => false

Active Record proporciona la opcin: inverse_of para que pueda declarar explcitamente
asociaciones bidireccionales:

class Author < ApplicationRecord


has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord


belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

Al incluir la opcin: inverse_of en la declaracin de asociacin has_many , Active Record


reconocer ahora la asociacin bidireccional:

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => true

Existen algunas limitaciones con :inverse_of

No trabajan con asociaciones :through .


No funcionan con asociaciones polimrficas.
No trabajan con asociaciones :as

117
4- Referencia detallada de la asociacin

4. Referencia detallada de la asociacin


Las siguientes secciones proporcionan los detalles de cada tipo de asociacin, incluidos los
mtodos que agregan y las opciones que puede utilizar al declarar una asociacin.

118
4.1 Referencia de la asociacin belongs_to

4.1 Referencia de la asociacin belongs_to


La asociacin belongs_to crea una coincidencia uno-a-uno con otro modelo. En trminos de
base de datos, esta asociacin dice que esta clase contiene la clave fornea. Si la otra
clase contiene la clave fornea, entonces debera usar has_one en su lugar.

4.1.1 Mtodos agregados por belongs_to


Cuando declara una asociacin belongs_to , la clase declarante obtiene automticamente
cinco mtodos relacionados con la asociacin:

association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})

En todos estos mtodos, la palabra association se sustituye por el smbolo pasado como
el primer argumento a belongs_to . Por ejemplo, dada la declaracin:

class Book < ApplicationRecord


belongs_to :author
end

Cada instancia del modelo Book tendr estos mtodos:

Book.author
Book.author=
Book.build_author
Book.create_author
Book.create_author!

Al inicializar una nueva asociacin has_one o belongs_to , debe utilizar el prefijo build_
para crear la asociacin, en lugar del mtodo association.build que se utilizara para las
asociaciones has_many o has_and_belongs_to_many . Para crear uno, use el prefijo create_ .

4.1.1.1 association
El mtodo de association devuelve el objeto asociado, si lo hay. Si no se encuentra ningn
objeto asociado, devuelve nil.

119
4.1 Referencia de la asociacin belongs_to

@author = @book.author

Si el objeto asociado ya se ha recuperado de la base de datos para este objeto, se


devolver la versin almacenada en cach. Para anular este comportamiento (y forzar una
base de datos no leda), llame #reload en el objeto primario.

@author = @book.reload.author

4.1.1.2 association=(associate)
El mtodo association= asigna un objeto asociado a este objeto. Detrs de las escenas,
esto significa extraer la clave principal del objeto asociado y establecer la clave externa de
este objeto en el mismo valor.

@book.author = @author

4.1.1.3 build_association(attributes = {})


El mtodo build_association devuelve un nuevo objeto del tipo asociado. Este objeto se
instanciar a partir de los atributos pasados y se establecer el vnculo a travs de la clave
externa de este objeto, pero el objeto asociado an no se guardar.

@author = @book.build_author(author_number: 123,


author_name: "John Doe")

4.1.1.4 create_association(attributes = {})


El mtodo create_association devuelve un nuevo objeto del tipo asociado. Este objeto
ser instanciado a partir de los atributos pasados, se establecer el enlace a travs de la
clave externa de este objeto y, una vez que pase todas las validaciones especificadas en el
modelo asociado, se guardar el objeto asociado.

@author = @book.create_author(author_number: 123,


author_name: "John Doe")

4.1.1.5 create_association!(attributes = {})


Hace lo mismo que create_association , pero activa ActiveRecord::RecordInvalid si el
registro no es vlido.

120
4.1 Referencia de la asociacin belongs_to

4.1.2 Opciones para belongs_to


Mientras que Rails utiliza valores predeterminados inteligentes que funcionarn bien en la
mayora de las situaciones, puede haber ocasiones en las que desee personalizar el
comportamiento de la referencia de asociacin belongs_to . Tales personalizaciones
pueden realizarse fcilmente al pasar opciones y bloques de mbito al crear la asociacin.
Por ejemplo, esta asociacin utiliza dos opciones:

class Book < ApplicationRecord


belongs_to :author, dependent: :destroy,
counter_cache: true
end

La asociacin belongs_to soporta estas opciones:

:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:primary_key
:inverse_of
:polymorphic
:touch
:validate
:optional

4.1.2.1: autosave
Si configura la opcin :autosave en true , Rails guardar todos los miembros cargados y
destruir los miembros marcados para su destruccin cada vez que guarde el objeto
principal.

4.1.2.2: class_name
Si el nombre del otro modelo no puede derivarse del nombre de la asociacin, puede utilizar
la opcin: class_name para proporcionar el nombre del modelo. Por ejemplo, si un libro
pertenece a un autor, pero el nombre real del modelo que contiene los autores es Patron ,
se estableceran las cosas de esta manera:

class Book < ApplicationRecord


belongs_to :author, class_name: "Patron"
end

121
4.1 Referencia de la asociacin belongs_to

4.1.2.3: counter_cache
La opcin: counter_cache se puede utilizar para hacer ms eficiente el hallazgo del nmero
de objetos pertenecientes. Considere estos modelos:

class Book < ApplicationRecord


belongs_to :author
end
class Author < ApplicationRecord
has_many :books
end

Con estas declaraciones, pedir el valor de @author.books.size requiere realizar una


llamada a la base de datos para realizar una consulta COUNT(*) . Para evitar esta llamada,
puede agregar una cach de contador al modelo de pertenencia:

class Book < ApplicationRecord


belongs_to :author, counter_cache: true
end
class Author < ApplicationRecord
has_many :books
end

Con esta declaracin, Rails mantendr el valor de cach actualizado y, a continuacin,


devolver ese valor en respuesta al mtodo .size .

Aunque la opcin: counter_cache se especifica en el modelo que incluye la declaracin


belongs_to , la columna real se debe agregar al modelo asociado ( has_many ). En el caso

anterior, debera agregar una columna denominada books_count al modelo Author .

Puede reemplazar el nombre de columna predeterminado especificando un nombre de


columna personalizado en la declaracin counter_cache en lugar de true . Por ejemplo,
para usar count_of_books en lugar de books_count :

class Book < ApplicationRecord


belongs_to :author, counter_cache: :count_of_books
end
class Author < ApplicationRecord
has_many :books
end

Slo es necesario especificar la opcin: counter_cache en el lado belongs_to de la


asociacin.

122
4.1 Referencia de la asociacin belongs_to

Las columnas de Counter Cache se agregan a la lista de atributos de solo lectura de


modelo de contenedor a travs de attr_readonly .

4.1.2.4 :dependent
Controla qu sucede con los objetos asociados cuando se destruye a su propietario:

:destroy hace que los objetos asociados tambin se destruyan.

:delete_all hace que los objetos asociados sean eliminados directamente de la base

de datos (los callbacks no se ejecutan).


:nullify hace que las claves forneas se establezcan en NULL (callbacks no se

ejecutan).
:restrict_with_exception provoca que se genere una excepcin si hay registros

asociados.
:restrict_with_error causa que se agregue un error al propietario si hay objetos

asociados.

No debe especificar esta opcin en una asociacin belongs_to que est conectada con
una asociacin has_many en la otra clase. Hacerlo puede llevar a registros hurfanos en su
base de datos.

4.1.2.5 :foreign_key
Por convencin, Rails asume que la columna utilizada para mantener la clave fornea en
este modelo es el nombre de la asociacin con el sufijo _id aadido. La opcin:
foreign_key le permite establecer el nombre de la clave externa directamente:

class Book < ApplicationRecord


belongs_to :author, class_name: "Patron",
foreign_key: "patron_id"
end

En cualquier caso, Rails no crear columnas de clave foranea para usted. Debe definirlos
explcitamente como parte de sus migraciones.

4.1.2.6: primary_key
Por convencin, Rails asume que la columna id se utiliza para contener la clave primaria
de sus tablas. La opcin: primary_key le permite especificar una columna diferente.

123
4.1 Referencia de la asociacin belongs_to

Por ejemplo, dado que tenemos una tabla de usuarios con guid como la clave principal. Si
queremos una tabla separada de todos para mantener la clave externa user_id en la
columna guid , entonces podemos usar primary_key para lograr esto:

class User < ApplicationRecord


self.primary_key = 'guid' # primary key is guid and not id
end

class Todo < ApplicationRecord


belongs_to :user, primary_key: 'guid'
end

Cuando ejecutamos @user.todos.create entonces el registro @todo tendr su valor


user_id como el valor de guid de @user .

4.1.2.7 :inverse_of
La opcin :inverse_of especifica el nombre de la asociacin has_many o has_one que es
la inversa de esta asociacin. No funciona en combinacin con la opcion :polymorphic .

class Author < ApplicationRecord


has_many :books, inverse_of: :author
end

class Book < ApplicationRecord


belongs_to :author, inverse_of: :books
end

4.1.2.8 :polymorphic
Pasar true a la opcin :polymorphic indica que se trata de una asociacin polimrfica.
Las asociaciones polimrficas se discutieron en detalle anteriormente en esta gua.

4.1.2.9 :touch
Si establece la opcin: touch en true , entonces la marca de tiempo updated_at o
updated_on en el objeto asociado se establecer en la hora actual siempre que se guarde o

destruya este objeto:

124
4.1 Referencia de la asociacin belongs_to

class Book < ApplicationRecord


belongs_to :author, touch: true
end

class Author < ApplicationRecord


has_many :books
end

En este caso, guardar o destruir un libro actualizar la marca de tiempo en el autor


asociado. Tambin puede especificar un atributo de marca de tiempo especfico para
actualizar:

class Book < ApplicationRecord


belongs_to :author, touch: :books_updated_at
end

4.1.2.10 :validate
Si establece la opcin :validate en true , los objetos asociados se validarn cada vez
que guarde este objeto. De forma predeterminada es false. los objetos asociados no se
validarn cuando se guarde este objeto.

4.1.2.11 :optional
Si establece la opcin :optional en true , entonces la presencia del objeto asociado no
ser validada. De forma predeterminada, esta opcin se establece en false .

4.1.3 Scope de belongs_to


Puede haber ocasiones en las que desee personalizar la consulta utilizada por belongs_to .
Tales personalizaciones se pueden conseguir a travs de un bloque de Scope. Por ejemplo:

class Book < ApplicationRecord


belongs_to :author, -> { where active: true },
dependent: :destroy
end

Puede utilizar cualquiera de los mtodos de consulta estndar dentro del bloque de Scope.
A continuacin se analizan las siguientes:

125
4.1 Referencia de la asociacin belongs_to

where
includes
readonly
select

4.1.3.1 where
El mtodo where permite especificar las condiciones que debe cumplir el objeto asociado.

class book < ApplicationRecord


belongs_to :author, -> { where active: true }
end

4.1.3.2 include
Puede utilizar el mtodo include para especificar asociaciones de segundo orden que
deben cargarse con impaciencia cuando se utiliza esta asociacin. Por ejemplo, considere
estos modelos:

class LineItem < ApplicationRecord


belongs_to :book
end

class Book < ApplicationRecord


belongs_to :author
has_many :line_items
end

class Author < ApplicationRecord


has_many :books
end

Si con frecuencia recupera autores directamente de line_item ( @line_item.book.author ),


puede hacer que su cdigo sea un poco ms eficiente al incluir a los autores en la
asociacin de line_item a books :

126
4.1 Referencia de la asociacin belongs_to

class LineItem < ApplicationRecord


belongs_to :book, -> { includes :author }
end

class Book < ApplicationRecord


belongs_to :author
has_many :line_items
end

class Author < ApplicationRecord


has_many :books
end

No hay necesidad de utilizar include para asociaciones inmediatas, es decir, si tienes


Book belongs_to: author , entonces el autor se carga con impaciencia automticamente

cuando es necesario.

4.1.3.3 readonly
Si utiliza readonly, el objeto asociado ser de slo lectura cuando se recupera a travs de la
asociacin.

4.1.3.4 select
El mtodo select le permite anular la clusula SQL SELECT que se utiliza para recuperar
datos sobre el objeto asociado. De forma predeterminada, Rails recupera todas las
columnas. Si utiliza el mtodo select en una asociacin belongs_to , tambin debe
establecer la opcin: foreign_key para garantizar los resultados correctos.

4.1.4 Existen objetos asociados?


Puede ver si existen objetos asociados usando el metodo association.nil? :

if @book.author.nil?
@msg = "No author found for this book"
end

4.1.5 Cundo se guardan los objetos?


Asignar un objeto a una asociacin belongs_to no guarda automticamente el objeto.
Tampoco guarda el objeto asociado.

127
4.1 Referencia de la asociacin belongs_to

128
4.2 Referencia de la asociacin has_one

4.2 Referencia de la asociacin has_one


La asociacin has_one crea una coincidencia uno a uno con otro modelo. En trminos de
base de datos, esta asociacin dice que la otra clase contiene la clave externa. Si esta
clase contiene la clave externa, entonces debera usar belongs_to en su lugar.

4.2.1 Mtodos agregados por has_one


Cuando declara una asociacin has_one , la clase declarante gana automticamente cinco
mtodos relacionados con la asociacin:

association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})

En todos estos mtodos, la asociacin se sustituye por el smbolo pasado como el primer
argumento a has_one . Por ejemplo, dada la declaracin:

class Supplier < ApplicationRecord


has_one :account
end

Cada instancia del modelo de proveedor tendr estos mtodos:

Supplier.account
Supplier.account=
Supplier.build_account
Supplier.create_account
Supplier.create_account!

Al inicializar una nueva asociacin has_one o belongs_to , debe utilizar el prefijo build_
para crear la asociacin, en lugar del mtodo association.build que se utilizara para las
asociaciones has_many o has_and_belongs_to_many . Para crear uno, use el prefijo create_ .

4.2.1.1 association
El mtodo de asociacin devuelve el objeto asociado, si lo hay. Si no se encuentra ningn
objeto asociado, devuelve nil .

129
4.2 Referencia de la asociacin has_one

@account = @supplier.account

Si el objeto asociado ya se ha recuperado de la base de datos para este objeto, se


devolver la versin almacenada en cach. Para anular este comportamiento (y forzar una
base de datos leda), llame #reload en el objeto primario.

@account = @supplier.reload.account

4.2.1.2 association=(associate)
El mtodo association= asigna un objeto asociado a este objeto. Detrs de las escenas,
esto significa extraer la clave primaria de este objeto y establecer la clave externa del objeto
asociado al mismo valor.

@supplier.account = @account

4.2.1.3 build_association(attributes = {})


El mtodo build_association devuelve un nuevo objeto del tipo asociado. Este objeto ser
instanciado a partir de los atributos pasados, y el enlace a travs de su clave externa se
establecer, pero el objeto asociado an no se guardar.

@account = @supplier.build_account(terms: "Net 30")

4.2.1.4 create_association(attributes = {})


El mtodo create_association devuelve un nuevo objeto del tipo asociado. Este objeto se
instanciar a partir de los atributos pasados, se establecer el enlace a travs de su clave
externa y, una vez que se pasen todas las validaciones especificadas en el modelo
asociado, se guardar el objeto asociado.

@account = @supplier.create_account(terms: "Net 30")

4.2.1.5 create_association!(attributes = {})


Hace lo mismo que create_association , pero dispara ActiveRecord::RecordInvalid si el
registro no es vlido.

130
4.2 Referencia de la asociacin has_one

4.2.2 Opciones para has_one


Mientras que Rails utiliza valores predeterminados inteligentes que funcionarn bien en la
mayora de las situaciones, puede haber ocasiones en las que desee personalizar el
comportamiento de la referencia de asociacin has_one . Tales personalizaciones pueden
realizarse fcilmente pasando opciones al crear la asociacin. Por ejemplo, esta asociacin
utiliza dos opciones:

class Supplier < ApplicationRecord


has_one :account, class_name: "Billing", dependent: :nullify
end

La asociacin has_one soporta estas opciones:

:as
:autosave
:class_name
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate

4.2.2.1 :as
Establecer la opcin: as indica que se trata de una asociacin polimrfica. Las
asociaciones polimrficas se discutieron en detalle anteriormente en esta gua.

4.2.2.2 :autosave
Si configura la opcin: autosave en true , Rails guardar todos los miembros cargados y
destruir los miembros marcados para su destruccin cada vez que guarde el objeto
principal.

4.2.2.3 :class_name
Si el nombre del otro modelo no puede derivarse del nombre de la asociacin, puede utilizar
la opcin: class_name para proporcionar el nombre del modelo. Por ejemplo, si un
proveedor tiene una cuenta, pero el nombre real del modelo que contiene cuentas es
Facturacin, debera configurar las cosas de esta manera:

131
4.2 Referencia de la asociacin has_one

class Supplier < ApplicationRecord


has_one :account, class_name: "Billing"
end

4.2.2.4 :dependent
Controla qu sucede con el objeto asociado cuando se destruye su propietario:

:destroy hace que el objeto asociado tambin sea destruido

:delete hace que el objeto asociado sea eliminado directamente de la base de datos

(para que las devoluciones de llamada no se ejecuten)


:nullify hace que la clave externa se establezca en NULL. Las devoluciones de

llamada no se ejecutan.
:restrict_with_exception hace que se eleve una excepcin si hay un registro

asociado
:restrict_with_error provoca que se agregue un error al propietario si hay un objeto

asociado

Es necesario no establecer o dejar la opcion :nullify para aquellas asociaciones que


tienen NULL en la base de datos con restricciones. Si no establece dependencia para
destruir tales asociaciones, no podr cambiar el objeto asociado porque la clave externa del
objeto asociado inicial se establecer en el valor NULL no permitido.

4.2.2.5: foreign_key
Por convencin, Rails asume que la columna utilizada para mantener la clave externa en el
otro modelo es el nombre de este modelo con el sufijo _id aadido. La opcin:
foreign_key le permite establecer el nombre de la clave externa directamente:

class Supplier < ApplicationRecord


has_one :account, foreign_key: "supp_id"
end

En cualquier caso, Rails no crear columnas de clave externa para usted. Debe definirlos
explcitamente como parte de sus migraciones.

4.2.2.6 :inverse_of
La opcin :inverse_of especifica el nombre de la asociacin belongs_to que es la inversa
de esta asociacin. No funciona en combinacin con las opciones :through o :as .

132
4.2 Referencia de la asociacin has_one

class Supplier < ApplicationRecord


has_one :account, inverse_of: :supplier
end

class Account < ApplicationRecord


belongs_to :supplier, inverse_of: :account
end

4.2.2.7: primary_key
Por convencin, Rails asume que la columna utilizada para mantener la clave primaria de
este modelo es id . Puede sobrescribirlo y especificar explcitamente la clave principal con
la opcin: primary_key .

4.2.2.8: source
La opcin: source especifica el nombre de la asociacin de origen para una asociacin
has_one: through .

4.2.2.9: source_type
La opcin: source_type especifica el tipo de asociacin fuente para una asociacin
has_one: through que procede a travs de una asociacin polimrfica.

4.2.2.10: through
La opcin: through especifica un modelo de combinacin mediante el cual realizar la
consulta. Las asociaciones has_one: through se discutieron en detalle anteriormente en
esta gua.

4.2.2.11: validate
Si establece la opcin: validate en true , los objetos asociados se validarn cada vez
que guarde este objeto. De forma predeterminada, esto es false : los objetos asociados no
se validarn cuando se guarde este objeto.

4.2.3 Scope de has_one


Puede haber ocasiones en las que desee personalizar la consulta utilizada por has_one .
Tales personalizaciones se pueden conseguir a travs de un bloque de scope. Por ejemplo:

133
4.2 Referencia de la asociacin has_one

class Supplier < ApplicationRecord


has_one :account, -> { where active: true }
end

Puede utilizar cualquiera de los mtodos de consulta estndar dentro del bloque de mbito.
A continuacin se analizan las siguientes:

where
includes
readonly
select

4.2.3.1 where
El mtodo where permite especificar las condiciones que debe cumplir el objeto asociado.

class Supplier < ApplicationRecord


has_one :account, -> { where "confirmed = 1" }
end

4.2.3.2 include
Puede utilizar el mtodo include para especificar asociaciones de segundo orden que
deben cargarse con impaciencia cuando se utiliza esta asociacin. Por ejemplo, considere
estos modelos:

class Supplier < ApplicationRecord


has_one :account
end

class Account < ApplicationRecord


belongs_to :supplier
belongs_to :representative
end

class Representative < ApplicationRecord


has_many :accounts
end

Si obtiene con frecuencia representantes directamente de los proveedores


( @supplier.account.representative ), puede hacer que su cdigo sea un poco ms eficiente
al incluir representantes en la asociacin de proveedores a cuentas:

134
4.2 Referencia de la asociacin has_one

class Supplier < ApplicationRecord


has_one :account, -> { includes :representative }
end

class Account < ApplicationRecord


belongs_to :supplier
belongs_to :representative
end

class Representative < ApplicationRecord


has_many :accounts
end

4.2.3.3 readonly
Si utiliza el mtodo readonly , el objeto asociado ser de slo lectura cuando se recupera a
travs de la asociacin.

4.2.3.4 select
El mtodo select le permite anular la clusula SQL SELECT que se utiliza para recuperar
datos sobre el objeto asociado. De forma predeterminada, Rails recupera todas las
columnas.

4.2.4 Existen objetos asociados?


Puede ver si existen objetos asociados usando el mtodo association.nil? :

if @supplier.account.nil?
@msg = "No account found for this supplier"
end

4.2.5 Cundo se guardan los objetos?


Cuando asigna un objeto a una asociacin has_one , ese objeto se guarda
automticamente (para actualizar su clave externa). Adems, cualquier objeto que se est
reemplazando tambin se guarda automticamente, porque su clave externa tambin
cambiar.

Si cualquiera de estos guardados falla debido a errores de validacin, la instruccin de


asignacin devuelve false y la asignacin en s se cancela.

135
4.2 Referencia de la asociacin has_one

Si el objeto principal (el que declara la asociacin has_one ) no se guarda (es decir,
new_record? Devuelve true ) los objetos secundarios no se guardan. Automticamente

cuando se guarda el objeto principal.

Si desea asignar un objeto a una asociacin has_one sin guardar el objeto, utilice el
mtodo association.build .

136
4.3 Referencia de la asociacin has_many

4.3 Referencia de la asociacin has_many


La asociacin has_many crea una relacin uno-a-muchos con otro modelo. En trminos de
base de datos, esta asociacin dice que la otra clase tendr una clave externa que se
refiere a instancias de esta clase.

4.3.1 Mtodos Aadidos por has_many


Cuando declara una asociacin has_many , la clase declarante obtiene automticamente 16
mtodos relacionados con la asociacin:

collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})

En todos estos mtodos, la palabra collection se reemplaza con el smbolo pasado como
el primer argumento a has_many , y collection_singular se reemplaza con la versin
singularizada de ese smbolo. Por ejemplo, dada la declaracin:

class Author < ApplicationRecord


has_many :books
end

Cada instancia del modelo Author tendr estos mtodos:

137
4.3 Referencia de la asociacin has_many

Author.books
Author.books<<(object, ...)
Author.books.delete(object, ...)
Author.books.destroy(object, ...)
Author.books=(objects)
Author.book_ids
Author.book_ids=(ids)
Author.books.clear
Author.books.empty?
Author.books.size
Author.books.find(...)
Author.books.where(...)
Author.books.exists?(...)
Author.books.build(attributes = {}, ...)
Author.books.create(attributes = {})
Author.books.create!(attributes = {})

4.3.1.1 collection
El mtodo collection devuelve un array de todos los objetos asociados. Si no hay objetos
asociados, devuelve un array vaco.

@books = @author.books

4.3.1.2 collection<<(object, ...)


El mtodo collection<< agrega uno o ms objetos a la coleccin fijando sus llaves
forneas a la llave primaria del modelo llamante.

@author.books << @book1

4.3.1.3 collection.delete(object, ...)


El mtodo collection.delete elimina uno o ms objetos de la coleccin estableciendo sus
claves forneas en NULL .

@author.books.delete(@book1)

Adems, los objetos se destruirn si estn asociados con dependent: :destroy y se


eliminan si estn asociados con dependent: :delete_all .

4.3.1.4 collection.destroy (object, ...)

138
4.3 Referencia de la asociacin has_many

El mtodo collection.destroy elimina uno o ms objetos de la coleccin ejecutando


destroy en cada objeto.

@author.books.destroy(@book1)

Los objetos siempre se eliminarn de la base de datos, ignorando la opcin :dependent .

4.3.1.5 collection=(objects)
El mtodo collection= hace que la coleccin contenga slo los objetos suministrados,
agregando y eliminando segn corresponda. Los cambios son persistentes en la base de
datos.

4.3.1.6 collection_singular_ids
El mtodo collection_singular_ids devuelve un array de ids de los objetos de la
coleccin.

@book_ids = @author.book_ids

4.3.1.7 collection_singular_ids=(ids)
El mtodo collection_singular_ids= hace que la coleccin contenga slo los objetos
identificados por los valores de clave fornea suministrados, aadiendo y eliminando segn
corresponda. Los cambios son persistentes en la base de datos.

4.3.1.8 collection.clear
El mtodo collection.clear elimina todos los objetos de la coleccin de acuerdo con la
estrategia especificada por la opcin dependiente. Si no se da ninguna opcin, sigue la
estrategia predeterminada. La estrategia predeterminada para has_many: through es
delete_all , y para has_many es establecer las claves externas en NULL.

@author.books.clear

Los objetos se eliminarn si estn asociados con dependent: :destroy , al igual que
dependent: :delete_all .

4.3.1.9 collection.empty?

139
4.3 Referencia de la asociacin has_many

El Mtodo collection.empty? devuelve true si la coleccin no contiene ningn objeto


asociado.

<% if @author.books.empty? %>


No Books Found
<% end %>

4.3.1.10 collection.size
El mtodo collection.size devuelve el nmero de objetos de la coleccin.

@book_count = @author.books.size

4.3.1.11 collection.find(...)
El mtodo collection.find encuentra objetos dentro de la coleccin. Utiliza la misma
sintaxis y opciones como ActiveRecord::Base.find .

@available_books = @author.books.find(1)

4.3.1.12 collection.where(...)
El mtodo collection.where encuentra objetos dentro de la coleccin basados en las
condiciones suministradas, pero los objetos se cargan perezosamente, lo que significa que
la base de datos se consulta slo cuando se accede a los objetos.

@available_books = @author.books.where(available: true) # No query yet


@available_book = @available_books.first # Now the database will be queried

4.3.1.13 collection.exists?(...)
El mtodo collection.exists? comprueba si existe un objeto que cumple las condiciones
suministradas en la coleccin. Utiliza la misma sintaxis y opciones como
ActiveRecord::Base.exists? .

4.3.1.14 collection.build (attributes = {}, ...)


El mtodo collection.build devuelve una sola o una matriz de nuevos objetos del tipo
asociado. Los objetos sern instanciados a partir de los atributos pasados y el enlace a
travs de su clave externa se crear, pero los objetos asociados no se guardarn todava.

140
4.3 Referencia de la asociacin has_many

@book = @author.books.build(published_at: Time.now,


book_number: "A12345")

@books = @author.books.build([
{ published_at: Time.now, book_number: "A12346" },
{ published_at: Time.now, book_number: "A12347" }

4.3.1.15 collection.create(attributes = {})


El mtodo collection.create devuelve una sola o una matriz de nuevos objetos del tipo
asociado. Los objetos sern instanciados a partir de los atributos pasados, se crear el
enlace a travs de su clave externa y, una vez que se pasen todas las validaciones
especificadas en el modelo asociado, se guardar el objeto asociado.

@book = @author.books.create(published_at: Time.now,


book_number: "A12345")

@books = @author.books.create([
{ published_at: Time.now, book_number: "A12346" },
{ published_at: Time.now, book_number: "A12347" }
])

4.3.1.16 collection.create!(attributes = {})


Hace lo mismo que el anterior collection.create , pero dispara
ActiveRecord::RecordInvalid si el registro no es vlido.

4.3.2 Opciones para has_many


Mientras que Rails utiliza valores predeterminados inteligentes que funcionarn bien en la
mayora de las situaciones, puede haber ocasiones en las que desee personalizar el
comportamiento de la referencia de asociacin has_many . Tales personalizaciones pueden
realizarse fcilmente pasando opciones al crear la asociacin. Por ejemplo, esta asociacin
utiliza dos opciones:

class Author < ApplicationRecord


has_many :books, dependent: :delete_all, validate: false
end

La asociacin has_many soporta estas opciones:

141
4.3 Referencia de la asociacin has_many

:as
:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate

4.3.2.1: as
Establecer la opcin: as indica que se trata de una asociacin polimrfica, como se ha
explicado anteriormente en esta gua.

4.3.2.2: autosave
Si configura la opcin: autosave en true , Rails guardar todos los miembros cargados y
destruir los miembros marcados para su destruccin cada vez que guarde el objeto
principal.

4.3.2.3: class_name
Si el nombre del otro modelo no puede derivarse del nombre de la asociacin, puede utilizar
la opcin: class_name para proporcionar el nombre del modelo. Por ejemplo, si un autor
tiene muchos libros, pero el nombre real del modelo que contiene los libros es Transaction ,
configuraras las cosas de esta manera:

class Author < ApplicationRecord


has_many :books, class_name: "Transaction"
end

4.3.2.4: counter_cache
Esta opcin se puede utilizar para configurar un nombre personalizado denominado:
counter_cache . Slo necesita esta opcin cuando personaliza el nombre de su:

counter_cache en la asociacin belongs_to .

4.3.2.5 :dependent

142
4.3 Referencia de la asociacin has_many

Controla qu sucede con los objetos asociados cuando se destruye a su propietario:

:destroy hace que todos los objetos asociados tambin sean destruidos

:delete_all hace que todos los objetos asociados sean eliminados directamente de la

base de datos (por lo que las devoluciones de llamada no se ejecutarn)


:nullify hace que las claves externas se establezcan en NULL . Las devoluciones de

llamada no se ejecutan.
:restrict_with_exception provoca que se genere una excepcin si hay registros

asociados
:restrict_with_error causa que se agregue un error al propietario si hay objetos

asociados

4.3.2.6: foreign_key
Por convencin, Rails asume que la columna utilizada para mantener la clave externa en el
otro modelo es el nombre de este modelo con el sufijo _id aadido. La opcin:
foreign_key le permite establecer el nombre de la clave externa directamente:

class Author < ApplicationRecord


has_many :books, foreign_key: "cust_id"
end

En cualquier caso, Rails no crear columnas de clave externa para usted. Debe definirlos
explcitamente como parte de sus migraciones.

4.3.2.7 :inverse_of
La opcin: inverse_of especifica el nombre de la asociacin belongs_to que es la inversa
de esta asociacin. No funciona en combinacin con las opciones: through o: as .

class Author < ApplicationRecord


has_many :books, inverse_of: :author
end

class Book < ApplicationRecord


belongs_to :author, inverse_of: :books
end

4.3.2.8: primary_key

143
4.3 Referencia de la asociacin has_many

Por convencin, Rails asume que la columna utilizada para mantener la clave primaria de la
asociacin es id . Puede sobre-escribirlo y especificar explcitamente la clave principal con
la opcin: primary_key .

Digamos que la tabla users tiene id como primary_key pero tambin tiene una columna
guid . El requisito es que la tabla todos debe contener el valor de la columna guid como

la clave fornea y no el valor id . Esto se puede lograr as:

class User < ApplicationRecord


has_many :todos, primary_key: :guid
end

Ahora, si ejecutamos @todo= @user.todos.create entonces el valor user_id del registro


@todo ser el valor de guid de @user .

4.3.2.9: source
La opcin: source especifica el nombre de la asociacin de origen para una asociacin
has_many: through . Slo es necesario utilizar esta opcin si el nombre de la asociacin de

origen no puede deducirse automticamente del nombre de la asociacin.

4.3.2.10: source_type
La opcin: source_type especifica el tipo de asociacin fuente para un has_many: through
de la asociacin que procede a travs de una asociacin polimrfica.

4.3.2.11: through
La opcin: through especifica un modelo de combinacin mediante el cual realizar la
consulta. Las asociaciones has_many: through proveen una manera de implementar
relaciones muchos-a-muchos, como se discuti anteriormente en esta gua.

4.3.2.12: validate
Si establece la opcin: validate en false , los objetos asociados no se validarn cada
vez que guarde este objeto. De forma predeterminada, esto es true donde los objetos
asociados se validarn cuando se guarde este objeto.

4.3.3 Scope de has_many

144
4.3 Referencia de la asociacin has_many

Puede haber ocasiones en las que desee personalizar la consulta utilizada por has_many .
Tales personalizaciones se pueden conseguir a travs de un bloque de scope. Por ejemplo:

class Author < ApplicationRecord


has_many :books, -> { where processed: true }
end

Puede utilizar cualquiera de los mtodos de consulta estndar dentro del bloque de scope.
A continuacin se analizan las siguientes:

where
extending
group
includes
limit
offset
order
readonly
select
distinct

4.3.3.1 where
El mtodo where permite especificar las condiciones que debe cumplir el objeto asociado.

class Author < ApplicationRecord


has_many :confirmed_books, -> { where "confirmed = 1" },
class_name: "Book"
end

Tambin puede establecer las condiciones mediante un hash:

class Author < ApplicationRecord


has_many :confirmed_books, -> { where confirmed: true },
class_name: "Book"
end

Si utiliza una opcin hash-style where , entonces la creacin de registros a travs de esta
asociacin se escanear automticamente utilizando el hash. En este caso, el uso de
@author.confirmed_books.create o @author.confirmed_books.build crear libros donde la

columna confirmada tiene el valor true .

4.3.3.2 extending

145
4.3 Referencia de la asociacin has_many

El mtodo extending especifica un mdulo con nombre para extender el proxy de


asociacin. Las extensiones de la asociacin se discuten en detalle ms adelante en esta
gua.

4.3.3.3 group
El mtodo group proporciona un nombre de atributo para agrupar el conjunto de
resultados, utilizando una clusula GROUP BY en el buscador SQL .

class Author < ApplicationRecord


has_many :line_items, -> { group 'books.id' },
through: :books
end

4.3.3.4 includes
Puede utilizar el mtodo includes para especificar asociaciones de segundo orden que
deben cargarse con impaciencia cuando se utiliza esta asociacin. Por ejemplo, considere
estos modelos:

class Author < ApplicationRecord


has_many :books
end

class Book < ApplicationRecord


belongs_to :author
has_many :line_items
end

class LineItem < ApplicationRecord


belongs_to :book
end

Si con frecuencia recupera line_items directamente de los Author


( @author.books.line_items ), puede hacer que su cdigo sea algo ms eficiente al incluir
elementos de lnea en la asociacin de autores a libros:

146
4.3 Referencia de la asociacin has_many

class Author < ApplicationRecord


has_many :books, -> { includes :line_items }
end

class Book < ApplicationRecord


belongs_to :author
has_many :line_items
end

class LineItem < ApplicationRecord


belongs_to :book
end

4.3.3.5 limit
El mtodo limit le permite restringir el nmero total de objetos que se obtendrn a travs
de una asociacin.

class Author < ApplicationRecord


has_many :recent_books,
-> { order('published_at desc').limit(100) },
class_name: "Book",
end

4.3.3.6 offset
El mtodo offset le permite especificar el desplazamiento inicial para buscar objetos a
travs de una asociacin. Por ejemplo, ->{offset (11)} saltar los primeros 11 registros.

4.3.3.7 order
El mtodo order determina el orden en el que se recibirn los objetos asociados (en la
sintaxis utilizada por una clusula SQL ORDER BY ).

class Author < ApplicationRecord


has_many :books, -> { order "date_confirmed DESC" }
end

4.3.3.8 readonly
Si utiliza el mtodo readonly , los objetos asociados sern de slo lectura cuando se
recuperen a travs de la asociacin.

147
4.3 Referencia de la asociacin has_many

4.3.3.9 select
El mtodo select le permite anular la clusula SQL SELECT que se utiliza para recuperar
datos sobre los objetos asociados. De forma predeterminada, Rails recupera todas las
columnas. Si especifica su propia seleccin, asegrese de incluir la clave principal y las
columnas de clave externa del modelo asociado. Si no lo hace, Rails lanzar un error.

4.3.3.10 distinct
Utilice el mtodo distinct para mantener la coleccin libre de duplicados. Esto es til en
su mayora con la opcin: through .

class Person < ApplicationRecord


has_many :readings
has_many :articles, through: :readings
end

person = Person.create(name: 'John')


article = Article.create(name: 'a1')
person.articles << article
person.articles << article
person.articles.inspect # => [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1
">]
Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Readin
g id: 13, person_id: 5, article_id: 5>]

En el caso anterior hay dos readings y person.articles saca a relucir a ambos, aunque
estos registros apuntan al mismo artculo.

Ahora vamos a setear distinct :

class Person
has_many :readings
has_many :articles, -> { distinct }, through: :readings
end

person = Person.create(name: 'Honda')


article = Article.create(name: 'a1')
person.articles << article
person.articles << article
person.articles.inspect # => [#<Article id: 7, name: "a1">]
Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Readin
g id: 17, person_id: 7, article_id: 7>]

En el caso anterior todava hay dos lecturas. Sin embargo person.articles muestra slo
un artculo porque la coleccin carga slo registros nicos.

148
4.3 Referencia de la asociacin has_many

Si desea asegurarse de que, al insertar, todos los registros de la asociacin persistente son
distintos (para que pueda estar seguro de que al inspeccionar la asociacin que nunca
encontrar registros duplicados), debe agregar un ndice nico en La propia tabla. Por
ejemplo, si tiene una tabla denominada readings y desea asegurarse de que los artculos
slo se pueden agregar a una persona una vez, podra agregar lo siguiente en una
migracin:

add_index :readings, [:person_id, :article_id], unique: true

Una vez que tenga este ndice nico, intentar agregar el artculo a una persona dos veces
disparar un error de ActiveRecord::RecordNotUnique

person = Person.create(name: 'Honda')


article = Article.create(name: 'a1')
person.articles << article
person.articles << article # => ActiveRecord::RecordNotUnique

Tenga en cuenta que la comprobacin de la singularidad con algo como include? Est
sujeto a condiciones de carrera. No intente utilizar include? Para reforzar la distincin en
una asociacin. Por ejemplo, usando el ejemplo del artculo de arriba, el cdigo siguiente
sera racy porque varios usuarios podran estar intentando esto al mismo tiempo:

person.articles << article unless person.articles.include?(article)person.articles <<


article unless person.articles.include?(article)

4.3.4 Cundo se guardan los objetos?


Cuando asigna un objeto a una asociacin has_many , ese objeto se guarda
automticamente (para actualizar su clave externa). Si asigna varios objetos en una
sentencia, todos ellos se guardan.

Si alguno de estos registros falla debido a errores de validacin, la instruccin de asignacin


devuelve false y la asignacin en s se cancela.

Si el objeto principal (el que declara la asociacin has_many ) no se guarda (es decir,
new_record? Devuelve true ), los objetos secundarios no se guardan cuando se agregan.

Todos los miembros no guardados de la asociacin se guardarn automticamente cuando


se guarda el padre.

Si desea asignar un objeto a una asociacin has_many sin guardar el objeto, utilice el
mtodo collection.build .

149
4.3 Referencia de la asociacin has_many

150
4.4 Referencia de la asociacin has_and_belongs_to_many

4.4 Referencia de la asociacin


has_and_belongs_to_many
La asociacin has_and_belongs_to_many crea una relacin many-to-many con otro modelo.
En trminos de base de datos, esto asocia dos clases a travs de una tabla de unin
intermedia que incluye claves externas que se refieren a cada una de las clases.

4.4.1 Mtodos Aadido por has_and_belongs_to_many


Cuando declara una asociacin has_and_belongs_to_many , la clase declarante obtiene
automticamente 16 mtodos relacionados con la asociacin:

collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {})
collection.create(attributes = {})
collection.create!(attributes = {})

En todos estos mtodos, la coleccin se sustituye por el smbolo pasado como el primer
argumento a has_and_belongs_to_many , y collection_singular es reemplazado por la
versin singularizada de ese smbolo. Por ejemplo, dada la declaracin:

class Part < ApplicationRecord


has_and_belongs_to_many :assemblies
end

Cada instancia del modelo de Part tendr estos mtodos:

151
4.4 Referencia de la asociacin has_and_belongs_to_many

Part.assemblies
Part.assemblies<<(object, ...)
Part.assemblies.delete(object, ...)
Part.assemblies.destroy(object, ...)
Part.assemblies=(objects)
Part.assembly_ids
Part.assembly_ids=(ids)
Part.assemblies.clear
Part.assemblies.empty?
Part.assemblies.size
Part.assemblies.find(...)
Part.assemblies.where(...)
Part.assemblies.exists?(...)
Part.assemblies.build(attributes = {}, ...)
Part.assemblies.create(attributes = {})
Part.assemblies.create!(attributes = {})

4.4.1.1 Mtodos de columna adicionales


Si la tabla de unin de una asociacin has_and_belongs_to_many tiene columnas adicionales
ms all de las dos claves forneas, estas columnas se agregarn como atributos a los
registros recuperados a travs de esa asociacin. Los registros devueltos con atributos
adicionales siempre sern de slo lectura, ya que Rails no puede guardar los cambios en
esos atributos.

El uso de atributos adicionales en la tabla de combinacin en una asociacin


has_and_belongs_to_many est obsoleto. Si requiere este tipo de comportamiento complejo

en la tabla que une dos modelos en una relacin muchos-a-muchos, debera usar una
asociacin has_many: through en lugar de has_and_belongs_to_many .

4.4.1.2 collection
El mtodo collection devuelve una matriz de todos los objetos asociados. Si no hay
objetos asociados, devuelve una matriz vaca.

@assemblies = @part.assemblies

4.4.1.3 collection<<(object, ...)


El mtodo collection << agrega uno o ms objetos a la coleccin creando registros en la
tabla de unin. Este mtodo se denomina collection.concat y collection.push .

4.4.1.4 collection.delete (object, ...)

152
4.4 Referencia de la asociacin has_and_belongs_to_many

El mtodo collection.delete elimina uno o ms objetos de la coleccin eliminando


registros en la tabla de unin. Esto no destruye los objetos.

@part.assemblies.delete(@assembly1)

4.4.1.5 collection.destroy (object, ...)


El mtodo collection.destroy elimina uno o ms objetos de la coleccin eliminando
registros en la tabla de unin. Esto no destruye los objetos.

@part.assemblies.destroy(@ assembly1)

4.4.1.6 collection = (objects)


El mtodo collection = hace que la coleccin contenga slo los objetos suministrados,
agregando y eliminando segn corresponda. Los cambios son persistentes en la base de
datos

4.4.1.7 collection_singular_ids
El mtodo collection_singular_ids devuelve una matriz de ids de los objetos de la
coleccin.

@assembly_ids = @part.assembly_ids

4.4.1.8 collection_singular_ids = (ids)


El mtodo collection_singular_ids = hace que la coleccin contenga slo los objetos
identificados por los valores de clave primaria suministrados, aadiendo y eliminando segn
corresponda. Los son persistentes en la base de datos.

4.4.1.9 collection.clear
El mtodo collection.clear elimina todos los objetos de la coleccin eliminando las filas
de la tabla de unin. Esto no destruye los objetos asociados.

4.4.1.10 collection.empty?
El mtodo collection.empty? devuelve true si la coleccin no contiene ningn objeto
asociado.

153
4.4 Referencia de la asociacin has_and_belongs_to_many

<% If @ part.assemblies.empty? Unesdoc.unesco.org unesdoc.unesco.org


Esta parte no se utiliza en ninguna asamblea
<% End%>

4.4.1.11 collection.size
El mtodo collection.size devuelve el nmero de objetos de la coleccin.

@assembly_count = @ part.assemblies.size

4.4.1.12 collection.find (...)


El mtodo collection.find encuentra objetos dentro de la coleccin. Utiliza la misma
sintaxis y opciones como ActiveRecord :: Base.find . Tambin agrega la condicin
adicional de que el objeto debe estar en la coleccin.

@assembly = @part.assemblies.find(1)

4.4.1.13 collection.where (...)


El mtodo collection.where encuentra objetos dentro de la coleccin basados en las
condiciones suministradas, pero los objetos se cargan perezosamente, lo que significa que
la base de datos se consulta slo cuando se accede a los objetos. Tambin agrega la
condicin adicional de que el objeto debe estar en la coleccin.

@new_assemblies = @part.assemblies.where("created_at>?", 2.days.ago)

4.4.1.14 collection.exists? (...)


El mtodo collection.exist? comprueba si existe un objeto que cumple las condiciones
suministradas en la coleccin. Utiliza la misma sintaxis y opciones como
ActiveRecord::Base.exists? .

4.4.1.15 collection.build(attributes = {})


El mtodo collection.buid devuelve un nuevo objeto del tipo asociado. Este objeto se
instanciar a partir de los atributos pasados y se crear el vnculo a travs de la tabla de
unin, pero el objeto asociado an no se guardar.

154
4.4 Referencia de la asociacin has_and_belongs_to_many

@assembly = @part.assemblies.build({assembly_name: "Transmission housing"})

4.4.1.16 collection.create(attributes = {})


El mtodo collection.create devuelve un nuevo objeto del tipo asociado. Este objeto se
instanciar a partir de los atributos pasados, se crear el vnculo a travs de la tabla de
unin y, una vez que se pasen todas las validaciones especificadas en el modelo asociado,
se guardar el objeto asociado.

@assembly = @part.assemblies.create({assembly_name: "Transmission housing"})

4.4.1.17 collection.create!(attributes = {})


Hace lo mismo que collection.create , pero dispara un ActiveRecord::RecordInvalid si el
registro no es vlido.

4.4.2 Opciones para has_and_belongs_to_many


Mientras que Rails utiliza valores predeterminados inteligentes que funcionarn bien en la
mayora de las situaciones, puede haber ocasiones en las que desee personalizar el
comportamiento de la referencia de asociacin has_and_belongs_to_many . Tales
personalizaciones pueden realizarse fcilmente pasando opciones al crear la asociacin.
Por ejemplo, esta asociacin utiliza dos opciones:

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies, -> { readonly },
autosave: true
end

La asociacin has_and_belongs_to_many soporta estas opciones:

:association_foreign_key
:autosave
:class_name
:foreign_key
:join_table
:validate

4.4.2.1: association_foreign_key

155
4.4 Referencia de la asociacin has_and_belongs_to_many

Por convencin, Rails asume que la columna de la tabla de combinacin utilizada para
mantener la clave externa apuntando al otro modelo es el nombre de ese modelo con el
sufijo _id aadido. La opcin: association_foreign_key le permite establecer el nombre
de la clave externa directamente:

Las opciones: foreign_key y: association_foreign_key son tiles cuando se configura una


self-join many-to-many. Por ejemplo:

class User < ApplicationRecord


has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end

4.4.2.2: autosave
Si configura la opcin: autosave en true , Rails guardar todos los miembros cargados y
destruir los miembros marcados para su destruccin cada vez que guarde el objeto
principal.

4.4.2.3: class_name
Si el nombre del otro modelo no puede derivarse del nombre de la asociacin, puede utilizar
la opcin: class_name para proporcionar el nombre del modelo. Por ejemplo, si una parte
tiene muchos ensamblajes, pero el nombre real del modelo que contiene los ensamblajes
es Gadget , debera configurar las cosas de esta manera:

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies, class_name: "Gadget"
end

4.4.2.4: foreign_key
Por convencin, Rails asume que la columna de la tabla de combinacin utilizada para
mantener la clave externa apuntando a este modelo es el nombre de este modelo con el
sufijo _id aadido. La opcin: foreign_key le permite establecer el nombre de la clave
externa directamente:

156
4.4 Referencia de la asociacin has_and_belongs_to_many

class User < ApplicationRecord


has_and_belongs_to_many :friends,
class_name: "User",
foreign_key: "this_user_id",
association_foreign_key: "other_user_id"
end

4.4.2.5: join_table
Si el nombre predeterminado de la tabla de combinacin, basado en ordenamiento lxico,
no es lo que desea, puede utilizar la opcin: join_table para sustituir el valor
predeterminado.

4.4.2.6: validate
Si establece la opcin: validate en false , los objetos asociados no se validarn cada
vez que guarde este objeto. De forma predeterminada, esto es cierto: los objetos asociados
se validarn cuando se guarde este objeto.

4.4.3 Scope de has_and_belongs_to_many


Puede haber ocasiones en las que desee personalizar la consulta utilizada por
has_and_belongs_to_many . Tales personalizaciones se pueden conseguir a travs de un

bloque de scope. Por ejemplo:

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies, -> { where active: true }
end

Puede utilizar cualquiera de los mtodos de consulta estndar dentro del bloque de mbito.
A continuacin se analizan las siguientes:

where
extending
group
includes
limit
offset
order
readonly
select
distinct

157
4.4 Referencia de la asociacin has_and_belongs_to_many

4.4.3.1 where
El mtodo where permite especificar las condiciones que debe cumplir el objeto asociado.

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies,
-> { where "factory = 'Seattle'" }
end

Tambin puede establecer las condiciones mediante un hash:

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies,
-> { where factory: 'Seattle' }
end

Si utiliza un estilo hash para el where , a continuacin, la creacin de registros a travs de


esta asociacin se escanear automticamente utilizando el hash. En este caso, usar
@parts.assemblies.create o @parts.assemblies.build crear rdenes donde la columna de

fbrica tiene el valor "Seattle".

4.4.3.2 extending
El mtodo extendido especifica un mdulo con nombre para extender el proxy de
asociacin. Las extensiones de la asociacin se discuten en detalle ms adelante en esta
gua.

4.4.3.3 group
El mtodo group proporciona un nombre de atributo para agrupar el conjunto de
resultados, utilizando una clusula GROUP BY en el buscador SQL .

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies, -> { group "factory" }
end

4.4.3.4 includes
Puede utilizar el mtodo includes para especificar asociaciones de segundo orden que
deben cargarse con impaciencia cuando se utiliza esta asociacin.

4.4.3.5 limit

158
4.4 Referencia de la asociacin has_and_belongs_to_many

El mtodo de lmite le permite restringir el nmero total de objetos que se obtendrn a


travs de una asociacin.

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies,
-> { order("created_at DESC").limit(50) }
end

4.4.3.6 offset
El mtodo offset le permite especificar el desplazamiento inicial para buscar objetos a
travs de una asociacin. Por ejemplo, si ajusta el desplazamiento (11), omitir los primeros
11 registros.

4.4.3.7 order
El mtodo order determina el orden en el que se recibirn los objetos asociados (en la
sintaxis utilizada por una clusula SQL ORDER BY ).

class Parts < ApplicationRecord


has_and_belongs_to_many :assemblies,
-> { order "assembly_name ASC" }
end

4.4.3.8 readonly
Si utiliza el mtodo readonly , los objetos asociados sern de slo lectura cuando se
recuperen a travs de la asociacin.

4.4.3.9 select
El mtodo select le permite anular la clusula SQL SELECT que se utiliza para recuperar
datos sobre los objetos asociados. De forma predeterminada, Rails recupera todas las
columnas.

4.4.3.10 distinct
Utilice el mtodo distinct para quitar duplicados de la coleccin.

4.4.4 Cundo se guardan los objetos?

159
4.4 Referencia de la asociacin has_and_belongs_to_many

Cuando asigna un objeto a una asociacin has_and_belongs_to_many , ese objeto se guarda


automticamente (para actualizar la tabla de unin). Si asigna varios objetos en una
sentencia, todos ellos se guardan.

Si alguno de estos registros falla debido a errores de validacin, la instruccin de asignacin


devuelve false y la asignacin en s se cancela.

Si el objeto principal (el que declara la asociacin has_and_belongs_to_many ) no se guarda


(es decir, new_record? Devuelve true ) los objetos secundarios no se guardan cuando se
agregan. Todos los miembros no guardados de la asociacin se guardarn
automticamente cuando se guarda el padre.

Si desea asignar un objeto a una asociacin has_and_belongs_to_many sin guardar el objeto,


utilice el mtodo collection.build .

160
4.5 Callbacks de la asociacin

4.5 Callbacks de la asociacin


Las callbacks normales se conectan al ciclo de vida de los objetos Active Record,
permitindole trabajar con esos objetos en varios puntos. Por ejemplo, puede utilizar una
callbacks before_save para hacer que algo suceda justo antes de guardar un objeto.

Las callbacks de la asociacin son similares a las callbacks normales, pero son activadas
por eventos en el ciclo de vida de una coleccin. Existen cuatro callbacks de asociacin
disponibles:

before_add
after_add
before_remove
after_remove

Define las callbacks de asociacin agregando opciones a la declaracin de asociacin. Por


ejemplo:

class Author < ApplicationRecord


has_many :books, before_add: :check_credit_limit

def check_credit_limit(book)
...
end
end

Rails pasa el objeto que se agrega o se elimina a la callback.

Puede apilar callbacks en un solo evento pasndolas como una matriz:

class Author < ApplicationRecord


has_many :books,
before_add: [:check_credit_limit, :calculate_shipping_charges]

def check_credit_limit(book)
...
end
def calculate_shipping_charges(book)
...
end
end

161
4.5 Callbacks de la asociacin

Si una callback before_add lanza una excepcin, el objeto no se agrega a la coleccin. Del
mismo modo, si una callback before_remove produce una excepcin, el objeto no se quita
de la coleccin.

162
4.6 Extensiones de la asociacin

4.6 Extensiones de la asociacin


No se limita a la funcionalidad que Rails crea automticamente en objetos proxy de
asociacin. Tambin puede ampliar estos objetos a travs de mdulos annimos,
agregando nuevos buscadores, creadores u otros mtodos. Por ejemplo:

class Author < ApplicationRecord


has_many :books do
def find_by_book_prefix(book_number)
find_by(category_id: book_number[0..2])
end
end
end

Si tiene una extensin que debe ser compartida por muchas asociaciones, puede utilizar un
mdulo de extensin con nombre. Por ejemplo:

module FindRecentExtension
def find_recent
where("created_at > ?", 5.days.ago)
end
end

class Author < ApplicationRecord


has_many :books, -> { extending FindRecentExtension }
end

class Supplier < ApplicationRecord


has_many :deliveries, -> { extending FindRecentExtension }
end

Las extensiones pueden referirse a los internos del proxy de asociacin utilizando estos tres
atributos del accesor de proxy_association :

proxy_association.owner devuelve el objeto al que pertenece la asociacin.

proxy_association.reflection devuelve el objeto de reflexin que describe la

asociacin.
proxy_association.target devuelve el objeto asociado para belongs_to o has_one , o

la coleccin de objetos asociados para has_many o has_and_belongs_to_many .

163
5- Herencia de tabla nica

5. Herencia de tabla nica


A veces, es posible que desee compartir campos y comportamiento entre diferentes
modelos. Digamos que tenemos modelos de coches, motos y bicicletas. Queremos
compartir los campos de color y precio y algunos mtodos para todos ellos, pero teniendo
un comportamiento especfico para cada uno, y controladores separados tambin.

Rails hace esto muy fcil. Primero, vamos a generar la base Modelo del vehculo:

$ rails generate model vehicle type:string color:string price:decimal{10.2}

Ha notado que estamos agregando un campo de type ? Dado que todos los modelos se
guardarn en una sola tabla de base de datos, Rails guardar en esta columna el nombre
del modelo que se est guardando. En nuestro ejemplo, esto puede ser Car , Motorcycle
o Bicycle . STI no funcionar sin un campo type en la tabla.

A continuacin, vamos a generar los tres modelos que heredan de Vehculo. Para ello,
podemos usar la opcin --parent=PARENT , que generar un modelo que hereda del padre
especificado y sin migracin equivalente (ya que la tabla ya existe).

Por ejemplo, para generar el modelo de Car :

$ rails generate model car --parent=Vehicle

El modelo generado se ver as:

class Car < Vehicle


end

Esto significa que todos los comportamientos agregados a Vehculo tambin estn
disponibles para Car , como asociaciones, mtodos pblicos, etc.

La creacin de un Car lo guardar en la tabla de Vehicle con Car como el campo


Type :

Car.create(color: 'Red', price: 10000)

Generar el siguiente SQL :

164
5- Herencia de tabla nica

INSERT INTO "vehicles" ("type", "color", "price") VALUES ('Car', 'Red', 10000)

Consultar los registros de automviles solo buscar vehculos que sean automviles:

Car.all

Ejecutar una consulta como:

SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."type" IN ('Car')

165
VI- Interfaz de Consulta de Active Record

VI- Interfaz de Consulta de Active Record


Esta gua cubre diferentes maneras de recuperar datos de la base de datos con Active
Record.

Despus de leer esta gua, sabr:

Cmo encontrar registros usando una variedad de mtodos y condiciones.


Cmo especificar el orden, los atributos recuperados, el agrupamiento y otras
propiedades de los registros encontrados.
Cmo utilizar la carga ansiosa para reducir el nmero de consultas de base de datos
necesarias para la recuperacin de datos.
Cmo utilizar los mtodos de bsqueda dinmica.
Cmo utilizar el mtodo de encadenamiento para utilizar mltiples mtodos Active
Record juntos.
Cmo comprobar la existencia de registros particulares.
Cmo realizar varios clculos en los modelos Active Record.
Cmo ejecutar EXPLAIN en las relaciones.

Si est acostumbrado a usar SQL sin procesar para buscar registros de base de datos,
generalmente encontrar que hay mejores maneras de llevar a cabo las mismas
operaciones en Rails. Active Record le asla de la necesidad de usar SQL en la mayora de
los casos.

Los ejemplos de cdigo a lo largo de esta gua se referirn a uno o ms de los siguientes
modelos:

Todos los modelos siguientes usan id como clave principal, a menos que se especifique
lo contrario.

166
VI- Interfaz de Consulta de Active Record

class Client < ApplicationRecord


has_one :address
has_many :orders
has_and_belongs_to_many :roles
end

class Address < ApplicationRecord


belongs_to :client
end

class Order < ApplicationRecord


belongs_to :client, counter_cache: true
end

class Role < ApplicationRecord


has_and_belongs_to_many :clients
end

Active Record realizar consultas en la base de datos para usted y es compatible con la
mayora de los sistemas de bases de datos, incluyendo MySQL, MariaDB, PostgreSQL y
SQLite. Independientemente del sistema de base de datos que est utilizando, el formato
del mtodo Active Record siempre ser el mismo.

167
1- Recuperacin de objetos desde la base de datos

1. Recuperacin de objetos desde la base


de datos
Para recuperar objetos de la base de datos, Active Record proporciona varios mtodos
finder . Cada mtodo finder le permite pasar argumentos para realizar ciertas consultas

en su base de datos sin escribir SQL sin procesar.

Los mtodos son:

find
create_with
distinct
eager_load
extending
from
group
having
includes
joins
left_outer_joins
limit
lock
none
offset
order
preload
readonly
references
reorder
reverse_order
select
where

Los mtodos finder que devuelven una coleccin, como where y group , devuelven una
instancia de ActiveRecord::Relation . Los mtodos que encuentran una sola entidad, como
find y first , devuelven una sola instancia del modelo.

La operacin primaria de Model.find(options) se puede resumir como:

Convierta las options suministradas en una consulta SQL equivalente.


Dispara la consulta SQL y recupera los resultados correspondientes de la base de
datos.
Instancia el objeto Ruby equivalente del modelo apropiado para cada fila resultante.
Ejecute after_find y, a continuacin, los callbacks de after_initialize , si las

168
1- Recuperacin de objetos desde la base de datos

hubiere.

1.1 Recuperacin de un nico objeto


Active Record proporciona varias formas diferentes de recuperar un solo objeto.

1.1.1 find
Utilizando el mtodo find , puede recuperar el objeto correspondiente a la clave primaria
especificada que coincida con las opciones suministradas. Por ejemplo:

# Find the client with primary key (id) 10.


client = Client.find(10)
# => #<Client id: 10, first_name: "Ryan">

El equivalente SQL de lo anterior es:

SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1

El mtodo find generar una excepcin ActiveRecord::RecordNotFound si no se encuentra


ningn registro coincidente.

Tambin puede utilizar este mtodo para consultar varios objetos. Llame al mtodo find y
pase en una matriz de claves primarias. La devolucin ser una matriz que contiene todos
los registros coincidentes para las claves primarias suministradas. Por ejemplo:

# Find the clients with primary keys 1 and 10.


client = Client.find([1, 10]) # Or even Client.find(1, 10)
# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]

El equivalente SQL de lo anterior es:

SELECT * FROM clients WHERE (clients.id IN (1,10))

El mtodo find generar una excepcin ActiveRecord::RecordNotFound a menos que se


encuentre un registro coincidente para todas las claves primarias suministradas.

1.1.2 take
El mtodo take recupera un registro sin ningn orden implcito. Por ejemplo:

169
1- Recuperacin de objetos desde la base de datos

client = Client.take
# => #<Client id: 1, first_name: "Lifo">

El equivalente SQL de lo anterior es:

SELECT * FROM clients LIMIT 1

El mtodo take devuelve nil si no se encuentra ningn registro y no se generar


ninguna excepcin.

Puede pasar un argumento numrico al mtodo take para devolver ese nmero de
resultados. Por ejemplo

client = Client.take(2)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 220, first_name: "Sara">
# ]

El equivalente SQL de lo anterior es:

SELECT * FROM clients LIMIT 2

El mtodo take! se comporta exactamente como take , excepto que disparar


ActiveRecord::RecordNotFound si no se encuentra ningn registro coincidente.

El registro recuperado puede variar dependiendo del motor de base de datos.

1.1.3 first
El mtodo first encuentra el primer registro ordenado por clave principal
(predeterminado). Por ejemplo:

client = Client.first
# => #<Client id: 1, first_name: "Lifo">

El equivalente SQL de lo anterior es:

SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1

170
1- Recuperacin de objetos desde la base de datos

El mtodo first devuelve nil si no se encuentra ningn registro coincidente y no se


genera ninguna excepcin.

Si su scope predeterminado contiene un mtodo order , first devolver el primer


registro de acuerdo con ese order.

Puede pasar un argumento numrico al primer mtodo para traer ese nmero de
resultados. Por ejemplo

client = Client.first(3)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 2, first_name: "Fifo">,
# #<Client id: 3, first_name: "Filo">
# ]

El equivalente SQL de lo anterior es:

SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3

En una coleccin que se ordena utilizando order , first devolver el primer registro
ordenado por el atributo especificado para el order .

client = Client.order(:first_name).first
# => #<Client id: 2, first_name: "Fifo">

El equivalente SQL de lo anterior es:

SELECT * FROM clients ORDER BY clients.first_name ASC LIMIT 1

El mtodo first! se comporta exactamente como first , excepto que disparar


ActiveRecord::RecordNotFound si no se encuentra ningn registro coincidente.

1.1.4 last
El mtodo last encuentra el ltimo registro ordenado por clave principal (predeterminado).
Por ejemplo:

client = Client.last
# => #<Client id: 221, first_name: "Russel">

El equivalente SQL de lo anterior es:

171
1- Recuperacin de objetos desde la base de datos

SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1

El mtodo last devuelve nil si no se encuentra ningn registro coincidente y no se


generar ninguna excepcin.

Si su scope predeterminado contiene un mtodo order , last devolver el ltimo registro


de acuerdo con este order .

Puede pasar un argumento numrico al mtodo last para traer ese nmero de resultados.
Por ejemplo

client = Client.last(3)
# => [
# #<Client id: 219, first_name: "James">,
# #<Client id: 220, first_name: "Sara">,
# #<Client id: 221, first_name: "Russel">
# ]

El equivalente SQL de lo anterior es:

SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3

En una coleccin que se ordena utilizando order , last devolver el ltimo registro
ordenado por el atributo especificado para order .

client = Client.order(:first_name).last
# => #<Client id: 220, first_name: "Sara">

El equivalente SQL de lo anterior es:

SELECT * FROM clients ORDER BY clients.first_name DESC LIMIT 1

El mtodo last! se comporta exactamente como last , excepto que disparar


ActiveRecord::RecordNotFound si no se encuentra ningn registro coincidente.

1.1.5 find_by
El mtodo find_by encuentra el primer registro que coincide con algunas condiciones. Por
ejemplo:

172
1- Recuperacin de objetos desde la base de datos

Client.find_by first_name: 'Lifo'


# => #<Client id: 1, first_name: "Lifo">

Client.find_by first_name: 'Jon'


# => nil

Es equivalente a escribir

Client.where(first_name: 'Lifo').take

El equivalente SQL de lo anterior es:

SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1

El mtodo find_by! se comporta exactamente como find_by , excepto que disparar


ActiveRecord::RecordNotFound si no se encuentra ningn registro coincidente. Por ejemplo:

Client.find_by! first_name: 'does not exist'


# => ActiveRecord::RecordNotFound

Esto es equivalente a escribir

Client.where(first_name: 'does not exist').take!

1.2 Recuperacin de varios objetos en lotes


A menudo necesitamos iterar sobre un gran conjunto de registros, como cuando enviamos
un boletn a un gran grupo de usuarios o cuando exportamos datos.

Esto puede parecer sencillo:

# This may consume too much memory if the table is big.


User.all.each do |user|
NewsMailer.weekly(user).deliver_now
end

Pero este enfoque se vuelve poco prctico a medida que aumenta el tamao de la tabla, ya
que User.all.each le indica a Active Record que busque toda la tabla en un solo paso,
construya un objeto de modelo por fila y guarde toda la matriz de objetos de modelo en
memoria. De hecho, si tenemos un gran nmero de registros, toda la coleccin puede
exceder la cantidad de memoria disponible.

173
1- Recuperacin de objetos desde la base de datos

Rails proporciona dos mtodos que solucionan este problema dividiendo los registros en
lotes compatibles con la memoria para su procesamiento. El primer mtodo, find_each ,
recupera un lote de registros y luego produce cada registro en el bloque individualmente
como un modelo. El segundo mtodo, find_in_batches , recupera un lote de registros y
luego produce el lote completo en el bloque como una matriz de modelos.

Los mtodos find_each y find_in_batches estn diseados para su uso en el


procesamiento por lotes de un gran nmero de registros que no caben en la memoria
de una sola vez. Si slo necesita ejecutar la bsqueda en alrededor de mil registros,
los mtodos de bsqueda regulares son la opcin preferida.

1.2.1 find_each
El mtodo find_each recupera los registros en lotes y luego reproduce cada uno en el
bloque. En el ejemplo siguiente, find_each recupera usuarios en lotes de 1000 y los
entrega al bloque uno a uno:

User.find_each do |user|
NewsMailer.weekly(user).deliver_now
end

Este proceso se repite, obteniendo ms lotes segn sea necesario, hasta que todos los
registros hayan sido procesados.

find_each trabaja sobre las clases de los modelos, como se ha visto anteriormente, y

tambin sobre las relaciones:

User.where(weekly_subscriber: true).find_each do |user|


NewsMailer.weekly(user).deliver_now
end

Siempre y cuando no tengan un order , ya que el mtodo necesita forzar un orden interno
para iterar.

Si un mtodo order est presente en el receptor, el comportamiento depende del indicador


config.active_record.error_on_ignored_order . Si es true , ArgumentError se dispara, de lo

contrario se ignora el orden y se emite una advertencia, que es la predeterminada. Esto


puede anularse con la opcin: error_on_ignore , explicada abajo.

1.2.1.1 Opciones para find_each

:batch_size

174
1- Recuperacin de objetos desde la base de datos

La opcin: batch_size le permite especificar el nmero de registros que se recuperarn en


cada lote, antes de pasar individualmente al bloque. Por ejemplo, para recuperar registros
en lotes de 5000:

User.find_each(batch_size: 5000) do |user|


NewsMailer.weekly(user).deliver_now
end

:start

De forma predeterminada, los registros se obtienen en orden ascendente de la clave


principal, que debe ser un entero. La opcin: start le permite configurar el primer ID de
la secuencia siempre que el ID ms bajo no sea el que necesita. Esto sera til, por
ejemplo, si desea reanudar un proceso por lotes interrumpido, siempre que haya guardado
el ltimo ID procesado como punto de control.

Por ejemplo, para enviar boletines slo a usuarios con la clave principal a partir de 2000:

User.find_each(start: 2000) do |user|


NewsMailer.weekly(user).deliver_now
end

:finish

Similar a la opcin: start ,: finish le permite configurar el ltimo ID de la secuencia


siempre que el ID ms alto no sea el que usted necesita. Esto sera til, por ejemplo, si
desea ejecutar un proceso por lotes utilizando un subconjunto de registros basado en:
start y: finish .

Por ejemplo, para enviar boletines slo a usuarios con la clave principal desde 2000 hasta
10000:

User.find_each(start: 2000, finish: 10000) do |user|


NewsMailer.weekly(user).deliver_now
end

Otro ejemplo sera si desea que varios workers manejen la misma cola de procesamiento.
Podra hacer que cada worker maneje 10000 registros estableciendo las opciones
apropiadas: start y: finish en cada worker.

:error_on_ignore

Anula la configuracin de la aplicacin para especificar si se debe generar un error cuando


hay un pedido en la relacin.

175
1- Recuperacin de objetos desde la base de datos

1.2.2 find_in_batches
El mtodo find_in_batches es similar a find_each , ya que ambos recuperan lotes de
registros. La diferencia es que find_in_batches produce lotes al bloque como un array de
modelos, en lugar de individualmente. El ejemplo siguiente ceder al bloque suministrado
una matriz de hasta 1000 facturas a la vez, con el bloque final que contiene las facturas
restantes:

# Give add_invoices an array of 1000 invoices at a time.


Invoice.find_in_batches do |invoices|
export.add_invoices(invoices)
end

find_in_batches trabaja en las clases de los modelos, como se ha visto anteriormente, y

tambin en las relaciones:

Invoice.pending.find_in_batches do |invoice|
pending_invoices_export.add_invoices(invoices)
end

Siempre y cuando no tengan un order , ya que el mtodo necesita forzar un orden interno
para iterar.

1.2.2.1 Opciones para find_in_batches

El mtodo find_in_batches acepta las mismas opciones que find_each .

176
2- Condiciones

2. Condiciones
El mtodo where permite especificar condiciones para limitar los registros devueltos, que
representan la parte WHERE de la sentencia SQL . Las condiciones se pueden especificar
como una cadena, una matriz o un hash.

2.1 Condiciones con cadenas puras


Si desea agregar condiciones a su bsqueda, slo debe especificarlas all, solo agregue
Client.where("orders_count = '2'") . Esto encontrar todos los clientes donde el valor del

campo orders_count es 2.

Construir sus propias condiciones como cadenas puras puede dejarlo vulnerable a las
explotaciones de SQL injection. Por ejemplo, Client.where("first_name LIKE '%#
{params[:first_name]}%'") no es seguro. Consulte la seccin siguiente para conocer la

forma correcta de manejar las condiciones usando una matriz.

2.2 Condiciones con Array


Y si ese nmero pudiera variar, digamos como un argumento de alguna parte? La
bsqueda tomara entonces la forma:

Client.where("orders_count = ?", params[:orders])

Active Record tomar el primer argumento como la cadena de condiciones y cualquier


argumento adicional lo reemplazar por los signos de interrogacin (?)

Si desea especificar varias condiciones:

Client.where("orders_count = ? AND locked = ?", params[:orders], false)

En este ejemplo, el primer signo de interrogacin se reemplazar con el valor en


params[:orders] y el segundo se sustituir por la representacin SQL de false , que

depende del adapter.

Este cdigo es altamente recomendable:

Client.where("orders_count = ?", params[:orders])

177
2- Condiciones

En comparacin a este cdigo:

Client.where("orders_count = #{params[:orders]}")

Debido a la seguridad del argumento. Poner la variable directamente en la cadena de


condiciones pasar la variable a la base de datos tal como est. Esto significa que ser una
variable sin escapar directamente de un usuario que puede tener intencin maliciosa. Si lo
hace, pone toda su base de datos en riesgo, porque una vez que un usuario se entera de
que pueden explotar su base de datos puede hacer casi cualquier cosa a la misma. Nunca
pongas tus argumentos directamente dentro de la cadena de condiciones.

2.2.1 Condiciones de "Placeholder"


Similar al estilo de reemplazo (?) de los parmetros, tambin puede especificar las claves
en su cadena de condiciones junto con las claves/valore correspondientes a un hash:

Client.where("created_at >= :start_date AND created_at <= :end_date",


{start_date: params[:start_date], end_date: params[:end_date]})

Esto hace ms clara la legibilidad si usted tiene un gran nmero de condiciones variables.

2.3 Condiciones con Hash


Active Record tambin le permite pasar en las condiciones un hash que pueden aumentar la
legibilidad de su sintaxis de condiciones. Con las condiciones con hash, usted pasa en un
hash con las llaves de los campos que usted quiere buscar y los valores de cmo usted
desea buscarlos:

Slo la igualdad, los rangos y la comprobacin de subconjuntos son posibles para las
condiciones con Hash.

2.3.1 Condiciones de Igualdad

Client.where(locked: true)

Esto generar un SQL como ste:

SELECT * FROM clients WHERE (clients.locked = 1)

El nombre del campo tambin puede ser una cadena:

178
2- Condiciones

Client.where('locked' => true)

En el caso de una relacin belongs_to , se puede utilizar una clave de asociacin para
especificar el modelo si se utiliza un objeto Active Record como valor. Este mtodo funciona
con relaciones polimrficas tambin.

Article.where(author: author)
Author.joins(:articles).where(articles: { author: author })

Los valores no pueden ser smbolos. Por ejemplo, no puede hacer Client.where(status:
:active)

2.3.2 Condiciones de Rango

Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)

Esto encontrar todos los clientes creados ayer mediante una instruccin BETWEEN SQL :

SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '200
8-12-22 00:00:00')

Esto demuestra una sintaxis ms corta para los ejemplos en Array Conditions

2.3.3 Condiciones de subconjunto


Si desea buscar registros utilizando la expresin IN , puede pasar un array al hash de
condiciones:

Client.where(orders_count: [1,3,5])

Este cdigo generar un SQL como este:

SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))

2.4 Condiciones con NOT


Las consultas SQL NOT pueden ser construidas por where.not :

Client.where.not(locked: true)

179
2- Condiciones

En otras palabras, esta consulta puede ser generada llamando un where sin argumento, y
entonces inmediatamente lo concatena con not pasar a where las condiciones. Esto
generar SQL como este:

SELECT * FROM clients WHERE (clients.locked != 1)

180
3- Ordenando

3. Ordenando
Para recuperar registros de la base de datos en un orden especfico, puede utilizar el
mtodo order .

Por ejemplo, si obtiene un conjunto de registros y desea ordenarlos en orden ascendente


por el campo created_at en su tabla:

Client.order(:created_at)
# OR
Client.order("created_at")

Tambin puede especificar ASC o DESC :

Client.order(created_at: :desc)
# OR
Client.order(created_at: :asc)
# OR
Client.order("created_at DESC")
# OR
Client.order("created_at ASC")

O ordenar por mltiples campos:

Client.order(orders_count: :asc, created_at: :desc)


# OR
Client.order(:orders_count, created_at: :desc)
# OR
Client.order("orders_count ASC, created_at DESC")
# OR
Client.order("orders_count ASC", "created_at DESC")

Si desea llamar order varias veces, los mtodos order siguientes se aadirn al primero:

Client.order("orders_count ASC").order("created_at DESC")


# SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC

Si est utilizando MySQL 5.7.5 o superior, a continuacin, en la seleccin de campos


de un conjunto de resultados utilizando mtodos como select , pluck y ids ; El
mtodo order generar una excepcin ActiveRecord::StatementInvalid a menos que
el campo(s) utilizado(s) en la clusula order estn incluidos en la lista de seleccin.
Consulte la siguiente seccin para seleccionar campos del conjunto de resultados.

181
3- Ordenando

182
4- Seleccionando campos especficos

4. Seleccionando campos especficos


De forma predeterminada, Model.find selecciona todos los campos del conjunto de
resultados mediante select * .

Para seleccionar slo un subconjunto de campos del conjunto de resultados, puede


especificar el subconjunto mediante el mtodo select .

Por ejemplo, para seleccionar slo las columnas viewable_by y locked :

Client.select("viewable_by, locked")

La consulta SQL utilizada por esta llamada de bsqueda sera algo como:

SELECT viewable_by, locked FROM clients

Tenga cuidado porque esto tambin significa que est inicializando un objeto de modelo con
slo los campos que ha seleccionado. Si intenta acceder a un campo que no est en el
registro inicializado, recibir:

ActiveModel::MissingAttributeError: missing attribute: <attribute>

Donde <attribute> es el atributo que solicit. El mtodo id no disparar el


ActiveRecord::MissingAttributeError, as que tenga cuidado al trabajar con asociaciones

porque necesitan el mtodo id para funcionar correctamente.

Si desea capturar slo un registro por valor nico en un campo determinado, puede utilizar
distinct :

Client.select(:name).distinct

Esto generara un SQL como:

SELECT DISTINCT name FROM clients

Tambin puede eliminar la restriccin de unicidad:

183
4- Seleccionando campos especficos

query = Client.select(:name).distinct
# => Retorna names nicos

query.distinct(false)
# => Returns todos los names, incluso si estn duplicados

184
5- Lmite y desplazamiento

5. Lmite y desplazamiento
Para aplicar LIMIT al SQL disparado por el Model.find , puede especificar el lmite
utilizando los mtodos limit y offset en la relacin.

Puede utilizar limit para especificar el nmero de registros que se recuperarn y utilizar
offeset para especificar el nmero de registros a omitir antes de comenzar a devolver los

registros. Por ejemplo:

Client.limit(5)

Devolver un mximo de 5 clientes y, como no especifica ningn desplazamiento, devolver


los primeros 5 de la tabla. El SQL que ejecuta se ve as:

SELECT * FROM clients LIMIT 5

Aadiendo offset a este ejemplo:

Client.limit(5).offset(30)

Retornar un mximo de 5 clientes comenzando con el 31. El SQL se ve as:

SELECT * FROM clients LIMIT 5 OFFSET 30

185
6- Grupos

6. Grupos
Para aplicar una clusula GROUP BY al SQL disparado por el finder , puede utilizar el
mtodo de group .

Por ejemplo, si desea encontrar una coleccin de las fechas en las que se crearon los
pedidos:

Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("dat


e(created_at)")

Y esto le dar un nico objeto Order para cada fecha en la que haya pedidos en la base
de datos.

El SQL que se ejecutara sera algo como esto:

SELECT date(created_at) as ordered_date, sum(price) as total_price


FROM orders
GROUP BY date(created_at)

6.1 Total de elementos agrupados


Para obtener el total de elementos agrupados en una sola consulta, llame a count
despus de group .

Order.group(:status).count
# => { 'awaiting_approval' => 7, 'paid' => 12 }

El SQL que se ejecutara sera algo como esto:

SELECT COUNT (*) AS count_all, status AS status


FROM "orders"
GROUP BY status

186
7- Having

7. Having
SQL utiliza la clusula HAVING para especificar las condiciones en los campos GROUP BY .

Puede agregar la clusula HAVING al SQL disparado por el Model.find aadiendo el


mtodo having al find .

Por ejemplo:

Order.select("date(created_at) as ordered_date, sum(price) as total_price").


group("date(created_at)").having("sum(price) > ?", 100)

El SQL que se ejecutara sera algo como esto:

SELECT date(created_at) as ordered_date, sum(price) as total_price


FROM orders
GROUP BY date(created_at)
HAVING sum(price) > 100

Esto devuelve la fecha y el precio total de cada objeto de pedido, agrupados por el da en
que fueron pedidos y donde el precio es superior a $ 100.

187
8- Sobre-escribiendo condiciones

8- Sobre-escribiendo condiciones
8.1 Unscope
Puede especificar ciertas condiciones que se eliminarn mediante el mtodo unscope. Por
ejemplo:

Article.where('id > 10').limit(20).order('id asc').unscope(:order)

El SQL que se ejecutara:

SELECT * FROM articles WHERE id > 10 LIMIT 20

# Consulta original sin `unscope`


SELECT * FROM articles WHERE id > 10 ORDER BY id asc LIMIT 20

Tambin puede hacer unscope sobre clusulas where especficas. Por ejemplo:

Article.where(id: 10, trashed: false).unscope(where: :id)


# SELECT "articles".* FROM "articles" WHERE trashed = 0

Una relacin que ha utilizado el unscope afectar cualquier relacin en la que se haga
merge :

Article.order('id asc').merge(Article.unscope(:order))
# SELECT "articles".* FROM "articles"

8.2 only
Tambin puede anular las condiciones utilizando el mtodo only . Por ejemplo:

Article.where('id > 10').limit(20).order('id desc').only(:order, :where)

El SQL que se ejecutara es el siguiente:

SELECT * FROM articles WHERE id > 10 ORDER BY id DESC

# Consulta original sin 'only`


SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20

188
8- Sobre-escribiendo condiciones

8.3 reorder
El mtodo reorder reemplaza el orden del scope predeterminado. Por ejemplo:

class Article < ApplicationRecord


has_many :comments, -> { order('posted_at DESC') }
end

Article.find(10).comments.reorder('name')

El SQL que se ejecutara:

SELECT * FROM articles WHERE id = 10


SELECT * FROM comments WHERE article_id = 10 ORDER BY name

En el caso en que no se use la clusula reorder , el SQL ejecutado sera:

SELECT * FROM articles WHERE id = 10


SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC

8.4 reverse_order
El mtodo reverse_order invierte la clusula de ordenacin si se especifica.

Client.where("orders_count > 10").order(:name).reverse_order

El SQL que se ejecutara:

SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC

Si no se especifica ninguna clusula de orden en la consulta, la reverse_order ordena la


clave principal en orden inverso.

Client.where("orders_count > 10").reverse_order

El SQL que se ejecutara:

SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC

Este mtodo no acepta argumentos.

189
8- Sobre-escribiendo condiciones

8.5 rewhere
El mtodo rewhere reemplaza una condicin existente, denominada where . Por ejemplo:

Article.where(trashed: true).rewhere(trashed: false)

El SQL que se ejecutara:

SELECT * FROM articles WHERE `trashed` = 0

En caso de que no se utilice la clusula rewhere ,

Article.where(trashed: true).where(trashed: false)

El SQL ejecutado sera:

SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0

190
9- Relacin NULL

9. Relacin NULL
El mtodo none devuelve una relacin encadenable sin registros. Cualquier condicin
subsiguiente encadenada a la relacin devuelta continuar generando relaciones vacas.
Esto es til en escenarios donde se necesita una respuesta encadenable a un mtodo o un
scope que podra devolver resultados cero.

Article.none # Devuelve una relacin vaca y no activa ninguna consulta.

# Se espera que el mtodo visible_articles a continuacin devuelva una Relacin.


@articles = current_user.visible_articles.where(name: params[:name])

def visible_articles
case role
when 'Country Manager'
Article.where(country: country)
when 'Reviewer'
Article.published
when 'Bad User'
Article.none # => devolviendo [] o nil interrumpe el cdigo de llamada en este caso

end
end

191
10- Objetos de slo lectura

10- Objetos de slo lectura


Active Record proporciona el mtodo readonly en una relacin para rechazar
explcitamente la modificacin de cualquiera de los objetos devueltos. Cualquier intento de
alterar un registro de slo lectura no tendr xito, generando una excepcin
ActiveRecord::ReadOnlyRecord .

client = Client.readonly.first
client.visits += 1
client.save

Client se establece explcitamente para ser un objeto de slo lectura, el cdigo anterior

generar una excepcin ActiveRecord::ReadOnlyRecord al llamar a client.save con un valor


actualizado de visitas.

192
11- Bloqueo de registros para actualizacin

11. Bloqueo de registros para


actualizacin
El bloqueo es til para prevenir condiciones de carrera al actualizar registros en la base de
datos y garantizar actualizaciones atmicas.

Active Record ofrece dos mecanismos de bloqueo:

Bloqueo optimista
Bloqueo pesimista

11.1 Bloqueo optimista


El bloqueo optimista permite a varios usuarios acceder al mismo registro para ediciones y
asume un mnimo de conflictos con los datos. Esto lo hace comprobando si otro proceso ha
realizado cambios en un registro desde que se abri. Se produce una excepcin
ActiveRecord::StaleObjectError si se ha producido y la actualizacin se ignora.

Columna de bloqueo optimista

Para utilizar el bloqueo optimista, la tabla necesita tener una columna llamada
lock_version del tipo integer . Cada vez que se actualiza el registro, el registro activo

incrementa la columna lock_version . Si se realiza una solicitud de actualizacin con un


valor inferior en el campo lock_version que est actualmente en la columna lock_version
de la base de datos, la solicitud de actualizacin fallar con
ActiveRecord::StaleObjectError . Ejemplo:

c1 = Client.find(1)
c2 = Client.find(1)

c1.first_name = "Michael"
c1.save

c2.name = "should fail"


c2.save # Raises an ActiveRecord::StaleObjectError

Usted es responsable de resolver el conflicto rescatando la excepcin y retrotrayendo,


fusionando o aplicando la lgica comercial necesaria para resolver el conflicto.

Este comportamiento se puede desactivar estableciendo


ActiveRecord::Base.lock_optimistically=false.

193
11- Bloqueo de registros para actualizacin

Para reemplazar el nombre de la columna lock_version , ActiveRecord::Base proporciona


un atributo de clase denominado locking_column:

class Client < ApplicationRecord


self.locking_column = :lock_client_column
end

11.2 Bloqueo pesimista


El bloqueo pesimista utiliza un mecanismo de bloqueo proporcionado por la base de datos
subyacente. Usar lock al crear una relacin obtiene un bloqueo exclusivo en las filas
seleccionadas. Las relaciones que usan lock normalmente se envuelven dentro de una
transaccin para prevenir condiciones de bloqueo.

Por ejemplo:

Item.transaction do
i = Item.lock.first
i.name = 'Jones'
i.save!
end

La sesin anterior produce el siguiente SQL para un backend de MySQL :

SQL (0.2ms) BEGIN


Item Load (0.3ms) SELECT * FROM `items` LIMIT 1 FOR UPDATE
Item Update (0.4ms) UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name`
= 'Jones' WHERE `id` = 1
SQL (0.8ms) COMMIT

Tambin puede pasar el SQL sin procesar al mtodo de bloqueo para permitir diferentes
tipos de bloqueos. Por ejemplo, MySQL tiene una expresin llamada LOCK IN SHARE MODE
donde puede bloquear un registro pero an as permitir que otras consultas lo lean. Para
especificar esta expresin simplemente entre en la opcin lock :

Item.transaction do
i = Item.lock("LOCK IN SHARE MODE").find(1)
i.increment!(:views)
end

Si ya tiene una instancia de su modelo, puede iniciar una transaccin y adquirir el bloqueo
de una vez usando el siguiente cdigo:

194
11- Bloqueo de registros para actualizacin

item = Item.first
item.with_lock do
# This block is called within a transaction,
# item is already locked.
item.increment!(:views)
end

195
12- Unir tablas

12. Unir tablas


Active Record proporciona dos mtodos finder para especificar clusulas JOIN en el SQL
resultante: joins y left_outer_joins . Mientras que las combinaciones se deben utilizar
para INNER JOIN o consultas personalizadas, left_outer_joins se utiliza para consultas
utilizando LEFT OUTER JOIN .

12.1 joins
Hay varias maneras de utilizar el mtodo joins.

12.1.1 Uso de un fragmento SQL de cadena


Slo puede suministrar el SQL sin especificar la clusula JOIN para unir:

Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = '


t'")

Esto resultar en el siguiente SQL :

SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id = authors.id AND pos
ts.published = 't'

12.1.2 Uso de Array/Hash de Asociaciones Nombradas


Active Record le permite utilizar los nombres de las asociaciones definidas en el modelo
como un acceso directo para especificar las clusulas JOIN para esas asociaciones
cuando se utiliza el mtodo join .

Por ejemplo, considere los siguientes Modelos de Category, Article, Comment, Guest y Tag

196
12- Unir tablas

class Category < ApplicationRecord


has_many :articles
end

class Article < ApplicationRecord


belongs_to :category
has_many :comments
has_many :tags
end

class Comment < ApplicationRecord


belongs_to :article
has_one :guest
end

class Guest < ApplicationRecord


belongs_to :comment
end

class Tag < ApplicationRecord


belongs_to :article
end

Ahora todo lo siguiente producir las consultas join esperadas utilizando INNER JOIN :

12.1.2.1 Joining a una asociacin nica

Category.joins(:articles)

Esto produce:

SELECT categories.* FROM categories


INNER JOIN articles ON articles.category_id = categories.id

O, en espaol: "devolver un objeto Category para todas las categoras con artculos".
Tenga en cuenta que ver categoras duplicadas si ms de un artculo tiene la misma
categora. Si desea categoras nicas, puede utilizar Category.joins(:articles).distinct .

12.1.3 Joining a mltiples asociaciones

Article.joins(:category, :comments)

Esto produce:

197
12- Unir tablas

SELECT articles.* FROM articles


INNER JOIN categories ON articles.category_id = categories.id
INNER JOIN comments ON comments.article_id = articles.id

O, en espaol: "devolver todos los artculos que tienen una categora y al menos un
comentario". Tenga en cuenta que los artculos con varios comentarios se mostrarn varias
veces.

12.1.3.1 Unir Asociaciones Anidadas (Nivel nico)

Article.joins(comments: :guest)

Esto produce:

SELECT articles.* FROM articles


INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id

O, en espaol: "devolver todos los artculos que tienen un comentario hecho por un
invitado."

12.1.3.2 Unir Asociaciones Anidadas (Nivel Mltiple)

Category.joins(articles: [{ comments: :guest }, :tags])

Esto produce:

SELECT categories.* FROM categories


INNER JOIN articles ON articles.category_id = categories.id
INNER JOIN comments ON comments.article_id = articles.id
INNER JOIN guests ON guests.comment_id = comments.id
INNER JOIN tags ON tags.article_id = articles.id

O, en espaol: "devolver todas las categoras que tienen artculos, donde esos artculos
tienen un comentario hecho por un invitado, y donde esos artculos tambin tienen una
etiqueta".

12.1.4 Especificacin de condiciones en las joined tables

198
12- Unir tablas

Puede especificar condiciones en las tablas unidas usando las condiciones regulares
Array y String . Las condiciones de hash proporcionan una sintaxis especial para

especificar las condiciones de las tablas unidas:

time_range = (Time.now.midnight - 1.day)..Time.now.midnight


Client.joins(:orders).where('orders.created_at' => time_range)

Una sintaxis alternativa y ms limpia es anidar las condiciones con hash:

time_range = (Time.now.midnight - 1.day)..Time.now.midnight


Client.joins(:orders).where(orders: { created_at: time_range })

Esto encontrar todos los clientes que tienen rdenes que se crearon ayer, de nuevo
utilizando una expresin BETWEEN SQL .

12.2 left_outer_joins
Si desea seleccionar un conjunto de registros, independientemente de si tienen o no
registros asociados, puede utilizar el mtodo left_outer_joins .

Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_co


unt').group('authors.id')

Que produce:

SELECT DISTINCT authors.*, COUNT(posts.*) AS posts_count FROM "authors"


LEFT OUTER JOIN posts ON posts.author_id = authors.id GROUP BY authors.id

Lo que significa: "devolver todos los autores con su numero de posts, si tienen o no tienen
posts en absoluto"

199
13- Asociaciones de carga impaciente

13. Asociaciones de carga impaciente


La carga ansiosa es el mecanismo para cargar los registros asociados de los objetos
devueltos por Model.find utilizando el menor nmero posible de consultas.

Problema de consultas N + 1

Considere el siguiente cdigo, que encuentra 10 clientes e imprime sus cdigos postales:

clients = Client.limit(10)

clients.each do |client|
puts client.address.postcode
end

Este cdigo se ve bien a primera vista. Pero el problema est dentro del nmero total de
consultas ejecutadas. El cdigo anterior ejecuta 1 consulta (para encontrar 10 clientes) + 10
consultas adicionales (uno por cada cliente para cargar la direccin) = 11 consultas en total.

Solucin al problema de consultas N + 1

Active Record le permite especificar de antemano todas las asociaciones que se van a
cargar. Esto es posible especificando el mtodo includes de la llamada Model.find. Con
includes , Active Record garantiza que todas las asociaciones especificadas se cargan

utilizando el nmero mnimo posible de consultas.

Revisando el caso anterior, podramos reescribir Client.limit(10) para cargar


ansiosamente las direcciones:

clients = Client.includes(:address).limit(10)

clients.each do |client|
puts client.address.postcode
end

El cdigo anterior ejecutar slo 2 consultas, en lugar de 11 consultas en el caso anterior:

SELECT * FROM clients LIMIT 10


SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))

13.1 Carga de mltiples asociaciones

200
13- Asociaciones de carga impaciente

Active Record le permite cargar ansiosamente cualquier nmero de asociaciones con una
sola llamada Model.find utilizando un array, hash o un hash anidado de array/hash con el
mtodo includes .

13.1.1 Arreglo de Asociaciones Mltiples

Article.includes(:category, :comments)

Esto carga todos los artculos y la categora asociada y comentarios para cada artculo.

13.1.2 Asociaciones anidadas con Hash

Category.includes(articles: [{ comments: :guest }, :tags]).find(1)

Esto encontrar la categora con id 1 y carga ansiosamente todos los artculos asociados,
las etiquetas y comentarios de los artculos asociados y la asociacin de invitados de cada
comentario.

13.2 Especificacin de condiciones en las asociaciones


cargadas ansiosamente
Aunque Active Record le permite especificar condiciones en las asociaciones cargadas
ansiosamente como joins , la forma recomendada es usar joins en su lugar.

Sin embargo, si tiene que hacer esto, puede usarlo como lo hara normalmente.

Article.includes(:comments).where(comments: { visible: true })

Esto generara una consulta que contiene un LEFT OUTER JOIN , mientras que el mtodo
join generara uno utilizando la funcin INNER JOIN .

SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles"


LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comment
s.visible = 1)

Si no haba ninguna condicin where , esto generara el conjunto normal de dos consultas.

El uso de where slo funcionar cuando se pasa un Hash. Para los fragmentos SQL,
debe utilizar referencias para forzar joined tables :

201
13- Asociaciones de carga impaciente

Article.includes(:comments).where("comments.visible = true").references(:comments)

Si, en este caso se incluye una consulta, y no hubo comentarios para ningn artculo, todos
los artculos an se cargaran. Mediante el uso de joins (un INNER JOIN), las condiciones
de unin deben coincidir, de lo contrario no se devolvern registros.

Si una asociacin est cargada con impaciencia como parte de una join table , los
campos de una clusula de seleccin personalizada no aparecern en los modelos
cargados. Esto es porque es ambiguo si deben aparecer en el registro principal, o en el
child.

202
14- Ambitos

14. Ambitos
Scoping le permite especificar consultas de uso comn que pueden ser referenciadas como
llamadas de mtodo en los objetos o modelos de asociacin. Con estos mbitos, puede
utilizar todos los mtodos cubiertos anteriormente, como where , join e includes . Todos
los mtodos de mbito devolvern un objeto ActiveRecord::Relation que permitir que se
apliquen otros mtodos (o otros mbitos).

Para definir un mbito simple, utilizamos el mtodo scope dentro de la clase, pasando la
consulta que queremos ejecutar cuando se llama a este mbito:

class Article < ApplicationRecord


scope :published, -> { where(published: true) }
end

Esto es exactamente lo mismo que definir un mtodo de clase, y cul se utiliza es una
cuestin de preferencia personal:

class Article < ApplicationRecord


def self.published
where(published: true)
end
end

Los mbitos tambin se pueden encadenar dentro de otros mbitos:

class Article < ApplicationRecord


scope :published, -> { where(published: true) }
scope :published_and_commented, -> { published.where("comments_count > 0") }
end

Para llamar al mbito published , podemos llamarlo en la clase:

Article.published # => [published articles]

O en una asociacin que consiste en objetos del Article :

category = Category.first
category.articles.published # => [Artculos publicados pertenecientes a esta categora]

203
14- Ambitos

14.1 Pasando argumentos


Su scope puede tomar argumentos:

class Article < ApplicationRecord


scope :created_before, ->(time) { where("created_at < ?", time) }
end

Llama al mbito como si fuera un mtodo de clase:

Article.created_before(Time.zone.now)

Sin embargo, esto es slo la duplicacin de la funcionalidad que se le proporcionara a


usted por medio de un mtodo de clase.

class Article < ApplicationRecord


def self.created_before(time)
where("created_at < ?", time)
end
end

El uso de un mtodo de clase es la forma preferida de aceptar argumentos para mbitos.


Estos mtodos seguirn siendo accesibles en los objetos de la asociacin:

category.articles.created_before(time)

14.2 Uso de condicionales


Su scope puede utilizar condicionales:

class Article < ApplicationRecord


scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }
end

Al igual que los otros ejemplos, esto se comportar de manera similar a un mtodo de
clase.

class Article < ApplicationRecord


def self.created_before(time)
where("created_at < ?", time) if time.present?
end
end

204
14- Ambitos

Sin embargo, hay una advertencia importante: Un scope siempre devolver un objeto
ActiveRecord::Relation , incluso si el condicional se evala como false , mientras que un

mtodo de clase devolver nil . Esto puede causar NoMethodError al encadenar mtodos
de clase con condicionales, si cualquiera de los condicionales devuelve false .

14.3 Aplicacin de un mbito predeterminado


Si deseamos que se aplique un scope a todas las consultas del modelo, podemos utilizar
el mtodo default_scope dentro del propio modelo.

class Client < ApplicationRecord


default_scope { where("removed_at IS NULL") }
end

Cuando las consultas se ejecutan en este modelo, la consulta SQL ahora se ver como
esto:

SELECT * FROM clients WHERE removed_at IS NULL

Si necesita hacer cosas ms complejas con un mbito predeterminado, puede definirlo


como un mtodo de clase:

class Client < ApplicationRecord


def self.default_scope
# Debe devolver un ActiveRecord::Relation.
end
end

El default_scope tambin se aplica durante la creacin/construccin de un registro. No


se aplica durante la actualizacin de un registro. P.ej.:

class Client < ApplicationRecord


default_scope { where(active: true) }
end

Client.new # => #<Client id: nil, active: true>


Client.unscoped.new # => #<Client id: nil, active: nil>

14.4 Fusin de los mbitos


Funciona igual que cuando usamos las clusulas de los mbitos que se combinan utilizando
las condiciones AND .

205
14- Ambitos

class User < ApplicationRecord


scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end

User.active.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" =
'inactive'

Podemos mezclar y combinar el scope y las condiciones where y el sql final tendr todas
las condiciones unidas con AND .

User.active.where(state: 'finished')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" =
'finished'

Si queremos que la ltima clusula where gane entonces se puede usar Relation#merge

User.active.merge(User.inactive)
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'

Una advertencia importante es que default_scope se agregar en el scope y en las


condiciones where .

class User < ApplicationRecord


default_scope { where state: 'pending' }
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state"
= 'active'

User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state"
= 'inactive'

Como se puede ver arriba, el default_scope se est fusionando tanto en el scope como
en las condiciones.

14.5 Eliminacin de todo el mbito

206
14- Ambitos

Si queremos eliminar el scope por cualquier razn, podemos usar el mtodo unscoped .
Esto es especialmente til si se especifica un default_scope en el modelo y no se debe
aplicar para esta consulta en particular.

Client.unscoped.load

Este mtodo elimina todo el mbito y realizar una consulta normal en la tabla.

Client.unscoped.all
# SELECT "clients".* FROM "clients"

Client.where(published: false).unscoped.all
# SELECT "clients".* FROM "clients"

unscoped tambin puede aceptar un bloque.

Client.unscoped {
Client.created_before(Time.zone.now)
}

207
15- Buscadores dinmicos

15. Buscadores dinmicos


Para cada campo (tambin conocido como un atributo) que define en su tabla, Active
Record proporciona un mtodo finder . Si tiene un campo llamado first_name en su
modelo de cliente, por ejemplo, obtiene find_by_first_name gratis desde Active Record. Si
tiene un campo bloqueado en el modelo de cliente, tambin encontrar el mtodo
find_by_locked .

Puede especificar un punto de exclamacin (!) al final de los buscadores dinmicos para
que generen un error ActiveRecord::RecordNotFound si no devuelven registros, como
Client.find_by_name!("Ryan")

Si desea encontrar ambos por nombre y bloqueo, puede encadenar estos buscadores
simplemente escribiendo " and " entre los campos. Por ejemplo,
Client.find_by_first_name_and_locked("Ryan", true).

208
16- Enums

16. Enums
La macro de enumeracin asigna una columna de nmero entero a un conjunto de valores
posibles.

class Book < ApplicationRecord


enum availability: [:available, :unavailable]
end

Esto crear automticamente los mbitos correspondientes para consultar el modelo. Los
mtodos de transicin entre los estados y la consulta del estado actual tambin se agregan.

# Ambos ejemplos a continuacin consultan slo los libros disponibles.


Book.available
# or
Book.where(availability: :available)

book = Book.new(availability: :available)


book.available? # => true
book.unavailable! # => true
book.available? # => false

Lea la documentacin completa sobre enums en los documentos de la API de Rails.

209
17- Comprensin del mtodo Chaining

17. Comprensin del mtodo Chaining


El patrn Active Record implementa el mtodo Chaining , que nos permite usar mltiples
mtodos de Active Record juntos de una manera sencilla y directa.

Puede encadenar mtodos en una instruccin cuando el mtodo anterior devuelve un


ActiveRecord::Relation, como all , where y join . Los mtodos que devuelven un nico
objeto (consulte como recuperar datos de un solo objeto) deben estar al final de la
instruccin.

Hay algunos ejemplos a continuacin. Esta gua no cubrir todas las posibilidades, slo
algunas como ejemplos. Cuando se llama a un mtodo de Active Record, la consulta no se
genera inmediatamente y se enva a la base de datos, esto slo sucede cuando los datos
son realmente necesitados. As, cada ejemplo a continuacin genera una sola consulta.

17.1 Recuperacin de datos filtrados de multiples tablas

Person
.select('people.id, people.name, comments.text')
.joins(:comments)
.where('comments.created_at > ?', 1.week.ago)

El resultado debe ser algo como esto:

SELECT people.id, people.name, comments.text


FROM people
INNER JOIN comments
ON comments.person_id = people.id
WHERE comments.created_at > '2015-01-01'

17.2 Recuperacin de datos especficos de varias tablas

Person
.select('people.id, people.name, companies.name')
.joins(:company)
.find_by('people.name' => 'John') # Esta debe ser la ltima

Lo anterior debera generar:

210
17- Comprensin del mtodo Chaining

SELECT people.id, people.name, companies.name


FROM people
INNER JOIN companies
ON companies.person_id = people.id
WHERE people.name = 'John'
LIMIT 1

Tenga en cuenta que si una consulta coincide con varios registros, find_by buscar
slo el primero e ignorar los dems (consulte la instruccin LIMIT 1 anterior).

211
18- Buscar o construir un nuevo objeto

18. Buscar o construir un nuevo objeto


Es comn que necesite encontrar un registro o crearlo si no existe. Puede hacerlo con los
mtodos find_or_create_by o con find_or_create_by! .

18.1 find_or_create_by
El mtodo find_or_create_by comprueba si existe un registro con los atributos
especificados. Si no lo hace, entonces se llama a create . Veamos un ejemplo.

Suponga que desea buscar un cliente llamado 'Andy', y si no hay ninguno, cree uno. Puede
hacerlo ejecutando:

Client.find_or_create_by(first_name: 'Andy')
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: true, created_at: "2
011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">

El SQL generado por este mtodo se ve as:

SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1


BEGIN
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES (
'2011-08-30 05:22:57', 'Andy', 1, NULL, '2011-08-30 05:22:57')
COMMIT

find_or_create_by devuelve el registro que ya existe o el nuevo registro. En nuestro caso,

no tenemos un cliente llamado Andy por lo que el registro se crea y lo devuelve.

Es posible que el nuevo registro no se guarde en la base de datos; Que depende de si las
validaciones pasaron o no (al igual que crear).

Supongamos que queremos poner el atributo ' locked ' en false si estamos creando un
nuevo registro, pero no queremos incluirlo en la consulta. As que queremos encontrar el
cliente llamado "Andy", o si ese cliente no existe, crear un cliente llamado "Andy" que no
est bloqueado.

Podemos lograr esto de dos maneras. La primera es usar create_with :

Client.create_with(locked: false).find_or_create_by(first_name: 'Andy')

La segunda forma es usar un bloque:

212
18- Buscar o construir un nuevo objeto

Client.find_or_create_by(first_name: 'Andy') do |c|


c.locked = false
end

El bloque slo se ejecutar si se est creando el cliente. La segunda vez que ejecutamos
este cdigo, el bloque ser ignorado.

18.2 find_or_create_by!
Tambin puede utilizar find_or_create_by! Para generar una excepcin si el nuevo registro
no es vlido. Las validaciones no estn cubiertas en esta gua, pero supongamos por un
momento que las agregamos temporalmente

validates :orders_count, presence: true

A su modelo de cliente. Si intenta crear un nuevo cliente sin pasar un orders_count , el


registro no ser vlido y se generar una excepcin:

Client.find_or_create_by!(first_name: 'Andy')
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank

18.3 find_or_initialize_by
El mtodo find_or_initialize_by funcionar igual que find_or_create_by pero llamar
new en lugar de create . Esto significa que se crear una nueva instancia del modelo en

la memoria pero no se guardar en la base de datos. Continuando con el ejemplo


find_or_create_by , ahora queremos que el nombre del cliente sea 'Nick':

nick = Client.find_or_initialize_by(first_name: 'Nick')


# => #<Client id: nil, first_name: "Nick", orders_count: 0, locked: true, created_at:
"2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">

nick.persisted?
# => false

nick.new_record?
# => true

Dado que el objeto todava no est almacenado en la base de datos, el SQL generado se
ve as:

213
18- Buscar o construir un nuevo objeto

SELECT * FROM clients WHERE (clients.first_name = 'Nick') LIMIT 1

Cuando desee guardarlo en la base de datos, solo tiene que guardar:

nick.save
# => true

214
19- Bsqueda por SQL

19. Bsqueda por SQL


Si desea utilizar su propio SQL para buscar registros en una tabla, puede utilizar
find_by_sql . El mtodo find_by_sql devolver una matriz de objetos incluso si la consulta

subyacente devuelve un solo registro. Por ejemplo, puede ejecutar esta consulta:

Client.find_by_sql("SELECT * FROM clients


INNER JOIN orders ON clients.id = orders.client_id
ORDER BY clients.created_at desc")
# => [
# #<Client id: 1, first_name: "Lucas" >,
# #<Client id: 2, first_name: "Jan" >,
# ...
# ]

find_by_sql proporciona una forma sencilla de realizar llamadas personalizadas a la base

de datos y recuperar objetos instanciados.

19.1 select_all
find_by_sql tiene un pariente cercano llamado connection#select_all . select_all

recuperar objetos de la base de datos usando SQL personalizado como find_by_sql


pero no los instanciar. En su lugar, obtendr una matriz de hashes donde cada hash indica
un registro.

Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1


'")
# => [
# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
# ]

19.2 pluck
pluck se puede utilizar para consultar columnas mltiples o individuales de la tabla

subyacente de un modelo. Acepta una lista de nombres de columna como argumento y


devuelve una matriz de valores de las columnas especificadas con el tipo de datos
correspondiente.

215
19- Bsqueda por SQL

Client.where(active: true).pluck(:id)
# SELECT id FROM clients WHERE active = 1
# => [1, 2, 3]

Client.distinct.pluck(:role)
# SELECT DISTINCT role FROM clients
# => ['admin', 'member', 'guest']

Client.pluck(:id, :name)
# SELECT clients.id, clients.name FROM clients
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]

pluck hace posible reemplazar cdigo como:

Client.select(:id).map { |c| c.id }


# or
Client.select(:id).map(&:id)
# or
Client.select(:id, :name).map { |c| [c.id, c.name] }

por:

Client.pluck(:id)
# or
Client.pluck(:id, :name)

A diferencia de select , pluck convierte directamente un resultado de base de datos en


un Array de Ruby, sin construir objetos ActiveRecord. Esto puede significar un mejor
rendimiento para una consulta grande o frecuente. Sin embargo, cualquier sobre-escritura
del mtodo del modelo no estar disponible. Por ejemplo:

class Client < ApplicationRecord


def name
"I am #{super}"
end
end

Client.select(:name).map &:name
# => ["I am David", "I am Jeremy", "I am Jose"]

Client.pluck(:name)
# => ["David", "Jeremy", "Jose"]

216
19- Bsqueda por SQL

Adems, a diferencia de select y otros mbitos de Relacin, pluck desencadena una


consulta inmediata y, por lo tanto, no se puede encadenar con otros mbitos, aunque puede
trabajar con scopes ya construidos anteriormente:

Client.pluck(:name).limit(1)
# => NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>

Client.limit(1).pluck(:name)
# => ["David"]

19.3 ids
ids se puede utilizar para extraer ( pluck ) todos los identificadores de la relacin con la

clave principal de la tabla.

Person.ids
# SELECT id FROM people

class Person < ApplicationRecord


self.primary_key = "person_id"
end

Person.ids
# SELECT person_id FROM people

217
20- Existencia de Objetos

20. Existencia de Objetos


Si simplemente desea comprobar la existencia del objeto hay un mtodo llamado exist? .
Este mtodo consultar la base de datos utilizando la misma consulta que find , pero en
lugar de devolver un objeto o una coleccin de objetos, devolver true o false .

Client.exists?(1)

El mtodo exist? tambin toma varios valores, pero al capturarlo devolver true si
existe alguno de esos registros.

Client.exists?(id: [1,2,3])
# or
Client.exists?(name: ['John', 'Sergei'])

Incluso es posible utilizar exist? sin ningn argumento sobre un modelo o una relacin.

Client.where(first_name: 'Ryan').exists?

Lo anterior devuelve true si hay al menos un cliente con el first_name 'Ryan' y false
en caso contrario.

Client.exists?

Lo anterior devuelve false si la tabla de clientes est vaca y true en caso contrario.

Tambin puede utilizar any? y many? Para verificar la existencia de un modelo o relacin.

218
20- Existencia de Objetos

# via a model
Article.any?
Article.many?

# via a named scope


Article.recent.any?
Article.recent.many?

# via a relation
Article.where(published: true).any?
Article.where(published: true).many?

# via an association
Article.first.categories.any?
Article.first.categories.many?

219
21- Clculos

21. Clculos
Esta seccin usa count como un mtodo de ejemplo en este prembulo, pero las opciones
descritas se aplican a todas las subsecciones.

Todos los mtodos de clculo funcionan directamente en un modelo:

Client.count
# SELECT count(*) AS count_all FROM clients

O en una relacin:

Client.where(first_name: 'Ryan').count
# SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')

Tambin puede utilizar varios mtodos de bsqueda en una relacin para realizar clculos
complejos:

Client.includes("orders").where(first_name: 'Ryan', orders: { status: 'received' }).co


unt

Que ejecutar:

SELECT count(DISTINCT clients.id) AS count_all FROM clients


LEFT OUTER JOIN orders ON orders.client_id = clients.id WHERE
(clients.first_name = 'Ryan' AND orders.status = 'received')

21.1 Count
Si desea ver cuntos registros hay en la tabla de su modelo, puede llamar a Client.count
y devolver el nmero. Si desea ser ms especfico y encontrar todos los clientes con su
edad presentes en la base de datos puede utilizar Client.count(:age) .

21.2 Average
Si desea ver el promedio de un determinado nmero en una de sus tablas, puede llamar al
mtodo average de la clase que se relaciona con la tabla. Esta llamada al mtodo se ver
as:

220
21- Clculos

Client.average("orders_count")

Esto devolver un nmero (posiblemente un nmero de punto flotante tal como 3.14159265)
que representa el valor promedio en el campo.

21.3 Minimum
Si desea encontrar el valor mnimo de un campo en su tabla, puede llamar al mtodo
minimum de la clase que se relaciona con la tabla. Esta llamada al mtodo se ver as:

Client.minimum("age")

21.4 Maximum
Si desea encontrar el valor mximo de un campo en su tabla, puede llamar al mtodo
maximum de la clase que se relaciona con la tabla. Esta llamada al mtodo se ver as:

Client.maximum("age")

21.5 Sum
Si desea encontrar la suma de un campo para todos los registros de su tabla, puede llamar
al mtodo sum en la clase que se relaciona con la tabla. Esta llamada al mtodo se ver
as:

Client.sum("orders_count")

221
22- Ejecutando EXPLAIN

22. Ejecutando EXPLAIN


Puede ejecutar EXPLAIN en las consultas activadas por relaciones. Por ejemplo,

User.where(id: 1).joins(:articles).explain

Puede producir

EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_i
d` = `users`.`id` WHERE `users`.`id` = 1
+----+-------------+----------+-------+---------------+
| id | select_type | table | type | possible_keys |
+----+-------------+----------+-------+---------------+
| 1 | SIMPLE | users | const | PRIMARY |
| 1 | SIMPLE | articles | ALL | NULL |
+----+-------------+----------+-------+---------------+
+---------+---------+-------+------+-------------+
| key | key_len | ref | rows | Extra |
+---------+---------+-------+------+-------------+
| PRIMARY | 4 | const | 1 | |
| NULL | NULL | NULL | 1 | Using where |
+---------+---------+-------+------+-------------+

2 rows in set (0.00 sec)

Bajo MySQL y MariaDB.

Active Record realiza una impresin bonita que emula la del shell de base de datos
correspondiente. Por lo tanto, la misma consulta que se ejecuta con el adaptador de
PostgreSQL

EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_i
d" = "users"."id" WHERE "users"."id" = 1
QUERY PLAN
------------------------------------------------------------------------------
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
Join Filter: (articles.user_id = users.id)
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
Index Cond: (id = 1)
-> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4)
Filter: (articles.user_id = 1)
(6 rows)

222
22- Ejecutando EXPLAIN

La carga impaciente puede desencadenar ms de una consulta bajo el cap, y algunas


consultas pueden necesitar los resultados de las anteriores. Debido a eso, EXPLAIN
realmente ejecuta la consulta y, a continuacin, pregunta por los planes de consulta. Por
ejemplo,

User.where(id: 1).includes(:articles).explain

yields

EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1


+----+-------------+-------+-------+---------------+
| id | select_type | table | type | possible_keys |
+----+-------------+-------+-------+---------------+
| 1 | SIMPLE | users | const | PRIMARY |
+----+-------------+-------+-------+---------------+
+---------+---------+-------+------+-------+
| key | key_len | ref | rows | Extra |
+---------+---------+-------+------+-------+
| PRIMARY | 4 | const | 1 | |
+---------+---------+-------+------+-------+

1 row in set (0.00 sec)

EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1)


+----+-------------+----------+------+---------------+
| id | select_type | table | type | possible_keys |
+----+-------------+----------+------+---------------+
| 1 | SIMPLE | articles | ALL | NULL |
+----+-------------+----------+------+---------------+
+------+---------+------+------+-------------+
| key | key_len | ref | rows | Extra |
+------+---------+------+------+-------------+
| NULL | NULL | NULL | 1 | Using where |
+------+---------+------+------+-------------+

1 row in set (0.00 sec)

Bajo MySQL y MariaDB.

223
VII- Conceptos bsicos de Active Model

VII- Conceptos bsicos de Active Model


Esta gua debe proporcionarle todo lo que necesita para comenzar a usar clases en los
modelos. Active Model permite que los helpers de Action Pack interacten con objetos Ruby
sencillos. Active Model tambin ayuda a crear ORMs personalizados para su uso fuera del
marco de Rails.

Despus de leer esta gua, sabr:

Cmo se comporta un modelo de Active Record.


Cmo funcionan las callbacks y las validaciones.
Cmo funcionan los serializadores.
Cmo se integra Active Model con el marco de internacionalizacin de Rails (i18n).

224
1- Introduccin

1- Introduccin
Active Model es una biblioteca que contiene varios mdulos utilizados en el desarrollo de
clases que necesitan algunas caractersticas presentes en Active Record. A continuacin se
explican algunos de estos mdulos.

1.1 Mtodos de atributo


El mdulo ActiveModel::AttributeMethods puede agregar prefijos y sufijos personalizados
en los mtodos de una clase. Se utiliza mediante la definicin de los prefijos y sufijos y qu
mtodos en el objeto se utilizan.

class Person
include ActiveModel::AttributeMethods

attribute_method_prefix 'reset_'
attribute_method_suffix '_highest?'
define_attribute_methods 'age'

attr_accessor :age

private
def reset_attribute(attribute)
send("#{attribute}=", 0)
end

def attribute_highest?(attribute)
send(attribute) > 100
end
end

person = Person.new
person.age = 110
person.age_highest? # => true
person.reset_age # => 0
person.age_highest? # => false

1.2 Devoluciones de llamada


ActiveModel::Callbacks ofrece devoluciones de llamada al estilo de Active Record. Esto

proporciona una capacidad para definir devoluciones de llamada que se ejecutan en


momentos apropiados. Despus de definir las devoluciones de llamada, puede envolverlas
con mtodos personalizados antes, durante y despus.

225
1- Introduccin

class Person
extend ActiveModel::Callbacks

define_model_callbacks :update

before_update :reset_me

def update
run_callbacks(:update) do
# This method is called when update is called on an object.
end
end

def reset_me
# This method is called when update is called on an object as a before_update call
back is defined.
end
end

1.3 Conversion
Si una clase define persisted? y el mtodo id , puede incluir el mdulo
ActiveModel::Conversion en esa clase y llamar a los mtodos de conversin de Rails en

objetos de esa clase.

class Person
include ActiveModel::Conversion

def persisted?
false
end

def id
nil
end
end

person = Person.new
person.to_model == person # => true
person.to_key # => nil
person.to_param # => nil

1.4 Dirty
Un objeto se ensucia cuando ha pasado por uno o ms cambios en sus atributos y no se ha
guardado. ActiveModel::Dirty permite comprobar si un objeto ha sido cambiado o no.
Tambin tiene mtodos de acceso basados en atributo. Consideremos una clase Person

226
1- Introduccin

con los atributos first_name y last_name :

class Person
include ActiveModel::Dirty
define_attribute_methods :first_name, :last_name

def first_name
@first_name
end

def first_name=(value)
first_name_will_change!
@first_name = value
end

def last_name
@last_name
end

def last_name=(value)
last_name_will_change!
@last_name = value
end

def save
# do save work...
changes_applied
end
end

1.4.1 Consultando el objeto directamente para su lista de


todos los atributos cambiados.

227
1- Introduccin

person = Person.new
person.changed? # => false

person.first_name = "First Name"


person.first_name # => "First Name"

# returns true if any of the attributes have unsaved changes.


person.changed? # => true

# returns a list of attributes that have changed before saving.


person.changed # => ["first_name"]

# returns a Hash of the attributes that have changed with their original values.
person.changed_attributes # => {"first_name"=>nil}

# returns a Hash of changes, with the attribute names as the keys, and the
# values as an array of the old and new values for that field.
person.changes # => {"first_name"=>[nil, "First Name"]}

1.4.2 Mtodos de acceso basado en atributos

# attr_name_changed?
person.first_name # => "First Name"
person.first_name_changed? # => true

Rastrea el valor anterior del atributo.

# attr_name_was accessor
person.first_name_was # => nil

Rastrea el valor anterior y el actual del atributo cambiado. Devuelve una matriz si se
cambia, de lo contrario devuelve nil.

# attr_name_change
person.first_name_change # => [nil, "First Name"]
person.last_name_change # => nil

1.5 Validaciones
El mdulo ActiveModel::Validations agrega la capacidad de validar objetos como en Active
Record.

228
1- Introduccin

class Person
include ActiveModel::Validations

attr_accessor :name, :email, :token

validates :name, presence: true


validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
validates! :token, presence: true
end

person = Person.new
person.token = "2b1f325"
person.valid? # => false
person.name = 'vishnu'
person.email = 'me'
person.valid? # => false
person.email = 'me@vishnuatrai.com'
person.valid? # => true
person.token = nil
person.valid? # => raises ActiveModel::StrictValidationFailed

1.6 Nombrar
ActiveModel::Naming aade una serie de mtodos de clase que facilitan la gestin de

nomenclatura y enrutamiento. El mdulo define el mtodo de clase model_name que definir


un nmero de accessors usando algunos mtodos ActiveSupport::Inflector .

class Person
extend ActiveModel::Naming
end

Person.model_name.name # => "Person"


Person.model_name.singular # => "person"
Person.model_name.plural # => "people"
Person.model_name.element # => "person"
Person.model_name.human # => "Person"
Person.model_name.collection # => "people"
Person.model_name.param_key # => "person"
Person.model_name.i18n_key # => :person
Person.model_name.route_key # => "people"
Person.model_name.singular_route_key # => "person"

1.7 Modelo
ActiveModel::Model aade la capacidad de una clase para trabajar con Action Pack y

Action View justo fuera de la caja.

229
1- Introduccin

class EmailContact
include ActiveModel::Model

attr_accessor :name, :email, :message


validates :name, :email, :message, presence: true

def deliver
if valid?
# deliver email
end
end
end

Al incluir ActiveModel::Model obtienes algunas caractersticas como:

model name introspection


conversions
translation
validations

Tambin le da la capacidad de inicializar un objeto con un hash de atributos, al igual que


cualquier objeto de Active Record.

email_contact = EmailContact.new(name: 'David',


email: 'david@example.com',
message: 'Hello World')
email_contact.name # => 'David'
email_contact.email # => 'david@example.com'
email_contact.valid? # => true
email_contact.persisted? # => false

Cualquier clase que incluya ActiveModel::Model se puede utilizar con form_for , render y
cualquier otro mtodo de ayuda Action View, al igual que los objetos Active Record.

1.8 Serializacin
ActiveModel::Serialization proporciona serializacin bsica para su objeto. Debe declarar

un Hash de atributos que contiene los atributos que desea serializar. Los atributos deben
ser cadenas, no smbolos.

230
1- Introduccin

class Person
include ActiveModel::Serialization

attr_accessor :name

def attributes
{'name' => nil}
end
end

Ahora puede acceder a un Hash serializado de su objeto utilizando el mtodo


serializable_hash .

person = Person.new
person.serializable_hash # => {"name"=>nil}
person.name = "Bob"
person.serializable_hash # => {"name"=>"Bob"}

1.8.1 ActiveModel::Serializers
Active Model tambin proporciona el mdulo ActiveModel::Serializers::JSON para la
serializacin / deserializacin de JSON. Este mdulo incluye automticamente el mdulo de
ActiveModel::Serialization anteriormente discutido.

1.8.1.1 ActiveModel::Serializers::JSON

Para utilizar ActiveModel::Serializers::JSON slo necesita cambiar el mdulo que est


incluyendo de ActiveModel::Serialization a ActiveModel::Serializers::JSON .

class Person
include ActiveModel::Serializers::JSON

attr_accessor :name

def attributes
{'name' => nil}
end
end

El mtodo as_json , similar a serializable_hash , proporciona un Hash que representa el


modelo.

231
1- Introduccin

person = Person.new
person.as_json # => {"name"=>nil}
person.name = "Bob"
person.as_json # => {"name"=>"Bob"}

Tambin puede definir los atributos para un modelo de una cadena JSON. Sin embargo,
debe definir el mtodo attributes= en su clase:

class Person
include ActiveModel::Serializers::JSON

attr_accessor :name

def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end

def attributes
{'name' => nil}
end
end

Ahora es posible crear una instancia de Person y establecer atributos usando from_json .

json = { name: 'Bob' }.to_json


person = Person.new
person.from_json(json) # => #<Person:0x00000100c773f0 @name="Bob">
person.name # => "Bob"

1.9 Traduccin
ActiveModel::Translation proporciona integracin entre su objeto y el entorno de

internacionalizacin de Rails (i18n).

class Person
extend ActiveModel::Translation
end

Con el mtodo human_attribute_name , puede transformar los nombres de los atributos en un


formato ms legible por el ser humano. El formato legible por el usuario se define en su (s)
archivo (s) local (es).

config/locales/app.pt-BR.yml

232
1- Introduccin

pt-BR:
activemodel:
attributes:
person:
name: 'Nome'

Person.human_attribute_name('name') # => "Nome"

1.10 Lint Tests


ActiveModel::Lint::Tests le permite comprobar si un objeto cumple con la API del Active

Model.

app/models/person.rb

class Person
include ActiveModel::Model
end

test/models/person_test.rb

require 'test_helper'

class PersonTest < ActiveSupport::TestCase


include ActiveModel::Lint::Tests

setup do
@model = Person.new
end
end

$ rails test

Run options: --seed 14596

# Running:

......

Finished in 0.024899s, 240.9735 runs/s, 1204.8677 assertions/s.

6 runs, 30 assertions, 0 failures, 0 errors, 0 skips

233
1- Introduccin

No se requiere un objeto para implementar todas las API para trabajar con Action Pack.
Este mdulo slo tiene la intencin de proporcionar orientacin en caso de que desee todas
las caractersticas fuera de la caja.

1.11 SecurePassword
ActiveModel::SecurePassword proporciona una forma de almacenar de forma segura

cualquier contrasea en un formato cifrado. Cuando se incluye este mdulo, se proporciona


un mtodo de clase has_secure_password que define un accesor de contrasea con ciertas
validaciones en l.

1.11.1 Requisitos
ActiveModel::SecurePassword depende de bcrypt , as que incluya esta gema en su

Gemfile para usar ActiveModel::SecurePassword correctamente. Para que esto funcione, el

modelo debe tener un accesor llamado password_digest . El has_secure_password


agregar las siguientes validaciones en el accesor de contrasea:

1. La contrasea debe estar presente.


2. La contrasea debe ser igual a su confirmacin (se proporciona +
password_confirmation).
3. La longitud mxima de una contrasea es 72 (requerida por bcrypt de la que depende
ActiveModel::SecurePassword )

1.11.2 Ejemplos

234
1- Introduccin

class Person
include ActiveModel::SecurePassword
has_secure_password
attr_accessor :password_digest
end

person = Person.new

# When password is blank.


person.valid? # => false

# When the confirmation doesn't match the password.


person.password = 'aditya'
person.password_confirmation = 'nomatch'
person.valid? # => false

# When the length of password exceeds 72.


person.password = person.password_confirmation = 'a' * 100
person.valid? # => false

# When only password is supplied with no password_confirmation.


person.password = 'aditya'
person.valid? # => true

# When all validations are passed.


person.password = person.password_confirmation = 'aditya'
person.valid? # => true

235
VIII- Conceptos bsicos de Action View

VIII- Conceptos bsicos de Action View


Despus de leer esta gua, sabr:

Qu es Action View y cmo usarlo con Rails.


La mejor manera de usar templates, partials y layouts.
Qu helpers son proporcionados por Action View y cmo hacer sus propios helpers.
Cmo utilizar vistas localizadas.

236
1- Qu es Action View?

1 Qu es Action View?
En Rails, las solicitudes web son manejadas por Action Controller y Action View.
Normalmente, Action Controller se ocupa de comunicarse con la base de datos y realizar
acciones CRUD cuando sea necesario. Entonces, Action View es responsable de compilar
la respuesta.

Los templates de Action View se escriben utilizando Ruby incrustado en etiquetas


mezcladas con HTML. Para evitar el desorden de las plantillas con el cdigo boilerplate, un
nmero de clases helper proporcionan un comportamiento comn para los formularios,
fechas y strings. Tambin es fcil agregar nuevos helpers a su aplicacin a medida que
evoluciona.

Algunas caractersticas de Action View estn vinculadas a Active Record, pero eso no
significa que Action View depende de Active Record. Action View es un paquete
independiente que se puede utilizar con cualquier tipo de bibliotecas Ruby.

237
2- Uso de Action View con Rails

2- Uso de Action View con Rails


Para cada controlador hay un directorio asociado en el directorio app / views que contiene
los archivos de plantilla que forman las vistas asociadas con ese controlador. Estos archivos
se utilizan para mostrar la vista que resulta de cada accin del controlador.

$ bin/rails generate scaffold article


[...]
invoke scaffold_controller
create app/controllers/articles_controller.rb
invoke erb
create app/views/articles
create app/views/articles/index.html.erb
create app/views/articles/edit.html.erb
create app/views/articles/show.html.erb
create app/views/articles/new.html.erb
create app/views/articles/_form.html.erb
[...]

Existe una convencin de naming para las vistas en Rails. Normalmente, las vistas
comparten su nombre con la accin del controlador asociado, como se puede ver ms
arriba. Por ejemplo, la accin del controlador de index del articles_controller.rb
utilizar el archivo de vista index.html.erb en el directorio app / views / articles . El
HTML completo devuelto al cliente se compone de una combinacin de este archivo ERB ,

un template de diseo que lo envuelve y todos los partials al que la vista puede hacer
referencia. Dentro de esta gua encontrar una documentacin ms detallada sobre cada
uno de estos tres componentes.

238
3- Templates, Partials y Layouts

3- Templates, Partials y Layouts


Como se mencion, la salida HTML final es una composicin de tres elementos Rails:
Templates, Partials y Layouts. A continuacin se presenta un breve resumen de cada uno
de ellos.

3.1 Templates
Las plantillas de Action View se pueden escribir de varias maneras. Si el archivo de plantilla
tiene una extensin .erb , utiliza una mezcla de ERB (Embedded Ruby) y HTML . Si el
archivo de plantilla tiene una extensin .builder , se utiliza la biblioteca
Builder::XmlMarkup .

Rails soporta mltiples sistemas de plantilla y utiliza una extensin de archivo para distinguir
entre ellos. Por ejemplo, un archivo HTML que utiliza el sistema de plantillas ERB tendr
.html.erb como una extensin de archivo.

3.1.1 ERB
Dentro de una plantilla de ERB , el cdigo Ruby se puede incluir usando las etiquetas <%
%> y <%= %> . Las etiquetas <% %> se utilizan para ejecutar cdigo Ruby que no devuelven

nada, como condiciones, bucles o bloques, y las etiquetas <%= %> se utilizan cuando se
desea que se devuelva un resultado.

Considere el bucle siguiente para los nombres:

<h1>Names of all the people</h1>


<% @people.each do |person| %>
Name: <%= person.name %><br>
<% end %>

El bucle se configura utilizando etiquetas de insercin ( <% %> ) y el nombre se inserta


utilizando las etiquetas embebidas de salida (<%= %> ). Tenga en cuenta que esto no es
solo una sugerencia de uso: las funciones de salida regulares como print y puts no se
renderizarn a la vista con plantillas ERB . As que esto estara mal:

<%# WRONG %>


Hi, Mr. <% puts "Frodo" %>

239
3- Templates, Partials y Layouts

Para suprimir los espacios en blanco iniciales y remotos, puede utilizar <%- -%> de forma
intercambiable con <% y %> .

3.1.2 Builder
Las plantillas de Builder son una alternativa ms programtica a ERB . Son especialmente
tiles para generar contenido XML . Un objeto XmlMarkup denominado xml se pone
automticamente a disposicin de plantillas con una extensin . builder .

Estos son algunos ejemplos bsicos:

xml.em("emphasized")
xml.em { xml.b("emph & bold") }
xml.a("A Link", "href" => "http://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")

Que producira:

<em>emphasized</em>
<em><b>emph &amp; bold</b></em>
<a href="http://rubyonrails.org">A link</a>
<target option="fast" name="compile" />

Cualquier mtodo con un bloque ser tratado como una etiqueta de marcado XML con
marcas anidadas en el bloque. Por ejemplo, lo siguiente:

xml.div {
xml.h1(@person.name)
xml.p(@person.bio)
}

Producira algo como:

<div>
<h1>David Heinemeier Hansson</h1>
<p>A product of Danish Design during the Winter of '79...</p>
</div>

A continuacin se muestra un ejemplo completo de RSS utilizado en Basecamp:

240
3- Templates, Partials y Layouts

xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do


xml.channel do
xml.title(@feed_title)
xml.link(@url)
xml.description "Basecamp: Recent items"
xml.language "en-us"
xml.ttl "40"

for item in @recent_items


xml.item do
xml.title(item_title(item))
xml.description(item_description(item)) if item_description(item)
xml.pubDate(item_pubDate(item))
xml.guid(@person.firm.account.url + @recent_items.url(item))
xml.link(@person.firm.account.url + @recent_items.url(item))
xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
end
end
end
end

3.1.3 Jbuilder
Jbuilder es una gema que mantiene el equipo de Rails y que se incluye en el Railfile
Gemfile por defecto. Es similar a Builder, pero se utiliza para generar JSON , en lugar de
XML .

Si no lo tiene, puede agregar lo siguiente a su Gemfile:

gem 'jbuilder'

Un objeto Jbuilder llamado json se pone automticamente a disposicin de las plantillas


con una extensin .jbuilder .

Aqu est un ejemplo bsico:

json.name("Alex")
json.email("alex@example.com")

Lo que produce

{
"name": "Alex",
"email": "alex@example.com"
}

241
3- Templates, Partials y Layouts

Consulte la documentacin de Jbuilder para obtener ms ejemplos e informacin.

3.1.4 Cach de plantillas


De forma predeterminada, Rails compilar cada plantilla en un mtodo para procesarla. Al
modificar una plantilla, Rails comprobar la hora de modificacin del archivo y la
recompilar en modo de desarrollo.

3.2 Partials
Las plantillas parciales, usualmente llamadas "partials", son otro dispositivo para romper el
proceso de renderizado en bloques ms manejables. Con partials, puede extraer
fragmentos de cdigo de sus plantillas para separar archivos y tambin reutilizarlos a travs
de sus plantillas.

3.2.1 Nombrando partials


Para renderizar un parcial como parte de una vista, utilice el mtodo render dentro de la
vista:

<%= render "menu" %>

Esto convertir un archivo denominado _menu.html.erb en ese punto dentro de la vista que
se est procesando. Tenga en cuenta el carcter de subrayado principal: los partials se
nombran con un carcter de subrayado principal para distinguirlos de las vistas regulares,
aunque se consulten sin el subrayado. Esto es vlido incluso cuando est extrayendo una
parte de otra carpeta:

<%= render "shared/menu" %>

Ese cdigo extraer el parcial de app/views/shared/_menu.html.erb

3.2.2 Uso de Partials para simplificar las vistas


Una forma de usar partials es tratarlos como el equivalente de subrutinas; Una forma de
mover los detalles de una vista para que pueda captar lo que est pasando ms fcilmente.
Por ejemplo, puede tener una vista que tenga este aspecto:

242
3- Templates, Partials y Layouts

<%= render "shared/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>


<% @products.each do |product| %>
<%= render partial: "product", locals: { product: product } %>
<% end %>

<%= render "shared/footer" %>

En este caso, los partials _ad_banner.html.erb y _footer.html.erb pueden contener


contenido que se comparte entre muchas pginas de la aplicacin. No necesita ver los
detalles de estas secciones cuando se est concentrando en una pgina en particular.

3.2.3 render sin opciones de partials ni locals


En el ejemplo anterior, render toma 2 opciones: partial y locals . Pero si estas son las
nicas opciones que desea pasar, puede omitir el uso de estas opciones. Por ejemplo, en
lugar de:

<%= render partial: "product", locals: { product: @product } %>

Tambin puede hacer:

<%= render "product", product: @product %>

3.2.4 Las opciones as y object


De forma predeterminada ActionView::Partials::PartialRenderer tiene su objeto en una
variable local con el mismo nombre que la plantilla. Por lo tanto, dado:

<%= render partial: "product" %>

Dentro del partial _product obtendremos @product en la variable local product , como si
hubiramos escrito:

<%= render partial: "product", locals: { product: @product } %>

243
3- Templates, Partials y Layouts

La opcin de object se puede utilizar para especificar directamente qu objeto se procesa


en el partial; til cuando el objeto de la plantilla est en otro lugar (por ejemplo, en una
variable de instancia diferente o en una variable local).

Por ejemplo, en lugar de:

<%= render partial: "product", locals: { product: @item } %>

haramos:

<%= render partial: "product", object: @item %>

Con la opcin as podemos especificar un nombre diferente para dicha variable local. Por
ejemplo, si queremos que sea un elemento en lugar del producto haramos:

<%= render partial: "product", object: @item, as: "item" %>

que es equivalente a:

<%= render partial: "product", locals: { item: @item } %>

3.2.5 Renderizado de colecciones


Es muy comn que una plantilla necesite iterar sobre una coleccin y renderizar una sub-
plantilla para cada uno de los elementos. Este patrn se ha implementado como un nico
mtodo que acepta una matriz y hace una parcial para cada uno de los elementos de la
matriz.

As que este ejemplo para renderizar todos los productos:

<% @products.each do |product| %>


<%= render partial: "product", locals: { product: product } %>
<% end %>

Puede ser reescrito en una sola lnea:

<%= render partial: "product", collection: @products %>

Cuando un parcial es llamado con una coleccin, las instancias individuales del parcial
tienen acceso al miembro de la coleccin que se est procesando a travs de una variable
llamada despus del parcial. En este caso, el parcial es _product , y dentro de l puede

244
3- Templates, Partials y Layouts

hacer referencia al producto para obtener el miembro de coleccin que se est procesando.

Puede utilizar una sintaxis abreviada para procesar las colecciones. Suponiendo que
@products es una coleccin de instancias de product , simplemente puede escribir lo

siguiente para producir el mismo resultado:

<%= render @products %>

Rails determina el nombre del parcial a utilizar, mirando el nombre del modelo en la
coleccin, Product en este caso. De hecho, puede incluso hacer una coleccin compuesta
de instancias de diferentes modelos utilizando este shorthand, y Rails elegir el parcial
adecuado para cada miembro de la coleccin.

3.2.6 Spacer Templates


Tambin puede especificar un segundo parcial que se renderizar entre instancias del
parcial principal mediante la opcin: spacer_template :

<%= render partial: @products, spacer_template: "product_ruler" %>

Rails har que el partial _product_ruler (sin datos pasados a ella) entre a cada par de
parciales _product .

3.3 Layouts
Los layouts pueden usarse para renderizar una plantilla de vista comn alrededor de los
resultados de las acciones del controlador de Rails. Normalmente, una aplicacin de Rails
tendr un par de layouts que las pginas renderizarn internamente. Por ejemplo, un sitio
puede tener un layout para un usuario conectado y otro para el lado de marketing o ventas
del sitio. El layout de usuario registrado puede incluir navegacin de nivel superior que debe
estar presente en muchas acciones del controlador. El layout de ventas de una aplicacin
SaaS puede incluir navegacin de nivel superior para cosas como "Precios" y "Contactar
con nosotros". Es de esperar que cada layout tenga una apariencia diferente.

245
4- Partial Layouts

4 Partial Layouts
Los parciales pueden tener sus propios layouts aplicados a ellos. Estos layouts son
diferentes de los aplicados a una accin del controlador, pero funcionan de una manera
similar.

Digamos que estamos mostrando un artculo en una pgina que debe ser envuelto en un
div para fines de visualizacin. En primer lugar, crearemos un nuevo artculo:

Article.create(body: 'Partial Layouts are cool!')

En la plantilla de show, renderizaremos el partial _article envuelto en el layout box :


articles/show.html.erb

<%= render partial: 'article', layout: 'box', locals: { article: @article } %>

El layout box simplemente envuelve el partial _article en un div :


articles/_box.html.erb

<div class='box'>
<%= yield %>
</div>

Tenga en cuenta que el layout de partial tiene acceso a la variable local del artculo que se
pas a la llamada render . Sin embargo, a diferencia de los layouts de la aplicacin
general, los layouts parciales todava tienen el prefijo de subrayado.

Tambin puede procesar un bloque de cdigo dentro de un layout parcial en lugar de llamar
a yield . Por ejemplo, si no tuviramos el parcial _article , podramos hacer esto en su
lugar:

<% render(layout: 'box', locals: { article: @article }) do %>


<div>
<p><%= article.body %></p>
</div>
<% end %>

Suponiendo que usamos el mismo parcial _box de arriba, esto producira la misma salida
que el ejemplo anterior.

246
4- Partial Layouts

247
5- Rutas de las Vistas

5. Rutas de las Vistas


Al procesar una respuesta, el controlador necesita resolver dnde se encuentran las
diferentes vistas. De forma predeterminada, slo aparece en el directorio app/views .

Podemos agregar otras ubicaciones y darles una cierta prioridad cuando resolvamos rutas
usando los mtodos prepend_view_path y append_view_path .

5.1 Ruta de la vista Prepend


Esto puede ser til, por ejemplo, cuando queremos poner vistas dentro de un directorio
diferente para subdominios.

Podemos hacer esto usando:

prepend_view_path "app/views/#{request.subdomain}"

A continuacin, Action View buscar primero en este directorio cuando se resuelvan las
vistas.

5.2 Ruta de la vista Append


Del mismo modo, podemos aadir rutas:

append_view_path "app/views/direct"

Esto agregar app/views/direct al final de las rutas de bsqueda.

248
6- Visin general de los helpers proporcionados por Action View

6. Visin general de los helpers


proporcionados por Action View
Work In Progress: No todos los ayudantes se enumeran aqu. Para obtener una lista
completa, consulte la documentacin de la API

Lo siguiente es slo un breve resumen general de los helpers disponibles en Action View.
Se recomienda revisar la documentacin de la API, que cubre todos los ayudantes con ms
detalle, pero esto debera servir como un buen punto de partida.

249
6.1 AssetTagHelper

6.1 AssetTagHelper
Este mdulo proporciona mtodos para generar HTML que vincula vistas a elementos tales
como imgenes, archivos JavaScript, hojas de estilo y feeds.

De forma predeterminada, Rails enlaza con estos assets en el host actual de la carpeta
pblica, pero puede dirigir a Rails para enlazar con assets desde un servidor de assets
dedicados configurando config.action_controller.asset_host en la configuracin de la
aplicacin, normalmente en config/environments/production.rb . Por ejemplo, digamos que
el host de assets es assets.example.com :

config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" alt=
"Rails" />

6.1.1 auto_discovery_link_tag
Devuelve una etiqueta de link que los navegadores y los lectores de feeds pueden usar
para detectar automticamente un feed RSS o Atom .

auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", { title: "RSS Feed" }


) # =>
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="http://www.e
xample.com/feed.rss" />

6.1.2 image_path
Calcula la ruta de acceso a un recurso de imagen en el directorio app/assets/images . Se
pasarn a travs de rutas completas desde la raz del documento. Utilizado internamente
por image_tag para construir la ruta de la imagen.

image_path("edit.png") # => /assets/edit.png

Un fingerprint (huella digital) se agregar al nombre de archivo si config.assets.digest se


establece en true.

image_path("edit.png") # => /assets/edit-2d1a2db63fc738690021fedb5a65b68e.png

6.1.3 image_url

250
6.1 AssetTagHelper

Calcula la URL de un recurso de imagen en el directorio app/assets/images. Esto llamar a


image_path internamente y se fusionar con su host actual o con su host de assets.

image_url("edit.png") # => http://www.example.com/assets/edit.png

6.1.4 image_tag
Devuelve una etiqueta de imagen HTML desde el origen. El origen puede ser una ruta de
acceso completa o un archivo que existe en el directorio app/assets/images .

image_tag("icon.png") # => <img src="/assets/icon.png" alt="Icon" />

6.1.5 javascript_include_tag
Devuelve una etiqueta de secuencia de comandos HTML para cada uno de los orgenes
proporcionados. Puede pasar el nombre de archivo (la extensin .js es opcional) de los
archivos JavaScript que existen en el directorio app/assets/javascripts para su inclusin
en la pgina actual o puede pasar la ruta completa relativa a la raz del documento.

javascript_include_tag "common" # => <script src="/assets/common.js"></script>

Si la aplicacin no utiliza la assets pipeline, para incluir la biblioteca jQuery de JavaScript en


su aplicacin, pase : default como fuente. Cuando se utiliza :default , si existe un archivo
application.js en el directorio app/assets/javascripts , tambin se incluir.

javascript_include_tag :defaults

Tambin puede incluir todos los archivos JavaScript en el directorio app/assets/javascripts


utilizando :all como fuente.

javascript_include_tag :all

Tambin puede almacenar en cach varios archivos JavaScript en un solo archivo, lo que
requiere menos conexiones HTTP para descargar y puede ser mejor comprimirlo por gzip
(lo que lleva a transferencias ms rpidas). El almacenamiento en cach slo ocurrir si
ActionController::Base.perform_caching est establecido en true (que es el caso por

defecto para el entorno de produccin de Rails, pero no para el entorno de desarrollo).

251
6.1 AssetTagHelper

javascript_include_tag :all, cache: true # =><script src="/javascripts/all.js"></scrip


t>

6.1.6 javascript_path
Computa la ruta de acceso a un recurso de JavaScript en el directorio
app/assets/javascripts . Si el nombre de archivo de origen no tiene extensin, se agregar

.js . Se pasar a travs de rutas completas desde la raz del documento. Use

internamente el javascript_include_tag para construir la ruta del script.

javascript_path "common" # => /assets/common.js

6.1.7 javascript_url
Computa la URL de un recurso de JavaScript en el directorio app/assets/javascripts . Esto
llamar javascript_path internamente y se fusionar con su host actual o con su host de
assets.

javascript_url "common" # => http://www.example.com/assets/common.js

6.1.8 stylesheet_link_tag
Devuelve una etiqueta de enlace de hoja de estilo para los orgenes especificados como
argumentos. Si no especifica una extensin, .css se aadir automticamente.

stylesheet_link_tag "application" # => <link href="/assets/application.css" media="scr


een" rel="stylesheet" />

Tambin puede incluir todos los estilos en el directorio de hoja de estilo utilizando :all
como fuente:

stylesheet_link_tag :all

Tambin puede cachear varias hojas de estilo en un archivo, lo que requiere menos
conexiones HTTP y puede ser mejor si es compreso por gzip (lo que lleva a transferencias
ms rpidas). El almacenamiento en cach slo ocurrir si
ActionController::Base.perform_caching est establecido en true (que es el caso por

defecto para el entorno de produccin de Rails, pero no para el entorno de desarrollo).

252
6.1 AssetTagHelper

stylesheet_link_tag :all, cache: true # => <link href="/assets/all.css" media="screen"


rel="stylesheet" />

6.1.9 stylesheet_path
Computa la ruta de acceso a un elemento de hoja de estilo en el directorio
app/assets/stylesheets . Si el nombre de archivo de origen no tiene extensin, se agregar

.css . Se pasarn a travs de rutas completas desde la raz del documento. Se utiliza

internamente por stylesheet_link_tag para crear la ruta de la hoja de estilos.

stylesheet_path "application" # => /assets/application.css

6.1.10 stylesheet_url
Computa la URL de un elemento de hoja de estilo en el directorio app/assets/stylesheets .
Esto llamar a stylesheet_path internamente y se fusionar con su host actual o con su
host activo.

stylesheet_url "application" # => http://www.example.com/assets/application.css

253
6.2 AtomFeedHelper

6.2 AtomFeedHelper
6.2.1 atom_feed
Este helper facilita la creacin de un feed Atom . He aqu un ejemplo de uso completo:
config/routes.rb

resources :articles

app/controllers/articles_controller.rb

def index
@articles = Article.all

respond_to do |format|
format.html
format.atom
end
end

app/views/articles/index.atom.builder

atom_feed do |feed|
feed.title("Articles Index")
feed.updated(@articles.first.created_at)

@articles.each do |article|
feed.entry(article) do |entry|
entry.title(article.title)
entry.content(article.body, type: 'html')

entry.author do |author|
author.name(article.author_name)
end
end
end
end

254
6.3 BenchmarkHelper

6.3 BenchmarkHelper
6.3.1 benchmark
Permite medir el tiempo de ejecucin de un bloque en una plantilla y graba el resultado en
el registro. Envuelva este bloque alrededor de operaciones costosas o posibles cuellos de
botella para obtener una lectura de tiempo para la operacin.

<% benchmark "Process data files" do %>


<%= expensive_files_operation %>
<% end %>

Esto aadira algo como "Process data files (0.34523)" al registro, que puede utilizar para
comparar los tiempos al optimizar su cdigo.

255
6.4 CacheHelper

6.4 CacheHelper
6.4.1 cache
Un mtodo para almacenar en cach fragmentos de una vista en lugar de toda una accin o
pgina. Esta tcnica es til para almacenar en cach piezas como mens, listas de temas
de noticias, fragmentos HTML estticos, etc. Este mtodo toma un bloque que contiene el
contenido que desea almacenar en cach. Vea AbstractController::Caching::Fragments
para obtener ms informacin.

<% cache do %>


<%= render "shared/footer" %>
<% end %>

256
6.5 CaptureHelper

6.5 CaptureHelper
6.5.1 capture
El mtodo de captura le permite extraer parte de una plantilla en una variable. A
continuacin, puede utilizar esta variable en cualquier parte de las plantillas o del layout.

<% @greeting = capture do %>


<p>Welcome! The date and time is <%= Time.now %></p>
<% end %>

La variable capturada puede utilizarse en cualquier otro lugar.

<html>
<head>
<title>Welcome!</title>
</head>
<body>
<%= @greeting %>
</body>
</html>

6.5.2 content_for
Llamar content_for almacena un bloque de marcado en un identificador para uso
posterior. Puede realizar llamadas posteriores al contenido almacenado en otras plantillas o
el layout, pasando el identificador como argumento yield .

Por ejemplo, digamos que tenemos un diseo de aplicacin estndar, pero tambin una
pgina especial que requiere cierto cdigo JavaScript que el resto del sitio no necesita.
Podemos usar content_for para incluir este JavaScript en nuestra pgina especial sin
engordar el resto del sitio.
app/views/layouts/application.html.erb

257
6.5 CaptureHelper

<html>
<head>
<title>Welcome!</title>
<%= yield :special_script %>
</head>
<body>
<p>Welcome! The date and time is <%= Time.now %></p>
</body>
</html>

app/views/articles/special.html.erb

<p>This is a special page.</p>

<% content_for :special_script do %>


<script>alert('Hello!')</script>
<% end %>

258
6.6 DateHelper

6.6 DateHelper
6.6.1 date_select
Devuelve un conjunto de etiquetas de seleccin (una por ao, mes y da) preseleccionadas
para acceder a un atributo basado-en-fecha especificado.

date_select("article", "published_on")

6.6.2 datetime_select
Devuelve un conjunto de etiquetas de seleccin (una por ao, mes, da, hora y minuto)
preseleccionadas para acceder a un atributo de fecha y hora especificado.

datetime_select("article", "published_on")

6.6.3 distance_of_time_in_words
Informa la distancia aproximada en el tiempo entre dos objetos Time o Date o nmeros
enteros como segundos. Establezca include_seconds a true si desea obtener
aproximaciones ms detalladas.

distance_of_time_in_words(Time.now, Time.now + 15.seconds) # => less than a min


ute
distance_of_time_in_words(Time.now, Time.now + 15.seconds, include_seconds: true) # =
> less than 20 seconds

6.6.4 select_date
Devuelve un conjunto de etiquetas de seleccin HTML (uno para ao, mes y da)
preseleccionado con la fecha proporcionada.

# Genera una fecha seleccionada que se ajusta por defecto a la fecha proporcionada (se
is das despus de hoy)
select_date(Time.today + 6.days)

# Genera una fecha seleccionada por defecto a la fecha actual (sin fecha especificada)
select_date()

259
6.6 DateHelper

6.6.5 select_datetime
Devuelve un conjunto de etiquetas de seleccin de HTML (una para el ao, el mes, el da, la
hora y el minuto) preseleccionadas con la fecha y hora previstas.

# Genera una fecha y hora que selecciona por defecto la fecha y hora que se proporcion
a (cuatro das despus de hoy)
select_datetime(Time.now + 4.days)

# Genera una fecha y hora que selecciona por defecto a la fecha de hoy (no hay fecha e
specificada)
select_datetime()

6.6.6 select_day
Devuelve una etiqueta de seleccin con opciones para cada uno de los das del 1 al 31 con
el da actual seleccionado.

# Genera un campo de seleccin para los das cuyo valor predeterminado es el da de la


fecha proporcionada
select_day(Time.today + 2.days)

# Genera un campo de seleccin para los das que predeterminan el nmero dado
select_day(5)

6.6.7 select_hour
Devuelve una etiqueta de seleccin con opciones para cada una de las horas de 0 a 23 con
la hora actual seleccionada.

# Genera un campo de seleccin para las horas que predeterminan las horas para el tiem
po proporcionado
select_hour(Time.now + 6.hours)

6.6.8 select_minute
Devuelve una etiqueta de seleccin con opciones para cada uno de los minutos 0 a 59 con
el minuto actual seleccionado.

# Genera un campo de seleccin para los minutos que predeterminan los minutos para el
tiempo proporcionado.
select_minute(Time.now + 10.minutes)

260
6.6 DateHelper

6.6.9 select_month
Devuelve una etiqueta de seleccin con opciones para cada uno de los meses de enero a
diciembre con el mes actual seleccionado.

# Genera un campo de seleccin para meses que predetermina el mes actual


select_month(Date.today)

6.6.10 select_second
Devuelve una etiqueta de seleccin con opciones para cada uno de los segundos 0 a 59
con el segundo actual seleccionado.

# Genera un campo de seleccin para segundos que esta predeterminado a los segundos pa
ra el tiempo proporcionado
select_second(Time.now + 16.seconds)

6.6.11 select_time
Devuelve un conjunto de etiquetas de seleccin HTML (una por hora y minuto).

# Genera una seleccin de tiempo que se ajusta por defecto a la hora proporcionada
select_time(Time.now)

6.6.12 select_year
Devuelve una etiqueta de seleccin con opciones para cada uno de los cinco aos de cada
lado del actual que est seleccionado. El rango de cinco aos se puede cambiar usando las
teclas :start_year y :end_year en las opciones.

# Genera un campo de seleccin durante cinco aos a cada lado de Date.today predetermi
nado al ao actual
select_year(Date.today)

# Genera un campo de seleccin de 1900 a 2009 y el predeterminado es el ao actual


select_year(Date.today, start_year: 1900, end_year: 2009)

6.6.13 time_ago_in_words
Como distance_of_time_in_words , pero donde to_time est fijado a Time.now .

261
6.6 DateHelper

time_ago_in_words(3.minutes.from_now) # => 3 minutes

6.6.14 time_select
Devuelve un conjunto de etiquetas de seleccin (uno para hora, minuto y opcionalmente
segundos) preseleccionado para acceder a un atributo de tiempo especfico. Los select
se preparan para la asignacin de mltiples parmetros a un objeto de Active Record.

# Creates a time select tag that, when POSTed, will be stored in the order variable in
the submitted attribute
time_select("order", "submitted")

262
6.7 DebugHelper

6.7 DebugHelper
Devuelve una etiqueta pre que tiene un objeto dumping de YAML . Esto crea una forma
muy legible para inspeccionar un objeto.

my_hash = { 'first' => 1, 'second' => 'two', 'third' => [1,2,3] }


debug(my_hash)

<pre class='debug_dump'>---
first: 1
second: two
third:
- 1
- 2
- 3
</pre>

263
6.8 FormHelper

6.8 FormHelper
Los ayudantes de formularios estn diseados para facilitar el trabajo con modelos en
comparacin con el uso de elementos HTML estndar proporcionando un conjunto de
mtodos para crear formularios basados en sus modelos. Este ayudante genera HTML para
los formularios, proporcionando un mtodo para cada tipo de entrada (por ejemplo, text,
password, select, etc.). Cuando se enva el formulario (es decir, cuando el usuario pulsa el
botn de envo o form.submit se enva a travs de JavaScript), las entradas del formulario
se incluirn en el objeto params y se devolvern al controlador.

Hay dos tipos de ayudantes de formulario: los que trabajan especficamente con los
atributos del modelo y los que no. FormHeper se ocupa de los que trabajan con atributos del
modelo; Para ver un ejemplo de ayudantes de formulario que no funcionan con atributos de
modelo, consulte la documentacin ActionView::Helpers::FormTagHelper .

El mtodo principal de este ayudante, form_for , le da la capacidad de crear un formulario


para una instancia de modelo; Por ejemplo, digamos que usted tiene un modelo Person y
desea crear una nueva instancia del mismo:

# Nota: se habr creado una variable @person en el controlador (por ejemplo, @person =
Person.new)
<%= form_for @person, url: { action: "create" } do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= submit_tag 'Create' %>
<% end %>

El HTML generado para esto sera:

<form action="/people/create" method="post">


<input id="person_first_name" name="person[first_name]" type="text" />
<input id="person_last_name" name="person[last_name]" type="text" />
<input name="commit" type="submit" value="Create" />
</form>

El objeto params creado cuando se enva este formulario se vera as:

{ "action" => "create", "controller" => "people", "person" => { "first_name" => "Willi
am", "last_name" => "Smith" } }

264
6.8 FormHelper

El hash de parmetros tiene un valor de anidado de persona, por lo que se puede acceder
con params[:person] en el controlador.

6.8.1 check_box
Devuelve una etiqueta de casilla de verificacin adaptada para acceder a un atributo
especificado.

# Digamos que @article.validated? Es 1:


check_box("article", "validated")
# => <input type="checkbox" id="article_validated" name="article[validated]" value="1"
/>
# <input name="article[validated]" type="hidden" value="0" />

6.8.2 fields_for
Crea un scope alrededor de un objeto de modelo especfico como form_for , pero no crea
las etiquetas de formulario por s mismo. Esto hace que fields_for sea adecuado para
especificar objetos de modelo adicionales en el mismo form:

<%= form_for @person, url: { action: "update" } do |person_form| %>


First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>

<%= fields_for @person.permission do |permission_fields| %>


Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<% end %>

6.8.3 file_field
Devuelve una etiqueta de entrada de carga de archivo adaptada para acceder a un atributo
especificado.

file_field(:user, :avatar)
# => <input type="file" id="user_avatar" name="user[avatar]" />

6.8.4 form_for
Crea un formulario y un scope alrededor de un objeto de modelo especfico que se utiliza
como base para preguntar los valores de los campos.

265
6.8 FormHelper

<%= form_for @article do |f| %>


<%= f.label :title, 'Title' %>:
<%= f.text_field :title %><br>
<%= f.label :body, 'Body' %>:
<%= f.text_area :body %><br>
<% end %>

6.8.5 hidden_field
Devuelve una etiqueta de entrada oculta adaptada para acceder a un atributo especificado.

hidden_field(:user, :token)
# => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />

6.8.6 label
Devuelve una etiqueta label adaptada para etiquetar un campo de entrada para un
atributo especificado.

label(:article, :title)
# => <label for="article_title">Title</label>

6.8.7 password_field
Devuelve una etiqueta de entrada del tipo "contrasea" adaptada para acceder a un atributo
especificado.

password_field(:login, :pass)
# => <input type="text" id="login_pass" name="login[pass]" value="#{@login.pass}" />

6.8.8 radio_button
Devuelve una etiqueta de radio button para acceder a un atributo especificado.

# Digamos que @article.category devuelve "Rails":


radio_button("article", "category", "rails")
radio_button("article", "category", "java")
# => <input type="radio" id="article_category_rails" name="article[category]" value="r
ails" checked="checked" />
# <input type="radio" id="article_category_java" name="article[category]" value="ja
va" />

266
6.8 FormHelper

6.8.9 text_area
Devuelve un conjunto de etiquetas de apertura y cierre de textarea adaptadas para
acceder a un atributo especificado.

text_area(:comment, :text, size: "20x30")


# => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
# #{@comment.text}
# </textarea>

6.8.10 text_field
Devuelve una etiqueta de entrada del tipo "texto" adaptada para acceder a un atributo
especificado.

text_field(:article, :title)
# => <input type="text" id="article_title" name="article[title]" value="#{@article.tit
le}" />

6.8.11 email_field
Devuelve una etiqueta de entrada del tipo "correo electrnico" adaptada para acceder a un
atributo especificado.

email_field(:user, :email)
# => <input type="email" id="user_email" name="user[email]" value="#{@user.email}" />

6.8.12 url_field
Devuelve una etiqueta de entrada del tipo "url" adaptada para acceder a un atributo
especificado.

url_field(:user, :url)
# => <input type="url" id="user_url" name="user[url]" value="#{@user.url}" />

267
6.9 FormOptionsHelper

6.9 FormOptionsHelper
Proporciona una serie de mtodos para convertir diferentes tipos de contenedores en un
conjunto de etiquetas de opcin.

6.9.1 collection_select
Devuelve las etiquetas select y option para la coleccin de valores de retorno existentes
del mtodo para la clase del objeto.

Estructura de objetos de ejemplo para su uso con este mtodo:

class Article < ApplicationRecord


belongs_to :author
end

class Author < ApplicationRecord


has_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end

Ejemplo de uso (seleccionando el Author asociado para una instancia de Article ,


@article ):

collection_select(:article, :author_id, Author.all, :id, :name_with_initial, { prompt:


true })

Si @article.author_id es 1, esto devolver:

<select name="article[author_id]">
<option value="">Please select</option>
<option value="1" selected="selected">D. Heinemeier Hansson</option>
<option value="2">D. Thomas</option>
<option value="3">M. Clark</option>
</select>

6.9.2 collection_radio_buttons

268
6.9 FormOptionsHelper

Devuelve las etiquetas radio_button para la coleccin de valores de retorno existentes del
mtodo para la clase del objeto.

Estructura de objetos de ejemplo para su uso con este mtodo:

class Article < ApplicationRecord


belongs_to :author
end

class Author < ApplicationRecord


has_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end

Ejemplo de uso (seleccionando el Author asociado para una instancia de Article ,


@article ):

collection_radio_buttons(:article, :author_id, Author.all, :id, :name_with_initial)

Si @article.author_id es 1, esto devolver:

<input id="article_author_id_1" name="article[author_id]" type="radio" value="1" check


ed="checked" />
<label for="article_author_id_1">D. Heinemeier Hansson</label>
<input id="article_author_id_2" name="article[author_id]" type="radio" value="2" />
<label for="article_author_id_2">D. Thomas</label>
<input id="article_author_id_3" name="article[author_id]" type="radio" value="3" />
<label for="article_author_id_3">M. Clark</label>

6.9.3 collection_check_boxes
Devuelve etiquetas check_box para la coleccin de valores de retorno existentes del
mtodo para la clase del objeto.

Estructura de objetos de ejemplo para su uso con este mtodo:

269
6.9 FormOptionsHelper

class Article < ApplicationRecord


has_and_belongs_to_many :authors
end

class Author < ApplicationRecord


has_and_belongs_to_many :articles
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end

Ejemplo de uso (seleccionando el Author asociado para una instancia de Article ,


@article ):

collection_check_boxes(:article, :author_ids, Author.all, :id, :name_with_initial)

Si @article.author_ids es [1], esto devolver:

<input id="article_author_ids_1" name="article[author_ids][]" type="checkbox" value="1


" checked="checked" />
<label for="article_author_ids_1">D. Heinemeier Hansson</label>
<input id="article_author_ids_2" name="article[author_ids][]" type="checkbox" value="2
" />
<label for="article_author_ids_2">D. Thomas</label>
<input id="article_author_ids_3" name="article[author_ids][]" type="checkbox" value="3
" />
<label for="article_author_ids_3">M. Clark</label>
<input name="article[author_ids][]" type="hidden" value="" />

6.9.4 option_groups_from_collection_for_select
Devuelve una cadena de etiquetas option , como options_from_collection_for_select ,
pero las agrupa mediante etiquetas optgroup basadas en las relaciones de objeto de los
argumentos.

Estructura de objetos de ejemplo para su uso con este mtodo:

270
6.9 FormOptionsHelper

class Continent < ApplicationRecord


has_many :countries
# attribs: id, name
end

class Country < ApplicationRecord


belongs_to :continent
# attribs: id, name, continent_id
end

Ejemplo de uso:

option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3


)

Posible salida:

<optgroup label="Africa">
<option value="1">Egypt</option>
<option value="4">Rwanda</option>
...
</optgroup>
<optgroup label="Asia">
<option value="3" selected="selected">China</option>
<option value="12">India</option>
<option value="5">Japan</option>
...
</optgroup>

Nota: Slo se devuelven las etiquetas optgroup y option , por lo que todava tiene que
ajustar la salida en una etiqueta de seleccin adecuada.

6.9.5 options_for_select
Acepta un contenedor (hash, array, enumerable, your type) y devuelve una cadena de
etiquetas option .

options_for_select([ "VISA", "MasterCard" ])


# => <option>VISA</option> <option>MasterCard</option>

Nota: Slo se devuelven las etiquetas option , tiene que ajustar esta llamada en una
etiqueta de seleccin HTML normal.

6.9.6 options_from_collection_for_select

271
6.9 FormOptionsHelper

Devuelve una cadena de etiquetas option que se han compilado iterando sobre la
coleccin y asignando el resultado de una llamada al mtodo value como el valor de la
opcin y el text como el texto de la opcin.

# options_from_collection_for_select(collection, value_method, text_method, selected =


nil)

Por ejemplo, imagine un bucle que itera sobre cada persona en @project.people para
generar un input tag:

options_from_collection_for_select(@project.people, "id", "name")


# => <option value="#{person.id}">#{person.name}</option>

Nota: Slo se devuelven las etiquetas option, tiene que ajustar esta llamada en una etiqueta
de seleccin HTML normal.

6.9.7 select
Crea una etiqueta select y una serie de etiquetas option contenidas para el objeto y
mtodo proporcionados.

Ejemplo:

select("article", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_


blank: true })

Si @article.person_id es 1, esto se convertira en:

<select name="article[person_id]">
<option value=""></option>
<option value="1" selected="selected">David</option>
<option value="2">Eileen</option>
<option value="3">Rafael</option>
</select>

6.9.8 time_zone_options_for_select
Devuelve una cadena de etiquetas option para prcticamente cualquier zona horaria del
mundo.

6.9.9 time_zone_select

272
6.9 FormOptionsHelper

Devuelve las etiquetas select y option para el objeto y el mtodo dados, usando
time_zone_options_for_select para generar la lista de etiquetas de opciones.

time_zone_select( "user", "time_zone")

6.9.10 date_field
Devuelve una etiqueta de entrada del tipo "fecha" adaptada para acceder a un atributo
especificado.

date_field("user", "dob")

273
6.10 FormTagHelper

6.10 FormTagHelper
Proporciona una serie de mtodos para crear etiquetas de formulario que no dependen de
un objeto Active Record asignado a la plantilla como lo hace FormHelper. En su lugar,
proporciona los nombres y los valores manualmente.

6.10.1 check_box_tag
Crea una etiqueta de entrada de formulario de casilla de verificacin.

check_box_tag 'accept'
# => <input id="accept" name="accept" type="checkbox" value="1" />

6.10.2 field_set_tag
Crea un conjunto de campos para agrupar elementos de formulario HTML.

<%= field_set_tag do %>


<p><%= text_field_tag 'name' %></p>
<% end %>
# => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>

6.10.3 file_field_tag
Crea un campo de carga de archivos.

<%= form_tag({ action: "post" }, multipart: true) do %>


<label for="file">File to Upload</label> <%= file_field_tag "file" %>
<%= submit_tag %>
<% end %>

Ejemplo de salida:

file_field_tag 'attachment'
# => <input id="attachment" name="attachment" type="file" />

6.10.4 form_tag

274
6.10 FormTagHelper

Inicia una etiqueta de formulario que seala el action a una URL configurada con
url_for_options como ActionController::Base#url_for .

<%= form_tag '/articles' do %>


<div><%= submit_tag 'Save' %></div>
<% end %>
# => <form action="/articles" method="post"><div><input type="submit" name="submit" va
lue="Save" /></div></form>

6.10.5 hidden_field_tag
Crea un campo de entrada de formulario oculto que se utiliza para transmitir datos que se
perderan debido a la apatridia de HTTP o datos que deberan estar ocultos al usuario.

hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'


# => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />

6.10.6 image_submit_tag
Muestra una imagen que, cuando se hace clic, enviar el formulario.

image_submit_tag("login.png")
# => <input src="/images/login.png" type="image" />

6.10.7 label_tag
Crea un campo de etiqueta.

label_tag 'name'
# => <label for="name">Name</label>

6.10.8 password_field_tag
Crea un campo de contrasea, un campo de texto enmascarado que oculta la entrada de
los usuarios detrs de un carcter de mscara.

password_field_tag 'pass'
# => <input id="pass" name="pass" type="password" />

6.10.9 radio_button_tag

275
6.10 FormTagHelper

Crea un radio button; Utilice grupos de radio buttons con el mismo nombre para permitir a
los usuarios seleccionar de un grupo de opciones.

radio_button_tag 'gender', 'male'


# => <input id="gender_male" name="gender" type="radio" value="male" />

6.10.10 select_tag
Crea un cuadro de seleccin desplegable.

select_tag "people", "<option>David</option>"


# => <select id="people" name="people"><option>David</option></select>

6.10.11 submit_tag
Crea un botn de envo con el texto proporcionado como el ttulo.

submit_tag "Publish this article"


# => <input name="commit" type="submit" value="Publish this article" />

6.10.12 text_area_tag
Crea un rea de entrada de texto; Utilice una zona de texto para entradas de texto ms
largas, como publicaciones o descripciones de blogs.

text_area_tag 'article'
# => <textarea id="article" name="article"></textarea>

6.10.13 text_field_tag
Crea un campo de texto estndar; Utilice estos campos de texto para introducir fragmentos
ms pequeos de texto como un nombre de usuario o una consulta de bsqueda.

text_field_tag 'name'
# => <input id="name" name="name" type="text" />

6.10.14 email_field_tag
Crea un campo de entrada estndar del tipo email.

276
6.10 FormTagHelper

email_field_tag 'email'
# => <input id="email" name="email" type="email" />

6.10.15 url_field_tag
Crea un campo de entrada estndar de tipo url.

url_field_tag 'url'
# => <input id="url" name="url" type="url" />

6.10.16 date_field_tag
Crea un campo de entrada estndar de tipo fecha.

date_field_tag "dob"
# => <input id="dob" name="dob" type="date" />

277
6.11 JavaScriptHelper

6.11 JavaScriptHelper
Proporciona funcionalidad para trabajar con JavaScript en sus vistas.

6.11.1 escape_javascript
Retornos de portador de escape y comillas simples y dobles para segmentos de JavaScript.

6.11.2 javascript_tag
Devuelve una etiqueta JavaScript que envuelve el cdigo proporcionado.

javascript_tag "alert('All is good')"

<script>
//<![CDATA[
alert('All is good')
//]]>
</script>

278
6.12 NumberHelper

6.12 NumberHelper
Proporciona mtodos para convertir nmeros en cadenas formateadas. Los mtodos se
proporcionan para nmeros de telfono, moneda, porcentaje, precisin, notacin posicional
y tamao del archivo.

6.12.1 number_to_currency
Formatea un nmero en una cadena de moneda (por ejemplo, $ 13.65).

number_to_currency(1234567890.50) # => $1,234,567,890.50

6.12.2 number_to_human_size
Formatea los bytes de tamao en una representacin ms comprensible; til para reportar
el tamao de los archivos a los usuarios.

number_to_human_size(1234) # => 1.2 KB


number_to_human_size(1234567) # => 1.2 MB

6.12.3 number_to_percentage
Formatea un nmero como una cadena de porcentaje.

number_to_percentage(100, precision: 0) # => 100%

6.12.4 number_to_phone
Formatea un nmero en un nmero de telfono (EE.UU. por defecto).

number_to_phone(1235551234) # => 123-555-1234

6.12.5 number_with_delimiter
Formatea un nmero con miles agrupados usando un delimitador.

number_with_delimiter(12345678) # => 12,345,678

279
6.12 NumberHelper

6.12.6 number_with_precision
Formatea un nmero con el nivel de precisin especificado, que por defecto es 3.

number_with_precision(111.2345) # => 111.235


number_with_precision(111.2345, precision: 2) # => 111.23

280
6.13 SanitizeHelper

6.13 SanitizeHelper
El mdulo SanitizeHelper proporciona un conjunto de mtodos para borrar el texto de
elementos HTML no deseados.

6.13.1 sanitize
Este helper sanitize codificar en HTML todas las etiquetas y quitar todos los atributos que
no se permiten especficamente.

sanitize @article.body

Si se pasan las opciones :attributes o :tags , slo se permiten los atributos y etiquetas
mencionados y nada ms.

sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)

Para cambiar los valores predeterminados para usos mltiples, por ejemplo, agregar
etiquetas de tabla al valor predeterminado:

class Application < Rails::Application


config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
end

6.13.2 sanitize_css(style)
Desinfecta un bloque de cdigo CSS.

6.13.3 strip_links(html)
Borra todas las etiquetas de enlace del texto y deja slo el texto del enlace.

281
6.13 SanitizeHelper

strip_links('<a href="http://rubyonrails.org">Ruby on Rails</a>')


# => Ruby on Rails

strip_links('emails to <a href="mailto:me@email.com">me@email.com</a>.')


# => emails to me@email.com.

strip_links('Blog: <a href="http://myblog.com/">Visit</a>.')


# => Blog: Visit.

6.13.4 strip_tags(html)
Elimina todas las etiquetas HTML del html, incluidos los comentarios. Esta funcionalidad es
alimentada por la gema rails-html-sanitizer .

strip_tags("Strip <i>these</i> tags!")


# => Strip these tags!

strip_tags("<b>Bold</b> no more! <a href='more.html'>See more</a>")


# => Bold no more! See more

Nota: La salida todava puede contener caracteres ' < ', ' > ', ' & ' sin escape y confundir los
navegadores.

282
6.14 CsrfHelper

6.14 CsrfHelper
Devuelve las etiquetas meta " csrf-param " y " csrf-token " con el nombre del parmetro de
proteccin de falsificacin de solicitud de cross-site y smbolo, respectivamente.

<%= csrf_meta_tags %>

Los formularios regulares generan campos ocultos para que no utilicen estas etiquetas.
Se pueden encontrar ms detalles en la Gua de seguridad de Rails.

283
7- Vistas con Locale

7- Vistas con Locale


Action View tiene la capacidad de procesar diferentes plantillas dependiendo de la
configuracin local actual.

Por ejemplo, supongamos que tiene un ArticlesController con una accin show . De
forma predeterminada, al llamar a esta accin, renderizamos
app/views/articles/show.html.erb . Pero si usted fija I18n.locale =: de , a continuacin,

app/views/articles/show.de.html.erb se renderizar en su lugar. Si la plantilla localizada no

est presente, se usar la versin no decorada. Esto significa que no est obligado a
proporcionar vistas localizadas para todos los casos, pero se prefieren y utilizan si estn
disponibles.

Puede utilizar la misma tcnica para localizar los archivos de rescate en su directorio
pblico. Por ejemplo, establecer I18n.locale =: de y crear public/500.de.html y
public/404.de.html le permitira tener pginas de rescate localizadas.

Dado que Rails no restringe los smbolos que utiliza para configurar I18n.locale, puede
aprovechar este sistema para mostrar contenido diferente dependiendo de lo que quiera.
Por ejemplo, suponga que tiene algunos usuarios "expertos" que deberan ver diferentes
pginas en comparacin a los usuarios "normales". Podra agregar lo siguiente a
app/controllers/application.rb :

before_action :set_expert_locale

def set_expert_locale
I18n.locale = :expert if current_user.expert?
end

A continuacin, puede crear vistas especiales como


app/views/articles/show.expert.html.erb que slo se mostrar a los usuarios expertos.

284
IX- Layouts y Rendering en Rails

IX- Layouts y Rendering en Rails


Esta gua cubre las caractersticas bsicas de Action Controller y Action View.

Despus de leer esta gua, sabr:

Cmo utilizar los diversos mtodos de renderizado incorporados en Rails.


Cmo crear layouts con mltiples secciones de contenido.
Cmo utilizar partials para no repetir sus vistas.
Cmo utilizar layouts anidados (sub-templates).

285
1- Descripcin General: Cmo encajan las piezas

1- Descripcin General: Cmo encajan las


piezas
Esta gua se centra en la interaccin entre Controlador y Vista en el tringulo Modelo-Vista-
Controlador. Como saben, el Controlador es responsable de orquestar todo el proceso de
manejo de una solicitud en Rails, aunque normalmente entrega cualquier cdigo pasado al
Modelo. Pero entonces, cuando es hora de enviar una respuesta de vuelta al usuario, el
controlador entrega las cosas a la vista. Esa entrega es el tema de esta gua.

En trazos generales, esto implica decidir qu se debe enviar como respuesta y llamar a un
mtodo apropiado para crear esa respuesta. Si la respuesta es una vista completa, Rails
tambin hace un trabajo extra para envolver la vista en un layout y posiblemente tambien
deba obtener vistas parciales, y hace ese trabajo. Vers todas esas rutas ms adelante en
esta gua.

286
2- Creacin de respuestas

2- Creacin de respuestas
Desde el punto de vista del controlador, hay tres maneras de crear una respuesta HTTP :

Llamar a render para crear una respuesta completa para enviar de nuevo al
navegador
Llamar a redirect_to para enviar un cdigo de estado de redireccionamiento HTTP al
navegador
Llamar a head para crear una respuesta que consiste nicamente en encabezados
HTTP para enviar de nuevo al navegador

287
2.1 Rendering por defecto: Convencin sobre configuracin en Action

2.1 Rendering por defecto: Convencin


sobre configuracin en Action
Has escuchado que Rails promueve la "convencin sobre la configuracin". El rendering por
defecto es un ejemplo excelente de esto. De forma predeterminada, los controladores de
Rails automticamente reproducen vistas con nombres que corresponden a rutas vlidas.
Por ejemplo, si tiene este cdigo en su clase BooksController :

class BooksController < ApplicationController


end

Y lo siguiente en tu archivo de rutas:

resources :books

Y usted tiene un archivo de vistas app/views/books/index.html.erb :

<h1>Books are coming soon!</h1>

Rails renderizar automticamente app/views/books/index.html.erb cuando navegue a


/books y ver "Books are coming soon!" En la pantalla.

Sin embargo, este pantallazo es poco til, por lo que pronto deber crear su modelo de
book y agregar la accin de index de BooksController :

class BooksController < ApplicationController


def index
@books = Book.all
end
end

Tenga en cuenta que no tenemos que llamar a render de forma explicita al final de la
accin index de acuerdo con el principio de "convencin sobre configuracin". La regla es
que si no se hace explcitamente algo al final de una accin del controlador, Rails buscar
automticamente la plantilla action_name.html.erb en la ruta de vista del controlador y la
procesar. En este caso, Rails procesar el archivo app/views/books/index.html.erb .

Si queremos mostrar las propiedades de todos los libros en nuestra vista, podemos hacerlo
con una plantilla ERB como esta:

288
2.1 Rendering por defecto: Convencin sobre configuracin en Action

<h1>Listing Books</h1>

<table>
<tr>
<th>Title</th>
<th>Summary</th>
<th></th>
<th></th>
<th></th>
</tr>

<% @books.each do |book| %>


<tr>
<td><%= book.title %></td>
<td><%= book.content %></td>
<td><%= link_to "Show", book %></td>
<td><%= link_to "Edit", edit_book_path(book) %></td>
<td><%= link_to "Remove", book, method: :delete, data: { confirm: "Are you sure?"
} %></td>
</tr>
<% end %>
</table>

<br>

<%= link_to "New book", new_book_path %>

El rendering actual se realiza mediante subclases de ActionView::TemplateHandlers . Esta


gua no entra en ese proceso, pero es importante saber que la extensin de archivo en su
vista controla la eleccin del controlador de plantilla. Comenzando con Rails 2, las
extensiones estndar son .erb para ERB ( HTML con Ruby incrustado ) y .builder para
Builder (generador de XML).

289
2.2 Uso del render

2.2 Uso del render


En la mayora de los casos, el mtodo ActionController::Base#render hace el trabajo
pesado de renderizar el contenido de su aplicacin para su uso en el navegador. Hay una
variedad de maneras de personalizar el comportamiento de render . Puede renderizar la
vista predeterminada para una plantilla de Rails, o una plantilla especfica, o un archivo, o
cdigo inline, o nada en absoluto. Puede procesar texto, JSON o XML . Tambin puede
especificar el tipo de contenido o el estado HTTP de la respuesta renderizada.

Si desea ver los resultados exactos de una llamada a render sin necesidad de
inspeccionarla en un navegador, puede llamar a render_to_string . Este mtodo toma
exactamente las mismas opciones que render , pero devuelve una cadena en lugar de
enviar una respuesta al navegador.

2.2.1 Visualizacin de una accin


Si desea representar la vista que corresponde a una plantilla diferente dentro del mismo
controlador, puede utilizar render con el nombre de la vista:

def update
@book = Book.find(params[:id])
if @book.update(book_params)
redirect_to(@book)
else
render "edit"
end
end

Si falla la llamada a update , llama nuevamente a la accin update y este controlador


mostrar la plantilla edit.html.erb perteneciente al mismo controlador.

Si lo prefiere, puede utilizar un smbolo en lugar de una cadena para especificar la accin a
renderizar:

def update
@book = Book.find(params[:id])
if @book.update(book_params)
redirect_to(@book)
else
render :edit
end
end

290
2.2 Uso del render

2.2.2 Procesamiento de un template de una accin desde


otro controlador
Qu sucede si desea procesar una plantilla desde un controlador completamente distinto
del que contiene el cdigo de accin? Tambin puede hacer eso con render , que acepta la
ruta completa (relativa a app/views ) de la plantilla a renderizar. Por ejemplo, si ejecuta
cdigo en un AdminProductsController que vive en app/controllers/admin , puede
renderizar los resultados de una accin a una plantilla en app/views/products de esta
manera:

render "products/show"

Rails sabe que esta vista pertenece a un controlador diferente debido all carcter de barra
diagonal incrustado en la cadena. Si desea ser explcito, puede utilizar la opcin :template
(que se requera en Rails 2.2 y anteriores):

render template: "products/show"

2.2.3 Procesamiento de un archivo arbitrario


El mtodo render tambin puede utilizar una vista que est totalmente fuera de la
aplicacin:

render file: "/u/apps/warehouse_app/current/app/views/products/show"

La opcin :file toma una ruta absoluta del sistema de archivos. Por supuesto, necesita tener
derechos sobre la vista que est utilizando para procesar el contenido.

El uso de la opcin :file en combinacin con la entrada de los usuarios puede dar lugar
a problemas de seguridad ya que un atacante podra utilizar esta accin para acceder
a archivos sensibles a la seguridad en su sistema de archivos.

De forma predeterminada, el archivo se procesa utilizando el layout actual.

Si est ejecutando Rails en Microsoft Windows, debera utilizar la opcin :file para
procesar un archivo, ya que los nombres de archivo de Windows no tienen el mismo
formato que los nombres de archivo Unix.

2.2.4 Envolvindolo

291
2.2 Uso del render

Las tres formas de representacin anteriores (renderizar otra plantilla dentro del controlador,
renderizar una plantilla dentro de otro controlador y renderizar un archivo arbitrario en el
sistema de archivos) son en realidad variantes de la misma accin.

De hecho, en la clase BooksController , dentro de la accin update donde queremos


renderizar la plantilla edit si el book no se actualiza correctamente, todas las siguientes
llamadas de renderizaran se harn a la plantilla edit.html.erb que esta en views/books

render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"

Cul utiliza usted es realmente una cuestin de estilo y de convencin, pero la regla de oro
es utilizar el ms simple y que tiene sentido para el cdigo que usted est escribiendo.

2.2.5 Uso de render con :inline


El mtodo render puede prescindir de una vista completa, si usa la opcin :inline para
suministrar el ERB como parte de la llamada al mtodo. Esto es perfectamente vlido:

render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>"

Rara vez hay una buena razn para usar esta opcin. La mezcla de ERB en sus
controladores derrota la orientacin MVC de Rails y dificultar que otros desarrolladores
sigan la lgica de su proyecto. Utilice una vista erb separada y en su lugar respectivo.

De forma predeterminada, el rendering inline utiliza ERB . Puede forzarlo a que utilice
Builder en su lugar con la opcin :type .

render inline: "xml.p {'Horrid coding practice!'}", type: :builder

2.2.6 Renderizado de texto

292
2.2 Uso del render

Puede enviar texto plano (sin markups) - de nuevo al navegador usando la opcin :plain
para renderizar:

render plain: "OK"

Renderizar texto plano es muy til cuando respondes a un Ajax o a web services
request que esperan algo distinto del HTML apropiado.

De forma predeterminada, si utiliza la opcin :plain , el texto se procesa sin utilizar el


layout actual. Si desea que Rails coloque el texto en el layout actual, debe agregar la opcin
layout :true y usar la extensin .txt.erb para el archivo de diseo.

2.2.7 Renderizacin de HTML


Puede enviar una cadena HTML de vuelta al navegador mediante la opcin :html para
renderizar:

render html: "<strong>Not Found</strong>".html_safe

Esto es til cuando ests procesando un pequeo fragmento de cdigo HTML . Sin
embargo, es posible que desee considerar moverlo a un archivo template si el marcado
es complejo.

Cuando se utiliza la opcin :html , las entidades HTML se escaparn si la cadena no


est marcada como HTML seguro mediante el uso del mtodo html_safe .

2.2.8 Renderizacin de JSON


JSON es un formato de datos JavaScript utilizado por muchas bibliotecas de Ajax . Rails

tiene soporte incorporado para convertir objetos a JSON y hacer que JSON regrese al
navegador:

render json: @product

No es necesario llamar al mtodo to_json en el objeto que desea procesar. Si utiliza


la opcin :json , el render llamar automticamente a to_json por usted.

2.2.9 Rendering XML


Rails tambin tiene soporte incorporado para convertir objetos a XML y devolver ese XML
a quien lo llama:

293
2.2 Uso del render

render xml: @product

No es necesario llamar a to_xml en el objeto que desea procesar. Si utiliza la opcin


:xml , render le llamar automticamente a to_xml por usted.

2.2.10 Renderizado de Vanilla JavaScript


Rails puede hacer Vanilla JavaScript :

render js: "alert('Hello Rails');"

Esto enviar la cadena suministrada al navegador con un MIME de typo text/javascript .

2.2.11 Renderizacin de un body sin procesar


Puede enviar un contenido sin formato de nuevo al navegador, sin establecer ningn tipo de
contenido, utilizando la opcin :body para renderizar:

render body: "raw"

Esta opcin slo debe utilizarse si no le preocupa el tipo de contenido de la respuesta.


Usar :plain o :html podra ser ms apropiado para la mayor parte del tiempo.

A menos que se sobre-escriba, la respuesta devuelta de esta opcin de render ser


text/html , ya que es el tipo de contenido predeterminado de la respuesta de Action

Dispatch.

2.2.12 Opciones para el renderizado


Las llamadas al mtodo render generalmente aceptan cinco opciones:

:content_type
:layout
:location
:status
:formats

2.2.12.1 La opcin :content_type

294
2.2 Uso del render

De forma predeterminada, Rails entregar los resultados de una operacin de renderizado


con el tipo de contenido MIME text/html (o application/json si utiliza la opcin :json o
application/xml para la opcin :xml ). Hay ocasiones en las que puede que desee

cambiar esto, y puede hacerlo estableciendo la opcin :content_type :

render file: filename, content_type: "application/rss"

2.2.12.2 La opcin :layout


Con la mayora de las opciones de render , el contenido renderizado se muestra como
parte del layout actual. Aprender ms sobre los layput y cmo utilizarlos ms adelante en
esta gua.

Puede utilizar la opcin :layout para indicar a Rails que utilice un archivo especfico como
el layout de la accin actual:

render layout: "special_layout"

Tambin puede indicarle a Rails que lo procese sin ningn layout en absoluto:

render layout: false

2.2.12.3 La opcin :location


Puedes utilizar la opcin :location para establecer el encabezado HTTP Location

render xml: photo, location: photo_url(photo)

2.2.12.4 La opcin :status


Rails generar automticamente una respuesta con el cdigo de estado HTTP correcto (en
la mayora de los casos, esto es 200 OK). Puede usar la opcin :status para cambiar
esto:

render status: 500


render status: :forbidden

Rails entiende los cdigos numricos de estado y los smbolos correspondientes que se
muestran a continuacin.

295
2.2 Uso del render

Clase de Respuesta Codigo de Status HTTP Smbolo


Informacional 100 :continue

101 :switching_protocols

102 :processing
Success 200 :ok
201 :created
202 :accepted

203 :non_authoritative_information
204 :no_content

205 :reset_content
206 :partial_content
207 :multi_status
208 :already_reported
226 :im_used
Redirection 300 :multiple_choices
301 :moved_permanently
302 :found

303 :see_other
304 :not_modified
305 :use_proxy
307 :temporary_redirect

308 :premanent_redirect
Client Error 400 :bad_request

401 :unauthorized
402 :payment_required

403 :forbidden
404 :not_found

405 :method_not_allowed
406 :not_acceptable

407 :proxy_authentication_required

408 :request_timeout
409 :conflict

296
2.2 Uso del render

410 :gone
411 :length_required

412 :precondition_failed
413 :payload_too_large
414 :uri_too_long

415 :unsupported_media_type
416 :range_not_satisfiable
417 :expectation_failed

422 :unprocessable_entity
423 :locked
424 :failed_dependency
426 :upgrade_required
428 :precondition_required
429 :too_many_requests
431 :request_header_fields_too_large

Server Error 500 :internal_server_error


501 :not_implemented
502 :bad_gateway
503 :service_unavailable
504 :gateway_timeout

505 :http_version_not_supported
506 :variant_also_negotiates

507 :insufficient_storage
508 :loop_detected

510 :not_extended
511 :network_authentication_required

Si intenta procesar el contenido junto con un cdigo de estado sin contenido (100-199,
204, 205 o 304), se eliminar de la respuesta.

2.2.12.5 La opcin :formats

297
2.2 Uso del render

Rails utiliza el formato especificado en la solicitud (o :html de forma predeterminada).


Puede cambiar esto pasando la opcin :formats con un smbolo o un array:

render formats: :xml


render formats: [:json, :xml]

Si no existe una plantilla con el formato especificado, se genera un error


ActionView::MissingTemplate .

2.2.13 Bsqueda de layouts


Para encontrar el layout actual, Rails primero busca un archivo en app/views/layouts con
el mismo nombre de base que el controlador. Por ejemplo, las acciones de renderizacin de
la clase PhotosController usarn app/views/layouts/photos.html.erb (o
app/views/layouts/photos.builder ). Si no existe un layout especfico para el controlador,

rails utilizar app/views/layouts/application.html.erb o


app/views/layouts/application.builder . Si no existe un diseo .erb , rails utilizar un

diseo .builder si existe. Rails tambin proporciona varias maneras de asignar con mayor
precisin layouts especficos a controladores y acciones individuales.

2.2.13.1 Especificacin de layouts para controladores


Puede reemplazar las convenciones de layout predeterminadas en sus controladores
mediante la declaracin de un layout. Por ejemplo:

class ProductsController < ApplicationController


layout "inventory"
#...
end

Con esta declaracin, todas las vistas generadas por el ProductsController utilizarn
app/views/layouts/inventory.html.erb como su layout.

Para asignar un layout especfico para toda la aplicacin, utilice una declaracin de layout
en su clase ApplicationController :

class ApplicationController < ActionController::Base


layout "main"
#...
end

298
2.2 Uso del render

Con esta declaracin, todas las vistas de toda la aplicacin utilizarn


app/views/layouts/main.html.erb para su layout.

2.2.13.2 Eleccin de layouts en tiempo de ejecucin


Puede utilizar un smbolo para aplazar la eleccin del layout hasta que se procese una
solicitud:

class ProductsController < ApplicationController


layout :products_layout

def show
@product = Product.find(params[:id])
end

private
def products_layout
@current_user.special? ? "special" : "products"
end

end

Ahora, si el usuario actual es un usuario especial, ver un layout especial cuando acceda a
un producto.

Incluso puede utilizar un mtodo inline, como un Proc , para determinar el layout. Por
ejemplo, si pasa un objeto Proc , al bloque que le da el Proc se le dar la instancia del
controlador, por lo que el layout se puede determinar sobre la base de la solicitud actual:

class ProductsController < ApplicationController


layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end

2.2.13.3 Layouts condicionales


Los layouts especificados a nivel de controlador admiten las opciones :only y :except .
Estas opciones llevan un nombre de mtodo o un array de nombres de mtodos que
corresponden a nombres de mtodos dentro del controlador:

class ProductsController < ApplicationController


layout "product", except: [:index, :rss]
end

299
2.2 Uso del render

Con esta declaracin, el layout del producto se utilizara para todo, excepto los mtodos
rss e index .

2.2.13.4 Herencia de layouts


Las declaraciones de layouts caen en cascada hacia abajo en la jerarqua y las
declaraciones de layouts ms especficas siempre anulan las declaraciones ms generales.
Por ejemplo:

application_controller.rb

class ApplicationController < ActionController::Base


layout "main"
end

articles_controller.rb

class ArticlesController < ApplicationController


end

special_articles_controller.rb

class SpecialArticlesController < ArticlesController


layout "special"
end

old_articles_controller.rb

class OldArticlesController < SpecialArticlesController


layout false

def show
@article = Article.find(params[:id])
end

def index
@old_articles = Article.older
render layout: "old"
end
# ...
end

En esta aplicacin:

En general, las vistas se mostrarn en el layout principal

300
2.2 Uso del render

ArticlesController#index usar el layout principal

SpecialArticlesController#index usar el layout especial

OldArticlesController#show no utilizar ningn layout en absoluto

OldArticlesController#index utilizar el layout antiguo.

2.2.13.5 Herencia de plantilla


Similar a la lgica de herencia de layout, si un template o partial no se encuentra en la ruta
convencional, el controlador buscar una plantilla o parcial para procesar en su cadena de
herencia. Por ejemplo:

# in app/controllers/application_controller
class ApplicationController < ActionController::Base
end

# in app/controllers/admin_controller
class AdminController < ApplicationController
end

# in app/controllers/admin/products_controller
class Admin::ProductsController < AdminController
def index
end
end

El orden de bsqueda de la accin index de admin/products#index ser:

app/views/admin/products/
app/views/admin/
app/views/application/

Esto hace que app/views/application/ sea un gran lugar para tus patials compartidos, que
luego se pueden renderizar en tu ERB como tal:

<%# app/views/admin/products/index.html.erb %>


<%= render @products || "empty_list" %>

<%# app/views/application/_empty_list.html.erb %>


There are no items in this list <em>yet</em>.

2.2.14 Evitar errores de procesamiento doble


Tarde o temprano, la mayora de los desarrolladores de Rails vern el mensaje de error
"Can only render or redirect once per action". Si bien esto es molesto, es relativamente fcil
de arreglar. Por lo general, esto ocurre debido a un malentendido fundamental de la forma

301
2.2 Uso del render

en que funciona.

Por ejemplo, aqu hay un cdigo que activar este error:

def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show"
end
render action: "regular_show"
end

Si @book.special? se evala como true , Rails iniciar el proceso de renderizado para


volcar la variable @book en la vista special_show . Pero esto no detendr el resto del
cdigo en la accin show , y cuando Rails llegue al final de la accin, comenzar a mostrar
la vista regular_show y lanzar un error. La solucin es simple: asegrese de que slo
tiene una llamada para procesar o redirigir en una ruta de un solo cdigo. Una cosa que
puede ayudar es return . He aqu una versin parcheada del mtodo:

def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show" and return
end
render action: "regular_show"
end

Asegrese de usar and return en lugar solo return porque and return no funcionar
debido a la precedencia del operador en el lenguaje Ruby

Tenga en cuenta que el renderizado implcito realizado por ActionController detecta si


render se ha llamado, por lo que lo siguiente funcionar sin errores:

def show
@book = Book.find(params[:id])
if @book.special?
render action: "special_show"
end
end

Esto har que se renderice book con special? establecido con la plantilla special_show ,
mientras que otros books se procesarn con la plantilla de show predeterminada.

302
2.2 Uso del render

303
2.3 Uso de redirect_to

2.3 Uso de redirect_to


Otra forma de manejar las respuestas de retorno a una solicitud HTTP es con redirect_to .
Como se ha visto, render le dice a Rails qu vista (u otro asset) debe usar para construir
una respuesta. El mtodo redirect_to hace algo completamente diferente: le dice al
navegador que enve una nueva solicitud para una URL diferente. Por ejemplo, puede
redirigir desde donde quiera que est en su cdigo hasta el index de fotos de su
aplicacin con esta llamada:

redirect_to photos_url

Puede utilizar redirect_back para devolver al usuario a la pgina de la que proceden. Esta
ubicacin se extrae del encabezado HTTP_REFERER que el navegador no garantiza que se
establezca, por lo que debe proporcionar el fallback_location para utilizar en este caso.

redirect_back(fallback_location: root_path)

redirect_to y redirect_back no se detienen y retornan inmediatamente desde la

ejecucin del mtodo, sino que simplemente establecen respuestas HTTP . Las
declaraciones que ocurran despus de ellos en un mtodo sern ejecutadas. Usted
puede detener por un return explcito o algn otro mecanismo de detencin, si es
necesario.

2.3.1 Obtencin de un cdigo de estado de redireccin


diferente
Rails utiliza el cdigo de estado HTTP 302 , un redireccionamiento temporal, cuando llama a
redirect_to . Si desea utilizar un cdigo de estado diferente, tal vez 301 , un

redireccionamiento permanente, puede utilizar la opcin :status :

redirect_to photos_path, status: 301

Al igual que la opcin :status para render , :status para redirect_to acepta las
designaciones de encabezados numricos y simblicos.

2.3.2 La diferencia entre render y redirect_to

304
2.3 Uso de redirect_to

A veces, los desarrolladores sin experiencia piensan en redirect_to como una especie de
comando go-to , que mueve la ejecucin de un lugar a otro en su cdigo Rails. Esto no es
correcto. Su cdigo deja de ejecutarse y espera una nueva solicitud para el navegador.
Sucede que le has dicho al navegador qu solicitud debe hacer a continuacin, enviando un
cdigo de estado HTTP 302 .

Considere estas acciones para ver la diferencia:

def index
@books = Book.all
end

def show
@book = Book.find_by(id: params[:id])
if @book.nil?
render action: "index"
end
end

Con el cdigo de esta forma, probablemente habr un problema si la variable @book es


nil . Recuerde que una accin :render action no ejecuta ningn cdigo en la accin de

destino, de modo que nada configurar la variable @books que probablemente requiera la
vista de index . Una manera de arreglar esto es redireccionar en lugar de renderizar:

def index
@books = Book.all
end

def show
@book = Book.find_by(id: params[:id])
if @book.nil?
redirect_to action: :index
end
end

Con este cdigo, el navegador har una nueva solicitud para la pgina index , y el cdigo
en el mtodo index se ejecutar, y todo estar bien.

El nico inconveniente de este cdigo es que requiere un viaje de ida y vuelta al navegador:
el navegador solicita la accin show con /books/1 y el controlador detecta que no hay
libros, por lo que el controlador enva una respuesta de redireccin 302 al navegador
dicindole que vaya a /books/ , el navegador cumple y enva una nueva solicitud al
controlador solicitando ahora la accin index , el controlador obtiene todos los libros de la
base de datos y procesa la plantilla index , devolvindola a la navegador que luego lo
muestra en su pantalla.

305
2.3 Uso de redirect_to

Mientras que en una pequea aplicacin, esta latencia aadida podra no ser un problema,
es algo en lo que pensar si el tiempo de respuesta es una preocupacin. Podemos
demostrar una manera de manejar esto con un ejemplo:

def index
@books = Book.all
end

def show
@book = Book.find_by(id: params[:id])
if @book.nil?
@books = Book.all
flash.now[:alert] = "Your book was not found"
render "index"
end
end

Esto detectara que no hay libros con el ID especificado, llena la variable de instancia de
@books con todos los libros del modelo y luego renderiza directamente la plantilla

index.html.erb , devolvindola al navegador con un mensaje de alerta flash para decirle

al usuario lo que pas.

306
2.4 Uso de los headers para crear respuestas Header-Only

2.4 Uso de los headers para crear


respuestas Header-Only
El mtodo head se puede utilizar para enviar respuestas con slo encabezados al
navegador. El mtodo head acepta un nmero o smbolo (vase tabla de referencia) que
representa un cdigo de estado HTTP . El argumento options se interpreta como un hash
de nombres de encabezado y valores. Por ejemplo, slo puede devolver un encabezado de
error:

head :bad_request

Esto producira el siguiente encabezado:

HTTP/1.1 400 Bad Request


Connection: close
Date: Sun, 24 Jan 2010 12:15:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Runtime: 0.013483
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

O puede utilizar otros encabezados HTTP para transmitir otra informacin:

head :created, location: photo_path(@photo)

Lo que producira:

HTTP/1.1 201 Created


Connection: close
Date: Sun, 24 Jan 2010 12:16:44 GMT
Transfer-Encoding: chunked
Location: /photos/1
Content-Type: text/html; charset=utf-8
X-Runtime: 0.083496
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

307
3 Estructuracin de los layouts

3 Estructuracin de los layouts


Cuando Rails renderiza una vista como una respuesta, lo hace combinando la vista con el
layout actual, utilizando las reglas para encontrar el layout actual que se trat anteriormente
en esta gua. Dentro de un layout, tiene acceso a tres herramientas para combinar
diferentes bits de salida para formar la respuesta global:

Asset tags helpers


yield y content_for

Partials

308
3.1 Asset Tag Helpers

3.1 Asset Tag Helpers


Los assets helper tags (etiquetas helper de assets) proporcionan mtodos para generar
HTML que vinculan vistas a feeds, JavaScript, hojas de estilo, imgenes, vdeos y audios.

Existen seis etiquetas helper de assets disponibles en Rails:

auto_discovery_link_tag

javascript_include_tag

stylesheet_link_tag

image_tag

video_tag

audio_tag

Puede utilizar estas etiquetas en los layouts o en otras vistas, aunque la etiqueta
auto_discovery_link_tag , javascript_include_tag y stylesheet_link_tag se utilizan con

mayor frecuencia en la seccin <head> de un layout.

Las etiquetas helper de assets no verifican la existencia de los assets en las


ubicaciones especificadas; Simplemente asumen que sabes lo que ests haciendo y
generan el enlace.

3.1.1 Vinculacin a Feeds con la etiqueta


auto_discovery_link_tag
El ayudante auto_discovery_link_tag genera HTML que la mayora de los navegadores y
lectores de feeds pueden utilizar para detectar la presencia de feeds RSS o Atom. Se
necesita el tipo de enlace ( :rss o :atom ), un hash de opciones que se pasan a url_for
y un hash de opciones para la etiqueta:

<%= auto_discovery_link_tag(:rss, {action: "feed"},


{title: "RSS Feed"}) %>

Hay tres opciones de etiqueta disponibles para la etiqueta auto_discovery_link_tag :

:rel especifica el valor rel en el enlace. El valor predeterminado es "alternate".

:type especifica un tipo MIME explcito. Rails generar automticamente un tipo

MIME apropiado.
:title especifica el ttulo del enlace. El valor predeterminado es el valor del type en

mayscula: por ejemplo, " ATOM " o " RSS ".

309
3.1 Asset Tag Helpers

3.1.2 Vinculacin a archivos JavaScript con


javascript_include_tag
El helper javascript_include_tag devuelve una etiqueta de script HTML para cada source
proporcionada.

Si est utilizando Rails con el assets pipeline activado, este helper generar un enlace a
/assets/javascripts/ en lugar de public/javascripts que se utiliz en versiones

anteriores de Rails. Este enlace es servido por el assets pipeline.

Un archivo JavaScript dentro de una aplicacin Rails o un motor de Rails se encuentra en


una de tres ubicaciones: app/assets , lib/assets o vendor/assets . Estas ubicaciones se
explican en detalle en la seccin de organizacin de assets en la Gua de Assets Pipeline.

Puede especificar una ruta de acceso completa y relativa a la raz del documento, o una
URL, si lo prefiere. Por ejemplo, para vincular a un archivo JavaScript que est dentro de un
directorio llamado javascripts dentro de uno de app/assets , lib/assets o
vendor/assets , hara esto:

<%= javascript_include_tag "main" %>

Rails emitir una etiqueta de script como la siguiente:

<script src='/assets/main.js'></script>

La peticin a este asset es servida por la gema de Sprockets .

Para incluir multiples archivos como por ejemplo app/assets/javascripts/main.js y


app/assets/javascripts/columns.js al mismo tiempo:

<%= javascript_include_tag "main", "columns" %>

Para incluir app/assets/javascripts/main.js y app/assets/javascripts/photos/columns.js :

<%= javascript_include_tag "main", "/photos/columns" %>

Para incluir http://example.com/main.js :

<%= javascript_include_tag "http://example.com/main.js" %>

310
3.1 Asset Tag Helpers

3.1.3 Vinculacin a archivos CSS con la etiqueta


stylesheet_link_tag
El ayudante stylesheet_link_tag devuelve una etiqueta HTML <link> para cada source
proporcionada.

Si est utilizando Rails con el "Asset Pipeline" habilitado, este helper generar un enlace a
/assets/stylesheets/ . Este enlace es procesado por la gema de Sprockets. Un archivo de

hoja de estilo se puede almacenar en una de tres ubicaciones: app/assets , lib/assets o


vendor/assets .

<%= stylesheet_link_tag "main" %>

Para incluir app/assets/stylesheets/main.css y app/assets/stylesheets/columns.css :

<%= stylesheet_link_tag "main", "columns" %>

Para incluir app/assets/stylesheets/main.css y app/assets/stylesheets/photos/columns.css :

<%= stylesheet_link_tag "main", "photos/columns" %>

Para incluir http://example.com/main.css :

<%= stylesheet_link_tag "http://example.com/main.css" %>

De forma predeterminada, stylesheet_link_tag crea vnculos con media="screen"


rel="stylesheet" . Puede anular cualquiera de estos valores predeterminados especificando

una opcin apropiada ( :media , :rel ):

<%= stylesheet_link_tag "main_print", media: "print" %>

3.1.4 Vinculacin a imgenes con image_tag


El ayudante image_tag crea una etiqueta HTML <img/> en el archivo especificado. De
forma predeterminada, los archivos se cargan desde public/images .

Tenga en cuenta que debe especificar la extensin de la imagen.

<%= image_tag "header.png" %>

311
3.1 Asset Tag Helpers

Puede proporcionar una ruta a la imagen si lo desea:

<%= image_tag "icons/delete.gif" %>

Puede proporcionar un hash de opciones HTML adicionales:

<%= image_tag "icons/delete.gif", {height: 45} %>

Puede suministrar texto alternativo para la imagen que se utilizar si el usuario tiene
imgenes desactivadas en su navegador. Si no especifica un texto alt explcitamente, el
valor predeterminado es el nombre del archivo, en maysculas y sin extensin. Por ejemplo,
estas dos etiquetas de imagen devolveran el mismo cdigo:

<%= image_tag "home.gif" %>


<%= image_tag "home.gif", alt: "Home" %>

Tambin puede especificar una etiqueta de tamao especial, en el formato


" {width}x{height} ":

<%= image_tag "home.gif", size: "50x20" %>

Adems de las etiquetas especiales anteriores, puede proporcionar un hash de las opciones
HTML estndar, como :class , :id o :name :

<%= image_tag "home.gif", alt: "Go Home",


id: "HomeImage",
class: "nav_bar" %>

3.1.5 Vinculacin a vdeos con la etiqueta video


El helper video_tag crea una etiqueta HTML 5 <video> en el archivo especificado. De
forma predeterminada, los archivos se cargan desde public/videos .

<%= video_tag "movie.ogg" %>

produce

<video src="/videos/movie.ogg" />

312
3.1 Asset Tag Helpers

Al igual que un image_tag , puede proporcionar una ruta, absoluta o relativa al directorio
public/videos . Adems, puede especificar el tamao: " #{width}x#{height} " como una

image_tag . Las etiquetas de vdeo tambin pueden tener cualquiera de las opciones HTML

especificadas al final ( id , class y alt ).

La etiqueta de vdeo tambin admite todas las opciones de <video> HTML a travs del
hash de opciones HTML, incluyendo:

poster : " image_name.png ", proporciona una imagen para poner en lugar del video

antes de que comience a reproducirse.


autoplay:true , comienza a reproducir el video en carga de pgina.

loop :true , hace un loop el video una vez que llega al final.

controls :true , proporciona controles suministrados por el navegador para que el

usuario interacte con el video.


autobuffer :true , el video cargar previamente el archivo para el usuario en carga de

pgina.

Tambin puede especificar varios videos para reproducir pasando un array de videos a la
video_tag :

<%= video_tag ["trailer.ogg", "movie.ogg"] %>

El cual produce

<video>
<source src="/videos/trailer.ogg">
<source src="/videos/movie.ogg">
</video>

3.1.6 Vinculacin a archivos de audio con audio_tag


El asistente audio_tag crea una etiqueta HTML 5 <audio> en el archivo especificado. De
forma predeterminada, los archivos se cargan desde public/audios .

<%= audio_tag "music.mp3" %>

Puede proporcionar una ruta de acceso al archivo de audio si lo desea:

<%= audio_tag "music/first_song.mp3" %>

Tambin puede proporcionar un hash de opciones adicionales, tales como :id , :class
etc.

313
3.1 Asset Tag Helpers

Al igual que el video_tag , el audio_tag tiene opciones especiales:

autoplay :true , comienza a reproducir el audio en la carga de la pgina

controls :true , proporciona controles suministrados por el navegador para que el

usuario interacte con el audio.


autobuffer :true , el audio cargar previamente el archivo para el usuario en carga de

pgina.

314
3.2 Entendiendo Yield

3.2 Entendiendo Yield


En el contexto de un layout, yield identifica una seccin donde se debe insertar el
contenido de la vista. La forma ms sencilla de utilizar esto es tener un solo yield , en el
que se inserta todo el contenido de la vista que se est procesando actualmente:

<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>

Tambin puede crear un layout con multiples regiones con yield :

<html>
<head>
<%= yield :head %>
</head>
<body>
<%= yield %>
</body>
</html>

El cuerpo principal de la vista siempre renderizar el yield sin nombre. Para convertir el
render en un yield con nombre, utilice el mtodo content_for .

315
3.3 Uso del mtodo content_for

3.3 Uso del mtodo content_for


El mtodo content_for le permite insertar contenido en un bloque yield nombrado en su
layout. Por ejemplo, esta vista funcionara con el layout que acabas de ver (en el anterior
segmento 3.2):

<% content_for :head do %>


<title>A simple page</title>
<% end %>

<p>Hello, Rails!</p>

El resultado de renderizar esta pgina en el diseo proporcionado sera este HTML :

<html>
<head>
<title>A simple page</title>
</head>
<body>
<p>Hello, Rails!</p>
</body>
</html>

El mtodo content_for es muy til cuando el diseo contiene regiones distintas, como
barras laterales y pies de pgina, que deben tener sus propios bloques de contenido
insertados. Tambin es til para insertar etiquetas que cargan archivos JavaScript o CSS
especficos de la pgina en el encabezado de un diseo que de otra forma es genrico.

316
3.4 Uso de Partials

3.4 Uso de Partials


Las plantillas parciales, usualmente llamadas "partials", son otro dispositivo para romper el
proceso de renderizado en bloques ms manejables. Con un partial, puede mover el cdigo
para representar una pieza particular de una respuesta a su propio archivo.

3.4.1 Nombrar parciales


Para renderizar un parcial como parte de una vista, utilice el mtodo render dentro de la
vista:

<%= render "menu" %>

Esto renderizar un archivo denominado _menu.html.erb en ese punto dentro de la vista


que se est procesando. Tenga en cuenta el carcter de subrayado principal: los partials se
nombran con un carcter de subrayado principal para distinguirlos de las vistas regulares,
aunque se consulten sin el subrayado. Esto es vlido incluso cuando est extrayendo una
parte de otra carpeta:

<%= render "shared/menu" %>

Ese cdigo extraer el partial de app/views/shared/_menu.html.erb .

3.4.2 Uso de partials para simplificar vistas


Una forma de utilizar partials es tratarlos como el equivalente de subrutinas: como una
forma de mover los detalles de una vista para que pueda captar lo que est pasando ms
fcilmente. Por ejemplo, podra tener una vista que se vea as:

<%= render "shared/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>


...

<%= render "shared/footer" %>

317
3.4 Uso de Partials

En este caso, los partials _ad_banner.html.erb y _footer.html.erb pueden tener contenido


compartido por muchas pginas de la aplicacin. No necesita ver los detalles de estas
secciones cuando se est concentrando en una pgina en particular.

Como se ve en las secciones anteriores de esta gua, yield es una herramienta muy
poderosa para limpiar sus layouts. Tenga en cuenta que es Ruby puro, por lo que puede
utilizarlo casi en todas partes. Por ejemplo, podemos usarlo para no repetir cdigo en
definiciones de diseos de formulario para varios recursos similares:

users/index.html.erb

<%= render "shared/search_filters", search: @q do |f| %>


<p>
Name contains: <%= f.text_field :name_contains %>
</p>
<% end %>

roles/index.html.erb

<%= render "shared/search_filters", search: @q do |f| %>


<p>
Title contains: <%= f.text_field :title_contains %>
</p>
<% end %>

shared/_search_filters.html.erb

<%= form_for(search) do |f| %>


<h1>Search form:</h1>
<fieldset>
<%= yield f %>
</fieldset>
<p>
<%= f.submit "Search" %>
</p>
<% end %>

Para el contenido que se comparte entre todas las pginas de la aplicacin, puede utilizar
parciales directamente desde los layouts.

3.4.3 Partial Layouts


Un parcial puede utilizar su propio archivo de layout, tal como una vista puede utilizar un
layout. Por ejemplo, puede llamar a un parcial como este:

318
3.4 Uso de Partials

<%= render partial: "link_area", layout: "graybar" %>

Esto buscara un parcial denominado _link_area.html.erb y lo renderizara usando el


layout _graybar.html.erb . Tenga en cuenta que los layouts para parciales siguen el mismo
nombre de subrayado principal que los parciales regulares y se colocan en la misma
carpeta con el parcial al que pertenecen (no en la carpeta master de los payouts).

Tambin tenga en cuenta que se debe especificar explcitamente :partial al pasar


opciones adicionales como :layout.

3.4.4 Pasando variables locales


Tambin puede pasar variables locales a parciales, hacindolas an ms potentes y
flexibles. Por ejemplo, puede utilizar esta tcnica para reducir la duplicacin entre pginas
new y edit , manteniendo al mismo tiempo un poco de contenido distinto:

new.html.erb

<h1>New zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>

edit.html.erb

<h1>Editing zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>

_form.html.erb

<%= form_for(zone) do |f| %>


<p>
<b>Zone name</b><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

Aunque el mismo parcial se renderizar en ambas vistas, el ayudante de presentacin de


Action View devolver "New zone" para la nueva accin y "Editing zone" para la accin de
edicin.

Para pasar una variable local a un parcial slo en casos especficos, utilice local_assigns .

319
3.4 Uso de Partials

index.html.erb

<%= render user.articles %>

show.html.erb

<%= render article, full: true %>

_article.html.erb

<h2><%= article.title %></h2>

<% if local_assigns[:full] %>


<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>

De esta manera es posible utilizar el parcial sin necesidad de declarar todas las variables
locales.

Cada parcial tambin tiene una variable local con el mismo nombre que el parcial (menos el
subrayado). Puede pasar un objeto a esta variable local mediante la opcin :object :

<%= render partial: "customer", object: @new_customer %>

Dentro del parcial del cliente , la variable cliente se referir a @new_customer desde la vista
principal.

Si tiene una instancia de un modelo para renderizar en un parcial, puede usar una sintaxis
abreviada:

<%= render @customer %>

Suponiendo que la variable de instancia @customer contiene una instancia del modelo
Customer , se utilizar _customer.html.erb para procesarla y pasar al cliente de variable

local en el parcial que se referir a la variable de instancia @customer en la vista principal.

3.4.5 Renderizado de colecciones


Los parciales son muy tiles para renderizar colecciones. Cuando pasa una coleccin a un
parcial a travs de la opcin: collection , el parcial se insertar una vez para cada
miembro en la coleccin:

320
3.4 Uso de Partials

index.html.erb

<h1>Products</h1>
<%= render partial: "product", collection: @products %>

_product.html.erb

<p>Product Name: <%= product.name %></p>

Cuando un parcial es llamado con una coleccin pluralizada, entonces las instancias
individuales del parcial tienen acceso al miembro de la coleccin que se est procesando a
travs de una variable llamada despus del parcial. En este caso, el parcial es _product , y
dentro del parcial _product , puede referirse al producto para obtener la instancia que se
est procesando.

Tambin hay una abreviatura para esto. Suponiendo que @products es una coleccin de
instancias de producto, simplemente puede escribir esto en index.html.erb para producir
el mismo resultado:

<h1>Products</h1>
<%= render @products %>

Rails determina el nombre del parcial que se va a usar al mirar el nombre del modelo en la
coleccin. De hecho, incluso puede crear una coleccin heterognea y convertirlo de esta
manera, y Rails elegir el parcial adecuado para cada miembro de la coleccin:

index.html.erb

<h1>Contacts</h1>
<%= render [customer1, employee1, customer2, employee2] %>

customers/_customer.html.erb

<p>Customer: <%= customer.name %></p>

employees/_employee.html.erb

<p>Employee: <%= employee.name %></p>

En este caso, Rails usar el parcial de cliente o empleado segn corresponda para cada
miembro de la coleccin.

321
3.4 Uso de Partials

En el caso de que la coleccin est vaca, render volver nil, por lo que debera ser
bastante sencillo proporcionar contenido alternativo.

<h1>Products</h1>
<%= render(@products) || "There are no products available." %>

3.4.6 Variables locales


Para utilizar un nombre de variable local personalizada dentro del parcial, especifique la
opcin :as en la llamada al parcial:

<%= render partial: "product", collection: @products, as: :item %>

Con este cambio, puede acceder a una instancia de la coleccin @products como la
variable local item dentro del parcial.

Tambin puede pasar variables arbitrarias locales a cualquier parcial que est procesando
con los locales :{} option :

<%= render partial: "product", collection: @products,


as: :item, locals: {title: "Products Page"} %>

En este caso, el parcial tendr acceso a un ttulo de variable local con el valor "Products
page".

Rails tambin hace que una variable de contador est disponible dentro de un parcial
llamado por la coleccin, nombrado despus del miembro de la coleccin seguido de
_counter . Por ejemplo, si est procesando @products , en el parcial puede referirse a

product_counter para indicarle cuntas veces se ha procesado el parcial. Esto no

funciona junto con la opcin as: :value .

Tambin puede especificar un segundo parcial que se renderizar entre instancias del
parcial principal mediante la opcin: spacer_template :

3.4.7 Spacer Templates

<%= render partial: @products, spacer_template: "product_ruler" %>

Rails har que _product_ruler sea parcial (sin datos pasados a ella) entre cada par de
_product partials.

322
3.4 Uso de Partials

3.4.8 Collection Partial Layouts


Al renderizar colecciones tambin es posible utilizar la opcin :layout :

<%= render partial: "product", collection: @products, layout: "special_layout" %>

El diseo se renderizar junto con el parcial para cada elemento de la coleccin. Las
variables actuales de objeto y objeto-contador estarn disponibles en el layout, as como en
el parcial.

323
3.5 Uso de layouts anidados

3.5 Uso de layouts anidados


Es posible que su aplicacin requiera un layout que difiera ligeramente de su layout de
aplicacin habitual para dar soporte a un controlador en particular. En lugar de repetir el
layout principal y editarlo, puede lograr esto utilizando layouts anidados (a veces llamados
sub-templates). He aqu un ejemplo:

Supongamos que tiene el siguiente layout de ApplicationController :


app/views/layouts/application.html.erb

<html>
<head>
<title><%= @page_title or "Page Title" %></title>
<%= stylesheet_link_tag "layout" %>
<style><%= yield :stylesheets %></style>
</head>
<body>
<div id="top_menu">Top menu items here</div>
<div id="menu">Menu items here</div>
<div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
</body>
</html>

En las pginas generadas por NewsController , desea ocultar el men superior y agregar un
men de la derecha:

app/views/layouts/news.html.erb

<% content_for :stylesheets do %>


#top_menu {display: none}
#right_menu {float: right; background-color: yellow; color: black}
<% end %>
<% content_for :content do %>
<div id="right_menu">Right menu items here</div>
<%= content_for?(:news_content) ? yield(:news_content) : yield %>
<% end %>
<%= render template: "layouts/application" %>

Eso es. La vista News utilizarn el nuevo layout, ocultando el men superior y aadiendo un
nuevo men derecho dentro del div " content ".

Hay varias maneras de obtener resultados similares con diferentes esquemas de sub-
plantillas usando esta tcnica. Tenga en cuenta que no hay lmite en los niveles de
anidamiento. Puede usar el mtodo ActionView::render a travs de la plantilla de render:

324
3.5 Uso de layouts anidados

' layouts/news ' para basar un nuevo layout en el layout de news. Si est seguro de que no
quiere "sub-templear" el layout de noticias, puede reemplazar el content_for?
(:news_content) ? yield(:news_content) : yield por un simple yield .

325
X- Helpers de Formulario de Action View

X- Helpers de Formulario de Action View


Los formularios en las aplicaciones web son una interfaz esencial para la entrada de datos
por parte de los usuarios. Sin embargo, las etiquetas de los formularios pueden convertirse
rpidamente en un trabajo tedioso de escribir y de mantener debido a la necesidad de
manejar diferentes nombres para el control del formulario y sus numerosos atributos. Rails
elimina esta complejidad proporcionando helpers de vista para generar las etiquetas de los
formularios. Sin embargo, dado que estos helpers tienen diferentes casos de uso, los
desarrolladores necesitan conocer las diferencias entre los mtodos de ayuda antes de
utilizarlos.

Despus de leer esta gua, sabr:

Cmo crear formularios de bsqueda y otros formularios genricos similares, que no


representan ningn modelo especfico en su aplicacin.
Cmo crear formularios orientados a los modelos para crear y editar registros de base
de datos especficos.
Cmo generar cuadros de seleccin (select box) de varios tipos de datos.
Qu helpers de fecha y hora ofrece Rails.
Como hacer un formulario que suba archivos.
Cmo publicar formularios en recursos externos y especificar la configuracin de un
authenticity_token .

Cmo construir formularios complejos.

Esta gua no pretende ser una documentacin completa de los helpers de formulario
disponibles y de sus argumentos. Visite la documentacin de la API de Rails para
obtener una referencia completa.

326
1- Tratamiento de formularios bsicos

1- Tratamiento de formularios bsicos


El helper ms basico de los formulario es form_tag .

<%= form_tag do %>


Form contents
<% end %>

Cuando se llama sin argumentos como ste, crea una etiqueta <form> que, cuando se
enve, se publicar en la pgina actual. Por ejemplo, asumiendo que la pgina actual es
/home/index , el HTML generado se ver as:

<form accept-charset="UTF-8" action="/" method="post">


<input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9Pgw
J108nDHX/8Cts=" />
Form contents
</form>

Observar que el HTML contiene un elemento de entrada con el tipo oculto. Esta entrada es
importante, porque el formulario no se puede enviar correctamente sin l. El elemento de
entrada oculto con el nombre utf8 hace que los navegadores respeten debidamente la
codificacin de caracteres de su formulario y se genera para todos los formularios, ya sea
que su accin sea " GET " o " POST ".

El segundo elemento de entrada con el nombre authenticity_token es una caracterstica


de seguridad de Rails llamada "cross-site request forgery protection" (proteccin de
falsificacin de solicitud en el sitio), y los helpers del formulario lo generan para cada
formulario que no sea GET (siempre que esta caracterstica de seguridad est habilitada).
Puede leer ms sobre esto en la Gua de seguridad.

1.1 Formulario genrico de bsqueda


Uno de los formularios ms bsicos que ve en la web es un formulario de bsqueda. Este
formulario contiene:

Un elemento de formulario con el mtodo " GET "


Una etiqueta input ,
Un elemento input text , y
Un elemento submit .

327
1- Tratamiento de formularios bsicos

Para crear este formulario usar form_tag , label_tag , text_field_tag y submit_tag ,


respectivamente. Algo como esto:

<%= form_tag("/search", method: "get") do %>


<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>

Esto generar el siguiente HTML :

<form accept-charset="UTF-8" action="/search" method="get">


<input name="utf8" type="hidden" value="&#x2713;" />
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>

Para cada input del formulario, se genera un atributo ID desde su name (" q " en el
ejemplo anterior). Estos identificadores pueden ser muy tiles para los estilos CSS o la
manipulacin de controles de formulario con JavaScript .

Adems de text_field_tag y submit_tag , hay un helper similar para cada control de


formulario en HTML.

Siempre use " GET " como el mtodo para los formularios de bsqueda. Esto permite a
los usuarios marcar una bsqueda especfica y volver a ella. Ms generalmente, Rails
le anima a usar el verbo HTTP correcto para una accin.

1.2 Mltiples hashes en las llamadas de helper del


formulario
El helper form_tag acepta 2 argumentos: la ruta para la accin y un hash de opciones.
Este hash especifica el mtodo de envio del formulario y las opciones de HTML , como el
class del elemento de formulario.

form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")


# => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form"
method="post">'

Aqu, el mtodo y la clase se aaden a la cadena de consulta de la URL generada, ya que


aunque usted quiere escribir dos hashes, realmente slo especific uno. As que usted
necesita decirle a Ruby lo que, al delimitar el primer hash (o ambos) con corchetes. Esto

328
1- Tratamiento de formularios bsicos

generar el HTML que espera:

form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")


# => '<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_f
orm">'

1.3 Helpers para generar elementos del formulario


Rails proporciona una serie de helpers para generar los elementos de un formulario como:
casillas de verificacin, campos de texto y botones de opcin. Estos helpers bsicos, con
nombres que terminan en _tag (como text_field_tag y check_box_tag ), generan slo un
elemento <input> . El primer parmetro para estos siempre es el nombre del input .
Cuando se enva el formulario, el nombre se pasar junto con los datos del formulario, y
har su camino a los parmetros en el controlador con el valor introducido por el usuario
para ese campo. Por ejemplo, si el formulario contiene <%= text_field_tag (:query)%> ,
entonces podras obtener el valor de este campo en el controlador con params[:query] .

Cuando nombra los inputs, Rails utiliza ciertas convenciones que hacen posible enviar
parmetros con valores no escalares tales como arrays o hashes, que tambin sern
accesibles en params . Puede leer ms sobre ellos en el captulo 7 de esta gua. Para
obtener detalles sobre el uso preciso de estos ayudantes, consulte la documentacin de la
API.

1.3.1 Casillas de verificacin


Las casillas de verificacin son controles de formulario que proporcionan al usuario un
conjunto de opciones que pueden activar o desactivar:

<%= check_box_tag(:pet_dog) %>


<%= label_tag(:pet_dog, "I own a dog") %>
<%= check_box_tag(:pet_cat) %>
<%= label_tag(:pet_cat, "I own a cat") %>

Esto genera lo siguiente:

<input id="pet_dog" name="pet_dog" type="checkbox" value="1" />


<label for="pet_dog">I own a dog</label>
<input id="pet_cat" name="pet_cat" type="checkbox" value="1" />
<label for="pet_cat">I own a cat</label>

329
1- Tratamiento de formularios bsicos

El primer parmetro para check_box_tag , por supuesto, es el nombre del input . El


segundo parmetro, naturalmente, es el valor del input . Este valor se incluir en los datos
del formulario (y estar presente en los parmetros) cuando se marque la casilla de
verificacin.

1.3.2 Radio buttons


Los radio buttons, aunque son similares a las casillas de verificacin, son controles que
especifican un conjunto de opciones en las que son mutuamente excluyentes (es decir, el
usuario slo puede elegir uno):

<%= radio_button_tag(:age, "child") %>


<%= label_tag(:age_child, "I am younger than 21") %>
<%= radio_button_tag(:age, "adult") %>
<%= label_tag(:age_adult, "I'm over 21") %>

Salida:

<input id="age_child" name="age" type="radio" value="child" />


<label for="age_child">I am younger than 21</label>
<input id="age_adult" name="age" type="radio" value="adult" />
<label for="age_adult">I'm over 21</label>

Al igual que con check_box_tag , el segundo parmetro de radio_button_tag es el valor del


input. Debido a que estos dos radio buttons comparten el mismo nombre ( age ), el usuario
slo podr seleccionar uno de ellos y params[:age] contendr "child" o "adult".

Utilice siempre las etiquetas de checkbox o radio buttons. Asocian el texto a una
opcin especfica y, al expandir la regin seleccionable, facilitan que los usuarios
hagan clic en las entradas.

1.4 Otros ayudantes de inters


Otros controles de formulario que vale la pena mencionar son campos de texto, campos de
contrasea, campos ocultos, campos de bsqueda, campos de telfono, campos de fecha,
campos de tiempo, campos de color, campos de fecha y hora, campos de mes, campos de
semana, campos de URL, campos de correo electrnico, campos de nmeros y campos de
rango:

330
1- Tratamiento de formularios bsicos

<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>


<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
<%= search_field(:user, :name) %>
<%= telephone_field(:user, :phone) %>
<%= date_field(:user, :born_on) %>
<%= datetime_local_field(:user, :graduation_day) %>
<%= month_field(:user, :birthday_month) %>
<%= week_field(:user, :birthday_week) %>
<%= url_field(:user, :homepage) %>
<%= email_field(:user, :address) %>
<%= color_field(:user, :favorite_color) %>
<%= time_field(:task, :started_at) %>
<%= number_field(:product, :price, in: 1.0..20.0, step: 0.5) %>
<%= range_field(:product, :discount, in: 1..100) %>

Salida

<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>


<input id="password" name="password" type="password" />
<input id="parent_id" name="parent_id" type="hidden" value="5" />
<input id="user_name" name="user[name]" type="search" />
<input id="user_phone" name="user[phone]" type="tel" />
<input id="user_born_on" name="user[born_on]" type="date" />
<input id="user_graduation_day" name="user[graduation_day]" type="datetime-local" />
<input id="user_birthday_month" name="user[birthday_month]" type="month" />
<input id="user_birthday_week" name="user[birthday_week]" type="week" />
<input id="user_homepage" name="user[homepage]" type="url" />
<input id="user_address" name="user[address]" type="email" />
<input id="user_favorite_color" name="user[favorite_color]" type="color" value="#00000
0" />
<input id="task_started_at" name="task[started_at]" type="time" />
<input id="product_price" max="20.0" min="1.0" name="product[price]" step="0.5" type="
number" />
<input id="product_discount" max="100" min="1" name="product[discount]" type="range" /
>

Las entradas ocultas no se muestran al usuario, sino que en su lugar contienen datos como
cualquier entrada textual. Los valores dentro de ellos se pueden cambiar con JavaScript.

331
1- Tratamiento de formularios bsicos

Las entradas de bsqueda, telfono, fecha, hora, color, fecha, fecha, hora, mes,
semana, URL, correo electrnico, nmero y rango son controles HTML5. Si requiere
que su aplicacin tenga una experiencia consistente en navegadores antiguos,
necesitar un polyfill HTML5 (proporcionado por CSS y / o JavaScript).
Definitivamente no hay escasez de soluciones para esto, aunque una herramienta
popular en este momento es Modernizr , que proporciona una forma sencilla de
agregar funcionalidad basada en la presencia de caractersticas HTML5 detectadas.

Si utiliza campos de entrada de contrasea (para cualquier propsito), puede


configurar su aplicacin para evitar que se registren esos parmetros. Puede obtener
ms informacin al respecto en la Gua de seguridad.

332
2- Tratamiento de objetos del modelo

2- Tratamiento de objetos del modelo


2.1 Helper de objetos del modelo
Una tarea particularmente comn para un formulario es editar o crear un objeto del modelo.
Mientras que los helper de la forma * _tag se pueden utilizar ciertamente para esta tarea
son algo verbosos como para que cada etiqueta que usted necesite, tenga que asegurarse
que el nombre correcto del parmetro se esta utilizando y fijar el valor por defecto del
input apropiadamente. Rails proporciona helpers adaptados para esta tarea. Estos

helpers carecen del sufijo _tag , por ejemplo text_field , text_area .

Para estos helpers el primer argumento es el nombre de una variable de instancia y el


segundo es el nombre de un mtodo (generalmente un atributo) para llamar a ese objeto.
Rails establecer el valor del control del input en el valor de retorno de ese mtodo para
el objeto y establecer un nombre de input adecuado. Si en su controlador ha definido
@person y el name de esa persona es Henry, entonces el formulario contiene:

<%= text_field(:person, :name) %>

Producir una salida similar a:

<input id="person_name" name="person[name]" type="text" value="Henry"/>

Tras el envo del formulario, el valor ingresado por el usuario se almacenar en


params[:person][:name] . El hash params[:person] es adecuado para pasar a Person.new

o, si @person es una instancia de Person , @person.update . Mientras que el nombre de un


atributo es el segundo parmetro ms comn, para estos helpers esto no es obligatorio. En
el ejemplo anterior, siempre que los objetos persona tengan un name y un name= a un
mtodo Rails estar feliz.

Debe pasar el nombre de una variable de instancia, es decir :person o " person ", no una
instancia real del objeto de modelo.

Rails proporciona ayuda para mostrar los errores de validacin asociados con un objeto del
modelo. Estas son cubiertas en detalle por la gua de validacin de Active Record.

2.2 Vinculacin de un formulario a un objeto

333
2- Tratamiento de objetos del modelo

Mientras que esto es un aumento en la comodidad, est lejos de ser perfecto. Si Person
tiene muchos atributos para editar entonces estaramos repitiendo el nombre del objeto
editado muchas veces. Lo que queremos hacer de alguna manera es vincular un formulario
a un objeto del modelo, que es exactamente lo que form_for hace.

Supongamos que tenemos un controlador para tratar artculos


app/controllers/articles_controller.rb :

def new
@article = Article.new
end

La vista correspondiente es app/views/articles/new.html.erb y que utiliza form_for se


parece a esto:

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body, size: "60x12" %>
<%= f.submit "Create" %>
<% end %>

@article es el objeto real que se est editando.

Hay un solo hash de opciones. Las opciones de enrutamiento se pasan en un hash de


:url , y las opciones HTML se pasan en el hash :html . Tambin puede proporcionar

una opcin de namespace para su formulario para garantizar la unicidad de los atributos
de id en los elementos del formulario. El atributo spacename ser prefijado con
subrayado en el id del HTML generado.
El mtodo form_for produce un objeto form builder (la variable f ).
Los mtodos para crear controles del formulario se llaman en el objeto de constructor
de formularios f .

El HTML resultante es:

<form accept-charset="UTF-8" action="/articles" method="post" class="nifty_form">


<input id="article_title" name="article[title]" type="text" />
<textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
<input name="commit" type="submit" value="Create" />
</form>

El nombre pasado a form_for controla la clave utilizada en params para acceder a los
valores del formulario. Aqu el nombre es article y por lo tanto todos los inputs tienen
nombres del formulario article[attribute_name] . En consecuencia, en la accin de

334
2- Tratamiento de objetos del modelo

creacin params[:article] ser un hash con keys :title y :body . Puede leer ms sobre
la importancia de los nombres de entrada en la seccin parameter_names .

Los mtodos auxiliares llamados al constructor (builder) de formularios son idnticos a los
helpers de objetos del modelo, excepto que no es necesario especificar qu objeto se est
editando ya que ste ya est administrado por el constructor de formularios.

Puede crear un enlace similar sin crear realmente etiquetas <form> con el campo
fields_for helper. Esto es til para editar objetos de modelo adicionales con el mismo

formulario. Por ejemplo, si tuviera un modelo Person con un modelo ContactDetail


asociado, podra crear un formulario para crear ambos de la siguiente manera:

<%= form_for @person, url: {action: "create"} do |person_form| %>


<%= person_form.text_field :name %>
<%= fields_for @person.contact_detail do |contact_detail_form| %>
<%= contact_detail_form.text_field :phone_number %>
<% end %>
<% end %>

Que produce la siguiente salida:

<form accept-charset="UTF-8" action="/people" class="new_person" id="new_person" metho


d="post">
<input id="person_name" name="person[name]" type="text" />
<input id="contact_detail_phone_number" name="contact_detail[phone_number]" type="te
xt" />
</form>

El objeto dado por fields_for es un constructor de formularios como el que se obtiene por
form_for (de hecho form_for llama fields_for internamente).

2.3 Basndose en la identificacin de registros


El modelo Article est directamente disponible para los usuarios de la aplicacin, por lo
que - siguiendo las mejores prcticas para desarrollar con Rails - debe declararle un
resource :

resources :articles

Declarar un recurso tiene varios efectos secundarios. Vea "Rails Routing From Outside
In" para obtener ms informacin sobre cmo configurar y usar recursos.

335
2- Tratamiento de objetos del modelo

Cuando se trata de recursos RESTful , las llamadas a form_for pueden ser


significativamente ms fciles si confas en la identificacin de registros. En resumen,
puede pasar la instancia del modelo y hacer que Rails averige el nombre del modelo y el
resto:

## Creating a new article


# long-style:
form_for(@article, url: articles_path)
# same thing, short-style (record identification gets used):
form_for(@article)

## Editing an existing article


# long-style:
form_for(@article, url: article_path(@article), html: {method: "patch"})
# short-style:
form_for(@article)

Observe cmo la invocacin de form_for de estilo corto es convenientemente la misma,


independientemente de que el registro sea nuevo o existente. La identificacin de registros
es lo suficientemente inteligente como para averiguar si el registro es nuevo, solicitando
record.new_record? . Tambin selecciona la ruta correcta para enviar y el nombre basado

en la clase del objeto.

Rails tambin establecer automticamente la clase y el id de forma apropiada: un


formulario que crea un artculo tendra id y class new_article . Si estaba editando el
artculo con id 23, la clase se establecera en edit_article y en el id para
edit_article_23 . Estos atributos se omitirn por brevedad en el resto de esta gua.

Cuando est utilizando STI (herencia de tabla nica) con sus modelos, no puede
confiar en la identificacin de registros en una subclase, si slo su clase principal se
declara a un recurso. Deber especificar el nombre del modelo, :url , y :method
explicitamente.

2.3.1 Tratamiento de los namespaces


Si ha creado rutas con espacios de nombres, form_for tiene una taquigrafa til para eso
tambin. Si su aplicacin tiene un namespace admin entonces

form_for [:admin, @article]

Crear un formulario que se enva al ArticlesController dentro del espacio de nombres


admin (enviando a admin_article_path(@article) en el caso de un update ). Si tiene

varios niveles de namespacing entonces la sintaxis es similar:

336
2- Tratamiento de objetos del modelo

form_for [:admin, :management, @article]

Para obtener ms informacin sobre el sistema de enrutamiento de Rails y las


convenciones asociadas, consulte la gua de enrutamiento.

2.4 Cmo funcionan los formularios con mtodos PATCH,


PUT o DELETE?
El framework de Rails alienta el diseo RESTful de sus aplicaciones, lo que significa que
va a hacer un montn de solicitudes " PATCH " y " DELETE " (adems de " GET " y " POST ").
Sin embargo, la mayora de los navegadores no admiten mtodos distintos de " GET " y
" POST " cuando se trata de enviar formularios.

Rails trabaja alrededor de este problema emulando otros mtodos en POST con una
entrada oculta denominada " _method ", que se establece para reflejar el mtodo deseado:

form_tag(search_path, method: "patch")

salida:

<form accept-charset="UTF-8" action="/search" method="post">


<input name="_method" type="hidden" value="patch" />
<input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b
7a7ddf2b71" />
...
</form>

Al analizar datos POSTed, Rails tendr en cuenta el parmetro especial _method y actuar
como si el mtodo HTTP fuera el especificado dentro de l (" PATCH " en este ejemplo).

337
3- Hacer Select Boxes con facilidad

3- Hacer Select Boxes con facilidad


Las casillas de seleccin en HTML requieren una cantidad significativa de etiquetas (un
elemento OPTION para cada opcin a elegir), por lo tanto, tiene ms sentido que se
generen dinmicamente.

As es como podra lucir el etiquetado:

<select name="city_id" id="city_id">


<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...
<option value="12">Berlin</option>
</select>

Aqu tiene una lista de ciudades cuyos nombres se le presentan al usuario. Internamente, la
aplicacin slo quiere manejar sus IDs para que se usen como el atributo value de las
opciones. Veamos cmo Rails nos puede ayudar aqu.

3.1 Las etiquetas de select y option


El ayudante ms genrico es select_tag , que -como su nombre indica- simplemente
genera la etiqueta SELECT que encapsula una cadena de opciones:

<%= select_tag(:city_id, '<option value="1">Lisbon</option>...') %>

Esto es un comienzo, pero no crea dinmicamente las etiquetas option . Puede generar
etiquetas option con el helper options_for_select :

<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>

output:

<option value="1">Lisbon</option>
<option value="2">Madrid</option>
...

El primer argumento a options_for_select es un array anidado donde cada elemento tiene


dos elementos :text de la opcin (nombre de la ciudad) y valor de la opcin ( id de la
ciudad). El valor de la opcin es lo que se enviar a su controlador. A menudo, este ser el

338
3- Hacer Select Boxes con facilidad

id de un objeto de la base de datos correspondiente, pero este no tiene que ser siempre

el caso.

Sabiendo esto, puede combinar select_tag y options_for_select para lograr el etiquetado


completo deseado:

<%= select_tag(:city_id, options_for_select(...)) %>

options_for_select le permite pre-seleccionar una opcin pasando su valor.

<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %>

output:

<option value="1">Lisbon</option>
<option value="2" selected="selected">Madrid</option>
...

Siempre que Rails vea que el valor interno de una opcin generada coincide con este valor,
agregar el atributo selected a esa opcin.

Cuando :include_blank o :prompt no estn presentes, include_blank es forzado a true


si el atributo de select required es true , el size de la pantalla es uno y multiple no es
true .

Puede agregar atributos arbitrarios a las opciones utilizando hashes:

<%= options_for_select(
[
['Lisbon', 1, { 'data-size' => '2.8 million' }],
['Madrid', 2, { 'data-size' => '3.2 million' }]
], 2
) %>

output:

<option value="1" data-size="2.8 million">Lisbon</option>


<option value="2" selected="selected" data-size="3.2 million">Madrid</option>
...

3.2 Select box para tratar con modelos


En la mayora de los casos, los controles de formulario estarn vinculados a un modelo de
base de datos especfico y, como cabra esperar, Rails proporciona ayuda adaptada para
ese propsito. De acuerdo con otros helpers de formulario, cuando se trata de modelos se

339
3- Hacer Select Boxes con facilidad

dejara el sufijo _tag de select_tag :

# controller:
@person = Person.new(city_id: 2)

# view:
<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>

Observe que en el tercer parmetro, el array de opciones, es el mismo tipo de argumento


que pasa a options_for_select . Una ventaja aqu es que no tiene que preocuparse por la
preseleccin de la ciudad correcta si el usuario ya tiene una - Rails lo har por usted
leyendo desde el atributo @person.city_id .

Al igual que con otros helpers, si utilizas el helper select en un constructor de formularios
con un alcance al objeto @person , la sintaxis sera:

# select on a form builder


<%= f.select(:city_id, ...) %>

Tambin puede pasar un bloque para seleccionar el helper:

<%= f.select(:city_id) do %>


<% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>
<%= content_tag(:option, c.first, value: c.last) %>
<% end %>
<% end %>

Si est usando select (o helpers similares como collection_select , select_tag ) para


establecer una asociacin belongs_to debe pasar el nombre de la clave fornaea (en el
ejemplo anterior city_id ), no el nombre de la asociacin en s. Si especifica city en
lugar de city_id Active Record generar un error a lo largo de las lneas de
ActiveRecord :: ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got
String(#1138750) al pasar el params hash a Person.new o update . Otra forma de ver

esto es que los helpers de formulario slo editan los atributos. Tambin debe tener en
cuenta las potenciales ramificaciones de seguridad de permitir a los usuarios editar
claves forneas directamente.

3.3 Option Tags de una coleccin de objetos arbitrarios

340
3- Hacer Select Boxes con facilidad

La generacin de etiquetas de opciones con options_for_select requiere que se cree un


array que contenga el texto y el valor de cada opcin. Pero qu pasara si tuvieras un
modelo de City (quizs uno de Active Record) y quisieras generar etiquetas de opciones
de una coleccin de esos objetos? Una solucin sera hacer una matriz anidada iterando
sobre ellos:

<% cities_array = City.all.map { |city| [city.name, city.id] } %>


<%= options_for_select(cities_array) %>

Esta es una solucin perfectamente vlida, pero Rails proporciona una alternativa menos
verbosa: options_from_collection_for_select . Este ayudante espera una coleccin de
objetos arbitrarios y dos argumentos adicionales: los nombres de los mtodos para leer el
valor y texto de la opcin, respectivamente:

<%= options_from_collection_for_select(City.all, :id, :name) %>

Como su nombre lo indica, esto slo genera etiquetas de opcin. Para generar un cuadro
de seleccin que funcione, necesitar utilizarlo junto con select_tag , tal como lo hara con
options_for_select . Cuando se trabaja con objetos de un modelo, de la misma manera

que select combina select_tag y options_for_select , collection_select combina


select_tag con options_from_collection_for_select .

<%= collection_select(:person, :city_id, City.all, :id, :name) %>

Al igual que con otros helpers, si utilizas el helper collection_select en un constructor de


formularios con un scope en el objeto @person , la sintaxis sera:

<%= f.collection_select(:city_id, City.all, :id, :name) %>

Para recapitular, options_from_collection_for_select es un collection_select que


options_for_select debe seleccionar.

Los pares pasados a options_for_select deben tener el name primero y el id


segundo, sin embargo, con options_from_collection_for_select el primer argumento
es el mtodo value y el segundo el mtodo text .

3.4 Zona Horaria y Seleccin de Pas

341
3- Hacer Select Boxes con facilidad

Para aprovechar el soporte de zona horaria en Rails, debe preguntar a los usuarios en qu
zona horaria se encuentran. Debera generar opciones de seleccin de una lista de objetos
TimeZone predefinidos mediante collection_select , pero puede usar simplemente el

ayudante time_zone_select que ya envuelve esto:

<%= time_zone_select(:person, :time_zone) %>

Tambin hay un helper time_zone_options_for_select para una forma ms manual (por lo


tanto ms personalizable) de hacer esto. Lea la documentacin de la API para conocer los
posibles argumentos para estos dos mtodos.

Rails sola tener un ayudante country_select para elegir pases, pero esto se ha extrado
en el plugin country_select . Al usar esto, tenga en cuenta que la exclusin o inclusin de
ciertos nombres de la lista puede ser algo controversial (y fue la razn de que esta
funcionalidad se extrajo de Rails).

342
4- Uso de los helpers de formulario Fecha y Hora

4- Uso de los helpers de formulario Fecha


y Hora
Puede optar por no utilizar los helpers de formulario que generan campos de entrada de
fecha y hora en HTML5 y utilizar los helpers alternativos de fecha y hora. Estos helpers de
fecha y hora difieren de todos los dems helpers de formulario en dos aspectos
importantes:

Las fechas y los tiempos no son representables por un nico elemento input. En su
lugar, tiene varios, uno para cada componente (year, month, day etc.) y, por lo tanto, no
hay un valor nico en su params hash con su fecha o hora.
Otros helpers usan el sufijo _tag para indicar si un helper es un helper de barebones
o uno que opera con objetos de un modelo. En fechas y horas, select_date ,
select_time y select_datetime son los helpers de barebones, y date_select ,

time_select y datetime_select son los helpers de objetos de un modelo equivalente.

Ambas familias de helpers crearn una serie de cajas de seleccion para los diferentes
componentes (ao, mes, da, etc.).

4.1 Helpers Barebones


La familia select_ * de helpers toma como su primer argumento una instancia de Date ,
Time y DateTime que se utiliza como el valor seleccionado actualmente. Puede omitir este

parmetro, en cuyo caso se utilizar la fecha actual. Por ejemplo:

<%= select_date Date.today, prefix: :start_date %>

Salidas (con valores de opcin reales omitidos por brevedad)

<select id="start_date_year" name="start_date[year]"> ... </select>


<select id="start_date_month" name="start_date[month]"> ... </select>
<select id="start_date_day" name="start_date[day]"> ... </select>

Las entradas anteriores daran lugar a params[:start_date] siendo un hash con claves
:year , :month , :day . Para obtener un objeto Date , Time o DateTime real, tendra que

extraer estos valores y pasarlos al constructor apropiado, por ejemplo:

Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:


start_date][:day].to_i)

343
4- Uso de los helpers de formulario Fecha y Hora

La opcin :prefix es la clave utilizada para recuperar el hash de los componentes de


fecha del hash de params . Aqu se estableci en start_date , si se omite se
predeterminar hasta la fecha.

4.2 Helpers de objetos de un modelo


select_date no funciona bien con los formularios que se actualizan o crean objetos Active

Record ya que Active Record espera que cada elemento del hash params corresponda a
un atributo. Los helpers de objetos de un modelo para fechas y horas envan parmetros
con nombres especiales; Cuando Active Record ve parmetros con tales nombres sabe que
deben ser combinados con los otros parmetros y dados a un constructor apropiado al tipo
de columna. Por ejemplo:

<%= date_select :person, :birth_date %>

Salidas (con valores de opcin reales omitidos por brevedad)

<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>


<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>

Que da como resultado un hash params como:

{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' =


> '22'}}

Cuando se pasa a Person.new (o update ), Active Record indica que todos estos
parmetros deben usarse para construir el atributo birth_date y utiliza la informacin del
sufijo para determinar en qu orden debe pasar estos parmetros a funciones como
Date.civil .

4.3 Opciones comunes


Ambas familias de helpers utilizan el mismo conjunto bsico de funciones para generar las
etiquetas individuales de seleccin y por lo tanto aceptan en gran medida las mismas
opciones. En particular, por defecto Rails generar opciones de ao, 5 aos a cada lado del
ao en curso. Si no es un rango apropiado, las opciones :start_year y :end_year
reemplazan esto. Para obtener una lista exhaustiva de las opciones disponibles, consulte la
documentacin de la API.

344
4- Uso de los helpers de formulario Fecha y Hora

Como regla general, debe utilizar date_select cuando trabaje con objetos de un modelo y
select_date en otros casos, como un formulario de bsqueda que filtre los resultados por

fecha.

En muchos casos los date pickers incorporados son torpes ya que no ayudan al
usuario en la elaboracin de la relacin entre la fecha y el da de la semana.

4.4 Componentes individuales


De vez en cuando usted necesita exhibir apenas un solo componente de la fecha tal como
un ao o un mes. Rails proporciona una serie de ayudantes para esto, uno para cada
componente select_year , select_month , select_day , select_hour , select_minute ,
select_second . Estos helpers son bastante sencillos. De forma predeterminada, generarn

un input con el nombre del componente tiempo (por ejemplo, "year" para select_year ,
"month" para select_month , etc.), aunque esto puede anularse con la opcin :field_name .
La opcin :prefix funciona de la misma manera que hace para select_date y
select_time y tiene el mismo valor predeterminado.

El primer parmetro especifica qu valor debe seleccionarse y puede ser una instancia de
Date , Time o DateTime , en cuyo caso se extraer el componente relevante o un valor

numrico. Por ejemplo:

<%= select_year(2009) %>


<%= select_year(Time.now) %>

Producir la misma salida si el ao actual es 2009 y el valor elegido por el usuario puede
ser recuperado por params[:date] [:year] .

345
5- Carga de archivos

5- Carga de archivos
Una tarea comn es cargar algn tipo de archivo, ya sea una imagen de una persona o un
archivo CSV que contenga datos para procesar. Lo ms importante a recordar con las
subidas de archivos es que la codificacin de la forma renderizada DEBE establecerse en
" multipart/form-data ". Si utiliza form_for , esto se hace automticamente. Si utiliza
form_tag , debe configurarlo usted mismo, como en el ejemplo siguiente.

<%= form_tag({action: :upload}, multipart: true) do %>


<%= file_field_tag 'picture' %>
<% end %>

<%= form_for @person do |f| %>


<%= f.file_field :picture %>
<% end %>

Rails proporciona el par habitual de helpers: el barebones file_field_tag y el orientado a


modelos file_field . La nica diferencia con otros helpers es que no se puede establecer
un valor predeterminado para los inputs de archivos ya que esto no tendra ningn
significado. Como es de esperar en el primer caso el archivo subido est en
params[:picture] y en el segundo caso en params[:person][:picture] .

5.1 Lo que se sube


El objeto en el hash params es una instancia de una subclase de IO . Dependiendo del
tamao del archivo subido puede ser de hecho un StringIO o una instancia de Archivo
respaldado por un archivo temporal. En ambos casos, el objeto tendr un atributo
original_filename que contiene el nombre del archivo en el equipo del usuario y un

atributo content_type que contiene el tipo MIME del archivo subido. El fragmento siguiente
guarda el contenido cargado en #{Rails.root}/public/uploads con el mismo nombre que el
archivo original (suponiendo que el formulario era el del ejemplo anterior).

def upload
uploaded_io = params[:person][:picture]
File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb')
do |file|
file.write(uploaded_io.read)
end
end

346
5- Carga de archivos

Una vez que se ha cargado un archivo, hay una multitud de tareas potenciales, desde:
dnde almacenar los archivos (en disco, Amazon S3, etc.) y asociarlos con modelos para
cambiar el tamao de los archivos de imagen y generar miniaturas. Las complejidades de
esto estn ms all del alcance de esta gua, pero hay varias bibliotecas diseadas para
ayudar con ellas. Dos de los ms conocidos son CarrierWave y Paperclip.

Si el usuario no ha seleccionado un archivo, el parmetro correspondiente ser una


cadena vaca.

5.2 Tratamiento con Ajax


A diferencia de otros formularios, crear un archivo de carga asncrona no es tan sencillo
como proporcionar un form_for con remote :true . Con un formulario de Ajax la
serializacin se hace por JavaScript que se ejecuta dentro del navegador y ya que
JavaScript no puede leer archivos de su disco duro el archivo no se puede cargar. La
solucin ms comn es utilizar un iframe invisible que sirva como objetivo para la
presentacin del formulario.

347
6- Personalizacin de los constructores de formularios

6- Personalizacin de los constructores de


formularios
Como se mencion anteriormente, el objeto generado por form_for y fields_for es una
instancia de FormBuilder (o una subclase del mismo). Los constructores de formularios
encapsulan la nocin de mostrar elementos de formulario para un solo objeto. Mientras que
usted puede, por supuesto, escribir ayudantes para sus formularios de la manera habitual,
tambin puede usar la subclase FormBuilder y agregar los ayudantes all. Por ejemplo:

<%= form_for @person do |f| %>


<%= text_field_with_label f, :first_name %>
<% end %>

Puede ser reemplazado por

<%= form_for @person, builder: LabellingFormBuilder do |f| %>


<%= f.text_field :first_name %>
<% end %>

Definiendo una clase LabellingFormBuilder similar a la siguiente:

class LabellingFormBuilder < ActionView::Helpers::FormBuilder


def text_field(attribute, options={})
label(attribute) + super
end
end

Si reutiliza esto con frecuencia, puede definir un ayudante label_form_for que aplique
automticamente al constructor: por ejemplo la Opcin LabellingFormBuilder :

def labeled_form_for(record, options = {}, &block)


options.merge! builder: LabellingFormBuilder
form_for record, options, &block
end

El constructor de formularios utilizado tambin determina qu sucede cuando lo hace:

<%= render partial: f %>

348
6- Personalizacin de los constructores de formularios

Si f es una instancia de FormBuilder , esto har que el formulario sea parcial,


estableciendo el objeto parcial en el constructor de formularios. Si el constructor de
formularios es de la clase LabellingFormBuilder , entonces se renderizar la parte de
labelling_form en su lugar.

349
7- Entendiendo las convenciones de nombramiento de parmetros

7- Entendiendo las convenciones de


nombramiento de parmetros
Como se ha visto en las secciones anteriores, los valores de los formularios pueden estar
en el nivel superior del hash params o anidados en otro hash. Por ejemplo, en una accin
create estndar para un modelo Person , params[:person] normalmente sera un hash de

todos los atributos para la persona a crear. El hash de parmetros tambin puede contener
arrays, arrays de hashes y as sucesivamente.

Fundamentalmente los formularios HTML no conocen ningn tipo de datos estructurados,


todo lo que generan son pares nombre-valor, donde los pares son simplemente cadenas
simples. Los arrays y hashes que ve en su aplicacin son el resultado de algunas
convenciones de nombre de parmetros que Rails utiliza.

7.1 Estructuras Bsicas


Las dos estructuras bsicas son arrays y hashes. Los hash reflejan la sintaxis utilizada para
acceder al valor en los parmetros. Por ejemplo, si un formulario contiene:

<input id="person_name" name="person[name]" type="text" value="Henry"/>

El hash params contendr:

{'person' => {'name' => 'Henry'}}

Y params[:person][:name] recuperar el valor enviado en el controlador.

Las hashes pueden anidarse en tantos niveles como se requiera, por ejemplo:

<input id="person_address_city" name="person[address][city]" type="text" value="New Yo


rk"/>

Resultar en el hash params:

{'person' => {'address' => {'city' => 'New York'}}}

350
7- Entendiendo las convenciones de nombramiento de parmetros

Normalmente, Rails ignora los nombres de parmetros duplicados. Si el nombre del


parmetro contiene un conjunto vaco de corchetes [] , entonces se acumulan en un array.
Si desea que los usuarios puedan ingresar varios nmeros de telfono, puede colocarlo en
la forma siguiente:

<input name="person[phone_number][]" type="text"/>


<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>

Esto resultara en params[:person][:phone_number] siendo un array que contiene los


nmeros de telfono introducidos.

7.2 Combinndolos
Podemos mezclar y combinar estos dos conceptos. Un elemento de un hash podra ser un
array como en el ejemplo anterior, o puede tener un array de hashes. Por ejemplo, un
formulario puede permitirle crear cualquier nmero de direcciones repitiendo el siguiente
fragmento de formulario

<input name="addresses[][line1]" type="text"/>


<input name="addresses[][line2]" type="text"/>
<input name="addresses[][city]" type="text"/>

Esto resultara en params[:addresses] siendo un array de hashes con las claves line1 ,
line2 y city . Rails decide comenzar a acumular valores en un nuevo hash cada vez que

encuentre un input name que ya existe en el hash actual.

Hay una restriccin, sin embargo, mientras que los hashes pueden anidarse
arbitrariamente, solo se permite un nivel de "arrayness". Los arrays pueden usualmente ser
reemplazadas por hashes; Por ejemplo, en lugar de tener un array de objetos de un modelo,
se puede tener un hash de objetos de un modelo indexados por su id , un array de indices
o algn otro parmetro.

351
7- Entendiendo las convenciones de nombramiento de parmetros

Los parmetros de un array no funcionan bien con el helper check_box . De acuerdo


con las casillas de verificacin desmarcadas de la especificacin HTML no se enva
ningn valor. Sin embargo, a menudo es conveniente que una casilla de verificacin
siempre enve un valor. El helper check_box simula esto creando un input auxiliar
oculto con el mismo name. Si la casilla de verificacin est desmarcada, slo se
enviar el input oculto y si se marca, ambos se enviarn, pero el valor enviado por la
casilla de verificacin tendr prioridad. Al trabajar con parmetros de un array, este
envo duplicado confundir a Rails ya que los input names duplicados son la forma en
que decide cundo se debe iniciar un nuevo elemento array. Es preferible usar
check_box_tag o usar hashes en lugar de arrays.

7.3 Uso de los helpers de formularios


Las secciones anteriores no utilizaron los helpers de los formularios de Rails en absoluto.
Mientras que usted puede elaborar los input names usted mismo y pasarlos directamente a
los helpers como text_field_tag Rails tambin proporciona un soporte de nivel superior.
Las dos herramientas a su disposicin aqu son el parmetro name para form_for y
fields_for y la opcin :index que los helpers toman.

<%= form_for @person do |person_form| %>


<%= person_form.text_field :name %>
<% @person.addresses.each do |address| %>
<%= person_form.fields_for address, index: address.id do |address_form|%>
<%= address_form.text_field :city %>
<% end %>
<% end %>
<% end %>

Suponiendo que la persona tena dos direcciones, con ids 23 y 45 esto creara una salida
similar a esto:

<form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1"


method="post">
<input id="person_name" name="person[name]" type="text" />
<input id="person_address_23_city" name="person[address][23][city]" type="text" />
<input id="person_address_45_city" name="person[address][45][city]" type="text" />
</form>

Esto resultar en un hash params que se parece a:

{'person' => {'name' => 'Bob', 'address' => {'23' => {'city' => 'Paris'}, '45' => {'ci
ty' => 'London'}}}}

352
7- Entendiendo las convenciones de nombramiento de parmetros

Rails sabe que todas estas entradas deben ser parte del hash de Person porque has
llamado fields_for en el primer constructor de formularios. Al especificar una opcin
:index , le est diciendo a Rails que en lugar de nombrar los inputs person[address]

[city] , debe insertar ese index rodeado de [] entre la direccin y la ciudad. Esto es a

menudo til ya que es fcil localizar qu registro de direccin debe ser modificado. Puede
pasar nmeros con alguna otra significacin, cadenas o incluso nil (lo que resultar en un
parmetro array que se est creando).

<%= fields_for 'person[address][primary]', address, index: address do |address_form| %


>
<%= address_form.text_field :city %>
<% end %>

Crear inputs como:

<input id="person_address_primary_1_city" name="person[address][primary][1][city]" typ


e="text" value="bologna" />

Como regla general, el nombre del input final es la concatenacin del name dado a
fields_for / form_for , el valor del index y el nombre del atributo. Tambin puede pasar una
opcin index directamente a helpers como text_field , pero suele ser menos repetitivo
especificar esto a nivel del constructor de formularios en lugar de en controles de input
individuales.

<%= fields_for 'person[address][primary][]', address do |address_form| %>


<%= address_form.text_field :city %>
<% end %>

Produce exactamente la misma salida que el ejemplo anterior.

353
8- Formularios a Recursos Externos

8- Formularios a Recursos Externos


Los helpers de formulario de Rails tambin se pueden usar para crear un formulario y
publicar datos en un recurso externo. Sin embargo, a veces puede ser necesario establecer
un authenticity_token para el recurso; Esto puede hacerse pasando un parmetro
authenticity_token: 'your_external_token' a las opciones form_tag :

<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %>


Form contents
<% end %>

A veces, cuando se envan datos a un recurso externo, como una pasarela de pago, los
campos que se pueden utilizar en el formulario estn limitados por una API externa y puede
ser indeseable generar un authenticity_token . Para no enviar un token , simplemente
pase false a la opcin :authenticity_token :

<%= form_tag 'http://farfar.away/form', authenticity_token: false do %>


Form contents
<% end %>

La misma tcnica tambin est disponible para form_for :

<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|


%>
Form contents
<% end %>

O si no desea procesar un campo authenticity_token :

<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>


Form contents
<% end %>

354
9- Construccin de formularios complejos

9- Construccin de formularios complejos


Muchas aplicaciones crecen ms all de formularios simples para editar un solo objeto. Por
ejemplo, al crear el objeto Person , puede permitir que el usuario (en el mismo formulario)
cree varios registros de direcciones (casa, trabajo, etc.). Cuando ms tarde la edicin de
esa persona el usuario debe ser capaz de agregar, quitar o modificar las direcciones segn
sea necesario.

9.1 Configuracin del modelo


Active Record proporciona soporte a nivel de modelo a travs del mtodo
accepted_nested_attributes_for :

class Person < ApplicationRecord


has_many :addresses
accepts_nested_attributes_for :addresses
end

class Address < ApplicationRecord


belongs_to :person
end

Esto crea un mtodo addresses_attributes= en Person que le permite crear, actualizar y


(opcionalmente) destruir direcciones.

9.2 Formularios anidados


El siguiente formulario le permite al usuario crear un objeto Person y sus direcciones
asociadas.

355
9- Construccin de formularios complejos

<%= form_for @person do |f| %>


Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>

<%= addresses_form.label :street %>


<%= addresses_form.text_field :street %>
...
</li>
<% end %>
</ul>
<% end %>

Cuando una asociacin acepta atributos anidados fields_for devuelve su bloque una vez
para cada elemento de la asociacin. En particular, si una persona no tiene direcciones, no
renderiza nada. Un patrn comn es que el controlador construya uno o ms childrens
vacos de manera que al menos un conjunto de campos se muestre al usuario. El ejemplo
siguiente resultara en 2 conjuntos de campos de direccin que se representan en el
formulario de la nueva persona.

def new
@person = Person.new
2.times { @person.addresses.build}
end

El fields_for produce un constructor de formularios. El nombre de los parmetros ser lo


que accept_nested_attributes_for espera. Por ejemplo, al crear un usuario con 2
direcciones, los parmetros enviados se veran as:

356
9- Construccin de formularios complejos

{
'person' => {
'name' => 'John Doe',
'addresses_attributes' => {
'0' => {
'kind' => 'Home',
'street' => '221b Baker Street'
},
'1' => {
'kind' => 'Office',
'street' => '31 Spooner Street'
}
}
}
}

Las claves del hash :addresses_attributes no son importantes, solo necesitan ser
diferentes para cada direccin.

Si el objeto asociado ya est guardado, fields_for genera automticamente una entrada


oculta con el id del registro guardado. Puede inhabilitarlo pasando include_id :false a
fields_for . Es posible que desee hacer esto si la entrada generada automticamente se

coloca en una ubicacin donde una etiqueta de entrada no es HTML vlido o cuando se
utiliza un ORM donde los children no tienen un ID .

9.3 El controlador
Como de costumbre, necesitas incluir en la lista blanca los parmetros en el controlador
antes de pasarlos al modelo:

def create
@person = Person.new(person_params)
# ...
end

private
def person_params
params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
end

9.4 Eliminacin de objetos


Puede permitir que los usuarios eliminen objetos asociados pasando allow_destroy :true
a accept_nested_attributes_for

357
9- Construccin de formularios complejos

class Person < ApplicationRecord


has_many :addresses
accepts_nested_attributes_for :addresses, allow_destroy: true
end

Si el hash de los atributos de un objeto contiene la clave _destroy con un valor de 1 o


true entonces el objeto ser destruido. Este formulario permite a los usuarios eliminar

direcciones:

<%= form_for @person do |f| %>


Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.check_box :_destroy%>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
...
</li>
<% end %>
</ul>
<% end %>

No olvide actualizar los parmetros de la lista blanca en su controlador para incluir tambin
el campo _destroy :

def person_params
params.require(:person).
permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
end

9.5 Prevencin de registros vacos


A menudo es til ignorar conjuntos de campos que el usuario no ha rellenado. Puede
controlar esto pasando un proc: reject_if proc a accepted_nested_attributes_for . Este
proc ser llamado con cada hash de atributos enviados por el formulario. Si el proc

devuelve false entonces Active Record no crear un objeto asociado para ese hash. El
ejemplo siguiente slo intenta generar una direccin si se establece el atributo kind.

358
9- Construccin de formularios complejos

class Person < ApplicationRecord


has_many :addresses
accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes[
'kind'].blank?}
end

Como conveniencia, puede pasar el smbolo: all_blank que crear un proc que rechazar
los registros donde todos los atributos estn en blanco, excluyendo cualquier valor para
_destroy .

9.6 Adicin de campos al vuelo


En lugar de reproducir varios conjuntos de campos con antelacin, es posible que desee
aadirlos slo cuando un usuario haga clic en el botn "Agregar nueva direccin". Rails no
proporciona ningn soporte incorporado para esto. Al generar nuevos conjuntos de campos
debe asegurarse de que la id del array asociado es nico: la fecha actual de JavaScript
(milisegundos despus del evento) es una opcin comn.

359
XI- Descripcin general de Action Controller

XI- Descripcin general de Action


Controller
En esta gua aprender cmo funcionan los controladores y cmo encajan en el ciclo de
solicitudes en su aplicacin.

Despus de leer esta gua, sabr:

Cmo seguir el flujo de una solicitud a travs de un controlador.


Cmo restringir los parmetros pasados a su controlador.
Cmo y por qu almacenar datos en la sesin o en las cookies.
Cmo trabajar con filtros para ejecutar cdigo durante el proceso de solicitud.
Cmo utilizar la autenticacin HTTP integrada de Action Controller.
Cmo transmitir datos directamente al navegador del usuario.
Cmo filtrar los parmetros sensibles para que no aparezcan en el registro de la
aplicacin.
Cmo tratar las excepciones que pueden surgir durante el procesamiento de la
solicitud.

360
1- Qu hace un controlador?

1- Qu hace un controlador?
Action Controller es la C en MVC. Despus de que el enrutador ha determinado qu
controlador utilizar para una solicitud, el controlador es responsable de dar sentido a la
solicitud y producir la salida adecuada. Por suerte, el controlador de accin hace la mayor
parte del trabajo duro por usted y utiliza las convenciones inteligentes para hacer esto lo
ms directo posible.

Para la mayora de las aplicaciones convencionales RESTful, el controlador recibe la


solicitud o request (esto es invisible para usted como desarrollador), busca o guarda datos
de un modelo y utiliza una vista para crear una salida HTML. Si su controlador necesita
hacer cosas un poco diferentes, eso no es un problema, porque esta es slo la forma ms
comn en que un controlador trabaja.

Por lo tanto, un controlador puede considerarse como un intermediario entre los modelos y
las vistas. Hace que los datos del modelo estn disponibles para que la vista pueda mostrar
esos datos al usuario, y guarda o actualiza los datos que ingresa un usuario en el modelo.

Para obtener ms detalles sobre el proceso de enrutamiento, consulte Rails Routing.

361
2- Convencin de nombres del controlador

2- Convencin de nombres del controlador


La convencin de nombres de controladores en Rails favorece la pluralizacin de la ltima
palabra en el nombre del controlador, aunque no es estrictamente necesario (por ejemplo,
ApplicationController ). Por ejemplo, ClientsController es preferible a ClientController ,

SiteAdminsController es preferible a SiteAdminController o SitesAdminsController , y as

sucesivamente.

Siguiendo esta convencin, le permitir utilizar los routes generators (o generadores de


rutas) predeterminados (por ejemplo, resources , etc.) sin necesidad de llamar cada :path
o :controller , y mantendr el uso de los path y los helpers de URL coherentes en toda la
aplicacin. Consulte la Gua de Layouts y Rendering para obtener ms detalles.

La convencin de nombres del controlador difiere de la convencin de nombres de los


modelos, que se espera que sean nombrados en forma singular.

362
3- Mtodos y acciones

3- Mtodos y acciones
Un controlador es una clase Ruby que hereda de ApplicationController y tiene mtodos
como cualquier otra clase. Cuando su aplicacin recibe una solicitud, el enrutamiento
determinar qu controlador y accin ejecutar, entonces Rails crea una instancia de ese
controlador y ejecuta el mtodo con el mismo nombre que la accin.

class ClientsController < ApplicationController


def new
end
end

Por ejemplo, si un usuario va a /clients/new en su aplicacin para agregar un nuevo


cliente, Rails crear una instancia de ClientsController y llamar a su nuevo mtodo.
Tenga en cuenta que el mtodo vaco del ejemplo anterior funcionara correctamente
porque Rails procesar por defecto la vista new.html.erb , a menos que la accin indique lo
contrario. El nuevo mtodo podra poner a disposicin de la vista una variable de instancia
@client creando un nuevo Cliente:

def new
@client = Client.new
end

La Gua de Layouts y Rendering explica esto con ms detalle.

ApplicationController hereda de ActionController::Base , que define una serie de

mtodos tiles. Esta gua cubre algunos de estos, pero si tienes curiosidad de ver lo que
hay ah, puedes verlos todos en la documentacin de la API o en la fuente misma.

Slo los mtodos pblicos son llamados como acciones. Es una buena prctica reducir la
visibilidad de los mtodos (privados o protegidos) que no pretenden ser acciones, como los
mtodos auxiliares o los filtros.

363
4- Parmetros

4- Parmetros
Es probable que desee acceder a los datos enviados por el usuario u otros parmetros en
las acciones del controlador. Hay dos tipos de parmetros posibles en una aplicacin web.
Los primeros son los parmetros que se envan como parte de la URL, llamados
parmetros de la cadena de consulta. La cadena de consulta es todo lo que hay despus de
" ? " En la URL. El segundo tipo de parmetro se denomina normalmente datos POST . Esta
informacin normalmente proviene de un formulario HTML que ha sido llenado por el
usuario. Se llama datos POST porque slo se puede enviar como parte de una solicitud
HTTP POST. Rails no hace ninguna distincin entre los parmetros de cadena de consulta y
los parmetros de POST, y ambos estn disponibles en el hash params en su controlador:

class ClientsController < ApplicationController


# Esta accin utiliza parmetros de cadena de consulta porque se ejecuta
# por una solicitud HTTP GET, pero esto no hace ninguna diferencia
# a la forma en que se accede a los parmetros. La URL para
# esta accin se vera as en la URL con la lista activada
# clients: /clients?status=activated
def index
if params[:status] == "activated"
@clients = Client.activated
else
@clients = Client.inactivated
end
end

# Esta accin utiliza parmetros POST. Lo ms probable es que vengan


# de un formulario HTML que el usuario ha enviado. La URL para
# esta solicitud RESTful ser "/clients", y los datos sern
# enviados como parte del cuerpo de la solicitud.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
render "new"
end
end
end

4.1 Parmetros de hash y de array

364
4- Parmetros

El hash de parmetros no est limitado a claves y valores unidimensionales. Puede


contener arrays anidados y hashes. Para enviar un array de valores, agregue un par de
corchetes vacos " [] " al nombre de la clave:

GET /clients?ids[]=1&ids[]=2&ids[]=3

La URL real en este ejemplo se codificar como " /clients?


ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3 " ya que los caracteres " [ " y " ] " no estn

permitidos en las URL. La mayora de las veces no tienes que preocuparte por esto
porque el navegador lo codificar por ti, y Rails lo descodificar automticamente, pero
si alguna vez te encuentras teniendo que enviar esas solicitudes al servidor
manualmente debes tener esto en cuenta .

El valor de params[:ids] ser ahora ["1", "2", "3"] . Tenga en cuenta que los valores de
los parmetros son siempre cadenas; Rails no intenta adivinar o lanzar el type .

Valores como [nil] o [nil, nil, ...] en params se sustituyen por [] por motivos
de seguridad de forma predeterminada. Consulte la Gua de seguridad para obtener
ms informacin.

Para enviar un hash, debe incluir el nombre de la clave dentro de los corchetes:

<form accept-charset="UTF-8" action="/clients" method="post">


<input type="text" name="client[name]" value="Acme" />
<input type="text" name="client[phone]" value="12345" />
<input type="text" name="client[address][postcode]" value="12345" />
<input type="text" name="client[address][city]" value="Carrot City" />
</form>

Cuando se enve este formulario, el valor de params[:client] ser { "name" => "Acme",
"phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } } .

Tenga en cuenta el hash anidado en params[:client][: address] .

El objeto params acta como un Hash, pero le permite usar smbolos y cadenas de forma
intercambiable como claves.

4.2 Parmetros JSON


Si est escribiendo una API, es posible que se encuentre ms cmodo aceptando
parmetros en formato JSON . Si el encabezado " Content-Type " de su solicitud est
configurado como " application/json ", Rails cargar automticamente sus parmetros en
el hash de parmetros, al que puede acceder como lo hara normalmente.

Por ejemplo, si enva este contenido de JSON :

365
4- Parmetros

{ "company": { "name": "acme", "address": "123 Carrot Street" } }

Su controlador recibir params[:company] como {"name" => "acme", "address" => "123
Carrot Street"} .

Adems, si ha activado config.wrap_parameters en su inicializador o ha llamado


wrap_parameters en su controlador, puede omitir con seguridad el elemento raz en el

parmetro JSON . En este caso, los parmetros sern clonados y envueltos con una clave
elegida basada en el nombre de su controlador. Por lo tanto, la peticin JSON anterior
puede escribirse como:

{ "name": "acme", "address": "123 Carrot Street" }

Y, asumiendo que usted est enviando los datos a CompaniesController , entonces sera
envuelto dentro de la llave :company as:

{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 C
arrot Street" } }

Puede personalizar el nombre de la clave o los parmetros especficos que desea ajustar
consultando la documentacin de la API

El soporte para parsear parmetros XML se ha extrado en una gema denominada


actionpack-xml_parser .

4.3 Parmetros de enrutamiento


El hash params siempre contendr las claves :controller y :action , pero debera usar
los mtodos controller_name y action_name para acceder a estos valores. Otros
parmetros definidos por el enrutamiento, tales como :id , tambin estarn disponibles.
Por ejemplo, considere una lista de clientes donde la lista puede mostrar clientes activos o
inactivos. Podemos agregar una ruta que captura el parmetro :status en una URL
"bonita":

get '/clients/:status' => 'clients#index', foo: 'bar'

En este caso, cuando un usuario abre la URL /clients/active , params[:status] se


establecer en " active ". Cuando se utiliza esta ruta, params[:foo] tambin se
establecer en " bar ", como si se pasara en la cadena de consulta. Su controlador tambin
recibir params[:action] como " index " y params[:controller] como " clients ".

366
4- Parmetros

4.4 default_url_options
Puede definir parmetros globales predeterminados para la generacin de las URLs
definiendo un mtodo denominado default_url_options en su controlador. Tal mtodo
debe devolver un hash con los valores predeterminados deseados, cuyas keys deben ser
smbolos:

class ApplicationController < ActionController::Base


def default_url_options
{ locale: I18n.locale }
end
end

Estas opciones se utilizarn como punto de partida al generar las URLs, por lo que es
posible que se anulen por las opciones que se pasan a url_for en las llamadas.

Si define default_url_options en ApplicationController , como en el ejemplo anterior,


estos valores predeterminados se utilizarn para toda la generacin de todas las URLs. El
mtodo tambin puede definirse en un controlador especfico, en cuyo caso slo afecta a
las URLs all generadas.

En una solicitud dada, el mtodo no es realmente llamado para cada URL generada; Por
razones de rendimiento, el hash devuelto se almacena en cach, y hay como mximo una
invocacin por solicitud.

4.5 Strong Parameters


Con strong parameter, los parmetros de Action Controller estn prohibidos para ser
utilizados en las asignaciones masivas del Active Model hasta que hayan sido incluidos en
la lista blanca (white listed). Esto significa que usted tendr que tomar una decisin
consciente sobre qu atributos permite la actualizacin masiva. Esta es una mejor prctica
de seguridad, para ayudar a evitar accidentes, al permitir a los usuarios actualizar los
atributos de modelos sensibles.

Adems, los parmetros se pueden marcar como se requiera y fluirn a travs de un flujo
de raise/rescue predefinido que dar lugar a que se devuelva una 400 Bad Request si no se
pasan todos los parmetros requeridos.

367
4- Parmetros

class PeopleController < ActionController::Base


# Esto disparar una excepcin ActiveModel::ForbiddenAttributesError
# porque est utilizando una asignacin masiva sin un permiso explcito
# para este paso.
def create
Person.create(params[:person])
end

# Esto pasar sin problemas, siempre y cuando hay una key person
# en los parmetros, de lo contrario dispara una excepcin
# ActionController::ParameterMissing, que obtendr una captura
# de ActionController::Base y lo convierte en un error 400 Bad
# Request.
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end

private
# Utilizando un mtodo privado para encapsular los parmetros permitidos
# es slo un buen patrn ya que podr reutilizar el mismo
# permit del whitelist en create y update. Adems, puede especializarse
# este mtodo con la verificacin por usuario de los atributos permitidos.
def person_params
params.require(:person).permit(:name, :age)
end
end

4.5.1 Valores escalares permitidos


Dado

params.permit(:id)

La clave :id pasa la lista blanca si aparece en params y tiene un valor escalar permitido
asociado. De lo contrario, la clave se va a filtrar, por lo que no se pueden inyectar arrays,
hashes ni ningn otro objeto.

Los tipos escalares permitidos son String , Symbol , NilClass , Numeric , TrueClass ,
FalseClass , Date , Time , DateTime , StringIO , IO ,

ActionDispatch::Http::UploadedFile , y Rack::Test::UploadedFile .

Para declarar que el valor en params debe ser un array de valores escalares permitidos,
debe mapear la key a un array vaco:

368
4- Parmetros

params.permit(id: [])

A veces no es posible o conveniente declarar las keys vlidas de un parmetro hash o su


estructura interna. Slo tiene que asignar un hash vaco:

params.permit(preferences: {})

Pero tenga cuidado porque esto abre la puerta a un input arbitrario. En este caso, el
permit asegura que los valores en la estructura devuelta se permitan escalares y filtra

cualquier otra cosa.

Para la lista blanca de un conjunto de parmetros, el permit! puede utilizarse:

params.require(:log_entry).permit!

Esto marca los hash params :log_entry y cualquier sub-hash de l como permitido y no se
comprueba para los escalares permitidos, porque cualquier cosa es aceptada. Se debe
tener mucho cuidado cuando se usa permit! , ya que permitir asignar masivamente todos
los atributos actuales y futuros del modelo.

4.5.2 Parmetros anidados


Tambin puede utilizar permit en parmetros anidados, como:

params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])

Esta declaracin de lista blanca (whitelist) de name , emails y friends espera que los
emais sean un array de valores escalares permitidos y que los friends sean un array de

recursos con atributos especficos: deben tener un atributo name (cualquier valor escalar
permitido permitido), un atributo hobbies como un array escalar permitido, y valores y un
atributo de family que est restringido a tener un name (tambin se permiten valores
escalares aqu).

4.5.3 Ms ejemplos
Tambin puede usar los atributos de permit en su nueva accin. Esto dispara el problema
que no puede utilizar require en la clave raz porque, normalmente, no existe al llamar a
new :

369
4- Parmetros

# using `fetch` you can supply a default and use


# the Strong Parameters API from there.
params.fetch(:blog, {}).permit(:title, :author)

El mtodo de clase de modelos llamado accepted_nested_attributes_for le permite


actualizar y destruir registros asociados. Esto se basa en los parmetros id y _destroy :

Los hashs con claves enteras se tratan de forma diferente y se pueden declarar los
atributos como si fueran hijos directos. Obtiene este tipo de parmetros cuando utiliza
accept_nested_attributes_for en combinacin con una asociacin has_many :

# To whitelist the following data:


# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}

params.require(:book).permit(:title, chapters_attributes: [:title])

4.5.4 Fuera del scope de los strong parameters


La API de los strong parameters fue diseada con los casos de uso ms comunes en
mente. No se entiende como una bala de plata para manejar todos sus problemas de lista
blanca. Sin embargo, puede combinar fcilmente la API con su propio cdigo para
adaptarse a su situacin.

Imagine un escenario en el que tenga parmetros que representen un nombre de producto


y un hash de datos arbitrarios asociados con ese producto y que desee incluir en la lista
blanca el atributo de nombre de producto y tambin el hash de datos completo. La API de
los strong parameters no le permite directamente incluir en la lista blanca el conjunto de un
hash anidado con ninguna clave, pero puede utilizar las claves de su hash anidado para
declarar lo que desea para la lista blanca:

def product_params
params.require(:product).permit(:name, data: params[:product][:data].try(:keys))
end

370
5- Session

5- Session
Su aplicacin tiene una sesin para cada usuario en la que puede almacenar pequeas
cantidades de datos que se mantendrn entre solicitudes. La sesin slo est disponible en
el controlador y la vista y puede utilizar uno de varios mecanismos de almacenamiento
diferentes:

ActionDispatch::Session::CookieStore - Almacena todo en el cliente.

ActionDispatch::Session::CacheStore - Almacena los datos en la cach de Rails.

ActionDispatch::Session::ActiveRecordStore - Almacena los datos en una base de

datos utilizando Active Record. (se requiere la gema activerecord-session_store ).


ActionDispatch::Session::MemCacheStore - Almacena los datos en un clster de

memcached (esto es una implementacin heredada; considere usar CacheStore en su


lugar).

Todo el almacenamiento de las sesiones utiliza una cookie para almacenar un ID


exclusivo para cada sesin (debe usar una cookie, Rails no le permitir pasar el ID de
sesin en la URL, ya que es poco seguro).

Para la mayora del almacenamiento, este ID se utiliza para buscar los datos de sesin en
el servidor, por ejemplo: en una tabla de base de datos. Hay una excepcin, que es el
almacenamiento de sesin predeterminado y recomendado - el CookieStore - que
almacena todos los datos de sesin en la propia cookie (el ID an est disponible para
usted si lo necesita). Esto tiene la ventaja de ser muy ligero y requiere cero configuracin en
una nueva aplicacin para poder usar la sesin. Los datos de las cookies se firman
criptogrficamente para que sean inviolables. Y tambin est encriptada para que
cualquiera con acceso a ella no pueda leer su contenido. (Rails no lo aceptar si se ha
editado).

El CookieStore puede almacenar alrededor de 4kB de datos - mucho menos que los otros -
pero esto es generalmente suficiente. El almacenamiento de grandes cantidades de datos
en la sesin se desaconseja sin importar el almacn de sesin que utilice su aplicacin.
Debe evitar especialmente el almacenamiento de objetos complejos (cualquier objeto que
no sea bsico de Ruby, el ejemplo ms comn es instancias de un modelo) en la sesin, ya
que es posible que el servidor no pueda volver a montarlos entre las solicitudes, lo que
resultar en un error.

Si sus sesiones de usuario no almacenan datos crticos o no necesitan estar presentes


durante perodos largos (por ejemplo, si slo utiliza flash para mensajera), puede
considerar el uso de ActionDispatch::Session::CacheStore . Esto almacenar sesiones

371
5- Session

utilizando la implementacin de cach que haya configurado para su aplicacin. La ventaja


de esto es que puede utilizar la infraestructura de cach existente para almacenar sesiones
sin necesidad de ninguna configuracin o administracin adicional. La desventaja, por
supuesto, es que las sesiones sern efmeras y podran desaparecer en cualquier
momento.

Obtenga ms informacin sobre el almacenamiento de sesiones en la Gua de Seguridad.

Si necesita un mecanismo de almacenamiento de sesin diferente, puede cambiarlo en el


inicializador:

# Utilice la base de datos para las sesiones en lugar del predeterminado basado en coo
kies,
# que no debe usarse para almacenar informacin altamente confidencial
# (create the session table with "rails g active_record:session_migration")
# Rails.application.config.session_store :active_record_store

Rails configura una clave de sesin (el nombre de la cookie) al firmar los datos de la sesin.
Estos tambin se pueden cambiar en un inicializador:

# Be sure to restart your server when you modify this file.


Rails.application.config.session_store :cookie_store, key: '_your_app_session'

Tambin puede pasar una clave de dominio y especificar el nombre de dominio para la
cookie:

# Be sure to restart your server when you modify this file.


Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain:
".example.com"

Rails configura (para CookieStore) una clave secreta utilizada para firmar los datos de la
sesin. Esto se puede cambiar en config/secrets.yml

372
5- Session

# Asegrese de reiniciar su servidor cuando modifique este archivo.


# Su clave secreta se utiliza para verificar la integridad de las cookies firmadas.

# Si cambia esta clave, todas las viejas cookies firmadas sern invlidas!
# asegrese de que la clave es de al menos 30 caracteres y todo aleatorio,
# y no hay palabras regulares o estars expuesto a ataques de diccionario.

# Puede utilizar `rails secret` para generar una clave secreta segura.
# asegrese de que las claves de este archivo se mantengan privadas
# si est compartiendo su cdigo pblicamente.

development:
secret_key_base: a75d...

test:
secret_key_base: 492f...

# Do not keep production secrets in the repository,


# instead read values from the environment.
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Cambiar la secret al usar CookieStore invalidar todas las sesiones existentes.

5.1 Acceso a la sesin


En su controlador puede acceder a la sesin a travs del mtodo de instancia de sesin.

Las sesiones son de carga perezosa. Si no accede a las sesiones en el cdigo de la


accin, no se cargarn. Por lo tanto, nunca tendr que desactivar las sesiones,
simplemente no acceder a ellos har el trabajo.

Los valores de sesin se almacenan utilizando pares clave/valor como un hash:

class ApplicationController < ActionController::Base

private

# Encuentra el usuario con el ID almacenado en la sesin con la clave


# :current_user_id Esta es una forma comn de manejar el inicio de sesin de un usua
rio en
# una aplicacin Rails; El inicio de sesin establece el valor de la sesin y
# el cierre de sesin lo elimina.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by(id: session[:current_user_id])
end
end

373
5- Session

Para almacenar algo en la sesin, slo tiene que asignarla a la clave como un hash:

class LoginsController < ApplicationController


# "Create" a login, aka "log the user in"
def create
if user = User.authenticate(params[:username], params[:password])
# Save the user ID in the session so it can be used in
# subsequent requests
session[:current_user_id] = user.id
redirect_to root_url
end
end
end

Para quitar algo de la sesin, asigne esa clave a nil :

class LoginsController < ApplicationController


# "Delete" a login, aka "log the user out"
def destroy
# Remove the user id from the session
@_current_user = session[:current_user_id] = nil
redirect_to root_url
end
end

Para restablecer la sesin completa, use reset_session .

5.2 El Flash
El flash es una parte especial de la sesin que se borra con cada solicitud. Esto significa
que los valores almacenados all solo estarn disponibles en la siguiente peticin, que es
til para pasar mensajes de error, etc.

Se accede de la misma manera que la sesin, como un hash (es una instancia de
FlashHash).

Utilicemos el acto de cerrar sesin como ejemplo. El controlador puede enviar un mensaje
que se mostrar al usuario en la siguiente solicitud:

class LoginsController < ApplicationController


def destroy
session[:current_user_id] = nil
flash[:notice] = "You have successfully logged out."
redirect_to root_url
end
end

374
5- Session

Tenga en cuenta que tambin es posible asignar un mensaje de flash como parte de una
redireccin. Usted puede asignarlo como :notice , :alert o de propsito general
:flash :

redirect_to root_url, notice: "You have successfully logged out."


redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }

La accin destroy vuelve a dirigir al root_url de la aplicacin, donde se mostrar el


mensaje. Tenga en cuenta que hasta que no se ejecuta la siguiente accin no disparar el
flash. Es una convencin mostrar cualquier alerta o aviso de error de flash en el diseo de
la aplicacin:

<html>
<!-- <head/> -->
<body>
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>

<!-- more content -->


</body>
</html>

De esta manera, si una accin establece un aviso o un mensaje de alerta, el layout lo


mostrar automticamente.

Puede pasar cualquier cosa que la sesin pueda almacenar; No se limita a avisos y alertas:

<% if flash[:just_signed_up] %>


<p class="welcome">Welcome to our site!</p>
<% end %>

Si desea que un valor de flash se transfiera a otra solicitud, utilice el mtodo keep :

375
5- Session

class MainController < ApplicationController


# Let's say this action corresponds to root_url, but you want
# all requests here to be redirected to UsersController#index.
# If an action sets the flash and redirects here, the values
# would normally be lost when another redirect happens, but you
# can use 'keep' to make it persist for another request.
def index
# Will persist all flash values.
flash.keep

# You can also use a key to keep only some kind of value.
# flash.keep(:notice)
redirect_to users_url
end
end

5.2.1 flash.now
De forma predeterminada, agregar valores a flash los har disponibles para la siguiente
solicitud, pero a veces es posible que desee acceder a esos valores en la misma solicitud.
Por ejemplo, si la accin create no puede guardar un recurso y renderizar la nueva
plantilla directamente, no resultar en una nueva solicitud, pero es posible que quiera
mostrar un mensaje utilizando flash. Para ello, puede utilizar flash.now de la misma
manera que utiliza el flash normal:

class ClientsController < ApplicationController


def create
@client = Client.new(params[:client])
if @client.save
# ...
else
flash.now[:error] = "Could not save client"
render action: "new"
end
end
end

376
6- Cookies

6- Cookies
Su aplicacin puede almacenar pequeas cantidades de datos en el cliente, llamadas
cookies , que se mantendrn en las solicitudes e incluso en las sesiones. Rails proporciona

un fcil acceso a las cookies a travs del mtodo cookies , que - al igual que la sesin -
funciona como un hash:

class CommentsController < ApplicationController


def new
# Auto-fill the commenter's name if it has been stored in a cookie
@comment = Comment.new(author: cookies[:commenter_name])
end

def create
@comment = Comment.new(params[:comment])
if @comment.save
flash[:notice] = "Thanks for your comment!"
if params[:remember_name]
# Remember the commenter's name.
cookies[:commenter_name] = @comment.author
else
# Delete cookie for the commenter's name cookie, if any.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
else
render action: "new"
end
end
end

Tenga en cuenta que mientras que para los valores de sesin se establece la key a nil ,
para eliminar un valor de cookie debe utilizar cookies.delete (:key) .

Rails tambin proporciona un cookie jar (tarro de cookies) firmado (signed) y un tarro de
galletas encriptado para almacenar datos confidenciales. El cookie firmado anexa una firma
criptogrfica en los valores de cookie para proteger su integridad. El tarro de cookies cifrado
cifra los valores adems de firmarlos, de modo que no puedan ser ledos por el usuario
final. Consulte la documentacin de la API para obtener ms detalles.

Estos tarros de galletas especiales utilizan un serializador para serializar los valores
asignados en cadenas y los deserializa en objetos Ruby para la lectura.

Puede especificar qu serializador utilizar:

377
6- Cookies

Rails.application.config.action_dispatch.cookies_serializer = :json

El serializador predeterminado para las nuevas aplicaciones es :json . Para compatibilidad


con aplicaciones antiguas con cookies existentes, :marshal se utiliza cuando no se
especifica la opcin serializador.

Tambin puede establecer esta opcin en :hybrid , en cuyo caso Rails deserializara de
forma transparente las cookies existentes ( Marshal -serialized) en lectura y volver a
escribirlas en el formato JSON . Esto es til para migrar aplicaciones existentes a :json
serializer.

Tambin es posible pasar un serializador personalizado que responde a carga y volcado


(load and dump):

Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer

Cuando utilice el serializador :json o :hybrid , debe tener cuidado de que no todos los
objetos de Ruby se pueden serializar como JSON . Por ejemplo, los objetos Fecha y Hora
se serializarn como cadenas y los Hashes tendrn sus claves constreidas (stringified).

class CookiesController < ApplicationController


def set_cookie
cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
redirect_to action: 'read_cookie'
end

def read_cookie
cookies.encrypted[:expiration_date] # => "2014-03-20"
end
end

Es aconsejable que slo almacene datos sencillos (cadenas y nmeros) en las cookies. Si
tiene que almacenar objetos complejos, necesitar manejar la conversin manualmente al
leer los valores en solicitudes posteriores.

Si utiliza el almacn de sesin de cookies, esto tambin se aplicara a session y al hash de


flash .

378
7- Renderizando datos XML y JSON

7- Renderizando datos XML y JSON


ActionController facilita enormemente el procesamiento de datos XML o JSON . Si has
generado un controlador usando scaffolding, se vera algo como esto:

class UsersController < ApplicationController


def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render xml: @users}
format.json { render json: @users}
end
end
end

Puede notar en el cdigo anterior que estamos utilizando render xml: @users , no render
xml: @users.to_xml . Si el objeto no es una cadena, Rails invocar automticamente to_xml

por nosotros.

379
8- Filters

8- Filters
Los filtros son mtodos que se ejecutan before "antes", after "despus" o around
"alrededor" de una accin del controlador.

Los filtros se heredan, por lo que si se establece un filtro en ApplicationController , se


ejecutar en cada controlador de la aplicacin.

Un filtro "before" puede detener el ciclo de una solicitud. Un filtro "before" comn es aquel
que requiere que un usuario est conectado para que se ejecute una accin. Puede definir
el mtodo de filtro de esta manera:

class ApplicationController < ActionController::Base


before_action :require_login

private

def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
end

El mtodo simplemente almacena un mensaje de error en el flash y redirige al formulario


de inicio de sesin si el usuario no ha iniciado sesin. Si un filtro "before" hace o
redirecciona, la accin no se ejecutar. Si hay filtros adicionales programados para
ejecutarse despus de ese filtro, tambin se cancelarn.

En este ejemplo, el filtro se agrega a ApplicationController y, por lo tanto, todos los


controladores de la aplicacin lo heredan. Esto har que todo en la aplicacin requiera que
el usuario est conectado para poder usarlo. Por razones obvias (el usuario no podra
acceder a nada en la aplicacin hasta iniciar sesin en primer lugar!), No todos los
controladores o acciones deben requerir esto. Puede evitar que este filtro se ejecute antes
de acciones particulares con skip_before_action :

class LoginsController < ApplicationController


skip_before_action :require_login, only: [:new, :create]
end

380
8- Filters

Ahora, las acciones new y create de LoginsController funcionarn como antes sin
requerir que el usuario est conectado. La opcin :only se usa para omitir este filtro solo
para estas acciones, y tambin hay una opcin :except que funciona de manera opuesta.
Estas opciones tambin se pueden utilizar al agregar filtros, por lo que puede agregar un
filtro que slo se ejecuta para acciones seleccionadas en primer lugar.

8.1 Filtros "after" y "around"


Adems de los filtros "before", tambin puede ejecutar filtros despus de que se haya
ejecutado una accin o ambos (antes y despus).

Los filtros "after" son similares a los filtros "before", pero debido a que la accin ya se ha
ejecutado tienen acceso a los datos de respuesta que estn a punto de ser enviados por el
cliente. Obviamente, los filtros "after" no pueden detener la ejecucin de la accin.

Los filtros "around" son responsables de ejecutar sus acciones asociadas por yielding,
similar a cmo funcionan los middlewares Rack.

Por ejemplo, en un sitio web en el que los cambios tienen un flujo de trabajo de aprobacin,
un administrador puede obtener una vista previa de ellos fcilmente, y solo debe aplicarlos
en una transaccin:

class ChangesController < ApplicationController


around_action :wrap_in_transaction, only: :show

private

def wrap_in_transaction
ActiveRecord::Base.transaction do
begin
yield
ensure
raise ActiveRecord::Rollback
end
end
end
end

Tenga en cuenta que un filtro "around" tambin envuelve el rendering. En particular, si en el


ejemplo anterior, la vista se lee desde la base de datos (por ejemplo, a travs de un mbito),
lo har dentro de la transaccin y, de este modo, presentar los datos para obtener una
vista previa.

Usted puede elegir no ceder y construir la respuesta usted mismo, en cuyo caso la accin
no se ejecutar.

381
8- Filters

8.2 Otras formas de utilizar filtros


Aunque la forma ms comn de usar filtros es creando mtodos privados y usando *
_action para agregarlos, hay otras dos maneras de hacer lo mismo.

La primera es usar un bloque directamente con los mtodos * _action . El bloque recibe el
controlador como un argumento. El filtro require_login de arriba podra ser reescrito para
usar un bloque:

class ApplicationController < ActionController::Base


before_action do |controller|
unless controller.send(:logged_in?)
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url
end
end
end

Tenga en cuenta que el filtro en este caso utiliza send porque el metodo logged_in? es
privado y el filtro no se ejecuta en el mbito del controlador. Esta no es la forma
recomendada de implementar este filtro en particular, pero en casos ms simples podra ser
til.

La segunda forma es usar una clase (en realidad, cualquier objeto que responda a los
mtodos correctos lo har) para manejar el filtrado. Esto es til en casos que son ms
complejos y no se pueden implementar de una manera legible y reutilizable usando los
otros dos mtodos. Como ejemplo, podra volver a escribir el filtro de inicio de sesin de
nuevo para usar una clase:

class ApplicationController < ActionController::Base


before_action LoginFilter
end

class LoginFilter
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to access this section"
controller.redirect_to controller.new_login_url
end
end
end

Una vez ms, este no es un ejemplo ideal para este filtro, ya que no se ejecuta en el mbito
del controlador, pero obtiene el controlador pasado como un argumento. La clase filter debe
implementar un mtodo con el mismo nombre que el filtro, por lo que para el filtro

382
8- Filters

before_action la clase debe implementar un mtodo before y as sucesivamente. El

mtodo around debe ceder para ejecutar la accin.

383
9- Request Forgery Protection

9- Request Forgery Protection


La falsificacin de solicitudes entre sitios es un tipo de ataque en el que un sitio engaa a
un usuario para que realice solicitudes en otro sitio, posiblemente agregando, modificando o
eliminando datos en ese sitio sin el conocimiento o permiso del usuario.

El primer paso para evitar esto es asegurarse de que todas las acciones "destructivas"
( create , update y destroy ) slo se puede acceder con las solicitudes no-GET. Si sigues
las convenciones RESTful ya lo ests haciendo. Sin embargo, un sitio malicioso todava
puede enviar una solicitud no GET a su sitio con bastante facilidad, y ah es donde entra en
juego la proteccin contra la falsificacin de solicitudes. Como dice el nombre, protege de
las solicitudes falsificadas.

La forma en que se hace esto es agregando un token no adivinable que slo conoce su
servidor en cada solicitud. De esta manera, si una solicitud llega sin el token adecuado, se
le denegar el acceso.

Si genera un formulario como este:

<%= form_for @user do |f| %>


<%= f.text_field :username %>
<%= f.text_field :password %>
<% end %>

Ver cmo se agrega el token como un campo oculto:

<form accept-charset="UTF-8" action="/users/1" method="post">


<input type="hidden"
value="67250ab105eb5ad10851c00a5621854a23af5489"
name="authenticity_token"/>
<!-- fields -->
</form>

Rails agrega este token a cada formulario que se genera utilizando los ayudantes de
formulario, por lo que la mayor parte del tiempo no tiene que preocuparse por ello. Si est
escribiendo un formulario manualmente o necesita agregar el token por otro motivo, est
disponible a travs del mtodo form_authenticity_token :

form_authenticity_token genera un token de autenticacin vlido. Esto es til en lugares

en los que Rails no lo agrega automticamente, como en las llamadas personalizadas de


Ajax.

384
9- Request Forgery Protection

La Gua de seguridad tiene ms informacin sobre esto y muchos otros problemas


relacionados con la seguridad que debe tener en cuenta al desarrollar una aplicacin web.

385
10- Objetos de solicitud y respuesta

10- Objetos de solicitud y respuesta


En cada controlador hay dos mtodos de acceso que apuntan a la solicitud y los objetos de
respuesta asociados con el ciclo de solicitud que est actualmente en ejecucin. El mtodo
de solicitud contiene una instancia de ActionDispatch::Request y el mtodo de respuesta
devuelve un objeto de respuesta que representa lo que se va a devolver al cliente.

10.1 El objeto Request


El objeto request contiene mucha informacin til acerca de la solicitud que viene desde el
cliente. Para obtener una lista completa de los mtodos disponibles, consulte la
documentacin de la API de Rails y la documentacin en Rack. Entre las propiedades a las
que puede acceder en este objeto se encuentran:

Propiedad de la
Propsito
solicitud
host El nombre de host utilizado para esta solicitud.
Los primeros n segmentos del nombre de host,
domain(n=2)
comenzando desde la derecha (el TLD).
format El tipo de contenido solicitado por el cliente.
method El mtodo HTTP utilizado para la solicitud.
get?, post?, path?, put?, Devuelve true si el mtodo HTTP es GET / POST / PATCH
delete?, head? / PUT / DELETE / HEAD
Devuelve un hash que contiene los encabezados
headers
asociados con la peticin.

Nmero de puerto (nmero entero) utilizado para la


port
solicitud
Devuelve una cadena que contiene el protocolo utilizado
protocol
ms "://", por ejemplo "http://".

La cadena de consulta forma parte de la URL, es decir,


query_string
todo despus de "?".
remote_ip La direccin IP del cliente.

url URL completa utilizada para la solicitud.

10.1.1 path_parameters, query_parameters y


request_parameters

386
10- Objetos de solicitud y respuesta

Rails recopila todos los parmetros enviados junto con la solicitud en el hash de
parmetros, ya sea que se enven como parte de la cadena de consulta o el cuerpo de la
publicacin. El objeto de la peticin tiene tres accesores que le dan acceso a estos
parmetros dependiendo de su procedencia. El hash query_parameters contiene
parmetros que se enviaron como parte de la cadena de consulta mientras que el hash
request_parameters contiene los parmetros enviados como parte del cuerpo de la

publicacin. El hash path_parameters contiene parmetros que fueron reconocidos por el


enrutamiento como parte del camino que conduce a este controlador y accin en particular.

10.2 El objeto Response


El objeto de respuesta no suele usarse directamente, sino que se construye durante la
ejecucin de la accin y el procesamiento de los datos que se envan al usuario, pero a
veces, como en un filtro after , puede ser til acceder a la respuesta directamente.
Algunos de estos mtodos de acceso tambin tienen setters, lo que le permite cambiar sus
valores. Para obtener una lista completa de los mtodos disponibles, consulte la
documentacin de la API de Rails y la documentacin de Rack.

Propiedad de
Propsito
la respuesta

Es la cadena de datos que se devuelven al cliente. Esto es


body
frecuentemente HTML.

El cdigo de estado HTTP de la respuesta, como 200 para una


status
solicitud satisfactoria o 404 para el archivo no encontrado
location La URL a la que se est redireccionando el cliente, si lo hay.
content_type El tipo de contenido de la respuesta.

El conjunto de caracteres que se utiliza para la respuesta. El valor


charset
predeterminado es "utf-8".

headers Encabezados utilizados para la respuesta.

10.2.1 Configuracin de encabezados personalizados


Si desea establecer encabezados personalizados para una respuesta, entonces
response.headers es el lugar para hacerlo. El atributo headers es un hash que asigna

nombres de encabezado a sus valores, y Rails establecer algunos de ellos


automticamente. Si desea agregar o cambiar un encabezado, slo tiene que asignarlo a
response.headers de esta manera:

response.headers["Content-Type"] = "application/pdf"

387
10- Objetos de solicitud y respuesta

Nota: en el caso anterior tendra ms sentido usar directamente el setter content_type .

388
11- Autenticaciones HTTP

11- Autenticaciones HTTP


Rails viene con dos mecanismos de autenticacin HTTP integrados:

Autenticacin bsica
Autenticacin Digest

11.1 Autenticacin bsica HTTP


La autenticacin HTTP bsica es un esquema de autenticacin que es compatible con la
mayora de los navegadores y otros clientes HTTP . Por ejemplo, considere una seccin de
administracin que slo estar disponible introduciendo un nombre de usuario y una
contrasea en la ventana de dilogo HTTP bsica del navegador. El uso de la autenticacin
integrada es bastante sencilla y slo requiere que utilice un mtodo,
http_basic_authenticate_with .

class AdminsController < ApplicationController


http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end

Con esto en su lugar, puede crear controladores de namespaces que hereden de


AdminsController . El filtro se ejecutar para todas las acciones en esos controladores,

protegindolos con la autenticacin bsica HTTP .

11.2 Autenticacin HTTP Digest


La autenticacin HTTP digest es superior a la autenticacin bsica, ya que no requiere que
el cliente enve una contrasea sin cifrar a travs de la red (aunque la autenticacin HTTP
bsica es segura en HTTPS ). El uso de la autenticacin digest con Rails es bastante
sencillo y slo requiere el uso de un mtodo, authenticate_or_request_with_http_digest .

389
11- Autenticaciones HTTP

class AdminsController < ApplicationController


USERS = { "lifo" => "world" }

before_action :authenticate

private

def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end

Como se ve en el ejemplo anterior, el bloque authenticate_or_request_with_http_digest


slo tiene un argumento: el nombre de usuario. Y el bloque devuelve la contrasea.
Devolver false o nil desde el authenticate_or_request_with_http_digest causar un
error de autenticacin.

390
12- Streaming y descargas de archivos

12- Streaming y descargas de archivos


A veces es posible que desee enviar un archivo al usuario en lugar de renderizar una
pgina HTML . Todos los controladores de Rails tienen los mtodos send_data y
send_file , que transmitirn los datos al cliente. send_file es un mtodo de conveniencia

que le permite proporcionar el nombre de un archivo en el disco y transmitir el contenido


de ese archivo para usted.

Para transmitir datos al cliente, utilice send_data :

require "prawn"
class ClientsController < ApplicationController
# Generates a PDF document with information on the client and
# returns it. The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data generate_pdf(client),
filename: "#{client.name}.pdf",
type: "application/pdf"
end

private

def generate_pdf(client)
Prawn::Document.new do
text client.name, align: :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end

La accin download_pdf en el ejemplo anterior llamar a un mtodo privado que realmente


genera el documento PDF y lo devuelve como una cadena. Esta cadena se transmitir al
cliente como una descarga de archivos y se le sugerir al usuario un nombre de archivo. A
veces, al transferir archivos al usuario, es posible que no desee que descarguen el archivo.
Tomar imgenes, por ejemplo, que se pueden incrustar en pginas HTML. Para decirle al
navegador que un archivo no est destinado a ser descargado, puede establecer la opcin
:disposition en " inline ". El valor opuesto y el valor predeterminado para esta opcin es

" attachment ".

12.1 Envo de archivos

391
12- Streaming y descargas de archivos

Si desea enviar un archivo que ya existe en el disco, utilice el mtodo send_file .

class ClientsController < ApplicationController


# Stream a file that has already been generated and stored on disk.
def download_pdf
client = Client.find(params[:id])
send_file("#{Rails.root}/files/clients/#{client.id}.pdf",
filename: "#{client.name}.pdf",
type: "application/pdf")
end
end

Esto leer y transmitir el archivo 4kB en ese momento, evitando cargar todo el archivo en
la memoria a la vez. Puede desactivar la transmisin con la opcin :stream o ajustar el
tamao del bloque con la opcin :buffer_size .

Si :type no se especifica, se adivinar desde la extensin de archivo especificada en


:filename . Si el tipo de contenido no est registrado para la extensin, se utilizar

application/octet-stream .

Tenga cuidado al utilizar los datos procedentes del cliente (params, cookies, etc.) para
localizar el archivo en disco, ya que se trata de un riesgo de seguridad que podra
permitirle a alguien acceder a archivos a los que no estn destinados.

No se recomienda que transmita archivos estticos a travs de Rails si puede


guardarlos en una carpeta pblica en su servidor web. Es mucho ms eficiente dejar
que el usuario descargue el archivo directamente usando Apache u otro servidor web,
evitando que la peticin pase innecesariamente por toda la pila de Rails.

12.2 Descargas RESTful


Aunque send_data funciona correctamente, si no est creando una aplicacin RESTful que
tenga acciones separadas para descargas de archivos, normalmente no es necesario. En la
terminologa REST, el archivo PDF del ejemplo anterior puede considerarse simplemente
otra representacin del recurso del cliente. Rails proporciona una forma fcil y bastante
elegante de hacer "descargas RESTful". As es como puede volver a escribir el ejemplo
para que la descarga de PDF sea una parte de la accin show , sin ningn streaming:

392
12- Streaming y descargas de archivos

class ClientsController < ApplicationController


# The user can request to receive this resource as HTML or PDF.
def show
@client = Client.find(params[:id])

respond_to do |format|
format.html
format.pdf { render pdf: generate_pdf(@client) }
end
end
end

Para que este ejemplo funcione, debe agregar el tipo MIME PDF a Rails. Esto se puede
hacer agregando la siguiente lnea al archivo config/initializers/mime_types.rb :

Mime::Type.register "application/pdf", :pdf

Los archivos de config no se vuelven a cargar en cada solicitud, por lo que debe
reiniciar el servidor para que sus cambios surtan efecto.

Ahora el usuario puede solicitar para obtener una versin en PDF de un cliente simplemente
aadiendo " .pdf " a la URL:

GET /clients/1.pdf

12.3 Transmisin en directo de datos arbitrarios


Rails le permite transmitir ms que slo archivos. De hecho, puedes transmitir todo lo que
quieras en un objeto de respuesta. El mdulo ActionController::Live le permite crear una
conexin persistente con un navegador. Usando este mdulo, usted podr enviar datos
arbitrarios al navegador en puntos especficos en el tiempo.

12.3.1 Incorporacin de Live Streaming


Incluyendo ActionController::Live dentro de su clase controlador proporcionar todas las
acciones dentro del controlador la capacidad de transmitir datos. Puede mezclar el mdulo
de la siguiente manera:

393
12- Streaming y descargas de archivos

class MyController < ActionController::Base


include ActionController::Live

def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello world\n"
sleep 1
}
ensure
response.stream.close
end
end

El cdigo anterior mantendr una conexin persistente con el navegador y enviar 100
mensajes de " hola mundo \ n ", cada segundo.

Hay un par de cosas a notar en el ejemplo anterior. Necesitamos cerciorarnos de cerrar el


flujo de la respuesta. Olvidar cerrar el stream dejar el socket abierto para siempre.
Tambin tenemos que establecer el tipo de contenido en text/event-stream antes de
escribirlo en el flujo de respuesta. Esto se debe a que los encabezados no se pueden
escribir despus de que se ha enviado la respuesta (cuando response.committed? devuelve
un valor verdadero), que se produce cuando se escribe o se confirma el flujo de respuesta.

12.3.2 Ejemplo de uso


Supongamos que usted estaba haciendo una mquina de Karaoke y un usuario quiere
obtener las letras de una cancin en particular. Cada cancin tiene un nmero particular de
lneas y cada lnea toma el tiempo num_beats para terminar el canto.

Si queramos devolver las letras en modo Karaoke (slo enviar la lnea cuando el cantante
haya terminado la lnea anterior), entonces podramos usar ActionController::Live de la
siguiente manera:

394
12- Streaming y descargas de archivos

class LyricsController < ActionController::Base


include ActionController::Live

def show
response.headers['Content-Type'] = 'text/event-stream'
song = Song.find(params[:id])

song.each do |line|
response.stream.write line.lyrics
sleep line.num_beats
end
ensure
response.stream.close
end
end

El cdigo anterior enva la siguiente lnea slo despus de que el cantante haya completado
la lnea anterior.

12.3.3 Consideraciones del streaming


La transmisin de datos arbitrarios es una herramienta extremadamente poderosa. Como
se muestra en los ejemplos anteriores, puede elegir cundo y qu enviar a travs de un flujo
de respuesta. Sin embargo, tambin debe tener en cuenta lo siguiente:

Cada flujo de respuesta crea un nuevo subproceso y copia sobre las variables locales
de subproceso del subproceso original. Tener demasiadas variables locales de
subproceso puede afectar negativamente el rendimiento. Del mismo modo, un gran
nmero de hilos tambin pueden dificultar el rendimiento.
Si no se cierra el flujo de respuesta se deja el socket correspondiente abierto para
siempre. Asegrese de llamar close cada vez que utilice un flujo de respuesta.
Los servidores WEBrick almacenan todas las respuestas, por lo que incluir
ActionController::Live no funcionar. Debe utilizar un servidor web que no almacena

automticamente las respuestas.

395
13- Filtrado de logs

13- Filtrado de logs


Rails mantiene un archivo de logs para cada entorno en la carpeta log. Estos son
extremadamente tiles cuando se depura lo que realmente est pasando en su aplicacin,
pero en una aplicacin en vivo puede que no quiera que cada bit de informacin sea
almacenado en el archivo de logs.

13.1 Filtrado de parmetros


Puede filtrar los parmetros de solicitudes sensibles de sus archivos de logs agregndolos a
config.filter_parameters en la configuracin de la aplicacin. Estos parmetros se

marcarn [FILTERED] en el log.

config.filter_parameters << :password

Los parmetros proporcionados se filtrarn mediante una expresin regular coincidente


parcial. Rails aade el valor predeterminado :password en el inicializador apropiado
( initialalizers/filter_parameter_logging.rb ) y se preocupa por los parmetros tpicos de
la aplicacin password y password_confirmation .

13.2 Redirecciona el filtrado


A veces es deseable filtrar desde archivos de logs algunas ubicaciones sensibles a las que
est redirigiendo la aplicacin. Puede hacerlo usando la opcin de configuracin
config.filter_redirect :

config.filter_redirect << 's3.amazonaws.com'

Puede establecerla en una cadena, una Regex o un array de ambas.

config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]

Las URL coincidentes se marcarn como "[FILTERED]".

396
14- Rescue

14- Rescue
Lo ms probable es que su aplicacin va a contener errores o lanzar una excepcin que
debe tratarse. Por ejemplo, si el usuario sigue un vnculo a un recurso que ya no existe en
la base de datos, Active Record lanzar la excepcin ActiveRecord::RecordNotFound .

El control de excepciones por defecto de Rails muestra un mensaje "500 Server Error" para
todas las excepciones. Si la solicitud se realiz localmente, un buen rastreo y una cierta
informacin agregada se muestra para que pueda averiguar qu sali mal y tratar con l. Si
la solicitud era remota, Rails slo mostrar un simple mensaje de error "500 Server Error" al
usuario, o un "404 Not Found" si hubo un error de enrutamiento o un registro no se pudo
encontrar. A veces es posible que desee personalizar cmo se detectan estos errores y
cmo se muestran al usuario. Hay varios niveles de manejo de excepciones disponibles en
una aplicacin de Rails:

14.1 Las plantillas predeterminadas 500 y 404


De forma predeterminada, una aplicacin de produccin mostrar un mensaje de error 404
o 500, en el entorno de desarrollo se generarn todas las excepciones no controladas.
Estos mensajes estn contenidos en archivos HTML estticos en la carpeta pblica, en
404.html y 500.html respectivamente. Puede personalizar estos archivos para agregar

informacin adicional y estilos, pero recuerde que son HTML esttico; Es decir, no puede
utilizar ERB , SCSS , CoffeeScript o diseos para ellos.

14.2 rescue_from
Si quieres hacer algo ms elaborado al detectar errores, puedes usar rescue_from , que
maneja excepciones de cierto tipo (o varios tipos) en un controlador completo y sus
subclases.

Cuando se produce una excepcin que es detectada por una directiva rescue_from , el
objeto de excepcin se pasa al manejador (handler). El manejador (handler) puede ser un
mtodo o un objeto Proc pasado a la opcin :with . Tambin puede utilizar un bloque
directamente en lugar de un objeto Proc explcito.

A continuacin, le indicamos cmo puede utilizar rescue_from para interceptar todos los
errores de ActiveRecord::RecordNotFound y hacer algo con ellos.

397
14- Rescue

class ApplicationController < ActionController::Base


rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

private

def record_not_found
render plain: "404 Not Found", status: 404
end
end

Por supuesto, este ejemplo es todo menos elaborado y no mejora la manipulacin de


excepciones por defecto en absoluto, pero una vez que puedas capturar todas esas
excepciones eres libre de hacer lo que quieras con ellas. Por ejemplo, puede crear clases
de excepcin personalizadas que se lanzarn cuando un usuario no tenga acceso a una
determinada seccin de su aplicacin:

class ApplicationController < ActionController::Base


rescue_from User::NotAuthorized, with: :user_not_authorized

private

def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_back(fallback_location: root_path)
end
end

class ClientsController < ApplicationController


# Check that the user has the right authorization to access clients.
before_action :check_authorization

# Note how the actions don't have to worry about all the auth stuff.
def edit
@client = Client.find(params[:id])
end

private

# If the user is not authorized, just throw the exception.


def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
end

398
14- Rescue

No debe hacer la excepcin rescue_from o rescue_from StandardError a menos que


tenga una razn en particular, ya que causar efectos secundarios graves (por
ejemplo, no podr ver detalles de excepcin y rastreos durante el desarrollo).

Cuando se ejecuta en el entorno de produccin, todos los errores


ActiveRecord::RecordNotFound generan la pgina de error 404. A menos que necesites

un comportamiento personalizado, no necesitas manejarlo

Ciertas excepciones slo se pueden rescatar de la clase ApplicationController, ya que


se generan antes de que el controlador se inicialice y se ejecute la accin.

399
15- Forzar el protocolo HTTPS

15- Forzar el protocolo HTTPS


En ocasiones es posible que desee forzar a un controlador en particular a que slo sea
accesible a travs de un protocolo HTTPS por razones de seguridad. Puede utilizar el
mtodo force_ssl en su controlador para hacer cumplir lo siguiente:

class DinnerController
force_ssl
end

Al igual que los filtros, tambin podra pasar :only y :except para hacer cumplir la
conexin segura slo a acciones especficas:

class DinnerController
force_ssl only: :cheeseburger
# or
force_ssl except: :cheeseburger
end

Ten en cuenta que si te encuentras aadiendo force_ssl a muchos controladores, quizs


quieras obligar a toda la aplicacin a usar HTTPS en su lugar. En ese caso, puede
configurar config. force_ssl en su archivo de entorno.

400
XII- Enrutamiento en Rails

XII- Enrutamiento en Rails


Esta gua cubre las caractersticas orientadas al usuario en el enrutamiento de Rails.

Despus de leer esta gua, sabr:

Cmo interpretar el cdigo en config/routes.rb .


Cmo construir sus propias rutas, utilizando el estilo de recurso preferido o el mtodo
de coincidencia.
Cmo declarar parmetros de ruta, que se pasan a las acciones del controlador.
Cmo crear automticamente rutas de acceso y direcciones URL mediante ayudantes
de rutas.
Tcnicas avanzadas como la creacin de restricciones y el montaje de end points de
Rack.

401
1- El Propsito del Enrutador de Rails

1- El Propsito del Enrutador de Rails


El router de Rails reconoce las URL y las enva a la accin de un controlador, o a una
aplicacin de Rack. Tambin puede generar rutas de acceso y direcciones URL, evitando la
necesidad de codificar las cadenas en sus vistas.

1.1 Conexin de URLs al cdigo


Cuando su aplicacin Rails recibe una solicitud de entrada para:

GET /patients/17

pide al router que coincida con una accin del controlador. Si la primera ruta coincidente es:

get '/patients/:id', to: 'patients#show'

La solicitud se enva al controlador patients de la accion show {id: '17'} en params.

1.2 Generacin de rutas y URL desde el cdigo


Tambin puede generar rutas de acceso y URL. Si la ruta de arriba es modificada para ser:

get '/patients/:id', to: 'patients#show', as: 'patient'

Y su aplicacin contiene este cdigo en el controlador:

@patient = Patient.find(17)

Y esto en la vista correspondiente:

<%= link_to 'Patient Record', patient_path(@patient) %>

Entonces el enrutador generar el path /patients/17 . Esto reduce la fragilidad de su vista


y hace que su cdigo sea ms fcil de entender. Tenga en cuenta que no es necesario
especificar el ID en el ayudante de ruta.

402
1- El Propsito del Enrutador de Rails

403
2- Ruta resource: el valor predeterminado de Rails

2- Ruta resource: el valor predeterminado


de Rails
El enrutamiento de recursos le permite declarar rpidamente todas las rutas comunes para
un controlador con recursos. En lugar de declarar rutas separadas para sus acciones
index , show , new , edit , create , update y destroy , una ruta de recursos las

declara en una sola lnea de cdigo.

2.1 Resources en la Web


Los navegadores solicitan pginas desde Rails realizando una solicitud de URL utilizando
un mtodo HTTP especfico, como GET , POST , PATCH , PUT y DELETE . Cada mtodo es
una solicitud para realizar una operacin en el recurso. Una ruta de recursos traza un
nmero de solicitudes relacionadas a acciones en un solo controlador.

Cuando su aplicacin Rails recibe una solicitud de entrada para:

DELETE /photos/17

Pide al enrutador que lo mande a una accin del controlador. Si la primera ruta coincidente
es:

resources :photos

Rails enviar esa solicitud a la accin destroy en el controlador de fotos con {id: '17'}
en params.

2.2 CRUD, verbos y acciones


En Rails, una ruta de recursos proporciona una correlacin entre los verbos HTTP y las
URL a las acciones del controlador. Por convencin, cada accin tambin se correlaciona

con una operacin CRUD especfica en una base de datos. Una sola entrada en el archivo
de enrutamiento, como por ejemplo:

resources :photos

crea siete rutas diferentes en su aplicacin, todas mapeando al controlador de fotos:

404
2- Ruta resource: el valor predeterminado de Rails

HTTP Verb Path Controller#Action Used for

muestra una lista de todas las


GET /photos photos#index
fotos
devuelve un formulario HTML
GET /photos/new photos#new
para crear una nueva foto
POST /photos photos#create crea una nueva foto

GET /photos/:id photos#show muestra una foto en especifico


retorna un formulario HTML
GET /photos/:id/edit photos#edit
para editar la foto
actualiza una foto en
PATCH/PUT /photos/:id photos#update
especifico

DELETE /photos/:id photos#destroy elimina una foto en especifico

Debido a que el enrutador usa el verbo HTTP y la URL para coincidir con las
solicitudes entrantes, cuatro direcciones URL asignan siete acciones diferentes.

Las rutas de Rails se emparejan en el orden en que se especifican, por lo que si tienes
resources :photos arriba de get 'photos/poll' la ruta de la accin show para la

lnea de resources coincidir antes de la lnea get . Para arreglar esto, mueva la
lnea get por encima de la lnea de resources para que coincida primero.

2.3 Ayudantes de ruta y URL


La creacin de una ruta resources tambin expondr una serie de ayudantes a los
controladores en su aplicacin. En el caso de resources :photos

photos_path retorna /photos

new_photo_path retorna /photos/new

edit_photo_path(:id) retorna /photos/:id/edit (por ejemplo, edit_photo_path(10)

retorna /photos/10/edit )
photo_path(:id) retorna /photos/:id (por ejemplo, photo_path(10) retorna

/photos/10 )

Cada uno de estos ayudantes tiene un ayudante _url correspondiente (como


photos_url ) que devuelve la misma ruta prefijada con el prefijo actual de host, puerto y

ruta.

2.4 Definir varios recursos al mismo tiempo


Si necesita crear rutas para ms de un recurso, puede guardar un poco de escritura
definindolas todas con una sola llamada a resources :

405
2- Ruta resource: el valor predeterminado de Rails

resources :photos, :books, :videos

Esto funciona exactamente igual que:

resources :photos
resources :books
resources :videos

2.5 Recursos Singulares


A veces, tiene un recurso que los clientes siempre buscan sin hacer referencia a un ID .
Por ejemplo, desea que /profile muestre siempre el perfil del usuario actualmente
conectado. En este caso, puede utilizar un recurso singular para asignar /profile (en
lugar de /profile/:id ) a la accin show :

get 'profile', to: 'users#show'

Pasar un string a get esperar un formato controller#action , y al pasar un smbolo se


asignar directamente a una accin, pero tambin debe especificar el controller: a
utilizar:

get 'profile', to: :show, controller: 'users'

Esta ruta resourceful (en singular):

resource :geocoder

Crea seis rutas diferentes en su aplicacin, todas mapeadas al controlador Geocoders :

406
2- Ruta resource: el valor predeterminado de Rails

HTTP Verb Path Controller#Action Used for

retorna un formulario HTML


GET /geocoder/new geocoders#new
para crear el geocoder
POST /geocoder geocoders#create crea el nuevo geocoder
muestra el nico recurso de
GET /geocoder geocoders#show
geocoder

devuelve un formulario HTML


GET /geocoder/edit geocoders#edit
para editar el geocoder

actualiza el nico recurso de


PATCH/PUT /geocoder geocoders#update
geocoder
DELETE /geocoder geocoders#destroy elimina el recurso geocoder

Como es posible que desee utilizar el mismo controlador para una ruta singular ( /account )
y una ruta plural ( /accounts/45 ), los recursos singulares se asignan a controladores
mltiples. Por ejemplo, resource: photo y resources: photos crea rutas singulares y
plurales que se asignan al mismo controlador ( PhotosController ).

Una ruta singular genera estos ayudantes:

new_geocoder_path retorna /geocoder/new

edit_geocoder_path retorna /geocoder/edit

geocoder_path retorna /geocoder

Al igual que con recursos en plural, los mismos helpers que terminan en _url tambin
incluirn el prefijo host, port y path.

Un error de larga data impide que form_for trabaje automticamente con recursos
singulares. Como solucin, especifique la URL del formulario directamente, de la siguiente
manera:

form_for @geocoder, url: geocoder_path do |f|

# snippet for brevity

2.6 Namespaces del controlador y enrutamiento


Es posible que desee organizar grupos de controladores en un espacio de nombres. Ms
comnmente, puede agrupar varios controladores administrativos bajo un Admin::
namespace. Usted puede colocar estos controladores en el directorio
app/controllers/admin , y puede agruparlos en su enrutador:

407
2- Ruta resource: el valor predeterminado de Rails

namespace :admin do
resources :articles, :comments
end

Esto crear una serie de rutas para cada uno de los controladores articles y comments .
Para Admin::ArticlesController , Rails crear:

HTTP Verb Path Controller#Action Named Helper


GET /admin/articles admin/articles#index admin_articles_path
GET /admin/articles/new admin/articles#new new_admin_article_path

POST /admin/articles admin/articles#create admin_articles_path


GET /admin/articles/:id admin/articles#show admin_article_path(:id)
GET /admin/articles/:id/edit admin/articles#edit edit_admin_article_path(:id)
PATCH/PUT /admin/articles/:id admin/articles#update admin_article_path(:id)
DELETE /admin/articles/:id admin/articles#destroy admin_article_path(:id)

Si desea enrutar /articles (sin el prefijo /admin ) a Admin::ArticlesController , puede


utilizar:

scope module: 'admin' do


resources :articles, :comments
end

O, en un solo caso:

resources :articles, module: 'admin'

Si desea enrutar /admin/artculos a ArticlesController (sin el prefijo Admin::module ),


puede utilizar:

scope '/admin' do
resources :articles, :comments
end

O, en un solo caso:

resources :articles, path: '/admin/articles'

408
2- Ruta resource: el valor predeterminado de Rails

En cada uno de estos casos, las rutas nombradas siguen siendo las mismas que si no
utiliza el mbito. En el ltimo caso, las siguientes rutas mapean a ArticlesController :

HTTP Verb Path Controller#Action Named Helper


GET /admin/articles articles#index articles_path

GET /admin/articles/new articles#new new_article_path


POST /admin/articles articles#create articles_path
GET /admin/articles/:id articles#show article_path(:id)
GET /admin/articles/:id/edit articles#edit edit_article_path(:id)

PATCH/PUT /admin/articles/:id articles#update article_path(:id)


DELETE /admin/articles/:id articles#destroy article_path(:id)

Si necesita usar un espacio de nombres de controlador diferente dentro de un bloque de


espacio de nombres, puede especificar una ruta de controlador absoluta, por ejemplo: get
'/foo' => '/foo#index ''.

2.7 Recursos anidados


Es comn tener recursos que son lgicamente hijos de otros recursos. Por ejemplo,
suponga que su aplicacin incluye estos modelos:

class Magazine < ApplicationRecord


has_many :ads
end

class Ad < ApplicationRecord


belongs_to :magazine
end

Las rutas anidadas permiten capturar esta relacin en el enrutamiento. En este caso, puede
incluir esta declaracin de ruta:

resources :magazines do
resources :ads
end

Adems de las rutas para magazines, esta declaracin tambin enruta los anuncios a un
AdsController. Las URL de los ads requieren un magazine:

HTTP Verb Path Controller#Action Used for

muestra una

409
2- Ruta resource: el valor predeterminado de Rails

lista de todos
GET /magazines/:magazine_id/ads ads#index los anuncios
de una
revista
especfica
devuelve un
formulario
HTML para
crear un
GET /magazines/:magazine_id/ads/new ads#new nuevo
anuncio
perteneciente
a una revista
especfica

crea un
nuevo
anuncio
POST /magazines/:magazine_id/ads ads#create
perteneciente
a una revista
especfica

muestra un
anuncio
especfico
GET /magazines/:magazine_id/ads/:id ads#show
perteneciente
a una revista
especfica

devuelve un
formulario
HTML para
editar un
GET /magazines/:magazine_id/ads/:id/edit ads#edit
anuncio
perteneciente
a una revista
especfica
actualiza un
anuncio
especfico
PATCH/PUT /magazines/:magazine_id/ads/:id ads#update
perteneciente
a una revista
especfica

elimina un
anuncio
especfico
DELETE /magazines/:magazine_id/ads/:id ads#destroy
perteneciente
a una revista
especfica

410
2- Ruta resource: el valor predeterminado de Rails

Esto tambin crear ayudantes de enrutamiento como magazine_ads_url y


edit_magazine_ad_path . Estos ayudantes toman una instancia de magazine como el primer

parmetro ( magazine_ads_url(@magazine) ).

2.7.1 Lmites de anidamiento


Puede anidar recursos dentro de otros recursos anidados si lo desea. Por ejemplo:

resources :publishers do
resources :magazines do
resources :photos
end
end

Los recursos profundamente anidados se vuelven rpidamente engorrosos. En este caso,


por ejemplo, la aplicacin reconocera rutas tales como:

/publishers/1/magazines/2/photos/3

El auxiliar de ruta correspondiente sera publisher_magazine_photo_url , lo que requiere que


especifique objetos en los tres niveles. De hecho, esta situacin es bastante confusa que un
artculo popular de Jamis Buck propone una regla de oro para el buen diseo de Rails:

Los recursos nunca deben anidarse ms de 1 nivel de profundidad.

2.7.2 Anidacin poco profunda


Una manera de evitar la anidacin profunda (como se recomienda anteriormente) es
generar una coleccion de acciones dentro de un ambito del pariente, para obtener un
sentido de la jerarqua, pero no anidar las acciones de los miembros. En otras palabras,
slo para construir rutas con la cantidad mnima de informacin para identificar de forma
exclusiva el recurso, como esto:

resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

Esta idea establece un equilibrio entre las rutas descriptivas y el anidamiento profundo.
Existe sintaxis abreviada para lograr eso, a travs de la opcin :shallow :

411
2- Ruta resource: el valor predeterminado de Rails

resources :articles do
resources :comments, shallow: true
end

Esto generar exactamente las mismas rutas que el primer ejemplo. Tambin puede
especificar la opcin :shallow en el recurso padre, en cuyo caso todos los recursos
anidados sern superficiales ( shallow ):

resources :articles, shallow: true do


resources :comments
resources :quotes
resources :drafts
end

El mtodo superficial ( shallow ) de la DSL crea un mbito dentro del cual cada anidado es
superficial. Esto genera las mismas rutas que el ejemplo anterior:

shallow do
resources :articles do
resources :comments
resources :quotes
resources :drafts
end
end

Existen dos opciones de mbito para personalizar rutas poco profundas. prefijos
:shallow_path del path miembro con el parmetro especificado:

scope shallow_path: "sekret" do


resources :articles do
resources :comments, shallow: true
end
end

El recurso de comentarios aqu tendr las siguientes rutas generadas:

412
2- Ruta resource: el valor predeterminado de Rails

HTTP Verb Path Controller#Action Named Helper

GET /articles/:article_id/comments(.:format) comments#index article_comment


POST /articles/:article_id/comments(.:format) comments#create article_comment
GET /articles/:article_id/comments/new(.:format) comments#new new_article_com

GET /sekret/comments/:id/edit(.:format) comments#edit edit_comment_p


GET /sekret/comments/:id(.:format) comments#show comment_path
PATCH/PUT /sekret/comments/:id(.:format) comments#update comment_path
DELETE /sekret/comments/:id(.:format) comments#destroy comment_path

La opcin: shallow_prefix aade el parmetro especificado a los ayudantes nombrados:

scope shallow_prefix: "sekret" do


resources :articles do
resources :comments, shallow: true
end
end

El recurso comments aqu tendr las siguientes rutas generadas para l:

HTTP Verb Path Controller#Action Named Helper


GET /articles/:article_id/comments(.:format) comments#index article_comment
POST /articles/:article_id/comments(.:format) comments#create article_comment
GET /articles/:article_id/comments/new(.:format) comments#new new_article_com

GET /comments/:id/edit(.:format) comments#edit edit_sekret_com


GET /comments/:id(.:format) comments#show sekret_comment

PATCH/PUT /comments/:id(.:format) comments#update sekret_comment


DELETE /comments/:id(.:format) comments#destroy sekret_comment

2.8 Routing concerns


El Routing concerns le permite declarar rutas comunes que pueden reutilizarse dentro de
otros recursos y rutas. Para definir un concerns:

413
2- Ruta resource: el valor predeterminado de Rails

concern :commentable do
resources :comments
end

concern :image_attachable do
resources :images, only: :index
end

Estos concerns pueden utilizarse en recursos para evitar la duplicacin de cdigo y


compartir el comportamiento entre rutas:

resources :messages, concerns: :commentable

resources :articles, concerns: [:commentable, :image_attachable]

Lo anterior es equivalente a:

resources :messages do
resources :comments
end

resources :articles do
resources :comments
resources :images, only: :index
end

Tambin puedes usarlos en cualquier lugar que quieras dentro de las rutas, por ejemplo en
una llamada de mbito o de espacio de nombres:

namespace :articles do
concerns :commentable
end

2.9 Creacin de rutas y URL desde objetos


Adems de utilizar los ayudantes de enrutamiento, Rails tambin puede crear rutas de
acceso y URL desde un array de parmetros. Por ejemplo, supongamos que tiene este
conjunto de rutas:

resources :magazines do
resources :ads
end

414
2- Ruta resource: el valor predeterminado de Rails

Cuando se utiliza magazine_ad_path , puede pasarle instancias de Magazine y Ad en lugar


de los ID numricos:

<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>

Tambin puede utilizar url_for con un conjunto de objetos y Rails determinar


automticamente la ruta que desea:

<%= link_to 'Ad details', url_for([@magazine, @ad]) %>

En este caso, Rails ver que @magazine es una revista y @ad es un anuncio y por lo tanto
utilizar el ayudante de magazine_ad_path . En helpers como link_to , puede especificar
slo el objeto en lugar de la llamada completa url_for :

<%= link_to 'Ad details', [@magazine, @ad] %>

Si quisieras linkearte a una sola revista:

<%= link_to 'Magazine details', @magazine %>

Para otras acciones, slo tiene que insertar el nombre de la accin como el primer elemento
del array:

<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>

Esto le permite tratar instancias de sus modelos como URLs, y es una ventaja clave para
usar el estilo resource.

2.10 Aadir ms acciones RESTful


No est limitado a las siete rutas que el enrutamiento RESTful crea de forma
predeterminada. Si lo desea, puede agregar rutas adicionales que se aplican a la coleccin
o miembros individuales de la coleccin.

2.10.1 Agregar rutas member


Para agregar una ruta member, simplemente agregue un bloque member en el bloque de
recursos:

415
2- Ruta resource: el valor predeterminado de Rails

resources :photos do
member do
get 'preview'
end
end

Esto reconocer /photos/1/preview con GET y enruta a la accin preview de


PhotosController , con el valor de id de recurso pasado en params[:id] . Tambin crear

los ayudantes preview_photo_url y preview_photo_path .

Dentro del bloque de rutas member, cada nombre de ruta especifica que el verbo HTTP
ser reconocido. Puedes usar aqu get , patch , put , post o delete . Si no tiene
mltiples rutas member , tambin puede pasar :on a una ruta, eliminando el bloque:

resources :photos do
get 'preview', on: :member
end

Puede omitir la opcin :on , esto crear la misma ruta member , excepto que el valor id
del recurso estar disponible en params[:photo_id] en lugar de params[:id] .

2.10.2 Adicin de collection routes


Para agregar una ruta a la coleccin:

resources :photos do
collection do
get 'search'
end
end

Esto permitir a Rails reconocer rutas como /photos/search con GET , y enruta a la accin
search de PhotosController . Tambin crear los helpers de bsqueda search_photos_url

y search_photos_path .

Al igual que con las rutas member , puede pasar :on a una ruta:

resources :photos do
get 'search', on: :collection
end

2.10.3 Agregar rutas para nuevas acciones adicionales

416
2- Ruta resource: el valor predeterminado de Rails

Para agregar una accin nueva alternativa se hace mediante el acceso directo :on

resources :comments do
get 'preview', on: :new
end

Esto permitir a Rails reconocer rutas como /comments/new/preview con GET , y enruta a la
accin preview de CommentsController . Tambin crear los auxiliares de ruta
preview_new_comment_url y preview_new_comment_path .

Si te encuentras agregando muchas acciones adicionales a una ruta de recursos, es


hora de parar y pregntarte si ests disfrazando la presencia de otro recurso.

417
3- Rutas sin resources

3- Rutas sin resources


Adems del enrutamiento por resources, Rails tiene un poderoso apoyo para el
enrutamiento de direcciones URL arbitrarias a las acciones. Aqu, no obtiene grupos de
rutas generadas automticamente por resources. En su lugar, configura cada ruta de su
aplicacin por separado.

Mientras que normalmente debe usar resources, todava hay muchos lugares donde el
enrutamiento simple es ms apropiado. No hay necesidad de intentar cazar cada pieza de
su aplicacin en un marco de recursos si no es un buen ajuste.

En particular, el enrutamiento sencillo facilita el mapeo de las URL heredadas a las nuevas
acciones de Rails.

3.1 Parmetros vinculados


Cuando configura una ruta regular, proporciona una serie de smbolos que Rails asigna a
partes de una solicitud HTTP entrante. Por ejemplo, considere esta ruta:

get 'photos(/:id)', to: :display

Si una solicitud entrante a /photos/1 es procesada por esta ruta (porque no ha coincidido
con ninguna ruta anterior en el archivo), entonces el resultado ser invocar la accin de
visualizacin del PhotosController y hacer que el parmetro final " 1 " este disponible
como params[:id] . Esta ruta tambin encaminar la solicitud entrante de /photos a
PhotosController#display , ya que el :id es un parmetro opcional, indicado por

parntesis.

3.2 Segmentos dinmicos


Puede configurar tantos segmentos dinmicos dentro de una ruta regular como desee.
Cualquier segmento estar disponible para la accin como parte de params . Si configura
esta ruta:

get 'photos/:id/:user_id', to: 'photos#show'

Una ruta entrante de /photos/1/2 ser enviada a la accin show del PhotosController .
params[: id] ser " 1 ", y params[:user_id] ser " 2 ".

418
3- Rutas sin resources

De forma predeterminada, los segmentos dinmicos no aceptan puntos, ya que el


punto se utiliza como separador para las rutas formateadas. Si necesita usar un punto
dentro de un segmento dinmico, aada una restriccin que reemplace esto: por
ejemplo, id: /[^\/]+/ permite cualquier cosa menos una barra.

3.3 Segmentos estticos


Puede especificar segmentos estticos al crear una ruta, no aadiendo dos puntos a un
fragmento:

get 'photos/:id/with_user/:user_id', to: 'photos#show'

Esta ruta respondera a rutas como /photos/1/with_user/2 . En este caso, params sera {
controller: 'photos', action: 'show', id: '1', user_id: '2' } .

3.4 Query string


Los parmetros tambin incluirn cualquier parmetro de la cadena de consulta. Por
ejemplo, con esta ruta:

get 'photos/:id', to: 'photos#show'

Una ruta entrante de /photos/1?user_id=2 ser enviada a la accin show del controlador
Photos . params ser { controller: 'photos', action: 'show', id: '1', user_id: '2' } .

3.5 Definicin de valores predeterminados


Puede definir valores predeterminados en una ruta suministrando un hash para la opcin
:defaults . Esto incluso se aplica a los parmetros que no especifica como segmentos

dinmicos. Por ejemplo:

get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }

Rails coincidira con las photos/12 con la accin show de PhotosController , y


establecera params[:format] a " jpg ".

Tambin puede utilizar valores predeterminados en un formato de bloque para definir los
valores predeterminados para varios elementos:

419
3- Rutas sin resources

defaults format: :json do


resources :photos
end

No puede anular los valores predeterminados a travs de parmetros de consulta, por


razones de seguridad. Los nicos valores por defecto que se pueden sobreescribir son
los segmentos dinmicos a travs de la sustitucin en la ruta de la URL.

3.6 Nombrar las rutas


Puede especificar un nombre para cualquier ruta utilizando la opcin :as :

get 'exit', to: 'sessions#destroy', as: :logout

Esto crear logout_path y logout_url como ayudantes nombrados en su aplicacin.


Llamando a logout_path retornar /exit

Tambin puede utilizar esta opcin para anular mtodos de enrutamiento definidos por
recursos, como estos:

get ':username', to: 'users#show', as: :user

Esto definir un mtodo user_path que estar disponible en los controladores, ayudantes y
vistas que irn a una ruta como /bob . Dentro de la accin show de UsersController ,
params[:username] contendr el nombre de usuario para el usuario. cambie :username en

la definicin de ruta si no desea que su nombre de parmetro sea :username .

3.7 Limitaciones del verbo HTTP


En general, debe usar los mtodos get , post , put , patch y delete para restringir una
ruta a un verbo en particular. Puede utilizar el mtodo de coincidencia con la opcin :via
para hacer coincidir varios verbos a la vez:

match 'photos', to: 'photos#show', via: [:get, :post]

Puede hacer coincidir todos los verbos con una ruta particular usando via: :all

match 'photos', to: 'photos#show', via: :all

420
3- Rutas sin resources

El enrutamiento de las solicitudes GET y POST a una sola accin tiene implicaciones
de seguridad. En general, debe evitar enrutar todos los verbos a una accin a menos
que tenga una buena razn para hacerlo.

' GET ' en Rails no comprobar el token CSRF. Nunca debe escribir en la base de datos
desde solicitudes 'GET', para obtener ms informacin, consulte la gua de seguridad
sobre contramedidas CSRF.

3.8 Limitaciones de segmentos


Puede utilizar la opcin :constraints para imponer un formato para un segmento
dinmico:

get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ }

Esta ruta coincidira con rutas como /photos/A12345, pero no /photos/893 . Puedes
expresar ms sucintamente la misma ruta de esta manera:

get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/

:constraints toma expresiones regulares con la restriccin de que los anchors con regexp

no se pueden usar. Por ejemplo, la siguiente ruta no funcionar:

get '/:id', to: 'articles#show', constraints: { id: /^\d/ }

Sin embargo, tenga en cuenta que no es necesario utilizar anchors porque todas las rutas
estn ancladas al principio.

Por ejemplo, las siguientes rutas permitiran que los artculos con valores to_param como
1-hello-world que siempre empiecen con un nmero y los usuarios con valores to_param

como david que nunca empiecen con un nmero para compartir el espacio de nombres raz:

get '/:id', to: 'articles#show', constraints: { id: /\d.+/ }


get '/:username', to: 'users#show'

3.9 Restricciones basadas en un request


Tambin puede restringir una ruta basada en cualquier mtodo en el objeto Request que
devuelve una cadena.

421
3- Rutas sin resources

get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }

Tambin puede especificar restricciones en un formulario de bloque:

namespace :admin do
constraints subdomain: 'admin' do
resources :photos
end
end

Las restricciones de solicitud funcionan llamando a un mtodo en el objeto Request


con el mismo nombre que la clave hash y luego compare el valor devuelto con el valor
hash. Por lo tanto, los valores de restriccin deben coincidir con el tipo de retorno del
mtodo de objeto Request correspondiente. Por ejemplo: constrains: {subdomain:
'api'} coincidir con un subdominio api como se esperaba, sin embargo, usando una

restriccin de smbolos: {subdomain: :api} no, porque request.subdomain devuelve


' api ' como String.

Hay una excepcin para la restriccin de formato: mientras que es un mtodo en el


objeto Request , tambin es un parmetro opcional implcito en cada ruta. Las
restricciones de segmento tienen prioridad y la restriccin de formato slo se aplica
como tal cuando se aplica a travs de un hash. Por ejemplo, get 'foo' , constraints:
{format: 'json'} coincidir con GET /foo porque el formato es opcional por defecto.

Sin embargo, puedes usar un lambda como en get 'foo' , las restricciones: lambda {|
req | Req.format ==: json} y la ruta slo coincidir con las solicitudes JSON explcitas.

3.10 Restricciones avanzadas


Si tiene una restriccin ms avanzada, puede proporcionar un objeto que responda a
matches? que Rails debe usar. Digamos que queras enrutar a todos los usuarios de una

lista negra al BlacklistController . Podras hacerlo:

422
3- Rutas sin resources

class BlacklistConstraint
def initialize
@ips = Blacklist.retrieve_ips
end

def matches?(request)
@ips.include?(request.remote_ip)
end
end

Rails.application.routes.draw do
get '*path', to: 'blacklist#index',
constraints: BlacklistConstraint.new
end

Tambin puede especificar restricciones como un lambda:

Rails.application.routes.draw do
get '*path', to: 'blacklist#index',
constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip)
}
end

Ambos metodos matches? y el lambda obtiene el objeto request como argumento.

3.11 Rutas globales y segmentos comodn (wildcards)


La globalizacin de las rutas es una forma de especificar que un parmetro en particular
debe coincidir con todas las partes restantes de una ruta. Por ejemplo:

get 'photos/*other', to: 'photos#unknown'

Esta ruta coincidira con las photos/12 o /photos/long/path/to/12 ,


configurando params[:other] a " 12 " o " long/path/to/12 ". Los fragmentos prefijados con
una estrella se llaman "segmentos comodn".

Los segmentos comodn pueden ocurrir en cualquier lugar de una ruta. Por ejemplo:

get 'books/*section/:title', to: 'books#show'

Coincidira con books/some/section/last-words-a-memoir con params[:section] es igual a


' some/section ', y params[:title] igual a ' last-words-a-memoir '.

423
3- Rutas sin resources

Tcnicamente, una ruta puede tener incluso ms de un segmento comodn. El asignador


asigna segmentos a los parmetros de una manera intuitiva. Por ejemplo:

get '*a/foo/*b', to: 'test#index'

Sera igual a zoo/woo/foo/bar/baz con params[:a] igual a ' zoo/woo ', y params[:b] es
igual a 'bar/baz '.

Al solicitar ' /foo/bar.json' , sus params[:pages] sern iguales a 'foo/bar ' con el formato
de solicitud de JSON . Si desea que el antiguo comportamiento 3.0.x, puede proporcionar el
formato :false como este:

get '*pages', to: 'pages#show', format: false

Si desea que el segmento de formato sea obligatorio, por lo que no se puede omitir, puede
proporcionar formato :true como este:

get '*pages', to: 'pages#show', format: true

3.12 Redireccionamiento
Puede redirigir cualquier ruta a otra ruta utilizando el ayudante de redireccin en su
enrutador:

get '/stories', to: redirect('/articles')

Tambin puede reutilizar segmentos dinmicos de la coincidencia en la ruta a redireccionar


a:

get '/stories/:name', to: redirect('/articles/%{name}')

Tambin puede proporcionar un bloque para redirigir, que recibe los parmetros de ruta
simbolizados y el objeto de la peticin:

get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params[:name


].pluralize}" }
get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }

424
3- Rutas sin resources

Tenga en cuenta que la redireccin predeterminada es un redirect 301 "Moved


Permanently". Tenga en cuenta que algunos navegadores web o servidores proxy
almacenarn en cach este tipo de redireccionamiento, haciendo que la pgina antigua sea
inaccesible. Puede usar la opcin :status para cambiar el estado de la respuesta:

get '/stories/:name', to: redirect('/articles/%{name}', status: 302)

En todos estos casos, si no proporciona el host principal ( http://www.example.com ), Rails


tomar esos detalles de la solicitud actual.

3.13 Enrutamiento a aplicaciones Rack


En lugar de una cadena como ' articles#index ', que corresponde a la accin de ndice en
el ArticlesController , puede especificar cualquier aplicacin Rack como punto final para
un matcher:

match '/application.js', to: MyRackApp, via: :all

Mientras MyRackApp responda a la llamada y devuelva un [status, headers, body], el


enrutador no sabr la diferencia entre la aplicacin Rack y una accin. Este es un uso
apropiado de via: :all , ya que usted querr permitir que su aplicacin Rack maneje todos
los verbos que considere apropiados.

Para los curiosos, ' articles#index ' se expande a ArticlesController.action(:index) ,


que devuelve una aplicacin de Rack vlida.

Si especifica una aplicacin Rack como punto final para un matcher, recuerde que la ruta no
cambiar en la aplicacin receptora. Con la siguiente ruta, la aplicacin de Rack debe
esperar que la ruta sea ' /admin ':

match '/admin', to: AdminApp, via: :all

Si prefiere que su aplicacin Rack reciba peticiones en la ruta raz, utilice mount :

mount AdminApp, at: '/admin'

3.14 Uso de root


Puede especificar Rails qu debe enrutar en ' / ' con el mtodo root :

425
3- Rutas sin resources

root to: 'pages#main'


root 'pages#main' # shortcut for the above

Debe poner la ruta raz en la parte superior del archivo, ya que es la ruta ms popular y
debe coincidir primero.

La ruta raz solo encamina solicitudes GET a la accin.

Tambin puede utilizar la ruta raz dentro de espacios de nombres y los mbitos. Por
ejemplo:

namespace :admin do
root to: "admin#index"
end

root to: "home#index"

3.15 Rutas de caracteres Unicode


Puede especificar rutas de carcter unicode directamente. Por ejemplo:

get '', to: 'welcome#index'

426
4- Personalizacin de rutas de recursos

4- Personalizacin de rutas de recursos


Mientras que las rutas predeterminadas y los ayudantes generados por resources:
:articles suelen servirle bien, es posible que desee personalizarlos de alguna manera.

Rails le permite personalizar prcticamente cualquier parte genrica de los helpers de


recursos.

4.1 Especificacin de un controlador a utilizar


La opcin :controller le permite especificar explcitamente un controlador para utilizarlo
en el recurso. Por ejemplo:

resources :photos, controller: 'images'

Reconocern los paths entrantes que empiezan con /photos , pero la ruta al controlador de
imgenes:

HTTP Verb Path Controller#Action Named Helper


GET /photos images#index photos_path
GET /photos/new images#new new_photo_path
POST /photos images#create photos_path
GET /photos/:id images#show photo_path(:id)
GET /photos/:id/edit images#edit edit_photo_path(:id)

PATCH/PUT /photos/:id images#update photo_path(:id)


DELETE /photos/:id images#destroy photo_path(:id)

Utilice photos_path , new_photo_path , etc. para generar rutas de acceso para este
recurso.

Para los controladores con espacios de nombres, puede utilizar la notacin de directorio.
Por ejemplo:

resources :user_permissions, controller: 'admin/user_permissions'

Esto har que se dirija al controlador Admin::UserPermissions .

427
4- Personalizacin de rutas de recursos

Slo se admite la notacin de directorios. Especificar el controlador con la notacin de


Constantes de Ruby (por ejemplo, controller: 'Admin::UserPermissions' ) puede
conducir a problemas de enrutamiento y los resultados en una advertencia.

4.2 Especificacin de restricciones


Puede utilizar la opcin :constraints para especificar un formato requerido en el id
implcito. Por ejemplo:

resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }

Esta declaracin limita el parmetro :id para que coincida con la expresin regular
suministrada. Por lo tanto, en este caso, el enrutador ya no coincidira con esta ruta
/photos/1 . En su lugar coincidira con, /photos/RR27 .

Puede especificar una restriccin nica para aplicar a un nmero de rutas utilizando el
formulario de bloque:

constraints(id: /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end

Por supuesto, puede utilizar las limitaciones ms avanzadas disponibles en rutas sin-
resources en este contexto.

De forma predeterminada, el parmetro id no acepta puntos, ya que el punto se


utiliza como separador para las rutas formateadas. Si necesitas usar un punto dentro
de un :id agrega una restriccin que anula esto - por ejemplo id: /[^\/]+/ permite
cualquier cosa menos una barra.

4.3 Sobre-escritura de los helpers nombrados


La opcin :as le permite anular la denominacin normal para los ayudantes de ruta
designados. Por ejemplo:

resources :photos, as: 'images'

Reconocern las rutas entrantes que empiezan con /photos y enrutarn las solicitudes a
PhotosController , pero usarn el valor de la opcin :as para nombrar a los helpers.

428
4- Personalizacin de rutas de recursos

HTTP Verb Path Controller#Action Named Helper

GET /photos photos#index images_path


GET /photos/new photos#new new_image_path
POST /photos photos#create images_path

GET /photos/:id photos#show image_path(:id)


GET /photos/:id/edit photos#edit edit_image_path(:id)
PATCH/PUT /photos/:id photos#update image_path(:id)
DELETE /photos/:id photos#destroy image_path(:id)

4.4 Sobre-escritura de los segmentos new y de edit


La opcin :path_names le permite sobre-escribir los segmentos new y edit
automticamente en las rutas:

resources :photos, path_names: { new: 'make', edit: 'change' }

Esto hara que el enrutamiento reconozca los paths como:

/photos/make
/photos/1/change

Esta opcin no cambia los nombres de los action reales. Los dos paths mostrados
seguiran la ruta hacia las acciones new y de edit .

Si desea cambiar esta opcin de manera uniforme para todas sus rutas, puede utilizar
un scope.

scope path_names: { new: 'make' } do


# rest of your routes
end

4.5 Prefijo de los ayudantes de ruta designados


Puede utilizar la opcin :as para prefijar los ayudantes de ruta designados que Rails
genera para una ruta. Utilice esta opcin para evitar colisiones de nombres entre rutas
utilizando un scope de ruta. Por ejemplo:

429
4- Personalizacin de rutas de recursos

scope 'admin' do
resources :photos, as: 'admin_photos'
end

resources :photos

Esto proporcionar ayudantes de ruta tales como admin_photos_path ,


new_admin_photo_path , etc.

Para prefijar un grupo de ayudantes de ruta, utilice :as con scope :

scope 'admin', as: 'admin' do


resources :photos, :accounts
end

resources :photos, :accounts

Esto generar rutas como admin_photos_path y admin_accounts_path que se asignan a


/admin/photos y /admin/accounts respectivamente.

El scope de namespaces agregar automticamente :as, as como los prefijos


:module y :path

Tambin puede prefijar rutas con un parmetro con nombre:

scope ':username' do
resources :articles
end

Esto le proporcionar URLs como /bob/articles/1 y le permitir hacer referencia a la parte


del nombre de usuario de la ruta de acceso como params[: username] en los controladores,
ayudantes y vistas.

4.6 Restriccin de las rutas creadas


De forma predeterminada, Rails crea rutas para las siete acciones predeterminadas
( index , show , new , create , edit , update y destroy ) para cada ruta RESTful en su
aplicacin. Puede utilizar las opciones :only y :except para ajustar este comportamiento.
La opcin :only le dice a Rails que cree slo las rutas especificadas:

resources :photos, only: [:index, :show]

430
4- Personalizacin de rutas de recursos

Ahora, una solicitud GET /photos tendra xito, pero una solicitud POST /photos (que
normalmente se encaminan a la accin create ) fallar.

La opcin :except especifica una ruta o lista de rutas que Rails no debe crear:

resources :photos, except: :destroy

En este caso, Rails crear todas las rutas normales excepto la ruta para destruir (una
solicitud DELETE a /photos/:id ).

Si su aplicacin tiene muchas rutas RESTful, use :only y :except para generar slo las
rutas que realmente necesita puede reducir el uso de memoria y acelerar el proceso de
enrutamiento.

4.7 Paths traducidos


Utilizando el scope, podemos alterar los nombres de ruta generados por los recursos:

scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do


resources :categories, path: 'kategorien'
end

Rails ahora crea rutas a CategoriesController .

HTTP Verb Path Controller#Action Named Helper


GET /kategorien categories#index categories_path

GET /kategorien/neu categories#new new_category_path


POST /kategorien categories#create categories_path

GET /kategorien/:id categories#show category_path(:id)


GET /kategorien/:id/bearbeiten categories#edit edit_category_path(:id)

PATCH/PUT /kategorien/:id categories#update category_path(:id)


DELETE /kategorien/:id categories#destroy category_path(:id)

4.8 Sobre-escribir la forma singular


Si desea definir la forma singular de un recurso, debe agregar reglas adicionales al Inflector:

ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'tooth', 'teeth'
end

431
4- Personalizacin de rutas de recursos

4.9 Uso de :as en recursos anidados


La opcin :as reemplaza al nombre generado automticamente para el recurso en
ayudantes de rutas anidadas. Por ejemplo:

resources :magazines do
resources :ads, as: 'periodical_ads'
end

Esto crear ayudantes de enrutamiento como magazine_periodical_ads_url y


edit_magazine_periodical_ad_path .

4.10 Sobre-escritura de los parmetros de ruta con nombre


La opcin :param reemplaza al identificador de recurso predeterminado :id (nombre del
segmento dinmico utilizado para generar las rutas). Puede acceder a ese segmento desde
su controlador usando params[<: param>] .

resources :videos, param: :identifier

videos GET /videos(.:format) videos#index


POST /videos(.:format) videos#create
new_videos GET /videos/new(.:format) videos#new
edit_videos GET /videos/:identifier/edit(.:format) videos#edit

Video.find_by(identifier: params[:identifier])

Puede reemplazar ActiveRecord::Base#to_param de un modelo relacionado para construir


una URL:

class Video < ApplicationRecord


def to_param
identifier
end
end

video = Video.find_by(identifier: "Roman-Holiday")


edit_videos_path(video) # => "/videos/Roman-Holiday"

432
5- Inspeccin y pruebas de las rutas

5- Inspeccin y pruebas de las rutas


Rails ofrece facilidades para inspeccionar y probar sus rutas.

5.1 Listado de rutas existentes


Para obtener una lista completa de las rutas disponibles en su aplicacin, visite
http://localhost:3000/rails/info/routes en su navegador mientras su servidor se est

ejecutando en el entorno de desarrollo. Tambin puede ejecutar el comando rails routes


en su terminal para producir la misma salida.

Ambos mtodos enumerarn todas sus rutas, en el mismo orden en que aparecen en
config/routes.rb . Para cada ruta, ver:

El nombre de la ruta (si existe)


El verbo HTTP utilizado (si la ruta no responde a todos los verbos)
El patrn de URL que coincide
Los parmetros de enrutamiento para la ruta

Por ejemplo, aqu hay una pequea seccin de las rutas de Rails de salida para una ruta
RESTful :

users GET /users(.:format) users#index


POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit

Puede buscar en sus rutas con la opcin grep: -g . Esto genera cualquier ruta que coincida
parcialmente con el nombre del mtodo de ayuda de URL, el verbo HTTP o la ruta de
acceso de URL.

$ bin/rails routes -g new_comment


$ bin/rails routes -g POST
$ bin/rails routes -g admin

Si slo desea ver las rutas que se asignan a un controlador especfico, existe la opcin -c .

$ bin/rails routes -c users


$ bin/rails routes -c admin/users
$ bin/rails routes -c Comments
$ bin/rails routes -c Articles::CommentsController

433
5- Inspeccin y pruebas de las rutas

Usted encontrar que la salida de las rutas de Rails son mucho ms legibles si usted
ensancha su ventana terminal hasta que las lneas de la salida no envuelvan.

5.2 Pruebas de rutas


Las rutas deben incluirse en su estrategia de testing (al igual que el resto de su aplicacin).
Rails ofrece tres aserciones integradas diseadas para simplificar las pruebas de las rutas:

assert_generates
assert_recognizes
assert_routing

5.2.1 La asercin assert_generates


Assert_generates afirma que un determinado conjunto de opciones genera una ruta

determinada y puede utilizarse con rutas predeterminadas o rutas personalizadas. Por


ejemplo:

assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }


assert_generates '/about', controller: 'pages', action: 'about'

5.2.2 La asercin assert_recognizes


Assert_recognizes es la inversa de assert_generates . Afirma que un camino determinado

es reconocido y lo encamina a un lugar particular de su aplicacin. Por ejemplo:

assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')

Puede proporcionar un argumento :method para especificar el verbo HTTP :

assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method:


:post })

5.2.3 Asercin assert_routing


La asercin assert_routing comprueba la ruta en ambos sentidos: prueba que la ruta
genera las opciones y que las opciones generan la ruta. As, combina las funciones de
assert_generates y assert_recognizes :

434
5- Inspeccin y pruebas de las rutas

assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'cre


ate' })

435
XIII - Active Support Extensiones Core

XIII - Active Support Extensiones Core


Active Support es el componente de Ruby on Rails responsable de proporcionar
extensiones del lenguaje Ruby, utilidades y otras cosas transversales.

Ofrece una lnea de fondo ms rica a nivel de idioma, dirigida tanto al desarrollo de
aplicaciones Rails, como al desarrollo de Ruby on Rails.

Despus de leer esta gua, sabr:

Qu son las extensiones core.


Cmo cargar todas las extensiones.
Cmo elegir slo las extensiones que desea.
Qu extensiones proporciona de Active Support.

436
1- Cmo cargar las extensiones core

1- Cmo cargar las extensiones core


1.1 Active Support autnomo
Active Support no carga nada de forma predeterminada. Se divide en pedazos pequeos de
modo que usted pueda cargar lo que necesita, y tambin tiene algunos entry points de
conveniencia para cargar las extensiones relacionadas en un solo shot, incluso todo.

Lo hacemos, despus de un simple require como:

require 'active_support'

sus objetos no responden a blank? . Veamos cmo cargar su definicin.

1.1.1 Seleccin de lo que desea en una sola definicin


La manera ms ligera de obtener blank? Es escoger el archivo que lo define.

Para cada mtodo definido como una extensin del ncleo (core), esta gua tiene una nota
que indica dnde se define dicho mtodo. En el caso de blank? La nota dice:

Se define en active_support/core_ext/object/blank.rb.

Eso significa que puedes requerirlo as:

require 'active_support'
require 'active_support/core_ext/object/blank'

Active Support ha sido cuidadosamente revisado para que la seleccin unitaria de un


archivo, cargue slo estrictamente lo necesario y las dependencias, si las hay.

1.1.2 Carga de extensiones de ncleo agrupadas


El siguiente paso es simplemente cargar todas las extensiones del Object. Como regla
general, las extensiones de SomeClass estn disponibles en una linea cargando:
active_support/core_ext/some_class

Por lo tanto, para cargar todas las extensiones de objeto (incluyendo blank? ):

require 'active_support'
require 'active_support/core_ext/object'

437
1- Cmo cargar las extensiones core

1.1.3 Cargando todas las extensiones de ncleo


Es posible que prefiera cargar todas las extensiones principales, hay un archivo para eso:

require 'active_support'
require 'active_support/core_ext'

1.1.4 Cargando todo el active support


Y, por ltimo, si quieres tener disponible todo el active support disponible:

require 'active_support/all'

Que ni siquiera es necesario poner todo el active support en la memoria por adelantado, de
hecho algunas cosas se configuran a travs de la carga automtica, por lo que slo se
carga si se utiliza.

1.2 Active Support dentro de una aplicacin Ruby on Rails


Una aplicacin de Ruby on Rails carga todo el Soporte activo a menos que
config.active_support.bare sea verdadero. En ese caso, la aplicacin slo cargar lo que

el propio framework selecciona para sus propias necesidades, y an puede seleccionarse a


s misma en cualquier nivel de granularidad, como se explic en la seccin anterior.

438
2- Extensiones a todos los objetos

2- Extensiones a todos los objetos


2.1 blank? Y present?
Los valores siguientes se consideran en blanco en una aplicacin de Rails:

nil y false ,

Strings compuestos slo de espacios en blanco (ver nota ms abajo),


Arrays h hashes vacos
Cualquier otro objeto que responde a empty? Y est vaco.

El predicado de las cadenas usa los caracteres Unicode-aware para la


class[:space:] , por lo que por ejemplo U+2029 (separador de prrafos) se considera

como espacio en blanco.

Tenga en cuenta que los nmeros no se mencionan. En particular, 0 y 0,0 no estn en


blanco.

Por ejemplo, este mtodo de


ActionController::HttpAuthentication::Token::ControllerMethods utiliza blank? Para

comprobar si un token est presente:

def authenticate(controller, &login_procedure)


token, options = token_and_options(controller.request)
unless token.blank?
login_procedure.call(token, options)
end
end

El mtodo present? Es equivalente a !blank? . Este ejemplo se toma de


ActionDispatch::Http::Cache::Response :

def set_conditional_cache_control!
return if self["Cache-Control"].present?
...
end

Se define en active_support/core_ext/object/blank.rb .

2.2 presence

439
2- Extensiones a todos los objetos

El mtodo de presence devuelve su receptor si est present? , y nil en caso contrario.


Es til para expresiones como esta:

host = config[:host].presence || 'localhost'

Se define en active_support/core_ext/object/blank.rb .

2.3 duplicable?
En Ruby 2.4 la mayora de los objetos pueden ser duplicados va dup o clone excepto los
mtodos y ciertos nmeros. Aunque Ruby 2.2 y 2.3 no pueden duplicar nil , false ,
true y smbolos, as como instancias de Float , Fixnum y Bignum .

"foo".dup # => "foo"


"".dup # => ""
1.method(:+).dup # => TypeError: allocator undefined for Method
Complex(0).dup # => TypeError: can't copy Complex

Active Support proporciona duplicable? para consultar un objeto acerca de esto:

"foo".duplicable? # => true


"".duplicable? # => true
Rational(1).duplicable? # => false
Complex(1).duplicable? # => false
1.method(:+).duplicable? # => false

duplicable coincide con el dup de Ruby segn la versin de Ruby.

As que en 2.4:

nil.dup # => nil


:my_symbol.dup # => :my_symbol
1.dup # => 1

nil.duplicable? # => true


:my_symbol.duplicable? # => true
1.duplicable? # => true

Considerando que en 2.2 y 2.3:

440
2- Extensiones a todos los objetos

nil.dup # => TypeError: can't dup NilClass


:my_symbol.dup # => TypeError: can't dup Symbol
1.dup # => TypeError: can't dup Fixnum

nil.duplicable? # => false


:my_symbol.duplicable? # => false
1.duplicable? # => false

Cualquier clase puede desautorizar la duplicacin al eliminar dup y clon o levantar


excepciones de ellos. As, slo el rescue puede decir si un objeto arbitrario dado es
duplicable. duplicable Depende de la lista codificada anteriormente, pero es mucho
ms rpido que el rescue . selo slo si sabe que la lista codificada es suficiente en
su caso de uso.

2.4 deep_dup
El mtodo deep_dup devuelve una copia profunda de un objeto dado. Normalmente,
cuando se hace dup a un objeto que contiene otros objetos, Ruby no los duplica, por lo
que crea una copia poco profunda del objeto. Si tiene un array con una cadena, por
ejemplo, se ver as:

array = ['string']
duplicate = array.dup

duplicate.push 'another-string'

# the object was duplicated, so the element was added only to the duplicate
array # => ['string']
duplicate # => ['string', 'another-string']

duplicate.first.gsub!('string', 'foo')

# first element was not duplicated, it will be changed in both arrays


array # => ['foo']
duplicate # => ['foo', 'another-string']

Como se puede ver, despus de duplicar la instancia Array, tenemos otro objeto, por lo
tanto podemos modificarlo y el objeto original permanecer sin cambios. Sin embargo, esto
no es cierto para los elementos del array. Puesto que el dup no hace la copia profunda, la
secuencia dentro del arsenal sigue siendo el mismo objeto.

Si necesita una copia profunda de un objeto, debe utilizar deep_dup . Aqu hay un ejemplo:

441
2- Extensiones a todos los objetos

array = ['string']
duplicate = array.deep_dup

duplicate.first.gsub!('string', 'foo')

array # => ['string']


duplicate # => ['foo']

Si el objeto no es duplicable, deep_dup slo lo devolver:

number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id # => true

2.5 try
Cuando desea llamar a un mtodo en un objeto slo si no es nil , la forma ms sencilla de
lograrlo es con sentencias condicionales, aadiendo desorden innecesario. La alternativa es
usar try . try es como Object#send excepto que devuelve nil si se enva nil .

Aqu hay un ejemplo:

# without try
unless @number.nil?
@number.next
end

# with try
@number.try(:next)

Otro ejemplo es este cdigo de ActiveRecord::ConnectionAdapters::AbstractAdapter donde


@logger podra ser nil. Puede ver que el cdigo utiliza try y evita una comprobacin

innecesaria.

def log_info(sql, name, ms)


if @logger.try(:debug?)
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end

PARAMOS EN ESTA PARTE DE ACTIVE SUPPORT POR LOS SIGUIENTES ASPECTOS:

Esto es temario mas avanzado, donde voy a extender ciertas caracteristicas de rails
A corto plazo necesitamos mas informacion sobre otros temas avanzados pero mas

442
2- Extensiones a todos los objetos

practicos, como por ejemplo Mailers, Background Jobs o Testing


Mas adelante retomaremos la traduccion de estas Core Extensions

443
XIV- API de Internacionalizacin de Rails (I18n)

XIV - API de Internacionalizacin de Rails


(I18n)
La gema Ruby I18n (abreviatura de internacionalizacin) que se entrega con Ruby on Rails
(a partir de Rails 2.2) proporciona un framework fcil de usar y extensible para traducir su
aplicacin a un nico idioma personalizado distinto al ingls o para proporcionar idiomas
multilinges de soporte en su aplicacin.

El proceso de "internacionalizacin" generalmente significa abstraer todas las cadenas y


otros bits especficos de la configuracin regional (como los formatos de fecha o moneda)
de su aplicacin. El proceso de "localizacin" significa proporcionar traducciones y formatos
localizados para estos bits.

Por lo tanto, en el proceso de internacionalizacin de su aplicacin de Rails usted tiene que:

Asegrese de que tiene soporte para i18n.


Decirle a Rails dnde encontrar los diccionarios locale .
Decirle a Rails cmo configurar, conservar y cambiar los entornos locales.

En el proceso de localizacin de su aplicacin probablemente desear hacer las siguientes


tres cosas:

Reemplazar o complementar la configuracin regional predeterminada de Rails, como


por ejemplo los formatos de fecha y hora, los nombres de mes, los nombres de
modelos de Active Record, etc.
Las cadenas abstractas en su aplicacin dentro de diccionarios con clave , por ejemplo
los mensajes flash, texto esttico en sus vistas, etc.
Guardar los diccionarios resultantes en alguna parte.

Esta gua lo guiar a travs de la API I18n y contiene un tutorial sobre cmo
internacionalizar una aplicacin de Rails desde el principio.

Despus de leer esta gua, sabr:

Cmo funciona I18n en Ruby on Rails


Cmo utilizar correctamente I18n en una aplicacin RESTful de varias maneras
Cmo utilizar I18n para traducir los errores de Active Record o de Action Mailer en los
subjects de correo electrnico
Algunas otras herramientas para ir ms all con el proceso de traduccin de su
aplicacin

444
XIV- API de Internacionalizacin de Rails (I18n)

El framework Ruby I18n le proporciona todos los medios necesarios para la


internacionalizacin/localizacin de su aplicacin de Rails. Tambin puedes usar varias
gemas disponibles para agregar funciones o funcionalidades adicionales. Vea la gema
de rails-i18n para ms informacin.

445
1- Cmo funciona I18n en Ruby on Rails

1- Cmo funciona I18n en Ruby on Rails


La internacionalizacin es un problema complejo. Los lenguajes naturales difieren de
muchas maneras (por ejemplo, en las reglas de pluralizacin) que es difcil proporcionar
herramientas para resolver todos los problemas a la vez. Por ello, la API de I18n se centra
en:

Proporcionar soporte para idiomas ingleses y similares "out of the box".


Por lo que es fcil de personalizar y ampliar todo para otros idiomas

Como parte de esta solucin, cada cadena esttica en el framework de Rails, por
ejemplo los mensajes de validacin de Active Record, y los formatos de fecha y hora, se
han internacionalizado . La localizacin (localization) de una aplicacin de Rails significa

definir valores traducidos para estas cadenas en los idiomas deseados.

1.1 La Arquitectura General de la Biblioteca


Por lo tanto, la gema Ruby I18n se divide en dos partes:

La API pblica del framework i18n - un mdulo Ruby con mtodos pblicos que definen
cmo funciona la biblioteca
Un backend predeterminado (que se nombra intencionalmente Backend simple) que
implementa estos mtodos

Como usuario siempre debes acceder solo a los mtodos pblicos en el mdulo I18n, pero
es til conocer las capacidades del backend.

Es posible intercambiar el backend simple enviado con un ms potente, que


almacenara datos de la traduccin en una base de datos relacional, un diccionario de
GetText, o similar. Consulte la seccin Utilizacin de diferentes backends en la seccin
6.1

1.2 La API pblica I18n


Los mtodos ms importantes de la API I18n son:

translate # Buscar traducciones de texto


localize # Localiza objetos de fecha y hora en formatos locales

Estos tienen los alias #t y #l para que pueda utilizarlos de esta manera:

446
1- Cmo funciona I18n en Ruby on Rails

I18n.t 'store.title'
I18n.l Time.now

Tambin hay lectores y escritores de atributos para los siguientes atributos:

load_path # Anuncie sus archivos de traduccin personalizados


locale # Obtener y establecer el locale actual
default_locale # Obtener y establecer la configuracin regional predetermin
ada
available_locales # Posibilidades de listas blancas disponibles para la aplica
cin
enforce_available_locales # Aplicar la lista blanca de configuracin regional (true o
false)
exception_handler # Utilizar un manejador de excepcin diferente
backend # Utilizar un backend diferente

As que internacionalizaremos una sencilla aplicacin de Rails desde el principio en los


prximos captulos.

447
2- Configuracin de una aplicacin de Rails para la internacionalizacin

2- Configuracin de una aplicacin de


Rails para la internacionalizacin
Hay algunos pasos para iniciar y ejecutar con el soporte para I18n para una aplicacin de
Rails.

2.1 Configurar el mdulo I18n


Siguiendo la filosofa de convencin sobre configuracin, Rails I18n proporciona cadenas de
traduccin por defecto que son razonables. Cuando se necesitan cadenas de traduccin
diferentes, pueden anularse.

Rails aade todos los archivos .rb y .yml desde el directorio config/locales a la ruta
de carga de las traducciones, automticamente.

La configuracin regional predeterminada de en.yml en este directorio que contiene un par


de cadenas de traduccin:

en:
hello: "Hello world"

Esto significa, que en el locale :en , la clave hello se asignar a la cadena Hello world.
Cada cadena dentro de Rails se internacionaliza de esta forma, consulte, por ejemplo, los
mensajes de validacin de Active Model en el archivo
activemodel/lib/active_model/locale/en.yml o los formatos de hora y fecha en el archivo
activesupport/lib/active_support/locale/en.yml . Puede usar YAML o el Ruby Hashes
estndar para almacenar las traducciones en el backend predeterminado (Simple).

La biblioteca I18n utilizar el ingls como configuracin regional predeterminada, es decir, si


no se ha establecido una configuracin regional diferente, :es se utilizar para buscar
traducciones.

448
2- Configuracin de una aplicacin de Rails para la internacionalizacin

La biblioteca i18n adopta un enfoque pragmtico de las claves locales (despus de un


debate), incluyendo slo la parte local ("idioma"), como :en , :pl , no la parte de la
regin, como :en-US o :en-GB , Que tradicionalmente se utilizan para separar
"lenguas" y "configuracin regional" o "dialectos". Muchas aplicaciones internacionales
usan slo el elemento "idioma" de una localidad como :cs , :th o :es (para checo,
tailands y espaol). Sin embargo, tambin hay diferencias regiones dentro de
diferentes grupos lingsticos que pueden ser importantes. Por ejemplo, en el
escenario :en-US tendra $ como smbolo de moneda, mientras que en :es-GB ,
tendra . Nada le impide separar la configuracin regional y de otra manera de esta
manera: slo tiene que proporcionar la configuracin completa "English - United
Kingdom" en el diccionario :en-GB . Algunas gemas como Globalize3 pueden ayudar a
implementarlo.

La ruta de carga de las traducciones ( I18n.load_path ) es una matriz de rutas de acceso a


los archivos que se cargarn automticamente. La configuracin de esta ruta permite la
personalizacin de la estructura de directorios de traducciones y el esquema de
nomenclatura de archivos.

El backend tiene una carga perezosa (lazy-load) de estas traducciones cuando se


busca una traduccin por primera vez. Este backend puede intercambiarse con otra
cosa incluso despus de que las traducciones ya se han anunciado.

Puede cambiar la configuracin regional predeterminada, as como configurar las rutas de


carga de las traducciones en config/application.rb de la siguiente manera:

config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]


config.i18n.default_locale = :de

Se debe especificar la ruta de carga antes de buscar las traducciones. Para cambiar la
configuracin regional predeterminada de un inicializador, se hace en
config/application.rb :

# config/initializers/locale.rb

# Where the I18n library should search for translation files


I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]

# Whitelist locales available for the application


I18n.available_locales = [:en, :pt]

# Set default locale to something other than :en


I18n.default_locale = :pt

449
2- Configuracin de una aplicacin de Rails para la internacionalizacin

2.2 Gestin de la configuracin regional a travs de las


solicitudes (request)
La configuracin regional predeterminada se utiliza para todas las traducciones a menos
que en el I18n.locale se establezca explcitamente.

Es probable que una aplicacin localizada necesite proporcionar soporte para varios
entornos locales. Para lograr esto, la configuracin regional se debe establecer al principio
de cada solicitud para que todas las cadenas se conviertan utilizando la configuracin
regional deseada durante la duracin de dicha solicitud.

La configuracin regional se puede establecer en un before_action en el


ApplicationController :

before_action :set_locale

def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end

Este ejemplo ilustra esto utilizando un parmetro de consulta URL para establecer el
entorno local (por ejemplo, http://example.com/books?locale=pt ). Con este enfoque,
http://localhost:3000?locale=pt renderiza la localizacin en portugus, mientras que

http://localhost:3000?locale=pde carga una localizacin alemana.

La configuracin regional se puede establecer utilizando uno de los diferentes enfoques.

2.2.1 Configuracin regional del nombre de dominio


Una opcin que tiene es establecer la configuracin regional desde el nombre de dominio
donde se ejecuta la aplicacin. Por ejemplo, queremos que www.example.com cargue el
idioma ingls (o predeterminado) y www.example.es para cargar el idioma espaol. Por lo
tanto, el nombre de dominio de nivel superior se utiliza para la configuracin regional. Esto
tiene varias ventajas:

La configuracin regional es una parte obvia de la URL.


La gente entiende intuitivamente en qu idioma se mostrar el contenido.
Es muy trivial implementar en Rails.
Los motores de bsqueda entendern que el contenido en diferentes idiomas vive en
diferentes dominios interrelacionados.

Puede implementarlo as en su ApplicationController :

450
2- Configuracin de una aplicacin de Rails para la internacionalizacin

before_action :set_locale

def set_locale
I18n.locale = extract_locale_from_tld || I18n.default_locale
end

# Get locale from top-level domain or return +nil+ if such locale is not available
# You have to put something like:
# 127.0.0.1 application.com
# 127.0.0.1 application.it
# 127.0.0.1 application.pl
# in your /etc/hosts file to try this out locally
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end

Tambin podemos establecer la configuracin regional desde el subdominio de una manera


muy similar:

# Get locale code from request subdomain (like http://it.application.local:3000)


# You have to put something like:
# 127.0.0.1 gr.application.local
# in your /etc/hosts file to try this out locally
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
end

Si su aplicacin incluye un men para el cambio del locale , entonces tendra que hacer
algo como esto en el:

link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['PATH_INFO']}")

Asumiendo que usted fijara APP_CONFIG[:deutsch_website_url] a un cierto valor como


http://www.application.de .

Esta solucin tiene las ventajas mencionadas, sin embargo, es posible que no pueda o no
desee proporcionar diferentes localizaciones ("versiones de idioma") en diferentes dominios.
La solucin ms obvia sera incluir el cdigo de configuracin regional en los parmetros de
URL (o ruta de peticin).

2.2.2 Configuracin regional de los parmetros de URL

451
2- Configuracin de una aplicacin de Rails para la internacionalizacin

La forma ms habitual de establecer (y pasar) el entorno local sera incluirlo en parmetros


de URL, como lo hicimos en I18n.locale = params[:locale] before_action en el primer
ejemplo. Nos gustara tener URLs como www.example.com/books?locale=ja o
www.example.com/ja/books en este caso.

Este enfoque tiene casi el mismo conjunto de ventajas que el establecimiento de la


configuracin regional del nombre de dominio: a saber, que es RESTful y de acuerdo con el
resto de la World Wide Web. Sin embargo, requiere un poco ms de trabajo para
implementar.

Obtener el locale de los parmetros y configurarlo en consecuencia no es difcil;


Incluyendo en cada URL y por lo tanto pasarlo a travs de las solicitudes. Para incluir una
opcin explcita en cada URL , por ejemplo link_to(books_url(locale: I18n.locale)) , sera
tedioso y probablemente imposible, por supuesto.

Rails contiene una infraestructura para "centralizar decisiones dinmicas sobre las URLs"
en su ApplicationController#default_url_options , lo cual es til precisamente en este
escenario: nos permite establecer "defaults" para url_for y los mtodos auxiliares
dependientes de l (mediante la implementacin o sobre-escritura de
default_url_options ).

Podemos incluir algo como esto en nuestro ApplicationController entonces:

# app/controllers/application_controller.rb
def default_url_options
{ locale: I18n.locale }
end

Cada mtodo helper dependiente de url_for (por ejemplo, ayudantes de rutas de nombre,
como root_path o root_url , rutas de resources como books_path o books_url , etc.)
ahora incluirn automticamente la configuracin regional en la cadena de consulta, de la
siguiente manera: http://localhost:3001?locale=ja .

Usted puede estar satisfecho con esto. Sin embargo, afecta a la legibilidad de las URL
cuando la configuracin regional se "pone" al final de cada URL de la aplicacin. Por otra
parte, desde el punto de vista arquitectnico, los locales suelen estar jerrquicamente por
encima de las otras partes del dominio de la aplicacin y las URL deben reflejar esto.

Es probable que desee que las URL se vean as: http://www.example.com/en/books (que
carga la configuracin regional en ingls) y http://www.example.com/nl/books (que carga la
configuracin regional holandesa) . Esto es posible con la estrategia de "over-riding
default_url_options " desde arriba: solo tienes que configurar tus rutas con el scope de

aplicacin:

452
2- Configuracin de una aplicacin de Rails para la internacionalizacin

# config/routes.rb
scope "/:locale" do
resources :books
end

Ahora, cuando llame al mtodo books_path , debe obtener " /en/books " (para la
configuracin regional predeterminada). Una URL como http://localhost:3001/nl/books
debe cargar la configuracin regional holandesa, y las siguientes llamadas a books_path
deben devolver " /nl/books " (porque cambi la configuracin regional).

Dado que el valor devuelto de default_url_options se almacena en cach por


solicitud, no se pueden generar URLs en un selector de localizacin invocando
ayudantes en un bucle que se establezca el I18n.locale correspondiente en cada
iteracin. En su lugar, deje el I18n.locale sin tocar y pase una opcin explcita
:locale al ayudante o modifique request.original_fullpath .

Si no desea forzar el uso de una configuracin regional en sus rutas, puede utilizar un
mbito de ruta opcional (indicado por los parntesis) de la siguiente manera:

# config/routes.rb
scope "(:locale)", locale: /en|nl/ do
resources :books
end

Con este enfoque no obtendr un error de enrutamiento cuando acceda a sus recursos
como http://localhost:3001/books sin una configuracin regional. Esto es til si desea
utilizar la configuracin regional predeterminada cuando no se especifica.

Por supuesto, debe tener especial cuidado con la URL raz (generalmente "homepage" o
"dashboar") de su aplicacin. Una URL como http://localhost:3001/nl no funcionar
automticamente, porque la raz: " books#index " en la declaracin de routes.rb no toma el
locale en cuenta. (Y con razn: slo hay una URL " root ").

Probablemente necesitar asignar URL como estas:

# config/routes.rb
get '/:locale' => 'dashboard#index'

Tenga especial cuidado con el orden de sus rutas, por lo que esta declaracin de ruta no
debe "comer" a los otros. (Es posible que desee aadirlo directamente antes de la
reclaracin a root )

453
2- Configuracin de una aplicacin de Rails para la internacionalizacin

Echa un vistazo a varias gemas que simplifican el trabajo con rutas: routing_filter, rails-
translate-routes, route_translator.

2.2.3 Configuracin regional de las preferencias de usuario


Una aplicacin con usuarios autenticados puede permitir a los usuarios establecer una
preferencia de configuracin regional a travs de la interfaz de la aplicacin. Con este
enfoque, la preferencia de configuracin de usuario seleccionada del usuario se mantiene
en la base de datos y se utiliza para establecer la configuracin regional para solicitudes
autenticadas por ese usuario.

def set_locale
I18n.locale = current_user.try(:locale) || I18n.default_locale
end

2.2.4 Eleccin de una ubicacin implcita


Cuando no se ha establecido una configuracin regional explcita para una solicitud (por
ejemplo, mediante uno de los mtodos anteriores), una aplicacin debe intentar inferir la
configuracin regional deseada.

2.2.4.1 Inferir la configuracin regional desde el encabezado del idioma

El encabezado HTTP de Accept-Language indica el idioma preferido para la respuesta de la


solicitud. Los navegadores establecen este valor de encabezado en funcin de la
configuracin de preferencias de idioma del usuario, lo que lo convierte en una buena
primera opcin al inferir una configuracin regional.

Una implementacin trivial de usar un encabezado Accept-Language sera:

def set_locale
logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
I18n.locale = extract_locale_from_accept_language_header
logger.debug "* Locale set to '#{I18n.locale}'"
end

private
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end

En la prctica, un cdigo ms robusto es necesario para hacer esto de forma fiable. La


biblioteca http_accept_language de Iain Hecker o el middleware de rack locale de Ryan
Tomayko proporcionan soluciones a este problema.

454
2- Configuracin de una aplicacin de Rails para la internacionalizacin

2.2.4.2 Inferir la localizacin de la geolocalizacin de la IP

La direccin IP del cliente que realiza la solicitud se puede utilizar para inferir la regin del
cliente y, por tanto, su entorno local. Servicios como GeoIP Lite Country o gemas como
geocoder pueden ser utilizados para implementar este enfoque.

En general, este enfoque es mucho menos fiable que el uso del encabezado de idioma y no
se recomienda para la mayora de las aplicaciones web.

2.2.5 Almacenamiento de la configuracin regional en la


sesin o en las cookies
Puede estar tentado a almacenar el escenario elegido en una sesin o una cookie. Sin
embargo, no lo hagas. La configuracin regional debe ser transparente y una parte de
la URL. De esta manera no rompers las suposiciones bsicas de la gente sobre la
propia web: si envas una URL a un amigo, deberan ver la misma pgina y el mismo
contenido que t. Una palabra de fantasa para esto sera que ests siendo RESTful .
Lea ms sobre el enfoque RESTful en los artculos de Stefan Tilkov. A veces hay
excepciones a esta regla y se discuten a continuacin

455
3- Internacionalizacin y localizacin

3- Internacionalizacin y localizacin
Ok! Ahora ha inicializado la compatibilidad con I18n para su aplicacin de Ruby on Rails y
le ha indicado qu idioma usar y cmo preservarla entre las solicitudes.

Luego necesitamos internacionalizar nuestra aplicacin abstrayendo cada elemento


especfico de la configuracin regional. Finalmente, necesitamos localizarlo
proporcionando las traducciones necesarias para estos resmenes.

Dado el siguiente ejemplo:

# config/routes.rb
Rails.application.routes.draw do
root to: "home#index"
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_locale

def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end

# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = "Hello Flash"
end
end

# app/views/home/index.html.erb
<h1>Hello World</h1>
<p><%= flash[:notice] %></p>

456
3- Internacionalizacin y localizacin

3.1 Abstrayendo el cdigo a localizar


Hay dos cadenas en nuestro cdigo que estn en ingls y que los usuarios renderizarn en
nuestra respuesta ("Hello Flash" y "Hello World"). Para internacionalizar este cdigo, estas
cadenas deben ser reemplazadas por llamadas al ayudante #t de Rails con una clave
apropiada para cada cadena:

# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
flash[:notice] = t(:hello_flash)
end
end

# app/views/home/index.html.erb
<h1><%=t :hello_world %></h1>
<p><%= flash[:notice] %></p>

Ahora, cuando se muestre esta vista, mostrar un mensaje de error que indica que faltan
las traducciones de las claves :hello_world y :hello_flash .

457
3- Internacionalizacin y localizacin

Rails aade un mtodo auxiliar t (translate) a sus vistas de modo que no necesite
deletrear I18n.t todo el tiempo. Adems, este ayudante capturar las traducciones
perdidas y envolver el mensaje de error resultante en un <span class =
"translation_missing"> .

3.2 Proporcionar traducciones para cadenas


internacionalizadas
Agregue las traducciones faltantes a los archivos del diccionario de traduccin:

# config/locales/en.yml
en:
hello_world: Hello world!
hello_flash: Hello flash!

# config/locales/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash

Debido a que default_locale no ha cambiado, las traducciones usan :en locale y la


respuesta procesa el string en ingls:

Si la configuracin regional se establece a travs de la URL de la configuracin regional


"pirata" ( http://localhost:3000?locale=pirate ), la respuesta procesa los strings "piratas":

458
3- Internacionalizacin y localizacin

Debe reiniciar el servidor cuando agrega nuevos archivos de configuracin regional.

Puede utilizar archivos YAML ( .yml ) o Ruby ( .rb ) para almacenar sus traducciones en
SimpleStore. YAML es la opcin preferida entre los desarrolladores de Rails. Sin embargo,
tiene una gran desventaja. YAML es muy sensible a espacios en blanco y caracteres
especiales, por lo que la aplicacin no puede cargar su diccionario correctamente. Los
archivos Ruby bloquearn su aplicacin en la primera solicitud, por lo que puede encontrar
fcilmente lo que est mal. (Si encuentra algn "problema extrao" con los diccionarios
YAML, intente colocar la parte relevante de su diccionario en un archivo Ruby).

Si sus traducciones se almacenan en archivos YAML, ciertas claves deben escaparse. Son:

true, por, yes


false, por, no

Ejemplos:

# config/locales/en.yml
en:
success:
'true': 'True!'
'on': 'On!'
'false': 'False!'
failure:
true: 'True!'
off: 'Off!'
false: 'False!'

I18n.t 'success.true' # => 'True!'


I18n.t 'success.on' # => 'On!'
I18n.t 'success.false' # => 'False!'
I18n.t 'failure.false' # => Translation Missing
I18n.t 'failure.off' # => Translation Missing
I18n.t 'failure.true' # => Translation Missing

3.3 Pasando variables a las traducciones


Una consideracin clave para internacionalizar con xito una aplicacin es evitar hacer
suposiciones incorrectas acerca de las reglas gramaticales al abstraer el cdigo localizado.
Las reglas de gramtica que parecen fundamentales en una localizacin pueden no ser
vlidas en otra.

La abstraccin incorrecta se muestra en el siguiente ejemplo, donde se hacen suposiciones


sobre el ordenamiento de las diferentes partes de la traduccin. Tenga en cuenta que Rails
proporciona un ayudante number_to_currency para manejar el siguiente caso.

459
3- Internacionalizacin y localizacin

# app/views/products/show.html.erb
<%= "#{t('currency')}#{@product.price}" %>

# config/locales/en.yml
en:
currency: "$"

# config/locales/es.yml
es:
currency: ""

Si el precio del producto es 10 entonces la traduccin correcta para el espaol es "10" en


lugar de "10", pero la abstraccin no puede darlo.

Para crear una abstraccin adecuada, la gema I18n viene con una funcin llamada
interpolacin de variables que le permite utilizar variables en las definiciones de traduccin y
pasar los valores de estas variables al mtodo de traduccin.

La abstraccin apropiada se muestra en el siguiente ejemplo:

# app/views/products/show.html.erb
<%= t('product_price', price: @product.price) %>

# config/locales/en.yml
en:
product_price: "$%{price}"

# config/locales/es.yml
es:
product_price: "%{price} "

Todas las decisiones gramaticales y de puntuacin se toman en la definicin misma, por lo


que la abstraccin puede dar una traduccin correcta.

Las palabras clave predeterminadas y de mbito estn reservadas y no se pueden utilizar


como nombres de variables. Si se utiliza, se genera una excepcin
I18n::ReservedInterpolationKey . Si una traduccin espera una variable de interpolacin,

pero esto no se ha pasado a #translate , se genera una excepcin


I18n::MissingInterpolationArgument .

3.4 Adicin de formatos de fecha / hora

460
3- Internacionalizacin y localizacin

Ok! Ahora vamos a agregar una marca de tiempo a la vista, por lo que tambin puede
mostrar la funcin de localizacin de fecha y hora. Para localizar el formato de hora, pasa el
objeto Time a I18n.l o (preferiblemente) usa el ayudante #l de Rails. Puede elegir un
formato pasando la opcin :format - por defecto se usa el formato :default .

# app/views/home/index.html.erb
<h1><%=t :hello_world %></h1>
<p><%= flash[:notice] %></p>
<p><%= l Time.now, format: :short %></p>

Y en nuestro archivo de traducciones piratas vamos a aadir un formato de hora (ya est
ah en los valores predeterminados de Rails en el ingls):

# config/locales/pirate.yml
pirate:
time:
formats:
short: "arrrround %H'ish"

As que eso te dara:

En este caso puede que tenga que agregar algunos formatos ms de fecha/hora para
hacer que el backend I18n funcione como se esperaba (al menos para el escenario
"pirata"). Por supuesto, hay una gran posibilidad de que alguien ya haya hecho todo el
trabajo al traducir los valores predeterminados de Rails para su entorno. Consulte el
repositorio rails-i18n en GitHub para obtener un archivo de varios archivos de
configuracin regional. Cuando pones tal archivo(s) en el directorio config/locales/ ,
automticamente estarn listos para su uso.

3.5 Reglas de inflexin para otros locales


Rails le permite definir reglas de inflexin (como reglas para la singularizacin y la
pluralizacin) para idiomas distintos del ingls. En config/initializers/inflections.rb ,
puede definir estas reglas para varias localidades. El inicializador contiene un ejemplo

461
3- Internacionalizacin y localizacin

predeterminado para especificar reglas adicionales para el ingls; Siga ese formato para
otras configuraciones regionales como mejor le parezca.

3.6 Vistas localizadas


Digamos que usted tiene un BooksController en su aplicacin. La accin index procesa
contenido en la plantilla app/views/books/index.html.erb . Cuando se coloca una variante
localizada de esta plantilla: index.es.html.erb en el mismo directorio, Rails procesar
contenido en esta plantilla, cuando la configuracin regional est establecida en :es .
Cuando la configuracin regional se establece en la configuracin regional predeterminada,
se utilizar la vista genrica index.html.erb . (Las versiones de Future Rails pueden traer
esta localizacin automagic a los assets en public , etc.)

Puede utilizar esta funcin por ejemplo, cuando se trabaja con una gran cantidad de
contenido esttico, lo que sera torpe para poner dentro de los diccionarios YAML o Ruby.
Tenga en cuenta, sin embargo, que cualquier cambio que le gustara hacer ms tarde a la
plantilla debe propagarse a todos ellos.

3.7 Organizacin de archivos locales


Cuando est utilizando el SimpleStore predeterminado que se entrega con la biblioteca
i18n, los diccionarios se almacenan en archivos de texto sin formato en el disco. Poner las
traducciones para todas las partes de la aplicacin en un archivo por localidad podra ser
difcil de gestionar. Puede almacenar estos archivos en una jerarqua que tenga sentido
para usted.

Por ejemplo, su directorio config/locales podra tener este aspecto:

462
3- Internacionalizacin y localizacin

|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb

De esta manera, puede separar los nombres de los atributos del modelo y el modelo de
texto dentro de vistas, y todo esto de los "valores por defecto" (por ejemplo, formatos de
fecha y hora). Otras stores para la biblioteca i18n podran proporcionar diferentes medios
para tal separacin.

El mecanismo de carga de configuracin predeterminada en Rails no carga archivos de


configuracin regional en diccionarios anidados, como tenemos aqu. Por lo tanto, para
que esto funcione, debemos decirle explcitamente a Rails que busque ms:

# config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]

463
4- Descripcin general de las funciones de la API I18n

4- Descripcin general de las funciones de


la API I18n
Debe tener una buena comprensin ahora para utilizar la biblioteca i18n y saber cmo
internacionalizar una aplicacin de Rails bsica. En los captulos siguientes, cubriremos sus
caractersticas con ms profundidad.

Estos captulos mostrarn ejemplos utilizando tanto el mtodo I18n.translate como el


mtodo auxiliar de traduccin de la vista (observando la caracterstica adicional
proporcionada por el mtodo de ayuda de la vista).

Cubriremos caractersticas como estas:

Buscar traducciones
Interpolando datos en traducciones
Traducciones pluralizadas
Usando traducciones seguras de HTML (solo ver el mtodo auxiliar)
Localizacin de fechas, nmeros, moneda, etc.

4.1 Buscar traducciones

4.1.1 Bsqueda bsica, scopes y claves anidadas


Las traducciones son buscadas por claves que pueden ser Smbolos o Strings, por lo que
estas llamadas son equivalentes:

I18n.t :message
I18n.t 'message'

El mtodo de traduccin tambin tiene una opcin :scope que puede contener una o ms
claves adicionales que se utilizarn para especificar un "espacio de nombres" o el alcance
para una clave de la traduccin:

I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]

Esto busca el mensaje: record_invalid en los mensajes de error de Active Record.

Adems, tanto la clave como los mbitos pueden especificarse como claves separadas por
puntos como en:

464
4- Descripcin general de las funciones de la API I18n

I18n.translate "activerecord.errors.messages.record_invalid"

As, las siguientes llamadas son equivalentes:

I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]

4.1.2 Default

Cuando se da una opcin :default , su valor ser devuelto si falta la traduccin:

I18n.t :missing, default: 'Not here'


# => 'Not here'

Si el valor predeterminado es un smbolo, se utilizar como clave y se traducir. Uno puede


proporcionar varios valores como predeterminados. Se devolver el primero que resulte en
un valor.

Por ejemplo, el siguiente trata de traducir la clave :missing y luego la clave


:also_missing . Como ambos no producen un resultado, la cadena "Not here" ser

devuelta:

I18n.t :missing, default: [:also_missing, 'Not here']


# => 'Not here'

4.1.3 Bsqueda en masa (bulk) y espacio de nombres

Para buscar mltiples traducciones a la vez, se puede pasar una matriz de claves:

I18n.t [:odd, :even], scope: 'errors.messages'


# => ["must be odd", "must be even"]

Adems, una clave puede traducirse a un hash (potencialmente anidado) de traducciones


agrupadas. Por ejemplo, uno puede recibir todos los mensajes de error Active Record como
un hash con:

I18n.t 'activerecord.errors.messages'
# => {:inclusion=>"is not included in the list", :exclusion=> ... }

4.1.4 Bsqueda "perezosa"

465
4- Descripcin general de las funciones de la API I18n

Rails implementa una forma conveniente de buscar la ubicacin dentro de las vistas.
Cuando tenga el siguiente diccionario:

es:
books:
index:
title: "Ttulo"

Puede buscar el valor books.index.title dentro de la aplicacin


/views/books/index.html.erb en una plantilla como esta (tenga en cuenta el punto):

<%= t '.title' %>

El alcance de la traduccin automtica por parcial slo est disponible en el mtodo de


ayuda de la vista de traduccin.

La bsqueda "perezosa" tambin se puede usar en los controladores:

en:
books:
create:
success: Book created!

Esto es til para configurar mensajes de flash como por ejemplo:

class BooksController < ApplicationController


def create
# ...
redirect_to books_url, notice: t('.success')
end
end

4.2 Pluralizacin
En ingls hay slo una forma singular y una plural para una cadena dada, por ejemplo. "1
mensaje" y "2 mensajes". Otros idiomas (rabe, japons, ruso y muchos ms) tienen
diferentes gramticas que tienen formas plurales adicionales o menos. Por lo tanto, la I18n
API proporciona una funcionalidad flexible de pluralizacin.

La variable de interpolacin :count tiene un papel especial en que se interpola a la


traduccin y se utiliza para escoger una pluralizacin a partir de las traducciones segn las
reglas de pluralizacin definidas por CLDR:

466
4- Descripcin general de las funciones de la API I18n

I18n.backend.store_translations :en, inbox: {


zero: 'no messages', # optional
one: 'one message',
other: '%{count} messages'
}
I18n.translate :inbox, count: 2
# => '2 messages'

I18n.translate :inbox, count: 1


# => 'one message'

I18n.translate :inbox, count: 0


# => 'no messages'

El algoritmo para pluralizaciones para :en es tan simple como:

lookup_key = :zero if count == 0 && entry.has_key?(:zero)


lookup_key ||= count == 1 ? :one : :other
entry[lookup_key]

La traduccin denotada como :one se considera singular, y el otro se usa como plural. Si
el recuento es cero y una entrada cero est presente, entonces se utilizar :other en su
lugar.

Si la bsqueda de la clave no devuelve un hash adecuado para la pluralizacin, se genera


una excepcin I18n::InvalidPluralizationData .

I18n.locale = :de
I18n.t :foo
I18n.l Time.now

Pasando explcitamente una configuracin regional:

I18n.t :foo, locale: :de


I18n.l Time.now, locale: :de

El I18n.locale tiene por defecto I18n.default_locale cuyo valor predeterminado es :en .


La configuracin regional predeterminada se puede establecer de la siguiente manera:

I18n.default_locale = :de

4.4 Uso de las traducciones HTML seguras

467
4- Descripcin general de las funciones de la API I18n

Las claves con un sufijo ' _html ' y las claves denominadas 'html' estn marcadas como
seguras para HTML. Cuando los uses en las vistas, el cdigo HTML no se escapar.

# config/locales/en.yml
en:
welcome: <b>welcome!</b>
hello_html: <b>hello!</b>
title:
html: <b>title!</b>

# app/views/home/index.html.erb
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>

Sin embargo, la interpolacin se escapa segn sea necesario. Por ejemplo, dado:

en:
welcome_html: "<b>Welcome %{username}!</b>"

Puede pasar con seguridad el nombre de usuario segn lo establecido por el usuario:

<%# This is safe, it is going to be escaped if needed. %>


<%= t('welcome_html', username: @current_user.username) %>

Las cadenas seguras por otro lado se interpolan literalmente.

La conversin automtica al texto de traduccin HTML seguro slo est disponible


desde el mtodo de ayuda de traduccin de las vistas.

4.5 Traducciones de Active Record Models

468
4- Descripcin general de las funciones de la API I18n

Puede utilizar los mtodos Model.model_name.human y


Model.human_attribute_name(attribute) para buscar traducciones transparentes para los

nombres de modelo y atributo.

Por ejemplo, al agregar las siguientes traducciones:

en:
activerecord:
models:
user: Dude
attributes:
user:
login: "Handle"
# will translate User attribute "login" as "Handle"

Entonces User.model_name.human devolver "Dude" y User.human_attribute_name("login")


devolver "Handle".

Tambin puede establecer una forma plural para los nombres de los modelos, agregandolos
como sigue:

en:
activerecord:
models:
user:
one: Dude
other: Dudes

Entonces User.model_name.human(count: 2) devolver "Dudes". Con count: 1 o sin params


devolver "Dude".

En caso de que necesite acceder a atributos anidados dentro de un modelo determinado,


debe anidarlos bajo model/attribute en el nivel de modelo de su archivo de traduccin:

en:
activerecord:
attributes:
user/gender:
female: "Female"
male: "Male"

Entonces User.human_attribute_name("gender.female") devolver "Female".

Si est utilizando una clase que incluye ActiveModel y no hereda de


ActiveRecord::Base , reemplace activerecord con activemodel en las rutas de clave

anteriores.

469
4- Descripcin general de las funciones de la API I18n

4.5.1 Scope de los mensajes de error


Los mensajes de error de validacin de Active record tambin se pueden traducirse
fcilmente. Active Record le ofrece un par de espacios de nombres donde puede colocar
sus traducciones de mensajes con el fin de proporcionar diferentes mensajes y traduccin
para ciertos modelos, atributos y / o validaciones. Tambin tiene en cuenta
transparentemente la herencia de una sola tabla.

Esto le da medios muy poderosos para ajustar con flexibilidad sus mensajes a las
necesidades de su aplicacin.

Considere un modelo de usuario con una validacin para el atributo de nombre como este:

class User < ApplicationRecord


validates :name, presence: true
end

La clave para el mensaje de error en este caso es :blank . Active Record buscar esta
clave en los espacios de nombres:

activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages

As, en nuestro ejemplo probar las siguientes claves en este orden y devolver el primer
resultado:

activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank

Cuando sus modelos tambin estn usando la herencia entonces los mensajes se buscan
en la cadena de herencia.

Por ejemplo, es posible que tenga un modelo Admin heredado de Usuario:

class Admin < User


validates :name, presence: true
end

470
4- Descripcin general de las funciones de la API I18n

Entonces Active Record buscar mensajes en este orden:

activerecord.errors.models.admin.attributes.name.blank
activerecord.errors.models.admin.blank
activerecord.errors.models.user.attributes.name.blank
activerecord.errors.models.user.blank
activerecord.errors.messages.blank
errors.attributes.name.blank
errors.messages.blank

De esta manera, puede proporcionar traducciones especiales para varios mensajes de error
en diferentes puntos de la cadena de herencia de los modelos y en los atributos, modelos o
mbitos predeterminados.

4.5.2 Interpolacin de mensajes de error


El nombre del modelo traducido, el nombre del atributo traducido y el valor siempre estn
disponibles para la interpolacin como modelo, atributo y valor, respectivamente.

Por ejemplo, en lugar del mensaje de error predeterminado "cannot be blank", puede utilizar
el nombre de atributo como este: "Please fill in your %{attribute}".

Cuando est disponible, puede utilizarse para la pluralizacin si est presente:

471
4- Descripcin general de las funciones de la API I18n

validation with option message interpolation

confirmation - :confirmation attribute


acceptance - :accepted -
presence - :blank -

absence - :present -
length :within, :in :too_short count
length :within, :in :too_long count
length :is :wrong_length count

length :minimum :too_short count


length :maximum :too_long count
uniqueness - :taken -
format - :invalid -
inclusion - :inclusion -
exclusion - :exclusion -

associated - :invalid -
non-optional
- :required -
association
numericality - :not_a_number -
numericality :greater_than :greater_than count
numericality :greater_than_or_equal_to :greater_than_or_equal_to count

numericality :equal_to :equal_to count


numericality :less_than :less_than count

numericality :less_than_or_equal_to :less_than_or_equal_to count


numericality :other_than :other_than count

numericality :only_integer :not_an_integer -


numericality :odd :odd -

numericality :even :even -

4.5.3 Traducciones para Helper error_messages_for de


Active Record
Rails incluye las siguientes traducciones:

472
4- Descripcin general de las funciones de la API I18n

en:
activerecord:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
body: "There were problems with the following fields:"

Para usar este ayudante, debe instalar la gema DynamicForm aadiendo esta lnea a su
Gemfile: gem 'dynamic_form' .

4.6 Traducciones del Subject de Email de Action Mailer.


Si no pasa un asunto al mtodo de correo, Action Mailer intentar encontrarlo en sus
traducciones. La bsqueda realizada utilizar el patrn <mailer_scope> . <action_name>
.subject para construir la clave.

# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
#...
end
end

en:
user_mailer:
welcome:
subject: "Welcome to Rails Guides!"

Para enviar parmetros a la interpolacin, use el mtodo default_i18n_subject en el mailer.

# user_mailer.rb
class UserMailer < ActionMailer::Base
def welcome(user)
mail(to: user.email, subject: default_i18n_subject(user: user.name))
end
end

en:
user_mailer:
welcome:
subject: "%{user}, welcome to Rails Guides!"

473
4- Descripcin general de las funciones de la API I18n

4.7 Descripcin general de otros mtodos integrados que


proporcionan compatibilidad con I18n
Rails utiliza cadenas fijas y otras localizaciones, como cadenas de formato y otra
informacin de formato en un par de ayudantes. He aqu un breve resumen.

4.7.1 Mtodos de ayuda de Action View

distance_of_time_in_words traduce y pluraliza su resultado e interpola el nmero de

segundos, minutos, horas, etc. Vea las traducciones de datetime.distance_in_words .


datetime_select y select_month utilizan nombres de mes traducidos para rellenar la

etiqueta de seleccin resultante. Vea date.month_names para traducciones.


datetime_select tambin busca la opcin de orden desde date.order (a menos que

pase la opcin explcitamente). Todos los ayudantes de seleccin de fechas traducen el


mensaje utilizando las traducciones en el mbito datetime.prompts si corresponde.
Los helpers number_to_currency , number_with_precision , number_to_percentage ,
number_with_delimiter y number_to_human_size utilizan los ajustes de formato

numrico situados en el mbito numrico.

4.7.2 Mtodos del Active Model

model_name.human y human_attribute_name utilizan traducciones para nombres de

modelos y nombres de atributos si estn disponibles en el mbito activerecord.models .


Tambin admiten traducciones de nombres de clases heredados (por ejemplo, para su
uso con STI) como se explic anteriormente en "Scope de mensaje de error".
ActiveModel::Errors#generate_message (que es usado por las validaciones de Active

Model pero tambin se puede usar manualmente) usa nombre_modelo.human y


human_attribute_name (ver arriba). Tambin traduce el mensaje de error y admite

traducciones para nombres de clases heredados como se explic anteriormente en


"Scope de mensaje de error".
ActiveModel::Errors#full_messages anula el nombre del atributo al mensaje de error

usando un separador que se buscar desde errors.format (y que por defecto es " %
{attribute} %{message} ").

4.7.3 Mtodos de Active Support

Array#to_sentence utiliza la configuracin de formato tal como se da en el mbito


support.array

474
5- Cmo almacenar sus traducciones personalizadas

5- Cmo almacenar sus traducciones


personalizadas
El backend simple incluido con Active Support le permite almacenar traducciones en
formato Ruby y YAML.

Por ejemplo, un Ruby Hash que proporciona traducciones puede verse as:

{
pt: {
foo: {
bar: "baz"
}
}
}

El archivo YAML equivalente sera as:

pt:
foo:
bar: baz

Como puede ver, en ambos casos la clave de nivel superior es la configuracin regional.
:foo es una clave de espacio de nombres y :bar es la clave para la traduccin " baz ".

Este es un ejemplo "real" del archivo YAML de Active Support en.yml translations:

en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"

Por lo tanto, todas las siguientes consultas equivalentes devolvern el formato de fecha
:short " %b %d ":

I18n.t 'date.formats.short'
I18n.t 'formats.short', scope: :date
I18n.t :short, scope: 'date.formats'
I18n.t :short, scope: [:date, :formats]

475
5- Cmo almacenar sus traducciones personalizadas

Por lo general, recomendamos usar YAML como un formato para almacenar las
traducciones. Hay casos, sin embargo, donde desea almacenar lambdas de Ruby como
parte de sus datos de configuracin regional, por ejemplo para formatos de fechas
especiales.

476
6- Personaliza tu configuracin de I18n

6- Personaliza tu configuracin de I18n


6.1 Uso de diferentes backends
Por varias razones, el back-end Simple incluido con Active Support solo hace lo "ms
sencillo que posiblemente podra funcionar" para Ruby on Rails3 ... lo que significa que slo
se garantiza que funcione para el ingls y como un efecto secundario, los idiomas que son
muy Similar al ingls. Adems, el backend simple slo es capaz de leer traducciones pero
no puede guardarlas dinmicamente en ningn formato.

Sin embargo, eso no significa que est atrapado con estas limitaciones. La gema Ruby I18n
hace que sea muy fcil intercambiar la implementacin simple de backend con algo ms
que se adapte mejor a sus necesidades. P.ej. Usted podra intercambiarlo con el backend
esttico de Globalize:

I18n.backend = Globalize::Backend::Static.new

Tambin puede utilizar el backend de cadena para encadenar mltiples backends juntos.
Esto es til cuando se desea usar traducciones estndar con un backend simple, pero
almacenar traducciones de aplicaciones personalizadas en una base de datos u otros
backends. Por ejemplo, puede utilizar el backend de Active Record y volver al backend
simple (por defecto):

I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)

6.2 Uso de diferentes manejadores de excepciones


La API I18n define las siguientes excepciones que sern generadas por backends cuando
se produzcan las condiciones inesperadas correspondientes:

MissingTranslationData # no translation was found for the requested key


InvalidLocale # the locale set to I18n.locale is invalid (e.g. nil)
InvalidPluralizationData # a count option was passed but the translation data is n
ot suitable for pluralization
MissingInterpolationArgument # the translation expects an interpolation argument that
has not been passed
ReservedInterpolationKey # the translation contains a reserved interpolation varia
ble name (i.e. one of: scope, default)
UnknownFileType # the backend does not know how to handle a file type tha
t was added to I18n.load_path

477
6- Personaliza tu configuracin de I18n

La API I18n captura todas estas excepciones cuando se lanzan en el backend y las pasan
al mtodo default_exception_handler . Este mtodo volver a aumentar todas las
excepciones, excepto las excepciones de MissingTranslationData . Cuando se ha detectado
una excepcin MissingTranslationData , devolver la cadena de mensaje de error de la
excepcin que contiene la clave o el mbito que faltan.

La razn de esto es que durante el desarrollo en el que normalmente desea que sus vistas
se usen a pesar de que falte una traduccin.

Sin embargo, en otros contextos puede que desee cambiar este comportamiento. P.ej. La
manipulacin de excepciones por defecto no permite capturar fcilmente las traducciones
perdidas durante las pruebas automatizadas. Para este propsito se puede especificar un
manejador de excepciones diferente. El manejador de excepciones especificado debe ser
un mtodo en el mdulo I18n o una clase con el mtodo #call :

module I18n
class JustRaiseExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslationData)
raise exception.to_exception
else
super
end
end
end
end

I18n.exception_handler = I18n::JustRaiseExceptionHandler.new

Esto volvera a levantar slo la excepcin MissingTranslationData , pasando todas las


dems entradas al controlador de excepciones predeterminado.

Sin embargo, si est utilizando I18n::Backend::Pluralization este manejador tambin


levantar la excepcin I18n::MissingTranslationData: translation missing:
en.i18n.plural.rule que normalmente se debe ignorar para volver a la regla de

pluralizacin por defecto para el idioma Ingls . Para evitar esto, puede usar un chequeo
adicional para la clave de traduccin:

if exception.is_a?(MissingTranslationData) && key.to_s != 'i18n.plural.rule'


raise exception.to_exception
else
super
end

478
6- Personaliza tu configuracin de I18n

Otro ejemplo donde el comportamiento por defecto es menos deseable, es el Rails


TranslationHelper que proporciona el mtodo #t (as como #translate ). Cuando se

produce una excepcin MissingTranslationData en este contexto, el ayudante envuelve el


mensaje en un intervalo con la clase CSS translation_missing .

Para ello, el helper fuerza a I18n#translate a generar excepciones, independientemente


del manejador de excepciones que se defina al establecer la opcin :raise :

I18n.t :foo, raise: true # always re-raises exceptions from the backend

479
XV- Fundamentos de Action Mailer

XV- Fundamentos de Action Mailer


Esta gua le proporciona todo lo que necesita para comenzar a enviar y recibir correos
electrnicos desde y hacia su aplicacin, y muchos internos de Action Mailer. Tambin
cubre cmo probar su mailers.

Despus de leer esta gua, sabr:

Cmo enviar y recibir correo electrnico dentro de una aplicacin de Rails.


Cmo generar y editar una clase Action Mailer y las vistas de mailer.
Cmo configurar Action Mailer para su entorno.
Cmo probar sus clases Action Mailer.

480
1- Introduccin

1- Introduccin
Action Mailer le permite enviar mensajes de correo electrnico desde su aplicacin usando
clases de mailer y vistas. Los mailers trabajan muy similar a los controladores. Ellos
heredan de ActionMailer::Base y viven en app/mailers , y tienen vistas asociadas que
aparecen en app/views .

481
2- Envo de mensajes de correo electrnico

2- Envo de mensajes de correo


electrnico
Esta seccin proporcionar una gua paso a paso para crear un mailer y sus vistas.

2.1 Pasos a seguir para generar un Mailer

2.1.1 Crear el Mailer

$ bin/rails generate mailer UserMailer


create app/mailers/user_mailer.rb
create app/mailers/application_mailer.rb
invoke erb
create app/views/user_mailer
create app/views/layouts/mailer.text.erb
create app/views/layouts/mailer.html.erb
invoke test_unit
create test/mailers/user_mailer_test.rb
create test/mailers/previews/user_mailer_preview.rb

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout 'mailer'
end

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

Como puede ver, puede generar correos electrnicos como si utilizara otros generadores
con Rails. Los mailers son conceptualmente similares a los controladores, y as recibimos
un mailer, un directorio para vistas y uno para pruebas.

Si no desea utilizar un generador, puede crear su propio archivo dentro de la app/mailers ,


slo asegrese de que hereda de ActionMailer::Base :

2.1.2 Editar el Mailer

482
2- Envo de mensajes de correo electrnico

Los mailers son muy similares a los controladores de Rails. Tambin tienen mtodos
llamados "acciones" y usan vistas para estructurar el contenido. Cuando un controlador
genera contenido como HTML para enviar de vuelta al cliente, un Mailer crea un mensaje
para ser entregado por correo electrnico.

app/mailers/user_mailer.rb contiene un correo vaco:

class UserMailer < ApplicationMailer


end

Vamos a agregar un mtodo llamado welcome_email , que enviar un correo electrnico a la


direccin de correo electrnico del usuario:

class UserMailer < ApplicationMailer


default from: 'notifications@example.com'

def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
end

Aqu hay una explicacin rpida de los elementos presentados en el mtodo anterior. Para
obtener una lista completa de todas las opciones disponibles, por favor, eche un vistazo
ms abajo en la lista completa de Action Mailer user-settable atributos de seccin.

default Hash: este es un hash de valores predeterminados para cualquier correo


electrnico que enve desde este mailer. En este caso, lo estamos estableciendo desde
:from en el encabezado hasta un valor para todos los mensajes de esta clase. Esto

puede anularse por correo electrnico.


mail - El mensaje de correo electrnico real, estamos pasandolo en los encabezados
por medio de :to y :subject .

Al igual que los controladores, cualquier variable de instancia que definamos en el mtodo
estar disponible para su uso en las vistas.

2.1.3 Crear una vista de correo


Cree un archivo llamado welcome_email.html.erb en app/views/user_mailer/ . Esta ser la
plantilla utilizada para el correo electrnico, formateada en HTML:

483
2- Envo de mensajes de correo electrnico

<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
You have successfully signed up to example.com,
your username is: <%= @user.login %>.<br>
</p>
<p>
To login to the site, just follow this link: <%= @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>

Hagamos tambin una parte de texto para este correo electrnico. No todos los clientes
prefieren los correos electrnicos HTML, por lo que el envo de ambos es la mejor prctica.
Para ello, cree un archivo denominado welcome_email.text.erb en app/views/user_mailer/ :

Welcome to example.com, <%= @user.name %>


===============================================

You have successfully signed up to example.com,


your username is: <%= @user.login %>.

To login to the site, just follow this link: <%= @url %>.

Thanks for joining and have a great day!

Cuando llame al mtodo de correo ahora, Action Mailer detectar las dos plantillas (texto y
HTML) y generar automticamente un correo electrnico de varias multipart/alternative .

2.1.4 Llamar al Mailer


Los mailers son realmente slo otra manera de hacer una vista. En lugar de representar una
vista y enviarla a travs del protocolo HTTP, simplemente la estn enviando a travs de los
protocolos de correo electrnico. Debido a esto, tiene sentido que su controlador le diga al
remitente que enve un correo electrnico cuando un usuario se cree correctamente.

Establecer esto es dolorosamente simple.

En primer lugar, vamos a crear un scaffold de usuario simple:

484
2- Envo de mensajes de correo electrnico

$ bin/rails generate scaffold user name email login


$ bin/rails db:migrate

Ahora que tenemos un modelo de usuario con el que jugar, solo editaremos
app/controllers/users_controller.rb para que instruya al UserMailer a entregar un correo

electrnico al usuario recin creado editando la accin create e insertando una llamada a
UserMailer.welcome_email Justo despus de que el usuario se guarde correctamente.

Action Mailer est muy bien integrado con Active Job para que pueda enviar correos
electrnicos fuera del ciclo de solicitud y respuesta, por lo que el usuario no tiene que
esperar:

class UsersController < ApplicationController


# POST /users
# POST /users.json
def create
@user = User.new(params[:user])

respond_to do |format|
if @user.save
# Tell the UserMailer to send a welcome email after save
UserMailer.welcome_email(@user).deliver_later

format.html { redirect_to(@user, notice: 'User was successfully created.') }


format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: 'new' }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
end

El comportamiento predeterminado de Active Job es ejecutar trabajos a travs del


:async adapter . Por lo tanto, puede utilizar deliver_later ahora para enviar

mensajes de correo electrnico de forma asncrona. El adaptador predeterminado de


Active Job ejecuta trabajos con un grupo de subprocesos en proceso. Es muy
adecuado para los entornos de development/test, ya que no requiere ninguna
infraestructura externa, pero es poco adecuado para produccin, ya que deja caer los
Jobs pendientes al reiniciar. Si necesita un backend persistente, necesitar utilizar un
adaptador de Active Job que tenga un backend persistente (Sidekiq, Resque, etc.).

Si quieres enviar correos electrnicos de inmediato (de un cronjob por ejemplo) slo tienes
que llamar a deliver_now :

485
2- Envo de mensajes de correo electrnico

class SendWeeklySummary
def run
User.find_each do |user|
UserMailer.weekly_summary(user).deliver_now
end
end
end

El mtodo welcome_email devuelve un objeto ActionMailer::MessageDelivery que luego se


le puede decir deliver_now o deliver_later para enviarse. El objeto
ActionMailer::MessageDelivery es slo un contenedor alrededor de un Mail::Message . Si

desea inspeccionar, alterar o hacer cualquier otra cosa con el objeto Mail::Message puede
acceder a l con el mtodo de mensaje en el objeto ActionMailer::MessageDelivery .

2.2 Valores de encabezado de codificacin automtica


Action Mailer gestiona la codificacin automtica de caracteres multibyte dentro de los
encabezados y los cuerpos.

Para ejemplos ms complejos, como la definicin de conjuntos de caracteres alternativos o


el texto de autocodificacin, consulte la biblioteca de Mail.

2.3 Lista completa de mtodos de Action Mailer


Slo hay tres mtodos que necesita para enviar prcticamente cualquier mensaje de correo
electrnico:

headers - Especifica cualquier encabezado en el correo electrnico que desee. Puede

pasar un hash de nombres de campo de encabezado y pares de valores, o puede


llamar a headers[:field_name] = 'value' .
attachments : le permite agregar archivos adjuntos a su correo electrnico. Por

ejemplo, los archivos attachments['file-name.jpg'] = File.read('file-name.jpg') .


mail - Enva el correo electrnico en s mismo. Puede pasarlo en los headers como

un hash al mtodo de mail como un parmetro, el mail crear un correo electrnico, ya


sea de texto plano o multipart, dependiendo de las plantillas de correo electrnico que
haya definido.

2.3.1 Adicin de archivos adjuntos


Action Mailer hace que sea muy fcil agregar archivos adjuntos.

Pase el nombre y el contenido del archivo y Action Mailer y la gema mail automticamente
adivinarn el tipo mime, establecern la codificacin y crearn el archivo adjunto.

486
2- Envo de mensajes de correo electrnico

attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

Cuando se active el mtodo de mail, enviar un correo electrnico multipart con un archivo
adjunto, correctamente anidado con el nivel superior que es multipart/mixed y la primera
parte es una multipart/alternative que contiene el texto sin formato y mensajes de correo
electrnico HTML.

Mail codificar automticamente un archivo adjunto. Si desea algo diferente, codifique


su contenido y pase el contenido codificado y la codificacin en un Hash al mtodo de
archivos adjuntos.

Pase el nombre del archivo y especifique los encabezados y el contenido, y Action Mailer
and Mail usar la configuracin que le pase.

encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
attachments['filename.jpg'] = {
mime_type: 'application/x-gzip',
encoding: 'SpecialEncoding',
content: encoded_content
}

Si especifica una codificacin, Mail asumir que su contenido ya est codificado y no


intenta codificar en Base64.

2.3.2 Creando archivos adjuntos inline


Action Mailer 3.0 crea adjuntos inline, lo que implicaba un montn de hacking en pre
versiones 3.0, y ya es mucho ms simple y trivial como deberan ser.

Primero, para decirle a Mail que convierta un archivo adjunto en un archivo adjunto inline,
simplemente llame a #inline en el mtodo de archivos adjuntos dentro de su Mailer:

def welcome
attachments.inline['image.jpg'] = File.read('/path/to/image.jpg')
end

A continuacin, en sus vistas, slo puede hacer referencia a los archivos adjuntos como un
hash y especificar qu adjunto desea mostrar, llamando a la url y luego pasando el
resultado al mtodo image_tag :

<p>Hello there, this is our image</p>

<%= image_tag attachments['image.jpg'].url %>

487
2- Envo de mensajes de correo electrnico

Como se trata de una llamada estndar a image_tag , puedes pasar un hash de opciones
despus de la URL del attachment como lo haras para cualquier otra imagen:

<p>Hello there, this is our image</p>

<%= image_tag attachments['image.jpg'].url, alt: 'My Photo', class: 'photos' %>

2.3.3 Envo de correo electrnico a varios destinatarios


Es posible enviar correo electrnico a uno o ms destinatarios en un mismo email (por
ejemplo, informar a todos los administradores de una nueva suscripcin) estableciendo la
lista de correos electrnicos en la llave :to . La lista de correos electrnicos puede ser una
matriz de direcciones de correo electrnico o una sola cadena con las direcciones
separadas por comas.

class AdminMailer < ActionMailer::Base


default to: Proc.new { Admin.pluck(:email) },
from: 'notification@example.com'

def new_registration(user)
@user = user
mail(subject: "New User Signup: #{@user.email}")
end
end

El mismo formato se puede utilizar para establecer "Con copia" (Cc :) y oculto (Bcc :),
utilizando las llaves :cc y :bcc respectivamente.

2.3.4 Envo de correo electrnico con nombre


A veces desea mostrar el nombre de la persona en lugar de su direccin de correo
electrnico cuando reciba el email. El truco para hacerlo es formatear la direccin de correo
electrnico en el formato " Full name <email> ".

def welcome_email(user)
@user = user
email_with_name = %("#{@user.name}" <#{@user.email}>)
mail(to: email_with_name, subject: 'Welcome to My Awesome Site')
end

2.4 Vistas de email

488
2- Envo de mensajes de correo electrnico

Las vistas de correo se encuentran en el directorio app/views/name_of_mailer_class . La


vista de correo especfico es reconocida por la clase porque su nombre es el mismo que el
mtodo de correo. En nuestro ejemplo de arriba, nuestra vista de correo para el mtodo
welcome_email estar en app/views/user_mailer/welcome_email.html.erb para la versin

HTML y welcome_email.text.erb para la versin de texto sin formato.

Para cambiar la vista de correo predeterminada de su accin, haga algo como:

class UserMailer < ApplicationMailer


default from: 'notifications@example.com'

def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site',
template_path: 'notifications',
template_name: 'another')
end
end

En este caso buscar plantillas en app/views/notifications con otro nombre. Tambin


puede especificar una matriz de rutas de template_path , y se buscarn en orden.

Si desea ms flexibilidad, tambin puede pasar un bloque y renderizar plantillas especficas


o incluso renderizar inline o texto sin utilizar un archivo de plantilla:

class UserMailer < ApplicationMailer


default from: 'notifications@example.com'

def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site') do |format|
format.html { render 'another_template' }
format.text { render text: 'Render text' }
end
end
end

Esto har que la plantilla 'another_template.html.erb ' para la parte HTML y utilice el texto
representado para la parte de texto. El comando render es el mismo utilizado dentro de
Action Controller, por lo que puede utilizar todas las mismas opciones, tales como :text ,
:inline etc.

489
2- Envo de mensajes de correo electrnico

2.4.1 Almacenamiento en cach de la vista de correo


Usted puede hacer la memoria cach en las vistas de correo como en las vistas de la
aplicacin utilizando el mtodo cache .

<% cache do %>


<%= @company.name %>
<% end %>

Y para poder utilizar esta funcin, necesitas configurar tu aplicacin con esto:

config.action_mailer.perform_caching = true

2.5 Plantillas de Action Mailer


Al igual que las vistas del controlador, tambin puede tener layouts de correo. El nombre de
layout debe ser el mismo que el de correo, como user_mailer.html.erb y
user_mailer.text.erb para ser reconocido automticamente por su mailer como un layout.

class UserMailer < ApplicationMailer


layout 'awesome' # use awesome.(html|text).erb as the layout
end

Al igual que con las vistas del controlador, utilice yield para representar la vista dentro del
layout.

Tambin puede pasar una opcin de layout: ' layout_name ' a la llamada de render dentro
del bloque de formato para especificar diferentes layouts para diferentes formatos:

class UserMailer < ApplicationMailer


def welcome_email(user)
mail(to: user.email) do |format|
format.html { render layout: 'my_layout' }
format.text
end
end
end

Proporcionar la parte HTML utilizando el archivo my_layout.html.erb y la parte de texto


con el archivo user_mailer.text.erb habitual si existe.

2.6 Vista previa de correos electrnicos

490
2- Envo de mensajes de correo electrnico

Las previsualizaciones de Action Mailer proporcionan una forma de ver cmo se ven los
correos electrnicos visitando una URL especial que los procesa. En el ejemplo anterior, la
clase de vista previa para UserMailer debe llamarse UserMailerPreview y se encuentra en
test/mailers/previews/user_mailer_preview.rb . Para ver la vista previa de welcome_email ,

implemente un mtodo que tenga el mismo nombre y llame a UserMailer.welcome_email :

class UserMailerPreview < ActionMailer::Preview


def welcome_email
UserMailer.welcome_email(User.first)
end
end

A continuacin, la vista previa estar disponible en


http://localhost:3000/rails/mailers/user_mailer/welcome_email.

Si cambia algo en app/views/user_mailer/welcome_email.html.erb o en el propio correo, se


volver a cargar automticamente y renderizarlo para que visualice el nuevo estilo al
instante. Una lista de vistas previas tambin est disponible en
http://localhost:3000/rails/mailers

De forma predeterminada, estas clases de vista previa estn en test/mailers/previews .


Esto se puede configurar usando la opcin preview_path . Por ejemplo, si desea cambiarlo
a lib/mailer_previews , puede configurarlo en config/application.rb :

config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"

2.7 Generacin de URLs en las vistas de Action Mailer


A diferencia de los controladores, la instancia de mailer no tiene ningn contexto sobre la
solicitud entrante por lo que tendr que proporcionar el parmetro :host usted mismo.

A medida que el :host suele ser consistente en la aplicacin, se puede configurar


globalmente en config/application.rb :

config.action_mailer.default_url_options = { host: 'example.com' }

Debido a este comportamiento no puede utilizar ninguno de los helpers *_path dentro de
un correo electrnico. En su lugar, necesitar utilizar el auxiliar asociado *_url . Por
ejemplo, en lugar de usar

<%= link_to 'welcome', welcome_path %>

491
2- Envo de mensajes de correo electrnico

Usted necesitar usar:

<%= link_to 'welcome', welcome_url %>

Al usar la URL completa, sus vnculos funcionarn ahora en sus correos electrnicos.

2.7.1 generando URLs con url_for


Url_for genera la URL completa por defecto en las plantillas.

Si no configur la opcin :host globalmente asegrese de pasarla a url_for .

<%= url_for(host: 'example.com',


controller: 'welcome',
action: 'greeting') %>

2.7.2 Generacin de URL con rutas con nombre


Los clientes de correo electrnico no tienen ningn contexto web y por lo tanto las rutas no
tienen una URL de base para formar direcciones web completas. Por lo tanto, siempre debe
utilizar la variante " _url " de ayudantes de ruta con nombre.

Si no configur la opcin :host globalmente, asegrese de pasarla al ayudante de la url.

<%= user_url(@user, host: 'example.com') %>

Los enlaces no-GET requieren rails-ujs o jQuery_UJS , y no funcionarn en las


plantillas de correo. Resultarn en peticiones GET normales.

2.8 Adicin de imgenes en las vistas de Action Mailer


A diferencia de los controladores, la instancia de correo no tiene ningn contexto sobre la
solicitud entrante por lo que tendr que proporcionar el parmetro :asset_host usted
mismo.

Como el :asset_host suele ser consistente en toda la aplicacin, puede configurarlo


globalmente en config/application.rb :

config.action_mailer.asset_host = 'http://example.com'

Ahora puede mostrar una imagen dentro de su correo electrnico.

492
2- Envo de mensajes de correo electrnico

<%= image_tag 'image.jpg' %>

2.9 Envo de correos electrnicos multipart


Action Mailer enviar automticamente correos electrnicos multipart si tiene plantillas
diferentes para la misma accin. Por lo tanto, para nuestro ejemplo de UserMailer , si tiene
welcome_email.text.erb y welcome_email.html.erb en app/views/user_mailer , Action

Mailer enviar automticamente un correo electrnico multipart con las versiones de texto
HTML y texto como multipart.

El orden de las piezas que se insertan es determinado por el :parts_order dentro del
mtodo ActionMailer::Base.default .

2.10 Envo de correos electrnicos con opciones dinmicas


de entrega
Si desea reemplazar las opciones de entrega predeterminadas (por ejemplo, las
credenciales de SMTP) durante la entrega de correos electrnicos, puede hacerlo utilizando
delivery_method_options en la accin de correo.

class UserMailer < ApplicationMailer


def welcome_email(user, company)
@user = user
@url = user_url(@user)
delivery_options = { user_name: company.smtp_user,
password: company.smtp_password,
address: company.smtp_host }
mail(to: @user.email,
subject: "Please see the Terms and Conditions attached",
delivery_method_options: delivery_options)
end
end

2.11 Envo de correos electrnicos sin renderizacin de


plantillas
Puede haber casos en los que desee omitir el paso de renderizacin de la plantilla y
suministrar el cuerpo del correo como una cadena. Puede lograr esto usando la
opcin :body . En estos casos, no olvide aadir la opcin :content_type . Rails
predeterminar el text/plain de lo contrario.

493
2- Envo de mensajes de correo electrnico

class UserMailer < ApplicationMailer


def welcome_email(user, email_body)
mail(to: user.email,
body: email_body,
content_type: "text/html",
subject: "Already rendered!")
end
end

494
3- Recibir correos electrnicos

3- Recibir correos electrnicos


Recibir y analizar correos electrnicos con Action Mailer puede ser un esfuerzo bastante
complejo. Antes de que tu correo electrnico llegue a tu aplicacin de Rails, tendras que
configurar tu sistema de alguna manera para enviar correos electrnicos a tu aplicacin,
que debe estar escuchando eso. Por lo tanto, para recibir correos electrnicos en tu
aplicacin de Rails necesitars:

Implementar un mtodo de recepcin en su mailer.


Configure su servidor de correo electrnico para reenviar los correos electrnicos de
la(s) direccin(es) a la que le gustara recibir su aplicacin a /path/to/app/bin/rails
runner 'UserMailer.receive (STDIN.read)'

Una vez que el mtodo receieve es llamado se define en cualquier mailer, Action Mailer
analizar el correo electrnico entrante sin procesar en un objeto, lo decodifica, lo instancia
en un nuevo mailer y pasa el objeto de mail al mtodo de la instancia de receieve del
cliente. He aqu un ejemplo:

class UserMailer < ApplicationMailer


def receive(email)
page = Page.find_by(address: email.to.first)
page.emails.create(
subject: email.subject,
body: email.body
)

if email.has_attachments?
email.attachments.each do |attachment|
page.attachments.create({
file: attachment,
description: email.subject
})
end
end
end
end

495
4- Action Mailer Callbacks

4- Action Mailer Callbacks


Action Mailer permite especificar una before_action , after_action y around_action .

Los filtros se pueden especificar con un bloque o un smbolo a un mtodo en la clase


de correo similar a los controladores.
Puede utilizar un before_action para rellenar el objeto de correo con defaults ,
delivery_method_options o insertar encabezados y archivos adjuntos predeterminados.

Usted podra usar un after_action para hacer una configuracin similar a


before_action pero usando variables de instancia establecidas en su accin de mailer.

496
4- Action Mailer Callbacks

class UserMailer < ApplicationMailer


after_action :set_delivery_options,
:prevent_delivery_to_guests,
:set_business_headers

def feedback_message(business, user)


@business = business
@user = user
mail
end

def campaign_message(business, user)


@business = business
@user = user
end

private

def set_delivery_options
# You have access to the mail instance,
# @business and @user instance variables here
if @business && @business.has_smtp_settings?
mail.delivery_method.settings.merge!(@business.smtp_settings)
end
end

def prevent_delivery_to_guests
if @user && @user.guest?
mail.perform_deliveries = false
end
end

def set_business_headers
if @business
headers["X-SMTPAPI-CATEGORY"] = @business.code
end
end
end

Filtros de correo abortan procesamiento adicional si el cuerpo se establece en un valor


non-nil.

497
5- Uso de Helpers de Action Mailer

5- Uso de Helpers de Action Mailer


Action Mailer acaba de heredar de AbstractController , por lo que tiene acceso a los
mismos ayudantes genricos como lo hace en Action Controller.

498
6- Configuracin de Action Mailer

6- Configuracin de Action Mailer


Las siguientes opciones de configuracin se hacen mejor en uno de los archivos de entorno
( environment.rb, production.rb , etc ...)

Configuracin Descripcin
Genera informacin sobre la ejecucin del correo si est
logger
disponible. Se puede establecer en nil para no registrar.
Compatible con los registradores Logger y Log4r propios de
Ruby..
Allows detailed configuration for :smtp delivery
method: :address - Allows you to use a remote mail server.
Just change it from its default "localhost" setting. :port -
On the off chance that your mail server doesn't run on port
25, you can change it. :domain - If you need to specify a
HELO domain, you can do it here. :user_name - If your mail
server requires authentication, set the username in this
setting. :password - If your mail server requires
authentication, set the password in this
setting. :authentication - If your mail server requires
authentication, you need to specify the authentication type
smtp_settings
here. This is a symbol and one of :plain (will send the
password in the clear), :login (will send password Base64
encoded) or :cram_md5 (combines a Challenge/Response
mechanism to exchange information and a cryptographic
Message Digest 5 algorithm to hash important
information) :enable_starttls_auto - Detects if STARTTLS is
enabled in your SMTP server and starts to use it. Defaults
to true . :openssl_verify_mode - When using TLS, you can
set how OpenSSL checks the certificate. This is really useful
if you need to validate a self-signed and/or a wildcard
certificate. You can use the name of an OpenSSL verify
constant ('none' or 'peer') or directly the constant
( OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER ).

Allows you to override options for the :sendmail delivery


sendmail_settings
method. :location - The location of the sendmail executable.
Defaults to /usr/sbin/sendmail . :arguments - The command
line arguments to be passed to sendmail. Defaults to -i .
Whether or not errors should be raised if the email fails to be
raise_delivery_errors delivered. This only works if the external email server is
configured for immediate delivery.

Defines a delivery method. Possible values


are: :smtp (default), can be configured by
using config.action_mailer.smtp_settings . :sendmail , can
be configured by

499
6- Configuracin de Action Mailer

delivery_method using config.action_mailer.sendmail_settings . :file : save


emails to files; can be configured by
using config.action_mailer.file_settings . :test : save
emails to ActionMailer::Base.deliveries array.SeeAPI
docsfor more info.

Determines whether deliveries are actually carried out when


perform_deliveries
the deliver method is invoked on the Mail message. By
default they are, but this can be turned off to help functional
testing.
Keeps an array of all the emails sent out through the Action
deliveries Mailer with delivery_method :test. Most useful for unit and
functional testing.

default_options
Allows you to set default values for the mail method options
( :from , :reply_to , etc.).

Para obtener una descripcin completa de las configuraciones posibles, consulte la


publicacin Configuracin de Action Mailer que vimos arriba en la seccin 3.10.

6.1 Ejemplo de configuracin de Action Mailer


Un ejemplo sera agregar lo siguiente a su archivo apropiado de
config/environments/$RAILS_ENV.rb :

config.action_mailer.delivery_method = :sendmail
# Defaults to:
# config.action_mailer.sendmail_settings = {
# location: '/usr/sbin/sendmail',
# arguments: '-i'
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = {from: 'no-reply@example.com'}

6.2 Configuracin de Action Mailer para Gmail


Como Action Mailer ahora usa la gema Mail, esto se vuelve tan simple como aadir a tu
archivo config/environments/$RAILS_ENV.rb :

500
6- Configuracin de Action Mailer

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'example.com',
user_name: '<username>',
password: '<password>',
authentication: 'plain',
enable_starttls_auto: true }

Nota: A partir del 15 de julio de 2014, Google aument sus medidas de seguridad y ahora
bloquea los intentos de las aplicaciones que considera poco seguras. Puedes cambiar la
configuracin de Gmail aqu para permitir los intentos. Si su cuenta de Gmail tiene
habilitada la autenticacin de dos factores, deber establecer una contrasea de aplicacin
y usarla en lugar de su contrasea normal. Alternativamente, puede usar otro ESP para
enviar correo electrnico reemplazando ' smtp.gmail.com ' con la direccin de su proveedor.

501
7- Mailer Testing

7- Mailer Testing
Puede encontrar instrucciones detalladas sobre cmo probar sus correos en la gua de
pruebas.

502
8- Intercepcin de correos electrnicos

8- Intercepcin de correos electrnicos


Hay situaciones en las que es necesario editar un correo electrnico antes de que sea
entregado. Afortunadamente Action Mailer proporciona hooks para interceptar cada correo
electrnico. Puede registrar un interceptor para realizar modificaciones en los mensajes de
correo antes de entregarlos a los agentes de entrega.

class SandboxEmailInterceptor
def self.delivering_email(message)
message.to = ['sandbox@example.com']
end
end

Antes de que el interceptor pueda hacer su trabajo, necesita registrarlo con el framework de
Action Mailer. Puede hacerlo en un archivo de
inicializacin config/initializers/sandbox_email_interceptor.rb

if Rails.env.staging?
ActionMailer::Base.register_interceptor(SandboxEmailInterceptor)
end

El ejemplo anterior utiliza un entorno personalizado denominado "staging" para un servidor


de produccin, pero con fines de prueba. Puede leer la seccin Creacin de entornos de
Rails para obtener ms informacin sobre los entornos personalizados de Rails.

503
XVI - Conceptos bsicos de Active Jobs

XVI - Conceptos bsicos de Active Jobs


Esta gua le proporciona todo lo que necesita para empezar a crear, en colar y ejecutar
trabajos en segundo plano.

Despus de leer esta gua, sabr:

Cmo crear trabajos.


Cmo encolar trabajos.
Cmo ejecutar trabajos en segundo plano.
Cmo enviar mensajes de correo electrnico desde su aplicacin de forma asncrona.

504
1- Introduccin

1- Introduccin
Active Jobs es un framework para declarar trabajos y hacer que funcionen en una variedad
de backends en cola. Estos trabajos pueden ser desde limpiezas programadas
regularmente, hasta cargos de facturacin, hasta envos por correo. Cualquier cosa que
pueda ser cortada en pequeas unidades de trabajo y correr en paralelo, en realidad.

505
2- El Propsito de Active Jobs

2- El Propsito de Active Jobs


El punto principal es asegurar que todas las aplicaciones de Rails tendrn la infraestructura
de jobs en su lugar. Podemos entonces tener las caractersticas del framework y otras
gemas construidas sobre eso, sin tener que preocuparse por las diferencias de las APIs
entre varios corredores de jobs como Delayed Job y Resque. Entonces, escoger el backend
de cola se convierte en una preocupacin operacional. Y usted podr variar entre ellos sin
tener que reescribir sus jobs.

Rails viene de forma predeterminada con una implementacin de cola asncrona que
ejecuta trabajos con un grupo de subprocesos en proceso. Los trabajos se ejecutarn
de forma asncrona, pero cualquier trabajo de la cola se eliminar al reiniciar.

506
3- Creacin de un Job

3- Creacin de un Job
Esta seccin proporcionar una gua paso a paso para crear un job y ponerlo en cola.

3.1 Crear el Job


Active Job proporciona un generador de Rails para crear trabajos. Lo siguiente crear un
job en app/jobs (con un caso de test adjunto en test/jobs ):

$ bin/rails generate job guests_cleanup


invoke test_unit
create test/jobs/guests_cleanup_job_test.rb
create app/jobs/guests_cleanup_job.rb

Tambin puede crear un trabajo que se ejecutar en una cola especfica:

$ bin/rails generate job guests_cleanup --queue urgent

Si no desea utilizar un generador, puede crear su propio archivo dentro de la app/ trabajos,
slo asegrese de que hereda de ApplicationJob .

As es como se ve un job:

class GuestsCleanupJob < ApplicationJob


queue_as :default

def perform(*guests)
# Do something later
end
end

Tenga en cuenta que puede definir perform con tantos argumentos como desee.

3.2 Encolando el job


Encole un job as:

# Encolara un job que se realizar tan pronto como el sistema de colas este libre
GuestsCleanupJob.perform_later guest

507
3- Creacin de un Job

# Encole un job que se ejecutar maana al medioda.


GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)

# Encole un job que se realizar en una semana a partir de ahora.


GuestsCleanupJob.set(wait: 1.week).perform_later(guest)

# `perform_now` y` perform_later` llamarn `perform` bajo la campana para que puedas p


asar
# tantos argumentos como se define en el segundo.
GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')

Eso es todo!

508
4- Ejecucin del Job

4- Ejecucin del Job


Para encolar y ejecutar jobs en produccin, debe configurar un backend de colas, es decir,
debe decidirse por una biblioteca de colas de terceros que Rails debera usar. Rails solo
proporciona un sistema de colas en proceso, que slo mantiene los trabajos en la RAM. Si
el proceso se bloquea o se restablece la mquina, todos los trabajos pendientes se pierden
con el backend asncrono predeterminado. Esto puede estar bien para aplicaciones ms
pequeas o trabajos no crticos, pero la mayora de las aplicaciones en produccin tendrn
que escoger un backend persistente.

4.1 Backends
Active Job tiene adaptadores integrados para mltiples backups de cola (Sidekiq, Resque,
Delayed Job y otros). Para obtener una lista actualizada de los adaptadores, consulte la
documentacin de la API para ActiveJob::QueueAdapters .

4.2 Configuracin del backend


Puede configurar fcilmente el backend de colas de esta manera:

# config/application.rb
module YourApp
class Application < Rails::Application
# Asegrate de tener la gema del adaptador en tu Gemfile
# y siga la instalacin especfica del adaptador
# y las instrucciones de implementacin.
config.active_job.queue_adapter = :sidekiq
end
end

Tambin puede configurar su back-end por cada Job.

class GuestsCleanupJob < ApplicationJob


self.queue_adapter = :resque
#....
end

# Ahora su job utilizar `resque` ya que es el adaptador de cola de backend que sobre-
escribe lo que
# se ha configurado en `config.active_job.queue_adapter`.

509
4- Ejecucin del Job

4.3 Inicio del backend


Dado que los jobs se ejecutan en paralelo con la aplicacin de Rails, la mayora de las
bibliotecas de colas requieren que se inicie un servicio de colas especfico de la biblioteca
(adems de iniciar la aplicacin Rails) para que el job funcione. Consulte la documentacin
de la biblioteca para obtener instrucciones sobre cmo iniciar el backend de la cola.

Aqu hay una lista no exhaustiva de documentacin:

Sidekiq
Resque
Sucker Punch
Queue Classic

510
5- Colas

5- Colas
La mayora de los adaptadores admiten varias colas. Con Active Job puede programar el
trabajo para que se ejecute en una cola especfica:

class GuestsCleanupJob < ApplicationJob


queue_as :low_priority
#....
end

Puede prefijar el nombre de la cola para todos sus trabajos utilizando


config.active_job.queue_name_prefix en application.rb :

# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
end
end

# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
#....
end

# Ahora su trabajo se ejecutar en la fila production_low_priority en su


# entorno de produccin y en staging_low_priority
# en su entorno de staging

El delimitador de prefijo de nombre de cola predeterminado es ' _ '. Esto se puede cambiar
configurando config.active_job.queue_name_delimiter en application.rb :

511
5- Colas

# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
config.active_job.queue_name_delimiter = '.'
end
end

# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
#....
end

# Ahora su trabajo se ejecutar en la fila production.low_priority en su


# entorno de produccin y en staging.low_priority
# en su entorno de staging

Si desea ms control sobre en qu cola se ejecutar un trabajo, puede pasar una opcin
:queue a :#set :

MyJob.set(queue: :another_queue).perform_later(record)

Para controlar la cola desde el nivel del job, puede pasar un bloque a #queue_as . El bloque
se ejecutar en el contexto del job (para poder acceder a self.arguments ) y debe devolver
el nombre de la cola:

class ProcessVideoJob < ApplicationJob


queue_as do
video = self.arguments.first
if video.owner.premium?
:premium_videojobs
else
:videojobs
end
end

def perform(video)
# Do process video
end
end

ProcessVideoJob.perform_later(Video.last)

Asegrese de que el servidor de cola "escucha" el nombre de su cola. Para algunos


backends es necesario especificar las colas para escuchar.

512
5- Colas

513
6- Callbacks

6- Callbacks
Trabajo activo proporciona hooks durante el ciclo de vida de un job. Las devoluciones de
llamada le permiten activar la lgica durante el ciclo de vida de un trabajo.

6.1 Devoluciones de llamada disponibles


before_enqueue

around_enqueue

after_enqueue

before_perform

around_perform

after_perform

6.2 Uso

class GuestsCleanupJob < ApplicationJob


queue_as :default

before_enqueue do |job|
# Do something with the job instance
end

around_perform do |job, block|


# Do something before perform
block.call
# Do something after perform
end

def perform
# Do something later
end
end

514
7- Action Mailer

7- Action Mailer
Uno de los trabajos ms comunes en una aplicacin web moderna es el envo de correos
electrnicos fuera del ciclo de solicitud y respuesta, por lo que el usuario no tiene que
esperar. Active Job se integra con Action Mailer para que pueda enviar correos electrnicos
de manera asincrnica:

# Si quieres enviar el correo electrnico ahora usa #deliver_now


UserMailer.welcome(@user).deliver_now

# Si desea enviar el correo electrnico a travs del active job use #deliver_later
UserMailer.welcome(@user).deliver_later

515
8- Internacionalizacin

8- Internacionalizacin
Cada job utiliza el conjunto I18n.locale cuando se crea el trabajo. Es til si enva
mensajes de correo electrnico de forma asncrona:

I18n.locale = :eo

UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.

516
9- GlobalID

9- GlobalID
Active Job admite GlobalID para los parmetros. Esto hace posible pasar los objetos vivos
de Active Record a su job en vez de los pares de class/id, que entonces usted tendra que
deserializar manualmente. Antes, los jobs se vean as:

class TrashableCleanupJob < ApplicationJob


def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
end
end

Ahora usted puede simplemente hacer:

class TrashableCleanupJob < ApplicationJob


def perform(trashable, depth)
trashable.cleanup(depth)
end
end

Esto funciona con cualquier clase que se mezcla en GlobalID::Identification , que por
defecto se ha mezclado en las clases Active Record.

517
10- Excepciones

10- Excepciones
Active Job proporciona una forma de detectar excepciones generadas durante la ejecucin
del job:

class GuestsCleanupJob < ApplicationJob


queue_as :default

rescue_from(ActiveRecord::RecordNotFound) do |exception|
# Do something with the exception
end

def perform
# Do something later
end
end

10.1 Deserializacin
GlobalID permite serializar los objetos Active Record completos pasados a #perform .

Si se elimina un registro pasado despus de que se encola el job pero antes de que el
mtodo #perform es llamado , Active Job generar una excepcin
ActiveJob::DeserializationError .

518
11- Job Testing

11- Job Testing


Puede encontrar instrucciones detalladas sobre cmo testear sus jobs en la gua de testing.

519
XVII - Testing de aplicaciones Rails

XVII - Testing de aplicaciones Rails


Esta gua cubre los mecanismos incorporados en Rails para probar su aplicacin.

Despus de leer esta gua, sabr:

Terminologa de testing de Rails.


Cmo escribir pruebas unitarias, funcionales, de integracin y de sistema para su
aplicacin.
Otros enfoques de testing populares y complementos.

520
1- Por qu escribir pruebas para sus aplicaciones Rails?

1- Por qu escribir pruebas para sus


aplicaciones Rails?
Rails hace que sea muy fcil escribir sus pruebas. Comienza realizando el esqueleto del
cdigo de prueba mientras que est creando sus modelos y controladores.

Simplemente ejecutando las pruebas de Rails, puede asegurarse de que su cdigo se


adhiera a la funcionalidad deseada incluso despus de algn refactoring importante de
cdigo.

Las pruebas de Rails tambin pueden simular solicitudes de navegador y, por tanto, puede
probar la respuesta de su aplicacin sin tener que probarla a travs de su navegador.

521
2- Introduccin a las pruebas

2- Introduccin a las pruebas


El soporte para hacer testing en Rails se realizo desde el principio. No fue como una
epifania tipo "oh, vamos a hacer el soporte para las pruebas de ejecucin porque son
nuevos y cool"

2.1 Rails se prepara para los testing desde la palabra Go


Rails crea un directorio de testing para usted tan pronto como crea un proyecto de Rails
utilizando los rails new application_name . Si enlista el contenido de este directorio, ver:

$ ls -F test
controllers/ helpers/
mailers/ system/ test_helper.rb
fixtures/ integration/
models/ application_system_test_case.rb

Los helpers, los mailers y los directorios de los modelos estn destinados a realizar pruebas
para los ayudantes de la vista, los mailers y los modelos, respectivamente. El directorio de
controladores est destinado a realizar pruebas para controladores, rutas y vistas. El
directorio de integracin est destinado a realizar pruebas de interacciones entre
controladores.

El directorio de prueba del sistema contiene pruebas del sistema, que se utilizan para la
prueba completa del navegador de su aplicacin. Las pruebas del sistema le permiten
probar su aplicacin de la misma manera que sus usuarios la experimentan y tambin
ayudarle a probar su JavaScript. Las pruebas del sistema heredan de Capybara y realizan
pruebas de navegador para su aplicacin.

Los fixtures son una manera de organizar los datos de la prueba; Residen en el directorio
fixtures .

Tambin se crear un directorio de jobs cuando se genere una prueba asociada.

El archivo test_helper.rb contiene la configuracin predeterminada para las pruebas.

El archivo application_system_test_case.rb contiene la configuracin predeterminada para


las pruebas del sistema.

2.2 El entorno de test


Por defecto, cada aplicacin de Rails tiene tres entornos: desarrollo, test y produccin.

522
2- Introduccin a las pruebas

La configuracin de cada entorno se puede modificar de forma similar. En este caso,


podemos modificar nuestro entorno de prueba cambiando las opciones encontradas en
config/environments/test.rb .

Sus pruebas se ejecutan bajo RAILS_ENV=test .

2.3 Rails usa Minitest


Si te acuerdas, usamos el comando rails generate model en la gua Getting Started with
Rails. Hemos creado nuestro primer modelo, y entre otras cosas cre stubs de prueba en el
directorio de prueba:

$ bin/rails generate model article title:string body:text


...
create app/models/article.rb
create test/models/article_test.rb
create test/fixtures/articles.yml
...

El stub de test predeterminado se encuentra en test/models/article_test.rb tiene este


aspecto:

require 'test_helper'

class ArticleTest < ActiveSupport::TestCase


# test "the truth" do
# assert true
# end
end

Un examen lnea por lnea de este archivo le ayudar a orientarlo a las pruebas cdigo con
Rails y la terminologa.

require 'test_helper'

Al solicitar este archivo, test_helper.rb se carga en la configuracin predeterminada para


ejecutar nuestras pruebas. Vamos a incluir esto con todas las pruebas que escribimos, por
lo que cualquier mtodo aadido a este archivo estn disponibles para todas nuestras
pruebas.

class ArticleTest < ActiveSupport::TestCase

523
2- Introduccin a las pruebas

La clase ArticleTest define un caso de prueba porque hereda de


ActiveSupport::TestCase . Por lo tanto, ArticleTest tiene todos los mtodos disponibles de

ActiveSupport::TestCase . Ms adelante en esta gua, veremos algunos de los mtodos que

nos brinda.

Cualquier mtodo definido en una clase heredada de Minitest::Test (que es la superclase


de ActiveSupport::TestCase ) que comienza con test_ (sensible a maysculas y
minsculas) se llama simplemente test. Por lo tanto, los mtodos definidos como
test_password y test_valid_password son nombres de prueba legales y se ejecutan

automticamente cuando se ejecuta el caso de prueba.

Rails tambin agrega un mtodo de prueba que toma un nombre de test y un bloque.
Genera una prueba normal de Minitest::Unit con nombres de mtodos prefijados con
test_ . As que no tienes que preocuparte por nombrar los mtodos, y puedes escribir algo

como:

test "the truth" do


assert true
end

Que es mas o menos como escribir esto:

def test_the_truth
assert true
end

Aunque todava puede utilizar definiciones de mtodos habituales, el uso de la macro de


prueba permite un nombre de prueba ms legible.

El nombre del mtodo se genera reemplazando espacios con subrayados. El resultado


no necesita ser un identificador de Ruby vlido, aunque el nombre puede contener
caracteres de puntuacin, etc. Eso es porque en Ruby tcnicamente cualquier cadena
puede ser un nombre de mtodo. Esto puede requerir el uso de define_method y
enviar llamadas para funcionar correctamente, pero formalmente hay poca restriccin
en el nombre.

A continuacin, echemos un vistazo a nuestra primera assertion:

assert true

Una asercin es una lnea de cdigo que evala un objeto (o expresin) para los resultados
esperados. Por ejemplo, una asercin puede comprobar:

524
2- Introduccin a las pruebas

Es este valor = a este valor?


Este objeto es nil?
Esta lnea de cdigo arroja una excepcin?
Es la contrasea del usuario mayor de 5 caracteres?

Cada prueba puede contener una o ms aserciones, sin restriccin en cuanto a cuntas
aserciones se permiten. Slo cuando todas las aserciones sean exitosas, la prueba pasar.

2.3.1 Su primera prueba fallida


Para ver cmo se informa un error de testing, puede agregar una prueba de error al caso de
prueba article_test.rb .

test "should not save article without title" do


article = Article.new
assert_not article.save
end

Vamos a ejecutar esta prueba recin agregada (donde 6 es el nmero de lnea donde se
define la prueba).

$ bin/rails test test/models/article_test.rb:6


Run options: --seed 44656

# Running:

Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/arti
cle_test.rb:6]:
Expected true to be nil or false

bin/rails test test/models/article_test.rb:6

Finished in 0.023918s, 41.8090 runs/s, 41.8090 assertions/s.

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

En la salida, F denota un fallo. Puede ver la traza correspondiente que se muestra en


Failure junto con el nombre de la prueba que falla. Las siguientes lneas contienen el

seguimiento de la pila seguido de un mensaje que menciona el valor real y el valor


esperado por la asercin. Los mensajes de asercin predeterminados proporcionan

525
2- Introduccin a las pruebas

suficiente informacin para ayudar a identificar el error. Para que el mensaje de fallo de
asercin sea ms legible, cada asercin proporciona un parmetro de mensaje opcional,
como se muestra aqu:

test "should not save article without title" do


article = Article.new
assert_not article.save, "Saved the article without a title"
end

Al ejecutar esta prueba se muestra el mensaje de asercin ms amigable:

Failure:
ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/arti
cle_test.rb:6]:
Saved the article without a title

Ahora para pasar esta prueba podemos agregar una validacin de nivel de modelo para el
campo de ttulo.

class Article < ApplicationRecord


validates :title, presence: true
end

Ahora la prueba debe pasar. Vamos a verificar la ejecucin de la prueba de nuevo:

$ bin/rails test test/models/article_test.rb:6


Run options: --seed 31252

# Running:

Finished in 0.027476s, 36.3952 runs/s, 36.3952 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

Ahora, si te fijaste, primero escribimos una prueba que falla para una funcionalidad
deseada, luego escribimos algn cdigo que agrega la funcionalidad y finalmente nos
aseguramos de que nuestra prueba pase. Este enfoque para el desarrollo de software se
conoce como Test-Driven Development (TDD).

2.3.2 Lo que parece un error


Para ver cmo se informa de un error, aqu hay una prueba que contiene un error:

526
2- Introduccin a las pruebas

test "should report error" do


# some_undefined_variable is not defined elsewhere in the test case
some_undefined_variable
assert true
end

Ahora puedes ver ms informacin en la salida de la consola ejecutando las pruebas:

$ bin/rails test test/models/article_test.rb


Run options: --seed 1808

# Running:

.E

Error:
ArticleTest#test_should_report_error:
NameError: undefined local variable or method 'some_undefined_variable' for #<ArticleT
est:0x007fee3aa71798>
test/models/article_test.rb:11:in 'block in <class:ArticleTest>'

bin/rails test test/models/article_test.rb:9

Finished in 0.040609s, 49.2500 runs/s, 24.6250 assertions/s.

2 runs, 1 assertions, 0 failures, 1 errors, 0 skips

Observe el 'E' en la salida. Denota una prueba con error.

La ejecucin de cada mtodo de prueba se detiene en cuanto se produce un error o un


fallo de asercin y el conjunto de pruebas contina con el siguiente mtodo. Todos los
mtodos de prueba se ejecutan en orden aleatorio. La opcin
config.active_support.test_order se puede utilizar para configurar el orden de las

pruebas.

Cuando una prueba falla se le presenta el backtrace correspondiente. De forma


predeterminada, Rails filtra el backtrace y slo imprime lneas relevantes para su aplicacin.
Esto elimina el ruido del framework y ayuda a centrarse en su cdigo. Sin embargo, hay
situaciones en las que desea ver el backtrace completo. Simplemente establezca el
argumento -b (o --backtrace ) para habilitar este comportamiento:

$ bin/rails test -b test/models/article_test.rb

527
2- Introduccin a las pruebas

Si queremos que pase esta prueba, podemos modificarla para usar assert_raises as:

test "should report error" do


# some_undefined_variable is not defined elsewhere in the test case
assert_raises(NameError) do
some_undefined_variable
end
end

Esta prueba debera pasar.

2.4 Aserciones disponibles


A estas alturas ya has visto algunas de las aserciones que estn disponibles. Las
aserciones son las abejas obreras de las pruebas. Son los que realmente realizan los
chequeos para asegurarse de que las cosas van como estn planificadas.

He aqu un extracto de las aserciones que puede utilizar con Minitest, la biblioteca de
pruebas predeterminada utilizada por Rails. El parmetro [msg] es un mensaje de cadena
opcional que puede especificar para que los mensajes de error de prueba sean ms claros.

Assertion Purpose
assert( test, [msg] ) Se asegura que test es true.
assert_not( test, [msg] ) Ensures that test es false.
assert_equal( expected,
actual, [msg] ) Se asegura que expected == actual es true.
assert_not_equal( expected,
actual, [msg] ) Se asegura que expected != actual es true.
assert_same( expected,
actual, [msg] ) Se asegura que expected.equal?(actual) es true.
assert_not_same( expected,
actual, [msg] ) Se asegura que expected.equal?(actual) es false.

assert_nil( obj, [msg] ) Se asegura que obj.nil? es true.


assert_not_nil( obj, [msg]
) Se asegura que obj.nil? es false.

assert_empty( obj, [msg] ) Se asegura que obj is empty? .


assert_not_empty( obj,
[msg] ) Se asegura que obj no es empty? .

assert_match( regexp, Se asegura que el match entre un string y una


string, [msg] ) expresion regular es true.
assert_no_match( regexp, Se asegura que un string no hace match con una
string, [msg] ) expresion regular.
assert_includes(

528
2- Introduccin a las pruebas

collection, obj, [msg] )

assert_not_includes(
collection, obj, [msg] ) Se asegura que obj no esta en collection .

assert_in_delta( expected, Se asegura que los numeros expected and actual are
actual, [delta], [msg] ) within delta of each other.
assert_not_in_delta(
expected, actual, [delta],
Se asegura que the numbers expected and actual are
[msg] ) not within delta of each other.
assert_throws( symbol,
[msg] ) { block } Se asegura que el bloque dado lanza un simbolo

assert_raises( exception1, Se asegura que el bloque dado plantea una de las


exception2, ... ) { block } excepciones dadas.
assert_instance_of( class,
obj, [msg] ) Se asegura que obj es una instancia de la class .
assert_not_instance_of(
class, obj, [msg] ) Se asegura que obj no es una instancia de la class .

assert_kind_of( class, obj, Se asegura que obj es una instancia de la class o es


[msg] ) decendente de la misma.
assert_not_kind_of( class, Se asegura que obj no es una instancia de la class y
obj, [msg] ) no es decendente de la misma
assert_respond_to( obj,
symbol, [msg] ) Se asegura que obj responde a un symbol .
assert_not_respond_to( obj,
symbol, [msg] ) Se asegura que obj no responde a un symbol .

assert_operator( obj1,
operator, [obj2], [msg] ) Se asegura que obj1.operator(obj2) es true.
assert_not_operator( obj1,
operator, [obj2], [msg] ) Se asegura que obj1.operator(obj2) es false.

assert_predicate ( obj, Se asegura que obj.predicate es true, por


predicate, [msg] ) ejemplo. assert_predicate str, :empty?

assert_not_predicate ( obj, Se asegura que obj.predicate es false, por


predicate, [msg] ) ejemplo. assert_not_predicate str, :empty?

flunk( [msg] )
Se asegura que sea failure. Esto es til para marcar
explcitamente una prueba que an no ha terminado.

Lo anterior es un subconjunto de afirmaciones que admite minitest. Para obtener una lista
exhaustiva y actualizada, consulte la documentacin de la API de Minitest, especficamente
Minitest::Assertions.

Debido a la naturaleza modular del framework de testing, es posible crear sus propias
aserciones. De hecho, eso es exactamente lo que hace Rails. Incluye algunas aserciones
especializadas para hacer su vida ms fcil.

Crear sus propias aserciones es un tema avanzado que no cubriremos en este tutorial.

529
2- Introduccin a las pruebas

2.5 Aserciones especficas de Rails


Rails aade algunas aserciones personalizadas propias al framework de minitest:

530
2- Introduccin a las pruebas

Assertion Purpose

assert_difference(expressions, Probar la diferencia numrica entre el valor


difference = 1, message = nil) devuelto de una expresin como resultado de lo
{...}
que se evala en el bloque cedido.
Afirma que el resultado numrico de evaluar
assert_no_difference(expressions,
message = nil, &block) una expresin no se cambia antes y despus
de invocar el pasado en un bloque.

assert_nothing_raised { block }
Asegura que el bloque dado no plantea ninguna
excepcin.
Afirma que el enrutamiento de la ruta de acceso
dada se ha manejado correctamente y que las
assert_recognizes(expected_options, opciones analizadas (dadas en el hash
path, extras={}, message=nil) expected_options) coinciden con la ruta.
Bsicamente, afirma que Rails reconoce la ruta
dada por expected_options.
Afirma que las opciones proporcionadas
pueden usarse para generar la ruta de acceso
proporcionada. Este es el inverso de
assert_recognizes. El parmetro extras se
assert_generates(expected_path,
options, defaults={}, extras = {},
utiliza para indicar a la solicitud los nombres y
message=nil) valores de los parmetros de peticin
adicionales que estaran en una cadena de
consulta. El parmetro de mensaje le permite
especificar un mensaje de error personalizado
para los fallos de asercin.
Afirma que la respuesta viene con un cdigo de
estado especfico. Puede especificar :success
para indicar 200-299, :redirect para indicar 300-
399, :missing para indicar 404, o :errorpara
assert_response(type, message =
nil) coincidir con el rango 500-599. Tambin puede
pasar un nmero de estado explcito o su
equivalente simblico. Para obtener ms
informacin, consulte la lista completa de los
cdigos de estado y cmo.

Afirma que las opciones de redireccin pasadas


coinciden con las de la redireccin que se llama
en la accin ms reciente. Este partido puede
ser parcial, por ejemplo, que
asert_redirected_to (controller: "weblog")
assert_redirected_to(options = {}, tambin coincide con la redireccin de
message=nil) redirector_to (controlador: "weblog", accin:
"show") y as sucesivamente. Tambin puede
pasar rutas nombradas como
asassert_redirected_to root_pathand Active
Record objects such asassert_redirected_to
@article.

531
2- Introduccin a las pruebas

Veremos el uso de algunas de estas aserciones en el prximo captulo.

2.6 Una breve nota sobre los casos de prueba


Todas las aserciones bsicas como assert_equal definidas en Minitest::Assertions
tambin estn disponibles en las clases que usamos en nuestros propios casos de prueba.
De hecho, Rails proporciona las siguientes clases para que usted herede de:

ActiveSupport::TestCase

ActionMailer::TestCase

ActionView::TestCase

ActionDispatch::IntegrationTest

ActiveJob::TestCase

ActionDispatch::SystemTestCase

Cada una de estas clases incluye Minitest::Assertions , lo que nos permite usar todas las
aserciones bsicas en nuestras pruebas.

Para obtener ms informacin sobre Minitest, consulte su documentacin.

2.7 El corredor de pruebas de Rails


Podemos ejecutar todas nuestras pruebas a la vez usando el comando bin / rails test .

O podemos ejecutar un solo archivo de prueba pasando el comando bin / rails test al
nombre de archivo que contiene los casos de prueba.

$ bin/rails test test/models/article_test.rb


Run options: --seed 1559

# Running:

..

Finished in 0.027034s, 73.9810 runs/s, 110.9715 assertions/s.

2 runs, 3 assertions, 0 failures, 0 errors, 0 skips

Esto ejecutar todos los mtodos de prueba del caso de prueba.

Tambin puede ejecutar un mtodo de prueba particular desde el caso de prueba


proporcionando el indicador -n o --name y el nombre del mtodo de la prueba.

532
2- Introduccin a las pruebas

$ bin/rails test test/models/article_test.rb -n test_the_truth


Run options: -n test_the_truth --seed 43583

# Running:

Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Tambin puede ejecutar una prueba en una lnea especfica proporcionando el nmero de
lnea.

$ bin/rails test test/models/article_test.rb:6 # run specific test and line

Tambin puede ejecutar un directorio completo de pruebas proporcionando la ruta al


directorio.

$ bin/rails test test/controllers # run all tests from specific directory

El corredor de prueba tambin proporciona muchas otras caractersticas como failing fast,
diferir la salida de la prueba al final de la prueba, y as sucesivamente. Compruebe la
documentacin del test runner de la siguiente manera:

533
2- Introduccin a las pruebas

$ bin/rails test -h
minitest options:
-h, --help Display this help.
-s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
-v, --verbose Verbose. Show progress processing files.
-n, --name PATTERN Filter run on /regexp/ or string.
--exclude PATTERN Exclude /regexp/ or string from run.

Known extensions: rails, pride

Usage: bin/rails test [options] [files or directories]


You can run a single test by appending a line number to a filename:

bin/rails test test/models/user_test.rb:27

You can run multiple files and directories at the same time:

bin/rails test test/controllers test/integration/login_test.rb

By default test failures and errors are reported inline during a run.

Rails options:
-e, --environment ENV Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test ru
n
-f, --fail-fast Abort test run on first failure or error
-c, --[no-]color Enable color in the output

534
3- Base de datos del testing

3- Base de datos del testing


Casi todas las aplicaciones de Rails interactan fuertemente con una base de datos y, como
resultado, sus pruebas tambin necesitarn una base de datos para interactuar. Para
escribir pruebas eficientes, necesitar entender cmo configurar esta base de datos y
rellenarla con datos de ejemplo.

Por defecto, cada aplicacin de Rails tiene tres entornos: desarrollo, testing y produccin.
La base de datos para cada uno de ellos se configura en config/database.yml .

Una base de datos de pruebas dedicada le permite configurar e interactuar con datos de
prueba de forma aislada. De esta manera sus pruebas pueden manipular datos de prueba
con confianza, sin preocuparse por los datos en las bases de datos de desarrollo o
produccin.

3.1 Mantenimiento del esquema de la base de datos de


prueba
Para ejecutar las pruebas, la base de datos de prueba necesitar tener la estructura actual.
El asistente de prueba comprueba si la base de datos de prueba tiene alguna migracin
pendiente. Intentar cargar su db/schema.rb o db/structure.sql en la base de datos de
prueba. Si las migraciones an estn pendientes, se generar un error. Por lo general, esto
indica que su esquema no se ha migrado completamente. La ejecucin de las migraciones
en la base de datos de desarrollo ( bin/rails db:migrate ) actualizar el esquema.

Si hubo modificaciones en las migraciones existentes, es necesario reconstruir la base de


datos de prueba. Esto se puede hacer ejecutando bin/ rails db:test:prepare .

3.2 El Low-Down en los Fixtures


Para tener buenos test, tendr que pensar un poco en la configuracin de los datos de
prueba. En Rails, puede manejar esto definiendo y personalizando los fixtures. Puede
encontrar la documentacin completa en la documentacin de la API de Fixtures.

3.2.1 Qu son los fixtures?


Fixtures es una palabra ficticia para los datos de la muestra. Los fixtures permiten llenar la
base de datos de pruebas con datos predefinidos antes de que se ejecuten las pruebas. Los
fixtures son independientes de la base de datos y estn escritos en YAML. Hay un archivo
por modelo.

535
3- Base de datos del testing

Los fixtures no estn diseados para crear todos los objetos que sus pruebas
necesitan, y se administran mejor cuando se utilizan slo para datos predeterminados
que se pueden aplicar al caso comn.

Encontrar los fixtures bajo su directorio de test/fixtures . Al ejecutar rails generate


model para crear un nuevo modelo, Rails crear automticamente el stub de fixtures en

este directorio.

3.2.2 YAML
Los fixtures con formato YAML son una manera amigable para el usuario de describir sus
datos de muestra. Estos tipos de fixtures tienen la extensin .yml (como en users.yml ).

He aqu un ejemplo de los fixtures en YAML:

# lo & behold! I am a YAML comment!


david:
name: David Heinemeier Hansson
birthday: 1979-10-15
profession: Systems development

steve:
name: Steve Ross Kellock
birthday: 1974-09-27
profession: guy with keyboard

A cada fixture se le asigna un nombre seguido por una lista identada de llave/valor
separados por dos puntos. Los registros suelen estar separados por una lnea en blanco.
Puede colocar comentarios en un archivo de fixtures se hace usando el carcter # en la
primera columna.

Si est trabajando con asociaciones, simplemente puede definir un nodo de referencia entre
dos fixtures diferentes. He aqu un ejemplo con una asociacin belongs_to / has_many :

# In fixtures/categories.yml
about:
name: About

# In fixtures/articles.yml
first:
title: Welcome to Rails!
body: Hello world!
category: about

536
3- Base de datos del testing

Observe la clave category del artculo first que se encuentra en fixtures/articles.yml


y que tiene un valor about . Esto le dice a Rails que cargue la categora que se encuentra
en fixtures/categories.yml .

Para que las asociaciones se refieran entre s por nombres, puede utilizar el nombre
del fixture en lugar de especificar el atributo :id en los fixtures asociados. Rails
asignar automticamente una clave primaria para que sea consistente entre las
ejecuciones. Para obtener ms informacin sobre este comportamiento de la
asociacin, lea la documentacin de la API de Fixtures.

3.2.3 ERB'in It Up
ERB le permite incrustar cdigo Ruby dentro de plantillas. El formato de fixtures YAML se
pre-procesa con ERB cuando Rails carga los fixtures. Esto le permite utilizar Ruby para
ayudarle a generar algunos datos de ejemplo. Por ejemplo, el cdigo siguiente genera un
millar de usuarios:

<% 1000.times do |n| %>


user_<%= n %>:
username: <%= "user#{n}" %>
email: <%= "user#{n}@example.com" %>
<% end %>

3.2.4 Fixtures en accin


Rails carga automticamente todos los accesorios del directorio de test/fixtures por
defecto. La carga implica tres pasos:

Elimine todos los datos existentes de la tabla correspondiente al fixture


Cargue los datos del fixture en la tabla
Vaciar los datos del ficture en un mtodo en caso de que desee acceder a l
directamente

Para eliminar los datos existentes de la base de datos, Rails intenta deshabilitar los
disparadores de integridad referencial (como claves externas y restricciones de
comprobacin). Si est recibiendo errores de permisos molestos en las pruebas en
ejecucin, asegrese de que el usuario de la base de datos tenga privilegios para
desactivar estos disparadores en el entorno de prueba. (En PostgreSQL, slo los
superusuarios pueden deshabilitar todos los disparadores.) Ms informacin sobre los
permisos de PostgreSQL aqu).

3.2.5 Los fixtures son objetos de Active Record

537
3- Base de datos del testing

Los fixtures son instancias de Active Record. Como se menciona en el punto # 3 anterior,
puede acceder directamente al objeto porque est disponible automticamente como un
mtodo cuyo mbito es local es el caso de prueba. Por ejemplo:

# this will return the User object for the fixture named david
users(:david)

# this will return the property for david called id


users(:david).id

# one can also access methods available on the User class


david = users(:david)
david.call(david.partner)

Para obtener varios fixtures a la vez, puede pasar una lista de nombres de los fixtures. Por
ejemplo:

# this will return an array containing the fixtures david and steve
users(:david, :steve)

538
4- Pruebas de modelos

4- Pruebas de modelos
Las pruebas del modelo se utilizan para probar varios modelos de su aplicacin.

Las pruebas del modelo Rails se almacenan en el directorio test/models . Rails


proporciona un generador para crear un esqueleto de pruebas de modelo por usted.

$ bin/rails generate test_unit:model article title:string body:text


create test/models/article_test.rb
create test/fixtures/articles.yml

Las pruebas de modelo no tienen su propia superclase como ActionMailer::TestCase en


cambio heredan de ActiveSupport::TestCase .

539
5- Pruebas del sistema

5- Pruebas del sistema


Las pruebas del sistema permiten probar las interacciones del usuario con su aplicacin,
ejecutando pruebas en un navegador real o por medio de los headers. Las pruebas del
sistema utilizan Capybara como base.

Para crear las pruebas del sistema Rails, utilice el directorio test/system de su aplicacin.
Rails proporciona un generador para crear un esqueleto de prueba del sistema por usted.

$ bin/rails generate system_test users


invoke test_unit
create test/system/users_test.rb

Esto es lo que parece una prueba de sistema recin generada:

require "application_system_test_case"

class UsersTest < ApplicationSystemTestCase


# test "visiting the index" do
# visit users_url
#
# assert_selector "h1", text: "Users"
# end
end

De forma predeterminada, las pruebas del sistema se ejecutan con el controlador Selenium,
mediante el navegador Chrome y un tamao de pantalla de 1400x1400. La siguiente
seccin explica cmo cambiar la configuracin predeterminada.

5.1 Cambio de la configuracin predeterminada


Rails hace que el cambio de la configuracin predeterminada para las pruebas del sistema
sea muy sencillo. Toda la configuracin se abstrae para que pueda concentrarse en escribir
sus pruebas.

Cuando genera una nueva aplicacin o scaffold, se crea un archivo


application_system_test_case.rb en el directorio de text. Aqu es donde debe vivir toda la

configuracin de las pruebas del sistema.

Si desea cambiar la configuracin predeterminada, simplemente puede cambiar lo que las


pruebas del sistema hacen como "driven by". Digamos que desea cambiar el controlador de
Selenium a Poltergeist. Primero agrega la gema Poltergeist a tu Gemfile. A continuacin, en

540
5- Pruebas del sistema

su archivo application_system_test_case.rb haga lo siguiente:

require "test_helper"
require "capybara/poltergeist"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase


driven_by :poltergeist
end

El nombre del controlador es un argumento obligatorio para driven_by . Los argumentos


opcionales que se pueden pasar a driven_by son :using para el navegador (esto slo
ser utilizado por Selenium), :screen_size para cambiar el tamao de la pantalla para
capturas de pantalla, y :options que se pueden usar para establecer opciones soportadas
por el driver.

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase


driven_by :selenium, using: :firefox
end

Si su configuracin de Capybara requiere ms configuracin que la proporcionada por Rails,


esta configuracin adicional podra agregarse al archivo application_system_test_case.rb .

Consulte la documentacin de Capybara para obtener informacin adicional.

5.2 Helper de captura de pantalla


El ScreenshotHelper es un ayudante diseado para capturar screenchots de tus pruebas.
Esto puede ser til para ver el navegador en el punto en el que fall una prueba o para ver
capturas de pantalla ms tarde para la depuracin.

Se proporcionan dos mtodos: take_screenshot y take_failed_screenshot .


take_failed_screenshot se incluye automticamente en after_teardown dentro de Rails.

El mtodo de ayuda de take_screenshot se puede incluir en cualquier lugar de las pruebas


para tomar una captura de pantalla del navegador.

5.3 Implementacin de una prueba del sistema


Ahora vamos a agregar una prueba del sistema a nuestra aplicacin de blog. Demostramos
que escribimos una prueba del sistema visitando la pgina de index y creando un nuevo
artculo de blog.

541
5- Pruebas del sistema

Si utiliz el scaffold, un esqueleto de test del sistema se cre automticamente por usted. Si
no us el scaffold, comience creando un esqueleto de prueba del sistema.

$ bin/rails generate system_test articles

Debera haber creado un marcador de archivo de prueba por nosotros. Con la salida del
comando anterior debera ver:

invoke test_unit
create test/system/articles_test.rb

Ahora vamos a abrir ese archivo y escribir su primera asercin:

require "application_system_test_case"

class ArticlesTest < ApplicationSystemTestCase


test "viewing the index" do
visit articles_path
assert_selector "h1", text: "Articles"
end
end

La prueba debe ver que hay un h1 en el ndice de artculos y pasa.

Ejecute las pruebas del sistema.

bin/rails test:system

De forma predeterminada, correr el comando bin / rails test no ejecutar las


pruebas del sistema. Asegrese de ejecutar bin / rails test:system para ejecutarlos
realmente.

5.3.1 Creacin de articulos para testearlos como prueba de


sistema
Ahora probemos el flujo para crear un nuevo artculo en nuestro blog.

542
5- Pruebas del sistema

test "creating an article" do


visit articles_path

click_on "New Article"

fill_in "Title", with: "Creating an Article"


fill_in "Body", with: "Created this article successfully!"

click_on "Create Article"

assert_text "Creating an Article"


end

El primer paso es llamar a articles_path . Esto llevar la prueba a la pgina de ndice de


artculos.

Entonces click_on "New Article" encontrar el botn "New Article" en la pgina index. Esto
redireccionar el navegador a /articles/new .

A continuacin, la prueba rellenar el ttulo y el cuerpo del artculo con el texto especificado.
Una vez rellenados los campos, se hace clic en "Crete Article" en el que se enviar una
solicitud POST para crear el nuevo artculo en la base de datos.

Seremos redirigidos de nuevo a la pgina de ndice de artculos y all hacemos un assert


para que el texto del ttulo del artculo est en la pgina de ndice de artculos.

5.3.2 Haciendo ms
La belleza de las pruebas del sistema es que es similar a las pruebas de integracin, ya que
prueba la interaccin del usuario con su controlador, modelo y vista, pero las pruebas del
sistema son mucho ms robustas y, de hecho, prueban su aplicacin como si un usuario
real la estuviera utilizando. A continuacin, puede probar cualquier cosa que el propio
usuario pueda hacer en su aplicacin, como comentar, eliminar artculos, publicar artculos
de borrador, etc.

543
6- Pruebas de integracin

6- Pruebas de integracin
Las pruebas de integracin se utilizan para probar cmo interactan varias partes de su
aplicacin. Generalmente se utilizan para probar flujos de trabajo importantes dentro de
nuestra aplicacin.

Para crear las pruebas de integracin de Rails, usamos el directorio de test/integration


para nuestra aplicacin. Rails proporciona un generador para crear un esqueleto de prueba
de integracin por nosotros.

$ bin/rails generate integration_test user_flows


exists test/integration/
create test/integration/user_flows_test.rb

Esto es lo que muestra una prueba de integracin recin generada:

require 'test_helper'

class UserFlowsTest < ActionDispatch::IntegrationTest


# test "the truth" do
# assert true
# end
end

Aqu la prueba hereda de ActionDispatch::IntegrationTest . Esto hace que algunos helpers


adicionales estn disponibles para que los utilicemos en nuestras pruebas de integracin.

6.1 Helpers disponibles para pruebas de integracin


Adems de los helpers de pruebas estndar, heredando de
ActionDispatch::IntegrationTest vendr con algunos helpers adicionales disponibles al

escribir las pruebas de integracin. Vamos a presentar brevemente a las tres categoras de
helpers que podemos elegir.

Para tratar con el corredor de pruebas de integracin, vea


ActionDispatch::Integration::Runner .

Al realizar las solicitudes, dispondremos de ActionDispatch::Integration::RequestHelpers


para nuestro uso.

Si necesitamos modificar la sesin o el estado de nuestra prueba de integracin, eche un


vistazo a ActionDispatch::Integration::Session para ver ayudas.

544
6- Pruebas de integracin

6.2 Implementacin de una prueba de integracin


Aadamos una prueba de integracin a nuestra aplicacin de blog. Comenzaremos con un
flujo de trabajo bsico para crear un nuevo artculo de blog, para verificar que todo funciona
correctamente.

Empezaremos generando nuestro esqueleto de integracin:

$ bin/rails generate integration_test blog_flow

Debera haber creado un marcador de archivo de prueba para nosotros. Con la salida del
comando anterior deberamos ver:

invoke test_unit
create test/integration/blog_flow_test.rb

Ahora vamos a abrir ese archivo y escribir su primera asercin:

require 'test_helper'

class BlogFlowTest < ActionDispatch::IntegrationTest


test "can see the welcome page" do
get "/"
assert_select "h1", "Welcome#index"
end
end

Echaremos un vistazo a assert_select para consultar el HTML resultante de una solicitud


en la seccin "Vistas de prueba" a continuacin. Se utiliza para probar la respuesta de
nuestra solicitud afirmando la presencia de elementos clave HTML y su contenido.

Cuando visitamos nuestro path de raz, debemos ver welcome/index.html.erb renderizado


por la vista. As que esta asercin debe pasar.

6.2.1 Creacin de integracin de artculos


Qu tal probar nuestra capacidad de crear un nuevo artculo en nuestro blog y ver el artculo
resultante.

545
6- Pruebas de integracin

test "can create an article" do


get "/articles/new"
assert_response :success

post "/articles",
params: { article: { title: "can create", body: "article successfully." } }
assert_response :redirect
follow_redirect!
assert_response :success
assert_select "p", "Title:\n can create"
end

Vamos a romper esta prueba para poder entenderla.

Empezamos llamando a la accin :new en nuestro controlador de artculos. Esta respuesta


debe tener xito.

Despus de esto hacemos una solicitud de post a la accin :create de nuestro controlador
de artculos:

post "/articles",
params: { article: { title: "can create", body: "article successfully." } }
assert_response :redirect
follow_redirect!

Las dos lneas que siguen a la peticin son para manejar la redireccin que configuramos al
crear un nuevo artculo.

No olvide llamar a follow_redirect ! Si planea realizar solicitudes posteriores despus


de realizar una redireccin.

Finalmente podemos afirmar(assert) que nuestra respuesta fue exitosa y nuestro nuevo
artculo es legible en la pgina.

6.2.2 Seguir avanzando


Hemos podido probar con xito un flujo de trabajo muy pequeo para visitar nuestro blog y
crear un nuevo artculo. Si quisiramos llevar esto ms lejos, podramos aadir pruebas
para comentar, eliminar artculos o editar comentarios. Las pruebas de integracin son un
gran lugar para experimentar con todo tipo de casos de uso para nuestras aplicaciones.

546
7- Pruebas funcionales para sus controladores

7- Pruebas funcionales para sus


controladores
En Rails, probar las diversas acciones de un controlador es una forma de escribir pruebas
funcionales. Recuerde que sus controladores manejan las solicitudes web entrantes en su
aplicacin y finalmente responden con una vista renderizada. Al escribir pruebas
funcionales, est probando cmo sus acciones manejan las solicitudes y el resultado
esperado o respuesta, que en algunos casos es una vista HTML.

7.1 Qu incluir en sus pruebas funcionales


Debe probar cosas como:

Fue la solicitud web exitosa?


Fue redirigido el usuario a la pgina correcta?
El usuario ha sido autenticado correctamente?
Fue almacenado el objeto correcto en la plantilla de respuesta?
Fue apropiado el mensaje mostrado al usuario en la vista?

La forma ms fcil de ver las pruebas funcionales en accin es generar un controlador


utilizando el generador scaffold:

$ bin/rails generate scaffold_controller article title:string body:text


...
create app/controllers/articles_controller.rb
...
invoke test_unit
create test/controllers/articles_controller_test.rb
...

Esto generar el cdigo del controlador y las pruebas de un recurso(resource) de artculos.


Puede echar un vistazo al archivo articles_controller_test.rb en el directorio
test/controllers .

Si ya tiene un controlador y slo desea generar el cdigo scaffold de prueba para cada una
de las siete acciones predeterminadas, puede utilizar el siguiente comando:

547
7- Pruebas funcionales para sus controladores

$ bin/rails generate test_unit:scaffold article


...
invoke test_unit
create test/controllers/articles_controller_test.rb
...

Echemos un vistazo a una prueba, test_should_get_index del archivo


articles_controller_test.rb .

# articles_controller_test.rb
class ArticlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get articles_url
assert_response :success
end
end

En la prueba test_should_get_index , Rails simula una solicitud en la accin llamada index,


asegurndose de que la solicitud se realiz correctamente y tambin asegurndose de que
se ha generado el cuerpo de respuesta correcto.

El mtodo get inicia la solicitud web y rellena los resultados en @response . Puede aceptar
hasta 6 argumentos:

La URI de la accin del controlador que est solicitando. Esto puede tener la forma de una
cadena o un helper de ruta (por ejemplo, articles_url ).

La opcion params: con un hash de parmetros de peticin para pasar a la accin (por
ejemplo, parmetros de cadena de consulta o variables de artculo).
headers: para establecer los encabezados que se pasarn con la solicitud.

env: para personalizar el entorno de solicitud segn sea necesario.

xhr: si la solicitud es Ajax o no. Se puede establecer en true para marcar la solicitud

como Ajax.
as: para codificar la solicitud con diferentes tipos de contenido. Soporta :json por

defecto.

Todos estos argumentos de palabra clave son opcionales.

Ejemplo: Llamando la accin : show y pasando un id de 12 como params y configurando el


encabezado HTTP_REFERER :

get article_url, params: { id: 12 }, headers: { "HTTP_REFERER" => "http://example.com/


home" }

548
7- Pruebas funcionales para sus controladores

Otro ejemplo: Llamar a la accin :update , pasando un id de 12 como params como una
solicitud de Ajax.

patch article_url, params: { id: 12 }, xhr: true

Si intenta ejecutar la prueba test_should_create_article desde


articles_controller_test.rb fallar teniendo en cuenta que de la validacin a nivel de

modelo ha sido recin agregada y con razn.

Modificemos la prueba test_should_create_article en articles_controller_test.rb para


que todos nuestros test pasen:

test "should create article" do


assert_difference('Article.count') do
post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello R
ails' } }
end

assert_redirected_to article_path(Article.last)
end

Ahora puedes intentar ejecutar todas las pruebas y deben pasar.

Si sigui los pasos de la seccin de autenticacin bsica, tendr que agregar lo siguiente al
bloque de instalacin para hacer que todas las pruebas pasen:

request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
encode_credentials('dhh', 'secret')

7.2 Tipos de solicitud disponibles para pruebas funcionales


Si est familiarizado con el protocolo HTTP, sabr que get es un tipo de solicitud. Hay 6
tipos de solicitud soportados en las pruebas funcionales de Rails:

get

post

patch

put

head

delete

Todos los tipos de solicitud tienen mtodos equivalentes que puede utilizar. En una tpica
aplicacin C.R.U.D. va a utilizar get , post , put y delete ms a menudo.

549
7- Pruebas funcionales para sus controladores

Las pruebas funcionales no verifican si el tipo de peticin especificado es aceptado por


la accin, estamos ms preocupados aqui por el resultado. Las pruebas de solicitud
existen para este caso de uso con el fin de hacer que sus pruebas sean ms tiles.

7.3 Prueba de solicitudes XHR (AJAX)


Para probar las solicitudes de AJAX, puede especificar la opcin xhr: true para los
metodos get , post , put y delete . Por ejemplo:

test "ajax request" do


article = articles(:one)
get article_url(article), xhr: true

assert_equal 'hello world', @response.body


assert_equal "text/javascript", @response.content_type
end

7.4 Los Tres Hashes del Apocalipsis


Despus de que se haya realizado y procesado una solicitud, tendr 3 objetos Hash listos
para su uso:

cookies - Cualquier cookie que se establezca


flash - Cualquier objeto que vive en el flash
session - Cualquier objeto que vive en variables de sesin

Como ocurre con los objetos Hash normales, puede acceder a los valores haciendo
referencia a las claves por cadena. Tambin puede hacer referencia a ellos por nombre de
smbolo. Por ejemplo:

flash["gordon"] flash[:gordon]
session["shmession"] session[:shmession]
cookies["are_good_for_u"] cookies[:are_good_for_u]

7.5 Variables de instancia disponibles


Tambin tiene acceso a tres variables de instancia en sus pruebas funcionales, despus de
hacer una solicitud:

@controller - El controlador que procesa la solicitud

@request - El objeto request

@response - El objeto de respuesta

550
7- Pruebas funcionales para sus controladores

class ArticlesControllerTest < ActionDispatch::IntegrationTest


test "should get index" do
get articles_url

assert_equal "index", @controller.action_name


assert_equal "application/x-www-form-urlencoded", @request.media_type
assert_match "Articles", @response.body
end
end

7.6 Configuracin de encabezados y variables CGI


Los encabezados HTTP y las variables CGI se pueden pasar como encabezados:

# setting an HTTP Header


get articles_url, headers: { "Content-Type": "text/plain" } # simulate the request wit
h custom header

# setting a CGI variable


get articles_url, headers: { "HTTP_REFERER": "http://example.com/home" } # simulate th
e request with custom env variable

7.7 Testing a los avisos de flash


Si recuerdas uno de los puntos anteriores, uno de los Tres Hashes del Apocalipsis fue
flash .

Queremos agregar un mensaje flash a nuestra aplicacin de blog cada vez que alguien
crea con xito un nuevo artculo.

Comencemos aadiendo este assert a nuestra prueba test_should_create_article :

test "should create article" do


assert_difference('Article.count') do
post article_url, params: { article: { title: 'Some title' } }
end

assert_redirected_to article_path(Article.last)
assert_equal 'Article was successfully created.', flash[:notice]
end

Si ejecutamos nuestra prueba ahora, deberamos ver un fallo:

551
7- Pruebas funcionales para sus controladores

$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_ar


ticle
Run options: -n test_should_create_article --seed 32266

# Running:

Finished in 0.114870s, 8.7055 runs/s, 34.8220 assertions/s.

1) Failure:
ArticlesControllerTest#test_should_create_article [/test/controllers/articles_controll
er_test.rb:16]:
--- expected
+++ actual
@@ -1 +1 @@
-"Article was successfully created."
+nil

1 runs, 4 assertions, 1 failures, 0 errors, 0 skips

Vamos a implementar el mensaje flash ahora en nuestro controlador. Nuestra accin


:create ahora debe verse as:

def create
@article = Article.new(article_params)

if @article.save
flash[:notice] = 'Article was successfully created.'
redirect_to @article
else
render 'new'
end
end

Ahora si ejecutamos nuestras pruebas, deberamos verlo pasar:

$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_ar


ticle
Run options: -n test_should_create_article --seed 18981

# Running:

Finished in 0.081972s, 12.1993 runs/s, 48.7972 assertions/s.

1 runs, 4 assertions, 0 failures, 0 errors, 0 skips

552
7- Pruebas funcionales para sus controladores

7.8 Juntndolo
En este punto, nuestro controlador de artculos testea las acciones :index , as como
:new y :create . Qu pasa con los datos existentes?

Escribamos una prueba para la accin show :

test "should show article" do


article = articles(:one)
get article_url(article)
assert_response :success
end

Recuerde nuestra discusin anterior en los fixtures, donde el mtodo de los articles()
nos dar acceso a nuestros fixtures de los articles.

Y qu hay sobre eliminar un artculo existente?

test "should destroy article" do


article = articles(:one)
assert_difference('Article.count', -1) do
delete article_url(article)
end

assert_redirected_to articles_path
end

Tambin podemos aadir una prueba para actualizar un artculo existente.

test "should update article" do


article = articles(:one)

patch article_url(article), params: { article: { title: "updated" } }

assert_redirected_to article_path(article)
# Reload association to fetch updated data and assert that title is updated.
article.reload
assert_equal "updated", article.title
end

Tenga en cuenta que estamos empezando a ver alguna duplicacin en estas tres pruebas,
ambos tienen acceso a los mismos datos del fixture del artculo. Podemos usar la filosofa
D.R.Y. Esto mediante el uso de los mtodos de configuracin y desmontaje proporcionados
por ActiveSupport::Callbacks .

553
7- Pruebas funcionales para sus controladores

Nuestra prueba ahora debe ser algo como lo que sigue. Ignore las otras pruebas por ahora,
las dejamos por brevedad.

require 'test_helper'

class ArticlesControllerTest < ActionDispatch::IntegrationTest


# called before every single test
setup do
@article = articles(:one)
end

# called after every single test


teardown do
# when controller is using cache it may be a good idea to reset it afterwards
Rails.cache.clear
end

test "should show article" do


# Reuse the @article instance variable from setup
get article_url(@article)
assert_response :success
end

test "should destroy article" do


assert_difference('Article.count', -1) do
delete article_url(@article)
end

assert_redirected_to articles_path
end

test "should update article" do


patch article_url(@article), params: { article: { title: "updated" } }

assert_redirected_to article_path(@article)
# Reload association to fetch updated data and assert that title is updated.
@article.reload
assert_equal "updated", @article.title
end
end

Similar a otras devoluciones de llamada en Rails, los mtodos de configuracin y


desmontaje tambin se pueden usar pasando un nombre de bloque, lambda o mtodo
como un smbolo para llamar.

7.9 Ayudantes de prueba


Para evitar la duplicacin de cdigo, puede agregar sus propios ayudantes de prueba. El
ayudante sign puede ser un buen ejemplo:

554
7- Pruebas funcionales para sus controladores

# test/test_helper.rb

module SignInHelper
def sign_in_as(user)
post sign_in_url(email: user.email, password: user.password)
end
end

class ActionDispatch::IntegrationTest
include SignInHelper
end

require 'test_helper'

class ProfileControllerTest < ActionDispatch::IntegrationTest

test "should show profile" do


# helper is now reusable from any controller test case
sign_in_as users(:david)

get profile_url
assert_response :success
end
end

555
8- Testeo de Rutas

8- Testeo de Rutas
Como todo lo dems en su aplicacin de Rails, puede probar sus rutas. Las pruebas de ruta
residen en las test/controllers/ o hacen parte de las pruebas del controlador.

Si su aplicacin tiene rutas complejas, Rails proporciona una serie de ayudantes tiles
para probarlos.

Para obtener ms informacin sobre las aserciones de enrutamiento disponibles en Rails,


consulte la documentacin de la API para ActionDispatch::Assertions::RoutingAssertions .

556
9- Testeo de las Vistas

9- Testeo de las Vistas


Testear la respuesta a su solicitud mediante la asercin de la presencia de elementos clave
de HTML y su contenido es una forma comn de probar las vistas de su aplicacin. Al igual
que las pruebas de ruta, las pruebas de vista residen en las test/controllers/ o son parte
de las pruebas del controlador. El mtodo assert_select permite consultar elementos
HTML de la respuesta mediante una sintaxis simple pero potente.

Hay dos formas de assert_select :

assert_select(selector, [equality], [message]) asegura que la condicin de igualdad se

cumpla en los elementos seleccionados a travs del selector. El selector puede ser una
expresin de selector CSS (String) o una expresin con valores de sustitucin.

assert_select(element, selector, [equality], [message]) garantiza que la condicin de

igualdad se cumpla en todos los elementos seleccionados a travs del selector a partir del
elemento (instancia de Nokogiri::XML::Node o Nokogiri::XML::NodeSet ) y sus
descendientes.

Por ejemplo, puede verificar el contenido del elemento de ttulo en su respuesta con:

assert_select 'title', "Welcome to Rails Testing Guide"

Tambin puede utilizar bloques assert_select anidados para una investigacin ms


profunda.

En el ejemplo siguiente, el assert_select interno de li.menu_item se ejecuta dentro de la


coleccin de elementos seleccionados por el bloque externo:

assert_select 'ul.navigation' do
assert_select 'li.menu_item'
end

Una coleccin de elementos seleccionados se puede iterar a travs de ella de modo que
assert_select puede ser llamado por separado en cada elemento.

Por ejemplo, si la respuesta contiene dos listas ordenadas, cada una con cuatro elementos
de lista anidados, se pasarn las siguientes pruebas.

557
9- Testeo de las Vistas

assert_select "ol" do |elements|


elements.each do |element|
assert_select element, "li", 4
end
end

assert_select "ol" do
assert_select "li", 8
end

Esta asercin es muy poderosa. Para un uso ms avanzado, consulte la documentacin.

9.1 Aserciones adicionales basadas en la vista


Hay ms afirmaciones que se utilizan principalmente en las vistas de prueba:

Assertion Purpose

assert_select_email
Le permite hacer aserciones en el
cuerpo de un e-mail..
Le permite hacer aserciones sobre
HTML codificado. Esto lo hace
assert_select_encoded
descodificando el contenido de cada
elemento y luego llamando al bloque
con todos los elementos no
codificados.
Devuelve un array de todos los
elementos seleccionados por el
selector. En la segunda variante
css_select(selector) or css_select(element, coincide primero con el elemento de
selector) base e intenta igualar esta expresin
de seleccin a cualquiera de sus hijos.
Si no hay coincidencias, ambas
variantes devuelven un array vaco.

He aqu un ejemplo de uso de assert_select_email :

assert_select_email do
assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.'
end

558
10- Testeo de los Helpers

10- Testeo de los Helpers


Un helper es simplemente un mdulo donde puedes definir los mtodos que estarn
disponibles en tus vistas.

Para probar los helpers, todo lo que necesitas hacer es comprobar que la salida del mtodo
del helper coincide con lo que esperas. Las pruebas relacionadas con los helpers se
encuentran en el directorio test/helpers .

Dado que tenemos el siguiente helper:

module UserHelper
def link_to_user(user)
link_to "#{user.first_name} #{user.last_name}", user
end
end

Podemos probar la salida de este mtodo con algo como esto:

class UserHelperTest < ActionView::TestCase


test "should return the user's full name" do
user = users(:david)

assert_dom_equal %{<a href="/user/#{user.id}">David Heinemeier Hansson</a>}, link_


to_user(user)
end
end

Adems, dado que la clase de testing se extiende desde ActionView::TestCase , tiene


acceso a los mtodos auxiliares de Rails como link_to o pluralize .

559
11- Testeo de los Mailers

11- Testeo de los Mailers


El testeo de las clases de Mailer requiere algunas herramientas especficas para hacer un
trabajo a fondo.

11.1 Mantener al Postman bajo control


Las clases de correo electrnico - como cualquier otra parte de su aplicacin de Rails -
deben ser probados para asegurarse de que estn funcionando como se esperaba.

Los objetivos de probar sus clases de correo son asegurarse que:

Los correos electrnicos estn siendo procesados (creados y enviados)


El contenido del correo electrnico es correcto (sujeto, remitente, cuerpo, etc)
Los correos electrnicos correctos se envan en los momentos correctos

11.1.1 Desde todos los lados


Hay dos aspectos en los test de su mailer, las pruebas unitarias y las pruebas funcionales.
En las pruebas unitarias, ejecuta el mailer de forma aislada con entradas estrictamente
controladas y compara la salida con un valor conocido (un fixture). En las pruebas
funcionales no se ponen a prueba los detalles minuciosos producidos por el remitente; En
su lugar, probamos que nuestros controladores y modelos estn utilizando el correo
electrnico de la manera correcta. Prueba para probar que el correo electrnico correcto fue
enviado en el momento adecuado.

11.2 Pruebas unitarias


Con el fin de probar que su mailer est funcionando como se esperaba, puede utilizar
pruebas unitarias para comparar los resultados reales del mailer con ejemplos preescritos
de lo que debe producirse.

11.2.1 Venganza de los fixtures


Para el propsito de hacer pruebas unitarias de un mailer, los fixtures se utilizan para
proporcionar un ejemplo de cmo el resultado debe lucir. Debido a que estos son ejemplos
de mensajes de correo electrnico, y no datos de Active Record como los otros fixtures, que
se mantienen en su propio subdirectorio aparte de los otros fixtures. El nombre del directorio

560
11- Testeo de los Mailers

dentro de test/fixtures corresponde directamente al nombre del mailer. Por lo tanto, para
un mailer llamado UserMailer , los fixtures deben residir en el directorio
test/fixtures/user_mailer.

Cuando gener su mailer, el generador crea fixtures de stub para cada una de las acciones
de los remitentes. Si no us el generador, tendr que crear esos archivos usted mismo.

11.2.2 El caso de prueba bsica


Aqu hay una prueba de unidad para probar un mailer llamado UserMailer cuya invitacin
de accin se utiliza para enviar una invitacin a un amigo. Es una versin adaptada de la
prueba base creada por el generador para una accin de invitacin.

require 'test_helper'

class UserMailerTest < ActionMailer::TestCase


test "invite" do
# Create the email and store it for further assertions
email = UserMailer.create_invite('me@example.com',
'friend@example.com', Time.now)

# Send the email, then test that it got queued


assert_emails 1 do
email.deliver_now
end

# Test the body of the sent email contains what we expect it to


assert_equal ['me@example.com'], email.from
assert_equal ['friend@example.com'], email.to
assert_equal 'You have been invited by me@example.com', email.subject
assert_equal read_fixture('invite').join, email.body.to_s
end
end

En la prueba enviamos el correo electrnico y almacenamos el objeto devuelto en la


variable de correo electrnico. Entonces nos aseguramos de que fue enviado (la primera
asercin), entonces, en el segundo lote de aserciones, nos aseguramos de que el correo
electrnico s contiene lo que esperamos. El ayudante read_fixture se utiliza para leer el
contenido de este archivo.

email.body.to_s est presente cuando slo hay una parte (HTML o texto) presente. Si el

mailer proporciona ambos, puede probar su fixture contra partes especficas con
email.text_part.body.to_s o email.html_part.body.to_s .

Aqu est el contenido del fixture de invitacin:

561
11- Testeo de los Mailers

Hi friend@example.com,

You have been invited.

Cheers!

Este es el momento adecuado para entender un poco ms sobre la escritura de pruebas


para sus mailers. La lnea ActionMailer::Base.delivery_method = :test en
config/environments/test.rb establece el mtodo delivery en modo de prueba para que

el correo electrnico no se entregue realmente (til para evitar el spam de sus usuarios
durante la prueba), sino que se anexar a una matriz ( ActionMailer::Base.deliveries ).

El array ActionMailer::Base.deliveries slo se restablece automticamente en las


pruebas ActionMailer::TestCase y ActionDispatch::IntegrationTest . Si desea tener
una pizarra limpia fuera de estos casos de prueba, puede restablecerla manualmente
con: ActionMailer::Base.deliveries.clear

11.3 Pruebas funcionales


Las pruebas funcionales para mailers envuelve ms que slo comprobar que el cuerpo del
correo electrnico, los destinatarios y as sucesivamente estn correctas. En las pruebas de
correo funcionales se llaman los mtodos delivery de correo y se comprueba que los
correos electrnicos apropiados se han agregado a la lista de entrega. Es bastante seguro
suponer que los mtodos de delivery hacen su trabajo. Usted est probablemente ms
interesado en si su propia lgica de negocio est enviando un email cuando usted espera
que lo haga. Por ejemplo, puede comprobar que la operacin invitar a un amigo est
enviando un correo electrnico de la manera adecuada:

require 'test_helper'

class UserControllerTest < ActionDispatch::IntegrationTest


test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post invite_friend_url, params: { email: 'friend@example.com' }
end
invite_email = ActionMailer::Base.deliveries.last

assert_equal "You have been invited by me@example.com", invite_email.subject


assert_equal 'friend@example.com', invite_email.to[0]
assert_match(/Hi friend@example.com/, invite_email.body.to_s)
end
end

562
11- Testeo de los Mailers

563
12- Testeo de los Jobs

12- Testeo de los Jobs


Dado que sus jobs personalizados pueden estar en cola en diferentes niveles dentro de su
aplicacin, tendr que probar ambos, los jobs mismos (su comportamiento cuando se ponen
en cola) y que otras entidades los encolen correctamente.

12.1 Un caso de prueba bsico


De forma predeterminada, al generar un job, una prueba asociada se generar tambin en
el directorio test/jobs . Aqu hay una prueba de ejemplo con un jobs de facturacin:

require 'test_helper'

class BillingJobTest < ActiveJob::TestCase


test 'that account is charged' do
BillingJob.perform_now(account, product)
assert account.reload.charged_for?(product)
end
end

Esta prueba es bastante simple y slo afirma que el job obtenga el trabajo realizado como
se esperaba.

De forma predeterminada, ActiveJob::TestCase establecer el adaptador de cola en


:test para que sus trabajos se realicen en lnea. Tambin se asegurar de que todos los

trabajos ejecutados previamente y en cola se borren antes de cualquier ejecucin de prueba


para que pueda asumir con seguridad que ya no se han ejecutado trabajos en el mbito de
cada prueba.

12.2 Aserciones personalizadas y pruebas de jobs dentro


de otros componentes
Active Job enva con un montn de aserciones personalizadas que se pueden utilizar para
disminuir la verbosidad de las pruebas. Para obtener una lista completa de aserciones
disponibles, consulte la documentacin de API para ActiveJob::TestHelper .

Es una buena prctica asegurarse de que sus jobs se pongan en cola correctamente o se
realicen dondequiera que los invoque (por ejemplo, dentro de sus controladores). Esto es
precisamente donde las aserciones personalizadas proporcionadas por Active Job son
bastante tiles. Por ejemplo, dentro de un modelo:

564
12- Testeo de los Jobs

require 'test_helper'

class ProductTest < ActiveJob::TestCase


test 'billing job scheduling' do
assert_enqueued_with(job: BillingJob) do
product.charge(account)
end
end
end

565
13- Recursos Adicionales de Pruebas

13- Recursos Adicionales de Pruebas


13.1 Prueba del cdigo dependiente del tiempo
Rails proporciona mtodos auxiliares integrados que le permiten afirmar que su cdigo
sensible al tiempo funciona de la manera esperada.

Aqu hay un ejemplo usando el ayudante travel_to :

# Lets say that a user is eligible for gifting a month after they register.
user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24))
assert_not user.applicable_for_gifting?
travel_to Date.new(2004, 11, 24) do
assert_equal Date.new(2004, 10, 24), user.activation_date # inside the `travel_to` b
lock `Date.current` is mocked
assert user.applicable_for_gifting?
end
assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible onl
y inside the `travel_to` block.

Consulte las documentacin de la API de ActiveSupport::Testing::TimeHelpers para


obtener informacin detallada sobre los ayudantes de tiempo disponibles.

566
XVIII - Seguridad de las aplicaciones de Rails

XVIII - Seguridad de las aplicaciones de


Rails
Este manual describe problemas de seguridad comunes en aplicaciones web y cmo
evitarlos con Rails.

Despus de leer esta gua, sabr:

Todas las contramedidas que se destacan.


El concepto de sesiones en Rails, qu poner alli y los mtodos de ataque populares.
Porque cuando acaban de visitar un sitio puede ser un problema de seguridad (con
CSRF)?
A lo que usted tiene que prestar atencin al trabajar con archivos o al proporcionar una
interfaz de administracin.
Cmo administrar usuarios: Iniciar sesin y salir y mtodos de ataque en todas las
capas.
Y los mtodos de ataque por inyeccin ms populares.

567
1- Introduccin

1- Introduccin
Los frameworks de aplicaciones Web se hacen para ayudar a los desarrolladores a crear
aplicaciones web. Algunos de ellos tambin le ayudan con la seguridad de la aplicacin
web. De hecho, un framework no es ms seguro que otro: si lo usa correctamente, podr
crear aplicaciones seguras con muchos frameworks. Ruby on Rails tiene algunos mtodos
auxiliares inteligentes, por ejemplo contra la inyeccin de SQL, de modo que esto no es un
problema.

En general no hay tal cosa como la seguridad plug-n-play. La seguridad depende de las
personas que utilizan el framework, y a veces en el mtodo de desarrollo. Y depende de
todas las capas de un entorno de la aplicacin web: el backend, el servidor web y la propia
aplicacin web (y posiblemente otras capas o aplicaciones).

El Gartner Group, sin embargo, estima que el 75% de los ataques estn en la capa de
aplicacin web, y descubri que "de los 300 sitios auditados, el 97% son vulnerables al
ataque". Esto es porque las aplicaciones web son relativamente fciles de atacar, ya que
son fciles de entender y manipular, incluso por una persona laica.

Las amenazas contra las aplicaciones web incluyen el secuestro de cuentas de usuario, el
desvo del control de acceso, la lectura o modificacin de datos confidenciales o la
presentacin de contenido fraudulento. O un atacante podra ser capaz de instalar un
programa de caballo de Troya o software de envo de correo electrnico no solicitado,
apuntar a enriquecimiento financiero o causar dao a la marca mediante la modificacin de
los recursos de la empresa. Con el fin de prevenir ataques, minimizar su impacto y eliminar
puntos de ataque, en primer lugar, usted tiene que entender completamente los mtodos de
ataque con el fin de encontrar las contramedidas correctas. Eso es lo que pretende esta
gua.

Con el fin de desarrollar aplicaciones web seguras tiene que mantenerse al da en todas las
capas y conocer a sus enemigos. Para mantenerse al da, suscrbase a las listas de correo
de seguridad, lea los blogs de seguridad y haga de la actualizacin y los controles de
seguridad un hbito (consulte el captulo Recursos adicionales). Se hace manualmente
porque as es como se encuentran los desagradables problemas de lgica de seguridad .

568
2- Sesiones

2- Sesiones
Un buen lugar para empezar a mirar la seguridad es en las sesiones, que pueden ser
vulnerables a los ataques en particular.

2.1 Qu son las sesiones?


HTTP es un protocolo sin estado. Las sesiones lo hacen con estado.

La mayora de las aplicaciones necesitan seguir un determinado estado de un usuario en


particular. ste podra ser el contenido de una cesta de la compra o el ID de usuario que
actualmente est conectado. Sin la idea de las sesiones, el usuario tendra que
identificarse, y probablemente autenticarse, en cada solicitud. Rails crear una nueva
sesin automticamente si un nuevo usuario accede a la aplicacin. Se cargar una sesin
existente si el usuario ya ha utilizado la aplicacin.

Una sesin normalmente consiste en un hash de valores y un ID de sesin, usualmente una


cadena de 32 caracteres, para identificar el hash. Cada cookie enviada al navegador del
cliente incluye el ID de sesin. Y al revs: el navegador lo enviar al servidor en cada
solicitud del cliente. En Rails puede guardar y recuperar valores mediante el mtodo de
sesin:

session[:user_id] = @current_user.id
User.find(session[:user_id])

2.2 ID de sesin
El identificador de sesin es una cadena hexadecimal aleatoria de 32 caracteres.

El identificador de sesin se genera utilizando SecureRandom.hex que genera una cadena


hexadecimal aleatoria utilizando mtodos especficos de plataforma (como OpenSSL,
/dev/urandom o Win32) para generar nmeros aleatorios criptogrficamente seguros.

Actualmente no es factible para los IDs de sesin de Rails de fuerza bruta.

2.3 Secuestro de sesin


Robar el ID de sesin de un usuario permite que un atacante use la aplicacin web en
nombre de la vctima.

569
2- Sesiones

Muchas aplicaciones web tienen un sistema de autenticacin: un usuario proporciona un


nombre de usuario y una contrasea, la aplicacin web los comprueba y almacena el
identificador de usuario correspondiente en el hash de sesin. A partir de ahora, la sesin
es vlida. En cada solicitud la aplicacin cargar al usuario, identificado por el identificador
de usuario en la sesin, sin necesidad de una nueva autenticacin. El ID de sesin en la
cookie identifica la sesin.

Por lo tanto, la cookie sirve como autenticacin temporal para la aplicacin web. Cualquier
persona que se apodera de una cookie de otra persona, puede utilizar la aplicacin web
como este usuario - con posibles consecuencias graves. Aqu hay algunas maneras de
secuestrar una sesin, y sus contramedidas:

Olfatea la cookie en una red insegura. Una LAN inalmbrica puede ser un ejemplo de
tal red. En una LAN inalmbrica sin cifrar, es especialmente fcil escuchar el trfico de
todos los clientes conectados. Para el generador de aplicaciones web, esto significa
proporcionar una conexin segura a travs de SSL. En Rails 3.1 y posteriores, esto
podra lograrse forzando siempre la conexin SSL en el archivo de configuracin de la
aplicacin:

config.force_ssl = true

La mayora de la gente no limpia las cookies despus de trabajar en una terminal


pblica. Por lo tanto, si el ltimo usuario no se desconect de una aplicacin web,
podra utilizarla como usuario. Proporcione al usuario un botn de cierre de sesin en la
aplicacin web y haga que se destaque.
Muchos intentos de scripts entre sitios (XSS) buscan obtener la cookie del usuario. Ms
adelante leer ms sobre XSS.
En lugar de robar una cookie desconocida por el atacante, ellos corrigen el identificador
de sesin de un usuario (en la cookie) que conocen. Lea ms sobre esta sesin de
fijacin ms adelante.

El objetivo principal de la mayora de los atacantes es ganar dinero. Los precios en el bajo
mundo para las cuentas robadas de acceso a bancos van de $10 - $1000 (dependiendo de
la cantidad disponible de fondos), de $0.40- $20 para nmeros de tarjeta de crdito, $1- $8
para las cuentas en lnea de un sitio de subasta y $4- $30 para las contraseas de email,
segn el Informe de amenazas de Symantec Global Internet Security.

2.4 Directrices de la Sesin


Aqu hay algunas pautas generales sobre las sesiones.

No guarde objetos grandes en una sesin. En su lugar debe guardarlos en la base de

570
2- Sesiones

datos y guardar su id en la sesin. Esto eliminar los dolores de cabeza de


sincronizacin y no llenar el espacio de almacenamiento de sesin (dependiendo del
almacenamiento de sesiones que elija, consltelo ms adelante). Esto tambin ser
una buena idea, si modifica la estructura de un objeto y las versiones antiguas de l
todava estn en las cookies de algunos usuarios. Con los almacenes de sesin del
lado del servidor puede borrar las sesiones, pero con los almacenes del lado del
cliente, esto es difcil de mitigar.
Los datos crticos no deben almacenarse en una sesin. Si el usuario borra sus cookies
o cierra el navegador, se perdern. Y con un almacenamiento de sesin del lado del
cliente, el usuario puede leer los datos.

2.5 Almacenamiento de sesin


Rails proporciona varios mecanismos de almacenamiento para los hashes de sesin.
El ms importante es ActionDispatch::Session::CookieStore .

Rails 2 introdujo un nuevo almacenamiento de sesin predeterminado, CookieStore.


CookieStore guarda el hash de sesin directamente en una cookie en el lado del cliente. El
servidor recupera el hash de sesin de la cookie y elimina la necesidad de un ID de sesin.
Eso aumentar en gran medida la velocidad de la aplicacin, pero es una opcin de
almacenamiento controvertida y hay que pensar en las implicaciones de seguridad de la
misma:

Las cookies implican un lmite de tamao estricto de 4kB. Esto est bien, ya que no
debe almacenar grandes cantidades de datos en una sesin de todos modos, como se
describi anteriormente. Almacenar el ID de la base de datos del usuario actual en una
sesin es normalmente aceptable.
El cliente puede ver todo lo que almacena en una sesin, ya que se almacena en texto
claro (en realidad codificado en Base64, no cifrado). As que, por supuesto, usted no
quiere guardar ningn secreto aqu. Para evitar la manipulacin de hash de sesin, se
calcula un digest a partir de la sesin con un secreto de servidor (secrets.secret_token)
e insertado en el final de la cookie.

Sin embargo, desde Rails 4, la tienda predeterminada es EncryptedCookieStore . Con


EncryptedCookieStore la sesin se cifra antes de ser almacenada en una cookie. Esto evita

que el usuario acceda y altere el contenido de la cookie. Por lo tanto, la sesin se convierte
en un lugar ms seguro para almacenar datos. El cifrado se realiza mediante una clave
secreta del servidor secrets.secret_key_base almacenada en config/secrets.yml .

Esto significa que la seguridad de este almacenamiento depende de este secret (y en el


algoritmo digest, que por defecto a SHA1, para la compatibilidad). Por lo tanto, no utilice un
secret trivial, es decir, una palabra de un diccionario, o una que sea ms corta de 30

571
2- Sesiones

caracteres, use los rails secret en su lugar.

secrets.secret_key_base se utiliza para especificar una clave que permite que las sesiones

de la aplicacin se verifiquen frente a una clave segura conocida para evitar la manipulacin
indebida. Las aplicaciones obtienen secrets.secret_key_base inicializado a una clave
aleatoria presente en config/secrets.yml , por ejemplo:

development:
secret_key_base: a75d...

test:
secret_key_base: 492f...

production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Las versiones anteriores de Rails utilizan CookieStore, que utiliza secret_token en lugar de
secret_key_base que es utilizado por EncryptedCookieStore . Lea la documentacin de

actualizacin para obtener ms informacin.

Si ha recibido una aplicacin en la que se descubri el secret (por ejemplo, una aplicacin
cuyo cdigo fuente se comparti), considere seriamente cambiar el secret.

2.6 Ataques de repeticin para las sesiones de CookieStore


Otro tipo de ataque que debes tener en cuenta al usar CookieStore es el ataque de
repeticin.

Funciona as:

Un usuario recibe crditos, la cantidad se almacena en una sesin (que es una mala
idea de todos modos, pero lo haremos para propsitos de demostracin).
El usuario compra algo.
El nuevo valor de crdito ajustado se almacena en la sesin.
El usuario toma la cookie del primer paso (que previamente copi) y reemplaza la
cookie actual en el navegador.
El usuario tiene su crdito original de nuevo.

Incluir un nonce (un valor aleatorio) en la sesin resuelve ataques de repeticin. Un nonce
es vlido slo una vez, y el servidor tiene que hacer un seguimiento de todos los nonces
vlidos. Se vuelve an ms complicado si tiene varios servidores de aplicaciones.
Almacenar nonces en una tabla de la base de datos anulara el propsito entero de
CookieStore (evitar el acceso a la base de datos).

572
2- Sesiones

La mejor solucin en contra es no almacenar este tipo de datos en una sesin, sino en la
base de datos. En este caso, guarde el crdito en la base de datos y en el
session_in_user_id en la sesin.

2.7 Fijacin de sesin


Aparte de robar el ID de sesin de un usuario, el atacante puede arreglar un ID de
sesin conocido por ellos. Esto se llama fijacin de sesin.

Este ataque se centra en la fijacin de un ID de sesin del usuario conocido por el atacante,
y obligando al navegador del usuario a utilizar este ID. Por lo tanto, no es necesario que el
atacante robe el ID de sesin despus. As es como funciona este ataque:

El atacante crea un ID de sesin vlido: Cargan la pgina de inicio de sesin de la


aplicacin web en la que quieren arreglar la sesin y toman el ID de sesin en la cookie
de la respuesta (consulte los nmeros 1 y 2 de la imagen).
Ellos mantienen la sesin accediendo a la aplicacin web peridicamente con el fin de
mantener viva una sesin que expira.
El atacante obliga al navegador del usuario a usar este ID de sesin (ver nmero 3 en
la imagen). Como no puede cambiar una cookie de otro dominio (debido a la misma
poltica de origen), el atacante debe ejecutar JavaScript desde el dominio de la
aplicacin web de destino. Inyectar el cdigo JavaScript en la aplicacin por XSS logra

573
2- Sesiones

este ataque. Aqu est un ejemplo: <script> document.cookie = "_ session_id =


16d5b78abb28e3d6206b60f22a03c8d9"; </ script> Ms informacin sobre XSS e inyeccin

ms adelante.

El atacante atrae a la vctima a la pgina infectada con el cdigo JavaScript. Al ver la


pgina, el navegador de la vctima cambiar el ID de sesin al ID de sesin de captura.

Como la nueva sesin de captura no se utiliza, la aplicacin web requerir que el


usuario se autentique.
A partir de ahora, la vctima y el atacante usarn conjuntamente la aplicacin web con
la misma sesin: La sesin se hizo vlida y la vctima no se dio cuenta del ataque.

2.8 Fijacin de sesin - Contramedidas


Una lnea de cdigo le proteger de la fijacin de la sesin.

La contramedida ms efectiva es emitir un nuevo identificador de sesin y declarar el


antiguo invlido despus de un inicio de sesin satisfactorio. De esta manera, un atacante
no puede usar el identificador de sesin fijo. Esta es una buena contramedida contra el
secuestro de sesin. A continuacin se muestra cmo crear una nueva sesin en Rails:

reset_session

Si utiliza la popular gema Devise para la gestin de usuarios, sta expirar


automticamente las sesiones de inicio y cierre de sesin por usted. Si hace la suya,
recuerde expirar la sesin despus de la accin de inicio de sesin (cuando se cree la
sesin). Esto eliminar los valores de la sesin, por lo que tendr que transferirlos a la
nueva sesin.

Otra contramedida es guardar las propiedades especficas del usuario en la sesin,


verificarlas cada vez que una solicitud llegue y denegar el acceso, si la informacin no
coincide. Tales propiedades podran ser la direccin IP remota o el agente de usuario (el
nombre del navegador web), aunque este ltimo es menos especfico que la del usuario. Al
guardar la direccin IP, hay que tener en cuenta que hay proveedores de servicios de
Internet o grandes organizaciones que colocan a sus usuarios detrs de los proxies. Estos
cambios pueden cambiar durante el curso de una sesin, por lo que estos usuarios no
podrn utilizar su aplicacin, o slo de forma limitada.

2.9 Expiracin de la sesin

574
2- Sesiones

Las sesiones que nunca caducan prolongan el marco de tiempo para ataques como la
falsificacin de solicitudes entre sitios (CSRF), el secuestro de sesin y la fijacin de
sesin.

Una posibilidad es establecer la marca de tiempo de caducidad de la cookie con el ID de


sesin. Sin embargo, el cliente puede editar las cookies que se almacenan en el navegador
web para expirar las sesiones en el servidor, hacindolo as ms seguro. Este es un
ejemplo de cmo expirar las sesiones en una tabla de base de datos. Llame a
Session.sweep("20 minutes") para que caduquen las sesiones que se utilizaron hace ms

de 20 minutos.

class Session < ApplicationRecord


def self.sweep(time = 1.hour)
if time.is_a?(String)
time = time.split.inject { |count, unit| count.to_i.send(unit) }
end

delete_all "updated_at < '#{time.ago.to_s(:db)}'"


end
end

La seccin sobre "la fijacin de la sesin" introdujo el problema de las sesiones mantenidas.
Un atacante que mantiene una sesin cada cinco minutos puede mantener la sesin viva
para siempre, aunque est expirando las sesiones. Una solucin simple para esto sera
agregar una columna created_at a la tabla de sesiones. Ahora puede eliminar las sesiones
que se crearon hace mucho tiempo. Utilice esta lnea en el mtodo de barrido anterior:

delete_all "updated_at < '#{time.ago.to_s(:db)}' OR


created_at < '#{2.days.ago.to_s(:db)}'"

575
3- Falsificacin de solicitudes entre sitios (CSRF)

3- Falsificacin de solicitudes entre sitios


(CSRF)
Este mtodo de ataque funciona mediante la inclusin de cdigo malicioso o un link en una
pgina que accede a una aplicacin web que el usuario cree que se ha autenticado. Si la
sesin para esa aplicacin web no ha expirado, un atacante puede ejecutar comandos no
autorizados.

En el captulo de sesiones has aprendido que la mayora de las aplicaciones de Rails


utilizan sesiones basadas en cookies. O almacenan el ID de sesin en la cookie y tienen un
hash de sesin en el lado del servidor, o el hash de sesin completo est en el lado del
cliente. En cualquier caso, el navegador enviar automticamente a lo largo de la cookie en
cada solicitud a un dominio, si puede encontrar una cookie para ese dominio. El punto
polmico es que si la solicitud proviene de un sitio de un dominio diferente, tambin enviar
la cookie. Comencemos con un ejemplo:

Bob navega por un tablero de mensajes y ve una entrada de un hacker donde hay un
elemento de imagen HTML creado. El elemento hace referencia a un comando en la

576
3- Falsificacin de solicitudes entre sitios (CSRF)

aplicacin de administracin de proyectos de Bob, en lugar de un archivo de imagen:


<img src = "http://www.webapp.com/project/1/destroy">

La sesin de Bob en www.webapp.com todava est viva, porque no se cerr la sesin


hace unos minutos.
Al ver el post, el navegador encuentra una etiqueta de imagen. Intentar cargar la
imagen sospechada de www.webapp.com . Como se explic anteriormente, tambin
enviar a lo largo de la cookie con el ID de sesin vlido.
La aplicacin web en www.webapp.com verifica la informacin del usuario en el hash de
sesin correspondiente y destruye el proyecto con el ID 1. A continuacin, devuelve
una pgina de resultados que es un resultado inesperado para el navegador, por lo que
no mostrar la imagen.
Bob no se da cuenta del ataque, pero pocos das despus descubre que el proyecto
nmero uno ha desaparecido.

Es importante notar que la imagen real o el enlace no necesariamente tiene que estar
situado en el dominio de la aplicacin web, puede estar en cualquier lugar, en un foro, una
publicacin de blog o un correo electrnico.

CSRF aparece muy raramente en CVE (Common Vulnerabilities and Exposures) - menos
del 0,1% en 2006 - pero es realmente un "gigante dormido" [Grossman]. Esto contrasta
fuertemente con los resultados en muchos contratos de seguridad - CSRF es un importante
problema de seguridad.

3.1 Contramedidas CSRF


Primero, como es requerido por el W3C, utilice GET y POST apropiadamente. En
segundo lugar, un token de seguridad en solicitudes no GET que protegern su
aplicacin de CSRF.

El protocolo HTTP bsicamente proporciona dos tipos principales de solicitudes: GET y


POST (y ms, pero no son compatibles con la mayora de los navegadores). El World Wide
Web Consortium (W3C) proporciona una lista de comprobacin para elegir HTTP GET o
POST:

Utilice GET si:

La interaccin se parece ms a una pregunta (es decir, es una operacin segura como
una consulta, una operacin de lectura o una bsqueda).

Utilice POST si:

La interaccin es ms bien una orden, o


La interaccin cambia el estado del recurso de una manera que el usuario percibira
(por ejemplo, una suscripcin a un servicio), o

577
3- Falsificacin de solicitudes entre sitios (CSRF)

El usuario es responsable por los resultados de la interaccin.

Si su aplicacin web es RESTful, podra estar acostumbrado a verbos HTTP adicionales,


como PATCH , PUT o DELETE . Sin embargo, la mayora de los navegadores web de hoy en
da no los admiten, slo GET y POST . Rails utiliza un campo _method oculto para manejar
esta barrera.

Las solicitudes POST tambin se pueden enviar automticamente. En este ejemplo, el


enlace www.harmless.com se muestra como el destino en la barra de estado del navegador.
Pero en realidad ha creado dinmicamente un nuevo formulario que enva una solicitud
POST.

<a href="http://www.harmless.com/" onclick="


var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = 'http://www.example.com/account/destroy';
f.submit();
return false;">To the harmless survey</a>

O el atacante coloca el cdigo en el controlador de eventos onmouseover de una imagen:

<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." />

Hay muchas otras posibilidades, como usar una etiqueta <script> para hacer una solicitud
entre sitios a una URL con una respuesta JSONP o JavaScript. La respuesta es cdigo
ejecutable en el que el atacante puede encontrar una manera de ejecutar, posiblemente
extrayendo datos sensibles. Para protegerse contra esta fuga de datos, debemos rechazar
las etiquetas <script> entre sitios. Las solicitudes de Ajax, sin embargo, obedecen la
poltica del mismo origen del navegador (solo su propio sitio puede iniciar XmlHttpRequest )
para que podamos permitirles devolver respuestas JavaScript de forma segura.

Nota: no podemos distinguir el origen de una etiqueta <script> , ya sea una etiqueta en su
propio sitio o en otro sitio malicioso, por lo que debemos bloquear todos los <script> en el
board, aunque sea realmente un origen seguro, como el Script de su propio sitio. En estos
casos, omita explcitamente la proteccin CSRF en las acciones que sirven JavaScript
destinado a una etiqueta <script> .

Para protegernos de todas las dems solicitudes falsificadas, introducimos un token de


seguridad requerido que nuestro sitio sabe, pero otros sitios no lo saben. Incluimos el token
de seguridad en las solicitudes y lo verificamos en el servidor. Esta es una lnea nica en su

578
3- Falsificacin de solicitudes entre sitios (CSRF)

controlador de aplicaciones y es el valor predeterminado para las aplicaciones de Rails


recin creadas:

protect_from_forgery with: :exception

Esto incluir automticamente un token de seguridad en todos los formularios y las


solicitudes Ajax generadas por Rails. Si el token de seguridad no coincide con lo esperado,
se lanzar una excepcin.

De forma predeterminada, Rails incluye un adaptador de secuencias de comandos


discreto que agrega un encabezado denominado X-CSRF-Token con el token de
seguridad en cada llamada Ajax no GET. Sin este encabezado, las solicitudes de Ajax
que no sean GET no sern aceptadas por Rails. Cuando utilice otra biblioteca para
realizar llamadas Ajax, es necesario agregar el token de seguridad como encabezado
predeterminado para las llamadas Ajax en su biblioteca. Para obtener el token, eche un
vistazo a la etiqueta <meta name='csrf-token' content='THE-TOKEN'> impresa por <%=
csrf_meta_tags%> en la vista de la aplicacin.

Es comn utilizar cookies persistentes para almacenar informacin de usuario, con


cookies.permanent , por ejemplo. En este caso, las cookies no se borrarn y la proteccin

CSRF no ser efectiva. Si est usando un almacenamiento de cookies diferente a la sesin

para obtener esta informacin, debe manejar lo que debe hacer con ella:

rescue_from ActionController::InvalidAuthenticityToken do |exception|


sign_out_user # Example method that will destroy the user cookies
end

El mtodo anterior se puede colocar en ApplicationController y se llamar cuando un


token CSRF no est presente o sea incorrecto en una solicitud que no sea GET.

Tenga en cuenta que las vulnerabilidades de XSS (cross-site scripting) pasan por alto todas
las protecciones CSRF. XSS proporciona al atacante acceso a todos los elementos de una
pgina, para que puedan leer el token de seguridad CSRF desde un formulario o enviar
directamente el formulario. Ms informacin sobre XSS ms adelante.

579
4- Redireccin y archivos

4- Redireccin y archivos
Otra clase de vulnerabilidades de seguridad rodea el uso de la redireccin y los archivos en
las aplicaciones web.

4.1 Redireccin
La redireccin en una aplicacin web es una herramienta de cracker subestimada: no
slo el atacante puede enviar al usuario a un sitio web falso, sino que tambin puede
crear un ataque autnomo.

Siempre que se permite al usuario pasar (partes de) la URL para la redireccin, es
posiblemente vulnerable. El ataque ms obvio sera redirigir a los usuarios a una aplicacin
web falsa que se ve y se siente exactamente como la original. Este llamado ataque phishing
funciona enviando un enlace no sospechoso en un correo electrnico a los usuarios,
inyectando el enlace por XSS en la aplicacin web o poniendo el enlace en un sitio externo.
Es poco sospechoso, porque el enlace comienza con la URL de la aplicacin web y la
direccin URL del sitio malintencionado est oculta en el parmetro de redireccin:
http://www.example.com/site/redirect?to=www.attacker.com . He aqu un ejemplo de una

accin heredada:

def legacy
redirect_to(params.update(action:'main'))
end

Esto redireccionar al usuario a la accin principal si intenta acceder a una accin


heredada. La intencin era preservar los parmetros de la URL a la accin heredada y
pasarlos a la accin principal. Sin embargo, puede ser explotado por el atacante si
incluyeron una clave de host en la URL:

http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com

Si se encuentra al final de la URL, difcilmente se notar y redireccionar al usuario al host


attacker.com . Una contramedida simple sera incluir slo los parmetros esperados en una

accin heredada (de nuevo un enfoque de lista blanca, en lugar de eliminar parmetros
inesperados). Y si rediriges a una URL, comprubala con una lista blanca o una expresin
regular.

4.1.1 XSS auto-contenido

580
4- Redireccin y archivos

Otra redireccin y el ataque auto-contenido XSS funciona en Firefox y Opera mediante el


uso del protocolo de datos. Este protocolo muestra su contenido directamente en el
navegador y puede ser cualquier cosa desde HTML o JavaScript a imgenes enteras:

data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K

Este ejemplo es un JavaScript codificado en Base64 que muestra un cuadro de mensaje


simple. En una URL de redireccin, un atacante podra redirigir a esta URL con el cdigo
malicioso en ella. Como contramedida, no permita que el usuario proporcione (partes de) la
URL a la que se redirigir.

4.2 Cargas de archivos


Asegrese de que las subidas de archivos no sobrescriben archivos importantes y procesen
los archivos multimedia de forma asncrona.

Muchas aplicaciones web permiten a los usuarios cargar archivos. Los nombres de archivo,
que el usuario puede elegir (en parte), siempre deben ser filtrados, ya que un atacante
podra usar un nombre de archivo malicioso para sobrescribir cualquier archivo del servidor.
Si almacena subidas de archivos en /var/www/uploads y el usuario introduce un nombre de
archivo como " ../../../etc/passwd ", puede sobrescribir un archivo importante. Por
supuesto, el intrprete de Ruby necesitara los permisos apropiados para hacerlo - una
razn ms para ejecutar servidores web, servidores de bases de datos y otros programas
como un usuario Unix menos privilegiado.

Al filtrar los nombres de los archivos de entrada del usuario, no intente eliminar partes
malintencionadas. Piense en una situacin en la que la aplicacin web quita todo el " ../ "
en un nombre de archivo y un atacante utiliza una cadena como " .... // " - el resultado
ser " ../ ". Lo mejor es utilizar un enfoque de lista blanca, que comprueba la validez de un
nombre de archivo con un conjunto de caracteres aceptados. Esto se opone a un enfoque
de lista negra que intenta eliminar caracteres no permitidos. En caso de que no sea un
nombre de archivo vlido, rechcelo (o reemplace los caracteres no aceptados), pero no los
elimine. Aqu est el nombre de archivo sanitizer del plugin attachment_fu :

def sanitize_filename(filename)
filename.strip.tap do |name|
# NOTE: File.basename doesn't work right with Windows paths on Unix
# get only the filename, not the whole path
name.sub! /\A.*(\\|\/)/, ''
# Finally, replace all non alphanumeric, underscore
# or periods with underscore
name.gsub! /[^\w\.\-]/, '_'
end
end

581
4- Redireccin y archivos

Una desventaja significativa del procesamiento sncrono de subidas de archivos (como con
el plugin attachment_fu que puede subir imgenes), es su vulnerabilidad a los ataques de
denegacin de servicio. Un atacante puede iniciar de forma sincronizada cargas de archivos
de imagen desde muchas computadoras, lo que aumenta la carga del servidor y
eventualmente se bloquea o bloquea el servidor.

La solucin a esto es mejor procesar los archivos de medios de forma asncrona: Guarde el
archivo de medios y programe una solicitud de procesamiento en la base de datos. Un
segundo proceso manejar el proceso del archivo en el background.

4.3 Cdigo ejecutable en cargas de archivos


El cdigo fuente en los archivos subidos puede ser ejecutado cuando se coloca en
directorios especficos. No coloque cargas de archivos en el directorio de Rails
/public si es el directorio inicial de Apache.

El popular servidor web Apache tiene una opcin denominada DocumentRoot . Este es el
directorio de inicio del sitio web, todo en este rbol de directorio ser servido por el servidor
web. Si hay archivos con una extensin de nombre de archivo determinada, el cdigo en
ella se ejecutar cuando se solicite (podra requerir algunas opciones para establecer).
Ejemplos de esto son archivos PHP y CGI . Ahora piensa en una situacin en la que un
atacante carga un archivo " file.cgi " con cdigo en l, que se ejecutar cuando alguien
descargue el archivo.

Si su Apache DocumentRoot apunta al directorio de Rails /public , no coloque archivos


cargados en l, almacene los archivos al menos un nivel hacia abajo.

4.4 Descargas de archivos


Asegrese de que los usuarios no pueden descargar archivos arbitrarios.

Del mismo modo que tiene que filtrar los nombres de archivo para las subidas, tiene que
hacerlo para las descargas. El mtodo send_file() enva archivos desde el servidor al
cliente. Si utiliza un nombre de archivo, que el usuario ingres, sin filtrar, el puede descargar
cualquier archivo:

send_file('/var/www/uploads/' + params[:filename])

Simplemente pase un nombre de archivo como este " ../../../etc/passwd " para descargar
la informacin de inicio de sesin del servidor. Una solucin simple contra esto es
comprobar que el archivo solicitado est en el directorio esperado:

582
4- Redireccin y archivos

basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))


filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename !=
File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, disposition: 'inline'

Otro enfoque (adicional) es almacenar los nombres de archivo en la base de datos y


nombrar los archivos en el disco despus de los ids en la base de datos. ste es tambin
un buen acercamiento para evitar el posible cdigo en un archivo subido para ser ejecutado.
El plugin attachment_fu hace esto de una manera similar.

583
5- Intranet y seguridad del administrador

5- Intranet y seguridad del administrador


Las interfaces de la intranet y de la administracin son blancos populares del ataque,
porque permiten el acceso privilegiado. Aunque esto requerira varias medidas de seguridad
extra, lo contrario es el caso en el mundo real.

En 2007 se produjo el primer troyano hecho a la medida que rob informacin de una
intranet, a saber, el sitio web Monster para empleadores de Monster.com, una aplicacin
web de reclutamiento en lnea. Troyanos hechos a la medida son muy raros, hasta ahora, y
el riesgo es bastante bajo, pero sin duda es una posibilidad y un ejemplo de cmo la
seguridad del cliente de acogida es importante, tambin. Sin embargo, la mayor amenaza
para las aplicaciones Intranet y Admin son XSS y CSRF.

XSS. Si su aplicacin vuelve a mostrar la entrada de usuario malintencionado desde la


extranet, la aplicacin ser vulnerable a XSS. Los nombres de usuario, comentarios,
informes de spam, direcciones de pedidos son slo algunos ejemplos poco comunes, donde
puede haber XSS.

Tener un solo lugar en la interfaz de administracin o Intranet, donde la entrada no ha sido


desinfectada, hace que toda la aplicacin sea vulnerable. Las posibles ventajas incluyen
robar la cookie del administrador privilegiado, inyectar un iframe para robar la contrasea
del administrador o instalar software malicioso a travs de los agujeros de seguridad del
navegador para hacerse cargo del ordenador del administrador.

Consulte la seccin de Inyeccin para las contramedidas en XSS.

CSRF. Cross-Site Request Forgery (CSRF), tambin conocido como Cross-Site Reference
Forgery (XSRF), es un mtodo de ataque gigantesco, que permite al atacante hacer todo lo
que el administrador o usuario de Intranet puede hacer. Como ya has visto anteriormente, la
CSRF funciona, aqu hay algunos ejemplos de lo que los atacantes pueden hacer en la
Intranet o en la interfaz de administracin.

Un ejemplo del mundo real es una reconfiguracin del enrutador por CSRF. Los atacantes
enviaron un correo electrnico malicioso, con CSRF en l, a los usuarios mexicanos. El e-
mail afirmaba que haba una tarjeta electrnica esperando al usuario, pero tambin contena
una etiqueta de imagen que result en una solicitud HTTP-GET para reconfigurar el
enrutador del usuario (que es un modelo popular en Mxico). La solicitud cambi los ajustes
de DNS para que las solicitudes a un sitio bancario con sede en Mxico se asignaran al sitio
del atacante. Todos los que accedieron al sitio de la banca a travs de ese enrutador vieron
el sitio web falso del atacante y se les robaron sus credenciales.

584
5- Intranet y seguridad del administrador

Otro ejemplo cambi la direccin de correo electrnico y la contrasea de Google Adsense.


Si la vctima ha iniciado sesin en Google Adsense, la interfaz de administracin de
campaas publicitarias de Google, un atacante podra cambiar las credenciales de la
vctima.

Otro ataque popular es el spam de su aplicacin web, su blog o foro para propagar XSS
malicioso. Por supuesto, el atacante tiene que conocer la estructura de URL, pero la
mayora de las URLs de Rails son bastante sencillas o sern fciles de averiguar, si se trata
de una interfaz de administracin de aplicacin abierta. El atacante puede incluso hacer
1.000 suposiciones con suerte, incluyendo las etiquetas maliciosas IMG que prueban todas
las posibles combinaciones.

Para las contramedidas contra CSRF en interfaces de administracin e intranet, consulte las
contramedidas en la seccin CSRF.

5.1 Precauciones Adicionales


La interfaz de administracin funciona de la siguiente manera: est ubicada en
www.example.com/admin , slo se puede acceder si el indicador de administrador se establece

en el modelo User , vuelve a mostrar la entrada del usuario y permite al administrador


borrar / agregar / editar lo que sea en los datos deseados. Aqu hay algunas reflexiones
sobre esto:

Es muy importante pensar en el peor caso: Qu pasa si alguien realmente se apodera


de sus cookies o credenciales de usuario. Podras introducir funciones para la interfaz
de administracin para limitar las posibilidades del atacante. O qu tal las credenciales
de inicio de sesin especiales para la interfaz de administracin, que no sean las
utilizadas para la parte pblica de la aplicacin. O una contrasea especial para
acciones muy graves?
El administrador realmente tiene que acceder a la interfaz desde cualquier parte del
mundo? Piense en limitar el inicio de sesin a un montn de direcciones IP de origen.
Examine request.remote_ip para obtener informacin sobre la direccin IP del usuario.
Esto no es a prueba de balas, sino una gran barrera. Sin embargo, recuerde que puede
haber un proxy en uso.
Poner la interfaz de administracin a un subdominio especial como
admin.application.com y convertirlo en una aplicacin independiente con su propia

administracin de usuarios. Esto hace que robar una cookie de administracin del
dominio habitual, www.application.com , sea imposible. Esto se debe a la misma poltica
de origen en su navegador: Un script inyectado (XSS) en www.application.com no
puede leer la cookie para admin.application.com y viceversa.

585
5- Intranet y seguridad del administrador

586
6- Gestin de usuarios

6- Gestin de usuarios
Casi todas las aplicaciones web tienen que lidiar con la autorizacin y la autenticacin.
En lugar de hacer su propio cdigo, es aconsejable utilizar plug-ins comunes. Pero
tambin, mantenerlos al da. Algunas precauciones adicionales pueden hacer que su
aplicacin sea an ms segura.

Existen varios complementos de autenticacin disponibles para Rails. Los buenos, como
devise y authlogic , almacenan slo contraseas encriptadas, no contraseas de texto

sin formato. En Rails 3.1 puede utilizar el mtodo has_secure_password incorporado que
tiene caractersticas similares.

Cada nuevo usuario recibe un cdigo de activacin para activar su cuenta cuando recibe un
correo electrnico con un enlace. Despus de activar la cuenta, las columnas
activation_code se establecern en NULL en la base de datos. Si alguien solicit una URL

como sta, se registrara como el primer usuario activado que se encuentre en la base de
datos (y es probable que se trate del administrador):

http://localhost:3006/user/activate
http://localhost:3006/user/activate?id=

Esto es posible porque en algunos servidores, de esta manera el parmetro id , como en


params[:id] , sera nil . Sin embargo, aqu est el buscador de la accin de activacin:

User.find_by_activation_code(params[:id])

Si el parmetro era nil , la consulta SQL resultante ser

SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1

Y as encontr al primer usuario en la base de datos, lo devolvi y lo registr. Puede


obtener ms informacin sobre l en esta entrada de blog. Es aconsejable actualizar sus
plug-ins de vez en cuando. Adems, puede revisar su aplicacin para encontrar ms fallas
como esta.

6.1 Ataques de fuerza bruta en accounts

587
6- Gestin de usuarios

Los ataques de fuerza bruta en las cuentas son ataques de prueba y error en las
credenciales de inicio de sesin. Abandonarlos con mensajes de error ms genricos y
posiblemente necesite introducir un CAPTCHA.

Una lista de nombres de usuario para su aplicacin web puede ser mal utilizada para forzar
las contraseas, porque la mayora de la gente no usa contraseas sofisticadas. La mayora
de las contraseas son una combinacin de palabras del diccionario y posiblemente
nmeros. As armado con una lista de nombres de usuario y un diccionario, un programa
automtico puede encontrar la contrasea correcta en cuestin de minutos.

Debido a esto, la mayora de las aplicaciones web mostrar un mensaje de error genrico
"nombre de usuario o contrasea no correcta", si una de ellas no es correcta. Si dice que
"no se ha encontrado el nombre de usuario que ha introducido", un atacante podra compilar
automticamente una lista de nombres de usuario.

Sin embargo, lo que la mayora de los diseadores de aplicaciones web descuidan, son las
pginas de "olvid mi contrasea". Estas pginas admiten a menudo que el nombre de
usuario introducido o la direccin de correo electrnico no se ha encontrado. Esto permite a
un atacante compilar una lista de nombres de usuario y forzar las cuentas.

Con el fin de mitigar estos ataques, mostrar un mensaje de error genrico en las pginas de
"olvid mi contrasea", tambin. Adems, puede requerir introducir un CAPTCHA despus
de un nmero de inicios de sesin fallidos de una determinada direccin IP. Tenga en
cuenta, sin embargo, que esto no es una solucin a prueba de balas contra programas
automticos, porque estos programas pueden cambiar su direccin IP a menudo. Sin
embargo, levanta la barrera de un ataque.

6.2 Secuestro de Cuenta


Muchas aplicaciones web hacen que sea fcil secuestrar cuentas de usuario. Por qu no
ser diferente y hacerlo ms difcil ?.

6.2.1 Contraseas
Piense en una situacin en la que un atacante ha robado la cookie de sesin de un usuario
y, por lo tanto, puede usar la aplicacin conjuntamente. Si es fcil cambiar la contrasea, el
atacante secuestrar la cuenta con unos pocos clics. O si el formulario de cambio de
contrasea es vulnerable a CSRF, el atacante podr cambiar la contrasea de la vctima
atrayndolas a una pgina web donde hay una etiqueta IMG que hace la CSRF. Como
contramedida, haga que los fomularios de cambio de contrasea sean seguras contra
CSRF, por supuesto. Y requiera que el usuario introduzca la contrasea antigua al
cambiarla.

588
6- Gestin de usuarios

6.2.2 E-Mail
Sin embargo, el atacante tambin puede hacerse cargo de la cuenta cambiando la direccin
de correo electrnico. Despus de que lo cambien, irn a la pgina de contrasea olvidada
y la (posiblemente nueva) contrasea ser enviada a la direccin de correo electrnico del
atacante. Como contramedida tambin debes requerir que el usuario introduzca la
contrasea al cambiar la direccin de correo electrnico.

6.2.3 Otros
Dependiendo de su aplicacin web, puede haber ms maneras de secuestrar la cuenta del
usuario. En muchos casos CSRF y XSS ayudarn a hacerlo. Por ejemplo, como en una
vulnerabilidad de CSRF en Gmail. En este ataque de prueba de concepto, la vctima habra
sido atrada a un sitio web controlado por el atacante. En ese sitio hay una etiqueta IMG
elaborada que da como resultado una solicitud HTTP GET que cambia la configuracin del
filtro de Google Mail. Si la vctima inici sesin en Google Mail, el atacante cambiara los
filtros para reenviar todos los correos electrnicos a su direccin de correo electrnico. Esto
es casi tan daino como secuestrar toda la cuenta. Como contramedida, revise la lgica de
su aplicacin y elimine todas las vulnerabilidades de XSS y CSRF.

6.3 CAPTCHAs
Un CAPTCHA es una prueba de desafo-respuesta para determinar que la respuesta
no es generada por una computadora. Se utiliza a menudo para proteger los
formularios de registro de los atacantes y los formularios de comentarios de los robots
de spam automticos pidiendo al usuario que escriba las letras de una imagen
distorsionada. Este es el CAPTCHA positivo, pero tambin hay el CAPTCHA negativo.
La idea de un CAPTCHA negativo no es para un usuario probar que son humanos,
pero revelan que un robot es un robot.

Una popular API CAPTCHA positiva es reCAPTCHA que muestra dos imgenes
distorsionadas de palabras de libros antiguos. Tambin aade una lnea angulada, en lugar
de un fondo distorsionado y altos niveles de deformacin en el texto como anteriores
CAPTCHAs, porque estos ltimos se rompieron. Como bonificacin, el uso de reCAPTCHA
ayuda a digitalizar libros antiguos. ReCAPTCHA es tambin un complemento Rails con el
mismo nombre que la API.

Obtendr dos claves de la API, una clave pblica y una privada, que debe poner en su
entorno de Rails. Despus de eso, puede utilizar el mtodo recaptcha_tags en la vista y el
mtodo verify_recaptcha en el controlador. verify_recaptcha devolver false si la
validacin falla. El problema con CAPTCHAs es que tienen un impacto negativo en la

589
6- Gestin de usuarios

experiencia del usuario. Adems, algunos usuarios con discapacidades visuales han
encontrado ciertos tipos de CAPTCHA distorsionados difciles de leer. Sin embargo, los
CAPTCHA positivos son uno de los mejores mtodos para evitar que todo tipo de bots
enven formularios.

La mayora de los bots son realmente tontos. Ellos rastrean la web y ponen su spam en el
campo de todos los formularios que pueden encontrar. Los CAPTCHA negativos se
aprovechan de eso e incluyen un campo " honeypot " en el formulario que se oculta del
usuario humano por CSS o JavaScript.

Tenga en cuenta que los CAPTCHA negativos slo son eficaces contra bots mudos y no
ser suficiente para proteger las aplicaciones crticas de los bots seleccionados. Sin
embargo, los CAPTCHA negativos y positivos pueden combinarse para aumentar el
rendimiento, por ejemplo, si el campo " honeypot " no est vaco (bot detectado), no
necesitar verificar el CAPTCHA positivo, lo que requerira una solicitud HTTPS a Google
ReCaptcha antes de calcular la respuesta.

Aqu estn algunas ideas sobre cmo ocultar los campos del honeypot por JavaScript y/o
CSS:

Posicionar los campos fuera del rea visible de la pgina


Hacer los elementos muy pequeos o colorearlos igual que el fondo de la pgina
Deje los campos mostrados, pero diga a los seres humanos que los dejen en blanco

El CAPTCHA negativo ms simple es un campo escondido del honeypot. En el lado del


servidor, comprobar el valor del campo: Si contiene cualquier texto, debe ser un bot. A
continuacin, puede ignorar la publicacin o devolver un resultado positivo, pero no guardar
la publicacin en la base de datos. De esta manera el bot estar satisfecho y seguir
adelante. Usted puede hacer esto con usuarios molestos, tambin.

Usted puede encontrar CAPTCHA negativos ms sofisticados en el blog de Ned Batchelder:

Incluya un campo con el sello de hora UTC actual y comprubelo en el servidor. Si est
demasiado lejos en el pasado, o si es en el futuro, el formulario no es vlido.
Aleatorizar los nombres de los campos
Incluir ms de un campo de honeypot de todos los tipos, incluidos los botones de envo.

Tenga en cuenta que esto slo lo protege de los bots automticos, bots hechos a medida no
puede ser detenido por esto. As que los CAPTCHA negativos podran no ser buenos para
proteger los formularios de inicio de sesin.

6.4 Logging
Dgale a Rails que no ponga contraseas en los archivos de registro.

590
6- Gestin de usuarios

De forma predeterminada, Rails "loguea" (hace logs) todas las solicitudes que se realizan
en la aplicacin web. Pero los archivos de logs pueden ser un problema de seguridad
enorme, ya que pueden contener credenciales de inicio de sesin, nmeros de tarjetas de
crdito y otros. Al disear un concepto de seguridad de aplicaciones web, tambin debe
pensar en lo que suceder si un atacante tiene acceso (completo) al servidor web. Cifrar los
secrets y contraseas en la base de datos ser bastante intil, si los archivos de logs los
enuncian en texto claro. Puede filtrar ciertos parmetros de solicitud de sus archivos de logs
agregndolos a config.filter_parameters en la configuracin de la aplicacin. Estos
parmetros se marcarn [FILTERED] en el log.

config.filter_parameters << :password

Los parmetros proporcionados se filtrarn mediante una expresin regular coincidente


parcial. Rails aade el valor por defecto :password en el inicializador apropiado
( initialalizers/filter_parameter_logging.rb ) y se preocupa por los parmetros tpicos
password y password_confirmation de la aplicacin

6.5 Buenas contraseas


Le resulta difcil recordar todas sus contraseas? No los escriba, sino que use las letras
iniciales de cada palabra en una frase fcil de recordar.

Bruce Schneier, un tecnlogo de seguridad, ha analizado 34.000 nombres de usuario y


contraseas reales del ataque de phishing de MySpace que se menciona a continuacin.
Resulta que la mayora de las contraseas son bastante fciles de romper. Las 20
contraseas ms comunes son:

password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1,


football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1,
iloveyou1, and monkey.

Es interesante que slo el 4% de estas contraseas eran palabras del diccionario y la gran
mayora es en realidad alfanumrica. Sin embargo, los diccionarios de "crackeo" de
contraseas contienen un gran nmero de contraseas de hoy, y prueban todo tipo de
combinaciones (alfanumricas). Si un atacante conoce su nombre de usuario y utiliza una
contrasea dbil, su cuenta se romper fcilmente.

Una buena contrasea es una larga combinacin alfanumrica de casos mixtos. Como esto
es muy difcil de recordar, es aconsejable introducir slo las primeras letras de una oracin
que pueda recordar fcilmente. Por ejemplo "El zorro marrn rpido salta sobre el perro

591
6- Gestin de usuarios

perezoso" ser "Tqbfjotld". Tenga en cuenta que esto es slo un ejemplo, no debe utilizar
frases bien conocidas como estas, ya que pueden aparecer en los diccionarios cracker,
tambin.

6.6 Expresiones Regulares


Una falla comn en las expresiones regulares de Ruby es coincidir con la cadena que
comienza y termina por ^ y $, en lugar de \A y \z.

Ruby utiliza un enfoque ligeramente diferente a muchos otros lenguajes para coincidir con el
comienzo y el final de un string. Es por eso que incluso muchos libros de Ruby y Rails se
equivocan. Entonces, cmo es esto una amenaza de seguridad? Digamos que quera
validar un campo de URL de manera flexible y utiliz una expresin regular simple como
sta:

/^https?:\/\/[^\n]+$/i

Esto puede funcionar bien en algunos lenguajes. Sin embargo, en Ruby ^ y $ coinciden con
el principio de la lnea y el final de la lnea. Y as una URL como sta pasa el filtro sin
problemas:

javascript:exploit_code();/*
http://hi.com
*/

Esta URL pasa el filtro porque la expresin regular coincide - la segunda lnea, el resto no
importa. Ahora imagine que tenamos una vista que mostraba la URL como esta:

link_to "Homepage", @user.homepage

El enlace parece inocente para los visitantes, pero cuando se hace clic en l, se ejecutar la
funcin JavaScript " exploit_code " o cualquier otro JavaScript que el atacante proporciona.

Para fijar la expresin regular, se debe usar \A y \z en vez de ^ y $, as:

/\Ahttps?:\/\/[^\n]+\z/i

Como este es un error frecuente, el validador de formato ( validates_format_of ) ahora


plantea una excepcin si la expresin regular proporcionada comienza con ^ o termina con
$. Si necesitas usar ^ y $ en lugar de \A y \z (lo cual es raro), puedes establecer la opcin
:multiline en true , as:

592
6- Gestin de usuarios

# content should include a line "Meanwhile" anywhere in the string


validates :content, format: { with: /^Meanwhile$/, multiline: true }

Tenga en cuenta que esto slo lo protege contra el error ms comn al usar el validador de
formato - siempre debe tener en cuenta que ^ y $ coinciden con el principio de la lnea y el
final de la lnea en Ruby, y no el principio y el final de una cadena.

6.7 Escalada de privilegios


Cambiar un solo parmetro puede dar al usuario acceso no autorizado. Recuerde que
cada parmetro puede ser cambiado, no importa cunto lo esconda u obstruya.

El parmetro ms comn que un usuario puede manipular, es el parmetro id, como en


http://www.domain.com/project/1 , mientras que 1 es el id . Estar disponible en

parmetros en el controlador. All, lo ms probable es hacer algo como esto:

@project = Project.find(params[:id])

Esto est bien para algunas aplicaciones web, pero ciertamente no si el usuario no est
autorizado a ver todos los proyectos. Si el usuario cambia el ID a 42, y no se les permite
ver esa informacin, tendrn acceso a ella de todos modos. En su lugar, consulte los
derechos de acceso del usuario:

@project = @current_user.projects.find(params[:id])

Dependiendo de su aplicacin web, habr muchos ms parmetros que el usuario puede


manipular. Como regla general, ningn dato de entrada del usuario es seguro, hasta que se
demuestre lo contrario, y cada parmetro del usuario se manipula potencialmente.

No se deje engaar por la seguridad por ofuscacin y por la seguridad de JavaScript. Las
herramientas para desarrolladores le permiten revisar y cambiar los campos ocultos de
cada formulario. JavaScript se puede utilizar para validar datos de entrada del usuario, pero
ciertamente no para evitar que los atacantes enven peticiones maliciosas con valores
inesperados. El complemento Firebug para Mozilla Firefox registra todas las solicitudes y
puede repetirlas y cambiarlas. Es una manera fcil de evitar cualquier validacin de
JavaScript. Y hay incluso los proxies del lado del cliente que permiten que usted intercepte
cualquier peticin y respuesta de y al Internet.

593
7- Injection

7- Injection
PD: este capitulo no tiene todo el cdigo completo, dado que tiene sentencias de
inyeccin SQL, y Gitbook lo analiza como tal, y lo evita para no generar
vulnerabilidades. Asi que debes leerlo con cuidado y dirigirte a la documentacin
oficial en ingls.

Injection es una clase de ataques que introducen cdigo malicioso o parmetros en


una aplicacin web para ejecutarlo dentro de su contexto de seguridad. Los ejemplos
prominentes de la inyeccin son secuencias de comandos entre sitios (XSS) e
inyeccin de SQL.

La inyeccin es muy complicada, porque el mismo cdigo o parmetro puede ser malicioso
en un contexto, pero totalmente inofensivo en otro. Un contexto puede ser un lenguaje de
programacin, consulta o de programacin, el shell o un mtodo Ruby / Rails. Las
secciones siguientes cubrirn todos los contextos importantes donde pueden ocurrir
ataques de inyeccin. La primera seccin, sin embargo, cubre una decisin arquitectnica
en relacin con la inyeccin.

7.1 Listas blancas versus listas negras


Al desinfectar, proteger o verificar algo, prefiera las listas blancas sobre listas negras.

Una lista negra puede ser una lista de direcciones de correo electrnico incorrectas,
acciones no pblicas o etiquetas HTML incorrectas. Esto se opone a una lista blanca que
enumera las buenas direcciones de correo electrnico, acciones pblicas, buenas etiquetas
HTML y as sucesivamente. Aunque a veces no es posible crear una lista blanca (en un
filtro SPAM, por ejemplo), prefiera utilizar enfoques de listas blancas:

Utilice before_action except: [...] instead of only: [...] para acciones relacionadas
con la seguridad. De esta manera, no olvide activar las comprobaciones de seguridad
de las acciones aadidas recientemente.

Permitir <strong> en lugar de eliminar <script> contra Cross-Site Scripting (XSS). Vea a
abajo para ms detalles

No trate de corregir la entrada de usuarios mediante listas negras:

Esto har que el ataque funcione: "<sc<script>ript>".gsub("<script>", "")

Pero rechazar el input malformada

594
7- Injection

Las listas blancas son tambin un buen enfoque contra el factor humano de olvidar algo en
la lista negra.

7.2 Inyeccin de SQL


Gracias a los mtodos inteligentes, esto no es un problema en la mayora de las
aplicaciones de Rails. Sin embargo, este es un ataque muy devastador y comn en las
aplicaciones web, por lo que es importante entender el problema.

7.2.1 Introduccin
Los ataques de inyeccin SQL apuntan a influenciar las consultas de la base de datos
mediante la manipulacin de los parmetros de la aplicacin web. Un objetivo popular de
los ataques de inyeccin de SQL es evitar la autorizacin. Otro objetivo es realizar la
manipulacin de datos o leer datos arbitrarios. A continuacin se muestra un ejemplo de
cmo no utilizar datos de entrada del usuario en una consulta:

Project.where("name = '#{params[:name]}'")

Esto podra estar en un action llamado search y el usuario puede introducir el nombre de
un proyecto que desea encontrar. Si un usuario malintencionado introduce 'OR 1 -- , la
consulta SQL resultante ser:

SELECT * FROM projects WHERE name = '' OR 1 --'

Los dos guiones empiezan un comentario ignorando todo despus de l. Por lo tanto, la
consulta devuelve todos los registros de la tabla de proyectos, incluidos los ocultos para el
usuario. Esto se debe a que la condicin es true para todos los registros.

7.2.2 Omisin de la autorizacin


Por lo general, una aplicacin web incluye control de acceso. El usuario introduce sus
credenciales de inicio de sesin y la aplicacin web trata de encontrar el registro coincidente
en la tabla de usuarios. La aplicacin concede acceso cuando encuentra un registro. Sin
embargo, un atacante puede evitar esta comprobacin con la inyeccin de SQL. A
continuacin se muestra una consulta de base de datos tpica en Rails para encontrar el
primer registro en la tabla de usuarios que coincide con los parmetros de credenciales de
inicio de sesin suministrados por el usuario.

User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")

595
7- Injection

Si un atacante introduce ' OR '1'='1 como nombre y ' OR '2'>'1 como contrasea, la consulta
SQL resultante ser:

SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1

Esto simplemente encontrar el primer registro en la base de datos y concede acceso a


este usuario.

7.2.3 Lectura no autorizada


La instruccin UNION conecta dos consultas SQL y devuelve los datos en un conjunto. Un
atacante puede usarlo para leer datos arbitrarios de la base de datos. Tomemos el ejemplo
de arriba:

Project.where("name = '#{params[:name]}'")

Y ahora vamos a inyectar otra consulta utilizando la sentencia UNION:

') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users --

Esto resultar en la siguiente consulta SQL:

SELECT * FROM projects WHERE (name = '') UNION


SELECT id,login AS name,password AS description,1,1,1 FROM users --'

El resultado no ser una lista de proyectos (porque no hay un proyecto con un nombre
vaco), sino una lista de nombres de usuario y su contrasea. As que esperamos que haya
encriptado las contraseas en la base de datos! El nico problema para el atacante es que
el nmero de columnas tiene que ser el mismo en ambas consultas. Es por eso que la
segunda consulta incluye una lista de unos (1), que ser siempre el valor 1, con el fin de
coincidir con el nmero de columnas en la primera consulta.

Adems, la segunda consulta cambia el nombre de algunas columnas con la instruccin AS


para que la aplicacin web muestre los valores de la tabla de usuario. Asegrese de
actualizar sus Rails a al menos 2.1.1.

7.2.4 Contramedidas
Ruby on Rails tiene un filtro incorporado para caracteres especiales de SQL, que escapar
",", caracteres NULL y saltos de lnea.Utilizando Model.find(id) o
Model.find_by_something(something) aplica automticamente esta contramedida. Para

596
7- Injection

fragmentos, especialmente en condiciones de fragmentos ( where("...") ), los mtodos


connection.execute() o Model.find_by_sql() , tiene que aplicarse manualmente.

En lugar de pasar una cadena a la opcin de condiciones, puede pasar una matriz para
desinfectar cadenas viciadas as:

Model.where("login = ? AND password = ?", entered_user_name, entered_password).first

Como puede ver, la primera parte de la matriz es un fragmento de SQL con signos de
interrogacin. Las versiones desinfectadas de las variables de la segunda parte de la matriz
sustituyen a los signos de interrogacin. O puede pasar un hash para el mismo resultado:

Model.where(login: entered_user_name, password: entered_password).first

La matriz o hash de los formularios slo est disponible en instancias de modelo. Puedes
intentar el sanitize_sql() en otro lugar. Haga un hbito pensar en las consecuencias de
seguridad cuando se utiliza una cadena externa en SQL.

7.3 Scripting entre sitios (XSS)


La ms extendida y una de las vulnerabilidades de seguridad ms devastadoras en las
aplicaciones web es XSS. Este ataque malicioso inyecta cdigo ejecutable del lado del
cliente. Rails proporciona mtodos auxiliares para evitar estos ataques.

7.3.1 Entry points


Un punto de entrada es una URL vulnerable y sus parmetros donde un atacante puede
iniciar un ataque.

Los puntos de entrada ms comunes son los mensajes de los post, los comentarios de los
usuarios y los libros de visitas, pero los ttulos de los proyectos, los nombres de los
documentos y las pginas de resultados de bsqueda tambin han sido vulnerables. Pero la
entrada no necesariamente tiene que venir de cuadros de entrada en sitios web, puede
estar en cualquier parmetro de URL - obvio, oculto o interno. Recuerde que el usuario
puede interceptar cualquier trfico. Las aplicaciones o los proxies de cliente-sitio facilitan el
cambio de solicitudes. Tambin hay otros vectores de ataque como anuncios de banner.

Los ataques XSS funcionan de la siguiente manera: Un atacante inyecta algn cdigo, la
aplicacin web lo guarda y lo muestra en una pgina, presentado posteriormente a una
vctima. La mayora de los ejemplos XSS simplemente muestran un cuadro de alerta, pero
es ms potente que eso. XSS puede robar la cookie, secuestrar la sesin, redirigir a la

597
7- Injection

vctima a un sitio web falso, mostrar anuncios para beneficio del atacante, cambiar
elementos en el sitio web para obtener informacin confidencial o instalar software
malicioso a travs de agujeros de seguridad en el navegador web.

Durante el segundo semestre de 2007, se registraron 88 vulnerabilidades en navegadores


Mozilla, 22 en Safari, 18 en IE y 12 en Opera. El informe de amenazas de Symantec Global
Internet Security tambin document 239 vulnerabilidades de complemento de explorador
en los ltimos seis meses de 2007. Mpack es un framework de ataque muy activo y
actualizado que aprovecha estas vulnerabilidades. Para hackers criminales, es muy
atractivo aprovechar una vulnerabilidad de SQL-Injection en un framework de aplicaciones
web e insertar cdigo malicioso en cada columna de tabla textual. En abril de 2008 ms de
510.000 sitios fueron hackeados como este, entre ellos el gobierno britnico, las Naciones
Unidas, y muchos ms altos objetivos.

7.3.2 Inyeccin de HTML / JavaScript


El lenguaje XSS ms comn es, por supuesto, JavaScript el ms popular del lenguaje de
scripts del cliente, a menudo en combinacin con HTML. Es esencial escapar de la entrada
del usuario.

Aqu est la prueba ms directa para comprobar XSS:

Este cdigo JavaScript simplemente mostrar un cuadro de alerta. Los siguientes ejemplos
hacen exactamente lo mismo, slo que en lugares muy poco comunes:

7.3.2.1 Robo de Cookie

Estos ejemplos no causan ningn dao hasta ahora, as que veamos cmo un atacante
puede robar la cookie del usuario (y as secuestrar la sesin del usuario). En JavaScript
puede utilizar la propiedad document.cookie para leer y escribir la cookie del documento.
JavaScript aplica la misma poltica de origen, lo que significa que un script de un dominio no
puede acceder a las cookies de otro dominio. La propiedad document.cookie contiene la
cookie del servidor web de origen. Sin embargo, puede leer y escribir esta propiedad si
inserta el cdigo directamente en el documento HTML (como sucede con XSS). Inyecte
esto en cualquier parte de su aplicacin web para ver su propia cookie en la pgina de
resultados:

598
7- Injection

Para un atacante, por supuesto, esto no es til, ya que la vctima ver su propia cookie. El
siguiente ejemplo intentar cargar una imagen desde la URL
http://www.attacker.com/ adems de la cookie. Por supuesto, esta URL no existe, por lo

que el navegador no muestra nada. Pero el atacante puede revisar los archivos de registro
de acceso de su servidor web para ver la cookie de la vctima.

Los archivos de logs en www.attacker.com se leern as:

Usted puede mitigar estos ataques (de la manera obvia) agregando la flag de httpOnly a las
cookies, de modo que document.cookie no pueda ser ledo por el Javascript. Slo se
pueden utilizar cookies HTTP de IE v6.SP1, Firefox v2.0.0.5, Opera 9.5, Safari 4 y Chrome
1.0.154. Pero otros navegadores antiguos (como WebTV e IE 5.5 en Mac) pueden causar
que la pgina no se cargue. Sin embargo, tenga en cuenta que las cookies seguirn siendo
visibles usando Ajax.

7.3.2.2 Destruccin

Con la degradacin de pginas web, un atacante puede hacer muchas cosas, por ejemplo,
presentar informacin falsa o atraer a la vctima en el sitio web de los atacantes para robar
la cookie, credenciales de inicio de sesin u otros datos confidenciales. La forma ms
popular es incluir cdigo de fuentes externas por iframes:

Esto carga HTML y/o JavaScript arbitrario de una fuente externa y lo incrusta como parte
del sitio. Este iframe se toma de un ataque real a sitios italianos legtimos usando el marco
de ataque de Mpack. Mpack intenta instalar software malicioso a travs de agujeros de
seguridad en el navegador web - con mucho xito, el 50% de los ataques tienen xito.

599
7- Injection

Un ataque ms especializado podra superponerse a todo el sitio web o mostrar un


formulario de inicio de sesin, que se ve igual al original del sitio, pero transmite el nombre
de usuario y la contrasea al sitio del atacante. O puede usar CSS y/o JavaScript para
ocultar un enlace legtimo en la aplicacin web y mostrar otro en su lugar que redirige a un
sitio web falso.

Los ataques de inyeccin reflejados son aquellos en los que la carga til no se almacena
para presentarla a la vctima posteriormente, pero se incluye en la URL. Especialmente los
formularios de bsqueda no pueden escapar de la cadena de bsqueda. El siguiente enlace
presentaba una pgina en la que se deca que "George Bush design a un nio de 9 aos
para que fuera el presidente ...":

7.3.2.3 Contramedidas

Es muy importante filtrar entradas maliciosas, pero tambin es importante escapar de la


salida de la aplicacin web.

Especialmente para XSS, es importante hacer filtrado de entrada de lista blanca en lugar de
lista negra. El filtrado de listas blancas indica los valores permitidos en oposicin a los
valores no permitidos. Las listas negras nunca estn completas.

Imagine que una lista negra elimina "script" de la entrada del usuario. Ahora el atacante
inyecta "<scrscriptipt>", y despus del filtro, "<script>" permanece. Las versiones anteriores
de Rails utilizaron un enfoque de lista negra para el mtodo strip_tags() , strip_links()
y sanitize() . As que este tipo de inyeccin era posible:

Esto devolvi "algunos <script>alert ('hello')</ script> ", lo que hace que un ataque
funcione. Es por eso que un enfoque de lista blanca es mejor, utilizando el mtodo
actualizado de Rails 2 sanitize() :

Esto permite slo las etiquetas dadas y hace un buen trabajo, incluso contra todo tipo de
trucos y etiquetas mal formadas.

600
7- Injection

Como segundo paso, es una buena prctica escapar de toda la salida de la aplicacin,
especialmente cuando vuelve a mostrar la entrada del usuario, que no ha sido filtrada por el
input (como en el ejemplo del formulario de bsqueda anterior). Utilice el mtodo
escapeHTML() o su mtodo alias h() para reemplazar los caracteres de entrada HTML &,

", <y> por sus representaciones no interpretadas en HTML ( &amp;, &quot;, &lt;, and
&gt; ).

7.3.2.4 Ofuscacin y codificacin de la inyeccin

El trfico de la red se basa sobre todo en el alfabeto occidental limitado, as que las nuevas
codificaciones de carcter, tales como Unicode, emergieron, para transmitir caracteres en
otros idiomas. Sin embargo, esto tambin es una amenaza para las aplicaciones web, ya
que el cdigo malicioso se puede ocultar en diferentes codificaciones que el navegador
puede procesar, pero la aplicacin web no. Aqu est un vector de ataque en codificacin
UTF-8:

Este ejemplo muestra un cuadro de mensaje. Sin embargo, ser reconocido por el filtro
sanitize() anterior. Una gran herramienta para ofuscar y codificar cadenas, y as "conocer

a su enemigo", es el Hackvertor. El mtodo sanitize() de Rails hace un buen trabajo para


evitar ataques de codificacin.

7.3.3 Ejemplos del Subterrneo


Para entender los ataques de hoy en las aplicaciones web, lo mejor es echar un vistazo a
algunos vectores de ataque del mundo real.

Lo que sigue es un extracto del Js.Yamanner@m Yahoo! Gusano del correo. Apareci el 11
de junio de 2006 y fue el primer gusano de interfaz webmail:

Los gusanos explotan un agujero en el filtro HTML/JavaScript de Yahoo, que usualmente


filtra todos los objetivos y atributos de carga de las etiquetas (porque puede haber
JavaScript). El filtro se aplica slo una vez, sin embargo, por lo que el atributo onload con el
cdigo de gusano permanece en su lugar. Este es un buen ejemplo de por qu los filtros de
listas negras nunca son completos y por qu es difcil permitir HTML/JavaScript en una
aplicacin web.

601
7- Injection

Otro gusano de webmail de prueba de concepto es Nduja, un gusano entre dominios para
cuatro servicios de webmail italianos. Encuentre ms detalles sobre el trabajo de Rosario
Valotta. Ambos gusanos webmail tienen el objetivo de recolectar direcciones de correo
electrnico, algo con lo que un hacker criminal podra ganar dinero.

En diciembre de 2006, 34.000 nombres de usuario y contraseas reales fueron robados en


un ataque de phishing de MySpace. La idea del ataque fue crear una pgina de perfil
llamada " login_home_index_html ", por lo que la URL pareca muy convincente. Un HTML y
CSS especialmente diseados se utilizaron para ocultar el contenido original de MySpace
de la pgina y en su lugar mostrar su propio formulario de inicio de sesin.

7.4 Inyeccin CSS


CSS Injection es en realidad inyeccin de JavaScript, porque algunos navegadores (IE,
algunas versiones de Safari y otros) permiten JavaScript en el CSS. Piense dos veces
antes de permitir CSS personalizado en su aplicacin web.

La injection de CSS se explica mejor con el conocido gusano Samy de MySpace. Este
gusano envi automticamente una solicitud de amistad a Samy (el atacante) simplemente
visitando su perfil. Dentro de varias horas tuvo ms de un milln de solicitudes de amigos, lo
que cre tanto trfico que MySpace qued fuera de lnea. La siguiente es una explicacin
tcnica de ese gusano.

MySpace bloque muchas etiquetas, pero permiti CSS. As el autor del gusano puso
JavaScript en un CSS como este:

As que la carga til est en el atributo de estilo. Pero no hay cotizaciones permitidas en la
carga til, ya que las comillas simples y dobles ya se han utilizado. Pero JavaScript tiene
una til funcin eval() que ejecuta cualquier cadena como cdigo.

La funcin eval() es una pesadilla para los filtros de entrada de la lista negra, ya que
permite que el atributo de estilo oculte la palabra " innerHTML ":

602
7- Injection

El siguiente problema para MySpace fue filtrar la palabra "javascript", por lo que el autor
utiliz " java <NEWLINE> script " para evitar esto:

Otro problema para el autor del gusano fueron las fichas de seguridad CSRF. Sin ellos no
pudo enviar una solicitud de amistad a travs de POST. Lo consigui enviando un GET a la
pgina justo antes de agregar un usuario y analizar el resultado del token CSRF.

Al final, consigui un gusano de 4 KB, que se inyect en su pgina de perfil.

La propiedad CSS de moz-binding result ser otra forma de introducir JavaScript en CSS en
navegadores basados en Gecko (Firefox, por ejemplo).

7.4.1 Contramedidas
Este ejemplo, otra vez, demostr que un filtro de la lista negra nunca esta completo. Sin
embargo, como el CSS personalizado en aplicaciones web es una caracterstica bastante
rara, puede ser difcil encontrar un buen filtro CSS de lista blanca. Si desea permitir colores
o imgenes personalizadas, puede permitir que el usuario las elija y cree el CSS en la
aplicacin web. Utilice el mtodo sanitize() de Rails como modelo para un filtro CSS de
lista blanca, si realmente necesita uno.

7.5 Inyeccin de texto


Si desea proporcionar un formato de texto que no sea HTML (debido a la seguridad), utilice
un lenguaje de marcado que se convierte en HTML en el lado del servidor. RedCloth es un
lenguaje para Ruby, pero sin las debidas precauciones, tambin es vulnerable a XSS.

Por ejemplo, RedCloth traduce _test_ a <em> test <em> , lo que hace que el texto sea en
cursiva. Sin embargo, hasta la versin actual 3.0.4, sigue siendo vulnerable a XSS. Obtenga
la nueva versin 4 que elimin los errores graves. Sin embargo, incluso esa versin tiene
algunos errores de seguridad, por lo que las contramedidas siguen aplicndose. Aqu hay
un ejemplo para la versin 3.0.4:

Utilice la opcin :filter_html para eliminar HTML que no fue creado por el procesador
Textile.

603
7- Injection

Sin embargo, esto no filtra todo el HTML, se dejarn algunas etiquetas (por diseo), por
ejemplo <a> :

7.5.1 Contramedidas
Se recomienda utilizar RedCloth en combinacin con un filtro de entrada de lista blanca,
como se describe en las contramedidas contra la seccin XSS.

7.6 Inyeccin de Ajax


Las mismas precauciones de seguridad deben tomarse para las acciones de Ajax
como para las "normales". Hay al menos una excepcin, sin embargo: La salida tiene
que ser escapada en el controlador, si la accin no renderiza una vista.

Si utiliza el complemento in_place_editor , o acciones que devuelven una cadena, en lugar


de representar una vista, tiene que escapar el valor devuelto en la accin. De lo contrario, si
el valor devuelto contiene una cadena XSS, el cdigo malicioso se ejecutar al regresar al
navegador. Escapar de cualquier valor de entrada utilizando el mtodo h() .

7.7 Inyeccin de Lnea de Comando


Utilice los parmetros de lnea de comandos proporcionados por el usuario con
precaucin.

Si su aplicacin tiene que ejecutar comandos en el sistema operativo subyacente, hay


varios mtodos en Ruby: exec(command) , syscall(command) , system(command) y command .
Deber tener especial cuidado con estas funciones si el usuario puede ingresar el comando
completo o una parte del mismo. Esto se debe a que en la mayora de los shells, puede
ejecutar otro comando al final del primero, concatenndolos con un punto y coma (;) o una
barra vertical (|).

Una contramedida es usar el mtodo del system(command, parameters) que pasa los
parmetros de la lnea de comandos de forma segura.

7.8 Inyeccin de cabeceras

604
7- Injection

Los encabezados HTTP se generan dinmicamente y bajo ciertas circunstancias se puede


inyectar la entrada del usuario. Esto puede conducir a la redireccin falsa, la divisin de
respuesta XSS o HTTP.

Los encabezados de solicitud HTTP tienen un Referer, User-Agent (software cliente) y


Cookie, entre otros. Por ejemplo, los encabezados de respuesta tienen un campo Cdigo de
estado, Cookie y Ubicacin (URL de destino de redireccin). Todos ellos son suministrados
por el usuario y pueden ser manipulados con ms o menos esfuerzo. Recuerde escapar de
estos campos de cabecera, tambin. Por ejemplo, cuando muestra el agente de usuario en
un rea de administracin.

Adems de eso, es importante saber lo que est haciendo al crear cabeceras de respuesta
en parte basadas en la entrada del usuario. Por ejemplo, desea redirigir al usuario de nuevo
a una pgina especfica. Para ello, introdujo un campo "referer" en un formulario para
redirigir a la direccin dada:

Lo que ocurre es que Rails pone la cadena en el campo de encabezado Location y enva un
estado 302 (redirect) al navegador. Lo primero que hara un usuario malintencionado es:

Y debido a un error en (Ruby y) Rails hasta la versin 2.1.2 (excluyendola), un hacker


puede inyectar campos de cabecera arbitrarios; Por ejemplo:

Tenga en cuenta que "%0d%0a" est codificado en URL para " r n", que es un retorno de
carro y un avance de lnea (CRLF) en Ruby. Por lo tanto, el encabezado HTTP resultante
del segundo ejemplo ser el siguiente, porque el segundo campo de encabezado Location
sobrescribe el primero.

Por lo tanto, los vectores de ataque para Header Injection se basan en la inyeccin de
caracteres CRLF en un campo de encabezado. Y qu podra hacer un atacante con una
redireccin falsa? Pueden redirigir a un sitio de phishing que se ve igual que el tuyo, pero
pide volver a iniciar sesin (y enva las credenciales de inicio de sesin al atacante). O
podran instalar software malicioso a travs de los agujeros de seguridad del navegador en
ese sitio. Rails 2.1.2 escapa de estos caracteres para el campo Location en el mtodo
redirect_to . Asegrese de hacerlo usted mismo cuando cree otros campos de

encabezado con la entrada del usuario.

7.8.1 Divisin de la respuesta


Si la inyeccin de encabezado era posible, la divisin de respuesta podra serlo tambin. En
HTTP, el bloque de encabezado es seguido por dos CRLF y los datos reales (normalmente
HTML). La idea de dividir la respuesta es inyectar dos CRLF en un campo de encabezado,
seguido de otra respuesta con HTML malicioso. La respuesta ser:

605
7- Injection

En ciertas circunstancias esto presentara el HTML malicioso a la vctima. Sin embargo,


esto slo parece funcionar con conexiones Keep-Alive (y muchos navegadores estn
utilizando conexiones de una sola vez). Pero no puedes confiar en esto. En cualquier caso,
se trata de un error grave y debe actualizar su Rails a la versin 2.0.5 o 2.1.2 para eliminar
los riesgos de inyeccin de cabecera (y, por tanto, de divisin de la respuesta).

606
8- Generacin de consultas inseguras

8- Generacin de consultas inseguras


Debido a la forma en que Active Record interpreta los parmetros en combinacin con la
forma en que Rack analiza los parmetros de consulta, es posible emitir consultas de base
de datos inesperadas con clusulas IS NULL . Como respuesta a este problema de
seguridad (CVE-2012-2660, CVE-2012-2694 y CVE-2013-0155) se introdujo el mtodo
deep_munge como una solucin para mantener Rails seguro de forma predeterminada.

Un ejemplo de cdigo vulnerable que podra ser utilizado por el atacante, si deep_munge no
se realiz es:

unless params[:token].nil?
user = User.find_by_token(params[:token])
user.reset_password!
end

Cuando params[:token] es uno de: [nil], [nil, nil, ...] o ['foo', nil] se omitir la
prueba de nil , pero IS NULL o IN ( 'foo', NULL ) Donde las clusulas todava se
agregarn a la consulta SQL.

Para mantener Rails seguro de forma predeterminada, deep_munge reemplaza algunos de


los valores con nil . Debajo de la tabla se muestra lo que parecen los parmetros basados
en JSON enviado en la solicitud:

JSON Parameters
{ "person": null } { :person => nil }

{ "person": [] } { :person => [] }

{ "person": [null] } { :person => [] }

{ "person": [null, null, ...] } { :person => [] }

{ "person": ["foo", null] } { :person => ["foo"] }

Es posible volver a un comportamiento antiguo e inhabilitar deep_munge configurando su


aplicacin si usted es consciente del riesgo y sabe manejarlo:

config.action_dispatch.perform_deep_munge = false

607
9- Encabezados predeterminados

9- Encabezados predeterminados
Cada respuesta HTTP de su aplicacin Rails recibe los siguientes encabezados de
seguridad predeterminados.

config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff'
}

Puede configurar encabezados predeterminados en config/application.rb .

config.action_dispatch.default_headers = {
'Header-Name' => 'Header-Value',
'X-Frame-Options' => 'DENY'
}

O usted puede quitarlos.

config.action_dispatch.default_headers.clear

Aqu hay una lista de encabezados comunes:

X-Frame-Options: 'SAMEORIGIN' en Rails por defecto - permite enmarcar en el


mismo dominio. Pngalo en 'DENY' para negar el framing en absoluto o 'ALLOWALL' si
desea permitir el framing para todo el sitio web.
X-XSS-Protection: '1; Mode = block 'en Rails por defecto - use XSS Auditor y bloquee
la pgina si se detecta un ataque XSS. Establecer a '0;' Si desea desactivar XSS
Auditor (til si los scripts de contenido de respuesta de los parmetros de solicitud)
X-Content-Type-Options: 'nosniff' en Rails de forma predeterminada: impide que el
navegador adivine el tipo MIME de un archivo.
X-Content-Security-Policy: Un potente mecanismo para controlar qu sitios se
pueden cargar ciertos tipos de contenido desde alli
Access-Control-Allow-Origin: Se utiliza para controlar qu sitios se les permite eludir
las mismas polticas de origen y enviar peticiones de origen cruzado.
Strict-Transport-Security: se utiliza para controlar si el navegador slo tiene acceso a
un sitio a travs de una conexin segura

608
9- Encabezados predeterminados

609
XIX- Debugging de aplicaciones Rails

XIX- Debugging de aplicaciones Rails


Esta gua introduce tcnicas para depurar las aplicaciones de Ruby on Rails.

Despus de leer esta gua, sabr:

El propsito de la depuracin.
Cmo detectar problemas e issues en su aplicacin que sus pruebas no estn
identificando.
Las diferentes formas de depuracin.
Cmo analizar el stack trace

610
1- Helpers de las Vistas para depurar

1- Helpers de las Vistas para depurar


Una tarea comn es inspeccionar el contenido de una variable. Rails ofrece tres formas
diferentes de hacer esto:

debug

to_yaml

inspect

1.1 debug
El helper debug devolver una etiqueta <pre> que procese el objeto utilizando el formato
YAML . Esto generar datos legibles desde cualquier objeto. Por ejemplo, si tiene este

cdigo en una vista:

<%= debug @article %>


<p>
<b>Title:</b>
<%= @article.title %>
</p>

Vers algo como esto:

--- !ruby/object Article


attributes:
updated_at: 2008-09-05 22:55:47
body: It's a very helpful guide for debugging your Rails app.
title: Rails debugging guide
published: t
id: "1"
created_at: 2008-09-05 22:55:47
attributes_cache: {}

Title: Rails debugging guide

1.2 to_yaml
Alternativamente, llamar to_yaml a cualquier objeto lo convierte en YAML. Puede pasar
este objeto convertido al mtodo simple_format helper para dar formato a la salida. As es
como debug hace su magia.

611
1- Helpers de las Vistas para depurar

<%= simple_format @article.to_yaml %>


<p>
<b>Title:</b>
<%= @article.title %>
</p>

El cdigo anterior procesar algo como esto:

--- !ruby/object Article


attributes:
updated_at: 2008-09-05 22:55:47
body: It's a very helpful guide for debugging your Rails app.
title: Rails debugging guide
published: t
id: "1"
created_at: 2008-09-05 22:55:47
attributes_cache: {}

Title: Rails debugging guide

1.3 inspect
Otro mtodo til para mostrar valores de objeto es inspeccionar, especialmente cuando se
trabaja con arrays o hashes. Esto imprimir el valor del objeto como una cadena. Por
ejemplo:

<%= [1, 2, 3, 4, 5].inspect %>


<p>
<b>Title:</b>
<%= @article.title %>
</p>

renderizara:

[1, 2, 3, 4, 5]

Title: Rails debugging guide

612
2- El Logger

2- El Logger
Tambin puede ser til guardar informacin en archivos log en tiempo de ejecucin. Rails
mantiene un archivo log separado para cada entorno de tiempo de ejecucin.

2.1 Qu es el logger?
Rails hace uso de la clase ActiveSupport::Logger para escribir informacin de los logs.
Otros logger, como Log4r , tambin pueden ser sustituidos.

Puede especificar un logger alternativo en config/application.rb o en cualquier otro


archivo de entorno, por ejemplo:

config.logger = Logger.new(STDOUT)
config.logger = Log4r::Logger.new("Application Log")

O bien, en la seccin initializer , agregue cualquiera de las siguientes opciones

Rails.logger = Logger.new(STDOUT)
Rails.logger = Log4r::Logger.new("Application Log")

De forma predeterminada, cada registro se crea en Rails.root/log/ y el archivo de logs


lleva el nombre del entorno en el que se ejecuta la aplicacin.

2.2 Niveles de logs


Cuando algo se registra, se imprime en el logs correspondiente si el nivel de log del
mensaje es igual o superior al nivel de log configurado. Si desea conocer el nivel de log
actual, puede llamar al mtodo Rails.logger.level .

Los niveles de log disponibles son: :debug , :info , : warn , :error, :fatal , y
:unknown , correspondiente a los nmeros de nivel de log de 0 a 5, respectivamente. Para

cambiar el nivel de log predeterminado, use:

config.log_level = :warn # In any environment initializer, or


Rails.logger.level = 0 # at any time

Esto es til cuando desea iniciar sesin bajo develpment o staging sin inundar su log de
produccin con informacin innecesaria.

613
2- El Logger

El nivel de log predeterminado de Rails es depurado en todos los entornos.

2.3 Envo de mensajes


Para escribir en el log actual, utilice el mtodo logger.(debug|info|warn|error|fatal) desde
un controlador, modelo o mailer:

logger.debug "Person attributes hash: #{@person.attributes.inspect}"


logger.info "Processing the request..."
logger.fatal "Terminating application, raised unrecoverable error!!!"

Aqu est un ejemplo de un mtodo instrumentado con logging adicional:

class ArticlesController < ApplicationController


# ...

def create
@article = Article.new(params[:article])
logger.debug "New article: #{@article.attributes.inspect}"
logger.debug "Article should be valid: #{@article.valid?}"

if @article.save
flash[:notice] = 'Article was successfully created.'
logger.debug "The article was saved and now the user is going to be redirected..
."
redirect_to(@article)
else
render action: "new"
end
end

# ...
end

A continuacin se muestra un ejemplo del log generado cuando se ejecuta esta accin del
controlador:

614
2- El Logger

Processing ArticlesController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]


Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNo
SUM6J0FjdGl
vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b
06c4d724596a4
Parameters: {"commit"=>"Create", "article"=>{"title"=>"Debugging Rails",
"body"=>"I'm learning how to print in logs!!!", "published"=>"0"},
"authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create",
"controller"=>"articles"}
New article: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how
to print in logs!!!",
"published"=>false, "created_at"=>nil}
Article should be valid: true
Article Create (0.000443) INSERT INTO "articles" ("updated_at", "title", "body", "
published",
"created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
The article was saved and now the user is going to be redirected...
Redirected to # Article:0x20af760>
Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/ar
ticles]

La adicin de logs como ste hace que sea fcil buscar un comportamiento inesperado o
inusual en sus logs. Si agrega extra logs, asegrese de hacer un uso razonable de los
niveles de logs para evitar llenar sus logs de produccin con trivialidades intiles.

2.4 Logging marcado


Cuando se ejecutan aplicaciones multiusuario y de varias cuentas, a menudo es til poder
filtrar los logs mediante algunas reglas personalizadas. TaggedLogging en Active Support le
ayuda a hacer exactamente eso mediante el tagueado de lneas de logs con subdominios,
ids de solicitud y cualquier otra cosa que ayude a depurar dichas aplicaciones.

logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX]
Stuff"
logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX]
[Jason] Stuff"
logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX]
[Jason] Stuff"

2.5 Impacto de los logs en el performance


El log tendr siempre un pequeo impacto en el rendimiento de su aplicacin de Rails, en
particular al iniciar sesin en el disco. Adems, hay algunas sutilezas:

615
2- El Logger

El uso del nivel de :debug tendr una penalizacin de rendimiento mayor que :fatal , ya
que se est evaluando y escribiendo un nmero mucho mayor de cadenas en la salida del
log (por ejemplo, disco).

Otra posible falla es demasiadas llamadas a Logger en tu cdigo:

logger.debug "Person attributes hash: #{@person.attributes.inspect}"

En el ejemplo anterior, habr un impacto en el rendimiento incluso si el nivel de salida


permitido no incluye debug. La razn es que Ruby tiene que evaluar estas cadenas, lo que
incluye instanciar el objeto String, lo que es algo pesado y debe interpolar las variables. Por
lo tanto, se recomienda pasar bloques a los mtodos de logger, ya que slo se evalen si el
nivel de salida es el mismo que el nivel permitido (es decir, carga lenta). El mismo cdigo
reescrito sera:

logger.debug {"Person attributes hash: #{@person.attributes.inspect}"}

El contenido del bloque, y por lo tanto la interpolacin de cadena, slo se evalan si debug
est habilitado. Este ahorro de rendimiento slo se nota realmente con grandes cantidades
de logs, pero es una buena prctica por emplear.

616
3- Debugging con la gema byebug

3- Debugging con la gema byebug


Cuando su cdigo se comporta de manera inesperada, puede intentar imprimirlo en los logs
o en la consola para diagnosticar el problema. Desafortunadamente, hay momentos en que
este tipo de seguimiento de errores no es eficaz para encontrar la causa raz de un
problema. Cuando realmente necesita viajar en su cdigo fuente en ejecucin, el depurador
es su mejor compaero.

El depurador tambin puede ayudarte si quieres aprender sobre el cdigo fuente de Rails
pero no sabes por dnde empezar. Slo tiene que depurar cualquier solicitud de su
aplicacin y utilizar esta gua para aprender a moverse en el cdigo que ha escrito en el
cdigo Rails subyacente.

3.1 Configuracin
Puede usar la gema byebug para establecer puntos de interrupcin (breakpoints) y pasar a
travs del cdigo en vivo en Rails. Para instalarlo, ejecute:

$ gem install byebug

Dentro de cualquier aplicacin Rails, puede invocar el depurador llamando al mtodo


byebug.

He aqu un ejemplo:

class PeopleController < ApplicationController


def new
byebug
@person = Person.new
end
end

3.2 El Shell
Tan pronto como su aplicacin llame al mtodo byebug, el debugger se iniciar en una shell
de debbug dentro de la ventana de terminal donde lanz su servidor de aplicaciones, y se
colocar en el prompt del debugger (byebug). Antes de la solicitud, se mostrar el cdigo
alrededor de la lnea que est a punto de ejecutarse y la lnea actual se marcar con ' => ',
as:

617
3- Debugging con la gema byebug

[1, 10] in /PathTo/project/app/controllers/articles_controller.rb


3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }

(byebug)

Si llegaste all por una solicitud del navegador, la pestaa del navegador que contiene la
solicitud se congelar hasta que el depurador haya terminado y el trace haya terminado de
procesar toda la solicitud.

Por ejemplo:

=> Booting Puma


=> Rails 5.1.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.4.0 (ruby 2.3.1-p112), codename: Owl Bowl Brawl
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200
ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schem
a_migrations"
Processing by ArticlesController#index as HTML

[3, 12] in /PathTo/project/app/controllers/articles_controller.rb


3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
(byebug)

Ahora es el momento de explorar su aplicacin. Un buen lugar para comenzar es pedirle al


depurador ayuda. Tipea: help

618
3- Debugging con la gema byebug

(byebug) help

break -- Sets breakpoints in the source code


catch -- Handles exception catchpoints
condition -- Sets conditions on breakpoints
continue -- Runs until program ends, hits a breakpoint or reaches a line
debug -- Spawns a subdebugger
delete -- Deletes breakpoints
disable -- Disables breakpoints or displays
display -- Evaluates expressions every time the debugger stops
down -- Moves to a lower frame in the stack trace
edit -- Edits source files
enable -- Enables breakpoints or displays
finish -- Runs the program until frame returns
frame -- Moves to a frame in the call stack
help -- Helps you using byebug
history -- Shows byebug's history of commands
info -- Shows several informations about the program being debugged
interrupt -- Interrupts the program
irb -- Starts an IRB session
kill -- Sends a signal to the current process
list -- Lists lines of source code
method -- Shows methods of an object, class or module
next -- Runs one or more lines of code
pry -- Starts a Pry session
quit -- Exits byebug
restart -- Restarts the debugged program
save -- Saves current byebug session to a file
set -- Modifies byebug settings
show -- Shows byebug settings
source -- Restores a previously saved byebug session
step -- Steps into blocks or methods one or more times
thread -- Commands to manipulate threads
tracevar -- Enables tracing of a global variable
undisplay -- Stops displaying all or some expressions when program stops
untracevar -- Stops tracing a global variable
up -- Moves to a higher frame in the stack trace
var -- Shows variables and its values
where -- Displays the backtrace

(byebug)

Para ver las diez lneas anteriores debe escribir list- (or l-).

619
3- Debugging con la gema byebug

(byebug) l-

[1, 10] in /PathTo/project/app/controllers/articles_controller.rb


1 class ArticlesController < ApplicationController
2 before_action :set_article, only: [:show, :edit, :update, :destroy]
3
4 # GET /articles
5 # GET /articles.json
6 def index
7 byebug
8 @articles = Article.find_recent
9
10 respond_to do |format|

De esta manera puede moverse dentro del archivo y ver el cdigo por encima de la lnea
donde agreg la llamada byebug. Por ltimo, para ver dnde se encuentra en el cdigo de
nuevo se puede escribir list=

(byebug) list=

[3, 12] in /PathTo/project/app/controllers/articles_controller.rb


3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
(byebug)

3.3 El contexto
Cuando inicie la depuracin de la aplicacin, se colocar en diferentes contextos a medida
que vaya a travs de las diferentes partes del stack.

El depurador crea un contexto cuando se alcanza un punto de parada o un evento. El


contexto tiene informacin sobre el programa suspendido que permite al depurador
inspeccionar el stack del frame, evaluar variables desde la perspectiva del programa
depurado y conocer el lugar donde se detiene el programa depurado.

En cualquier momento puede llamar al comando backtrace (o su alias where ) para


imprimir el backtrace de la aplicacin. Esto puede ser muy til para saber cmo llegaste
donde ests. Si alguna vez se pregunt acerca de cmo obtuvo algn lugar en su cdigo,

620
3- Debugging con la gema byebug

entonces backtrace proporcionar la respuesta.

(byebug) where
--> #0 ArticlesController.index
at /PathToProject/app/controllers/articles_controller.rb:8
#1 ActionController::BasicImplicitRender.send_action(method#String, *args#Array)
at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/basic_implicit_rende
r.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
at /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb:181
#3 ActionController::Rendering.process_action(action, *args)
at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/rendering.rb:30
...

El frame actual est marcado con --> . Puede moverse a cualquier lugar que desee en
este trace (cambiando as el contexto) utilizando el comando frame n , donde n es el
nmero del frame especificado. Si lo hace, byebug mostrar su nuevo contexto.

(byebug) frame 2

[176, 185] in /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb


176: # is the intended way to override action dispatching.
177: #
178: # Notice that the first argument is the method to be dispatched
179: # which is *not* necessarily the same as the action name.
180: def process_action(method_name, *args)
=> 181: send_action(method_name, *args)
182: end
183:
184: # Actually call the method associated with the action. Override
185: # this method if you wish to change how action methods are called,
(byebug)

Las variables disponibles son las mismas que si estuviera ejecutando el cdigo lnea por
lnea. Despus de todo, eso es lo que es la depuracin.

Tambin puede utilizar los comandos [n] y down [n] para cambiar el contexto n cuadros
hacia arriba o hacia abajo del stack, respectivamente. N por defecto es uno. Up en este
caso es hacia cuadros de stack de numeracin superior, y hacia abajo es hacia el stack de
nmero inferior.

3.4 Threads (Hilos)


El depurador puede enumerar, detener, reanudar y cambiar entre los subprocesos en
ejecucin mediante el comando thread (o el abreviado th ). Este comando tiene un
puado de opciones:

621
3- Debugging con la gema byebug

thread : muestra el hilo actual.

thread list : se utiliza para enumerar todos los hilos y sus estados. El hilo actual est

marcado con un signo ms (+).


thread stop n : detener el hilo n.

thread resume n : reanudar el subproceso n.

thread switch n : cambia el contexto de hilo actual a n.

Este comando es muy til cuando est depurando subprocesos simultneos y necesita
verificar que no hay condiciones de competencia en su cdigo.

3.5 Inspeccin de variables


Cualquier expresin puede ser evaluada en el contexto actual. Para evaluar una expresin,
simplemente escrbala!

En este ejemplo se muestra cmo se pueden imprimir las variables de instancia definidas
en el contexto actual:

[3, 12] in /PathTo/project/app/controllers/articles_controller.rb


3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }

(byebug) instance_variables
[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context,
:@_action_name, :@_response_body, :@marked_for_same_origin_verification,
:@_config]

Como se habr dado cuenta, se muestran todas las variables a las que puede acceder
desde un controlador. Esta lista se actualiza dinmicamente a medida que ejecuta el cdigo.
Por ejemplo, ejecute la siguiente lnea usando next (aprender ms sobre este comando
ms adelante en esta gua).

622
3- Debugging con la gema byebug

(byebug) next

[5, 14] in /PathTo/project/app/controllers/articles_controller.rb


5 # GET /articles.json
6 def index
7 byebug
8 @articles = Article.find_recent
9
=> 10 respond_to do |format|
11 format.html # index.html.erb
12 format.json { render json: @articles }
13 end
14 end
15
(byebug)

Y luego vuelve a preguntar por las instance_variables :

(byebug) instance_variables
[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context,
:@_action_name, :@_response_body, :@marked_for_same_origin_verification,
:@_config, :@articles]

Ahora @articles se incluye en las variables de instancia, porque la lnea que lo define se
ejecut.

Tambin puede entrar en el modo irb con el comando irb (por supuesto!). Esto
iniciar una sesin irb dentro del contexto en el que la invoc.

El mtodo var es la forma ms conveniente de mostrar las variables y sus valores.


Hagamos que byebug nos ayude con ella.

(byebug) help var

[v]ar <subcommand>

Shows variables and its values

var all -- Shows local, global and instance variables of self.


var args -- Information about arguments of the current scope
var const -- Shows constants of an object.
var global -- Shows global variables.
var instance -- Shows instance variables of self or a specific object.
var local -- Shows local variables in current scope.

623
3- Debugging con la gema byebug

Esta es una excelente forma de inspeccionar los valores de las variables de contexto
actuales. Por ejemplo, para comprobar que no tenemos variables locales definidas
actualmente:

(byebug) var local


(byebug)

Tambin puede inspeccionar un mtodo de objeto de esta manera:

(byebug) var instance Article.new


@_start_transaction_state = {}
@aggregation_cache = {}
@association_cache = {}
@attributes = #<ActiveRecord::AttributeSet:0x007fd0682a9b18 @attributes={"id"=>#<Activ
eRecord::Attribute::FromDatabase:0x007fd0682a9a00 @name="id", @value_be...
@destroyed = false
@destroyed_by_association = nil
@marked_for_destruction = false
@new_record = true
@readonly = false
@transaction_state = nil

Tambin puede utilizar la pantalla para comenzar a ver las variables. Esta es una buena
forma de rastrear los valores de una variable mientras la ejecucin contina.

(byebug) display @articles


1: @articles = nil

Las variables dentro de la lista mostrada se imprimirn con sus valores despus de moverlo
en el stack. Para detener la visualizacin de una variable use undisplay n donde n es el
nmero de variable (1 en el ltimo ejemplo).

3.6 Paso a paso


Ahora debe saber dnde se encuentra en el rastreo de la ejecucin y ser capaz de imprimir
las variables disponibles. Pero vamos a continuar y a seguir adelante con la ejecucin de la
aplicacin.

Utilice step (abreviado s ) para continuar ejecutando su programa hasta el siguiente


punto de parada lgico y devolver el control al depurador. next es similar step , pero
mientras step se detiene en la siguiente lnea de cdigo ejecutada, haciendo slo un solo
paso, next se mueve a la siguiente lnea sin descender dentro de los mtodos.

Por ejemplo, considere la siguiente situacin:

624
3- Debugging con la gema byebug

Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200


Processing by ArticlesController#index as HTML

[1, 6] in /PathToProject/app/models/article.rb
1: class Article < ApplicationRecord
2: def self.find_recent(limit = 10)
3: byebug
=> 4: where('created_at > ?', 1.week.ago).limit(limit)
5: end
6: end

(byebug)

Si usamos next , no vamos a profundizar dentro de las llamadas de mtodo. En su lugar,


byebug ir a la siguiente lnea dentro del mismo contexto. En este caso, es la ltima lnea
del mtodo actual, por lo que byebug volver a la lnea siguiente del mtodo llamador.

(byebug) next
[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
7: @articles = Article.find_recent
8:
=> 9: respond_to do |format|
10: format.html # index.html.erb
11: format.json { render json: @articles }
12: end
13: end

(byebug)

Si usamos step en la misma situacin, byebug literalmente ir a la siguiente instruccin


Ruby a ser ejecutada - en este caso, el mtodo week de Active Support.

625
3- Debugging con la gema byebug

(byebug) step

[49, 58] in /PathToGems/activesupport-5.1.0/lib/active_support/core_ext/numeric/time.r


b
49:
50: # Returns a Duration instance matching the number of weeks provided.
51: #
52: # 2.weeks # => 14 days
53: def weeks
=> 54: ActiveSupport::Duration.weeks(self)
55: end
56: alias :week :weeks
57:
58: # Returns a Duration instance matching the number of fortnights provided.
(byebug)

Esta es una de las mejores maneras de encontrar errores en su cdigo.

Tambin puede utilizar el step n o next n para avanzar n pasos a la vez.

3.7 Puntos de interrupcin


Un punto de interrupcin hace que su aplicacin se detenga cada vez que se alcanza un
determinado punto en el programa. El shell depurador se invoca en esa lnea.

Puede agregar puntos de interrupcin de forma dinmica con el comando break (o


simplemente b ). Existen 3 formas posibles de agregar puntos de interrupcin
manualmente:

break n : establece el punto de interrupcin en la lnea nmero n en el archivo fuente

actual.
break file:n [if expression] : establece el punto de interrupcin en el nmero de

lnea n dentro del archivo named file. Si se da una expresin se debe evaluar a true
para encender el depurador.
break class(.|\#)method [if expression] : define el punto de interrupcin en el mtodo

(.y # para la clase y el mtodo de instancia respectivamente) definidos en la clase. La


expresin funciona de la misma manera que con file:n .

626
3- Debugging con la gema byebug

[4, 13] in /PathToProject/app/controllers/articles_controller.rb


4: # GET /articles
5: # GET /articles.json
6: def index
7: @articles = Article.find_recent
8:
=> 9: respond_to do |format|
10: format.html # index.html.erb
11: format.json { render json: @articles }
12: end
13: end

(byebug) break 11
Successfully created breakpoint with id 1

Utilice info breakpoints para enumerar puntos de interrupcin. Si suministra un nmero,


enumera ese punto de interrupcin. De lo contrario, muestra todos los puntos de
interrupcin.

(byebug) info breakpoints


Num Enb What
1 y at /PathToProject/app/controllers/articles_controller.rb:11

Para eliminar puntos de interrupcin: utilice el comando delete n para quitar el nmero de
punto de interrupcin n. Si no se especifica ningn nmero, elimina todos los puntos de
interrupcin que estn actualmente activos.

(byebug) delete 1
(byebug) info breakpoints
No breakpoints.

Tambin puede habilitar o deshabilitar puntos de interrupcin:

enable breakpoints [n [m [...]]] : permite que una lista de punto de interrupcin

especfica o todos los puntos de interrupcin detengan su programa. Este es el estado


predeterminado al crear un punto de interrupcin.
disable breakpoints [n [m [...]]] : hacer que ciertos (o todos) los puntos de

interrupcin no tengan ningn efecto en su programa.

3.8 Catching de las excepciones


El comando catch exception-name (or just cat exception-name) se puede usar para
interceptar una excepcin del tipo exception-name cuando de otro modo no habra ningn
controlador para l.

627
3- Debugging con la gema byebug

Para enumerar todos los catchpoints activos use catch .

3.9 Reanudacin de la ejecucin


Hay dos formas de reanudar la ejecucin de una aplicacin que se detiene en el depurador:

continue [n] : reanuda la ejecucin del programa en la direccin donde se detuvo su

guin; Se anulan los puntos de interrupcin establecidos en esa direccin. El


argumento opcional n le permite especificar un nmero de lnea para establecer un
punto de interrupcin nico que se elimina cuando se alcanza ese punto de
interrupcin.
finish [n] : ejecutar hasta que se devuelva el frame de stack seleccionado. Si no se

proporciona ningn nmero de stack, la aplicacin se ejecutar hasta que se devuelva


el frame seleccionado actualmente. El frame seleccionado actualmente comienza el
stack ms reciente o 0 si no se ha realizado ninguna posicin de stack (por ejemplo,
arriba, abajo o stack). Si se da un nmero de stack, se ejecutar hasta que se devuelva
el frame especificado.

3.10 Edicin
Dos comandos le permiten abrir cdigo desde el depurador en un editor:

edit [file:n] : editar el archivo con el nombre del archivo utilizando el editor especificado

por la variable de entorno EDITOR. Tambin se puede dar una lnea especfica n.

3.11 Quitting
Para salir del depurador, utilice el comando quit (abreviado a q ). O, escriba q! Para
evitar el "Really quit? (y/n)" y salir sin condiciones.

Un simple abandono intenta terminar todos los threads en efecto. Por lo tanto su servidor
ser parado y usted tendr que comenzarlo otra vez.

3.12 Configuracin
Byebug tiene algunas opciones disponibles para ajustar su comportamiento:

628
3- Debugging con la gema byebug

(byebug) help set

set <setting> <value>

Modifies byebug settings

Boolean values take "on", "off", "true", "false", "1" or "0". If you
don't specify a value, the boolean setting will be enabled. Conversely,
you can use "set no<setting>" to disable them.

You can see these environment settings with the "show" command.

List of supported settings:

autosave -- Automatically save command history record on exit


autolist -- Invoke list command on every stop
width -- Number of characters per line in byebug's output
autoirb -- Invoke IRB on every stop
basename -- <file>:<line> information after every stop uses short paths
linetrace -- Enable line execution tracing
autopry -- Invoke Pry on every stop
stack_on_error -- Display stack trace when `eval` raises an exception
fullpath -- Display full file names in backtraces
histfile -- File where cmd history is saved to. Default: ./.byebug_history
listsize -- Set number of source lines to list by default
post_mortem -- Enable/disable post-mortem mode
callstyle -- Set how you want method call parameters to be displayed
histsize -- Maximum number of commands that can be stored in byebug history
savefile -- File where settings are saved to. Default: ~/.byebug_save

Puede guardar esta configuracin en un archivo .byebugrc en su directorio personal.


El depurador lee estas configuraciones globales cuando se inicia. Por ejemplo:

set callstyle short


set listsize 25

629
4- Depuracin con la gema web-console

4- Depuracin con la gema web-console


Web Console es un poco como byebug, pero se ejecuta en el navegador. En cualquier
pgina que est desarrollando, puede solicitar una consola en el contexto de una vista o de
un controlador. La consola se renderizar al lado de su contenido HTML.

4.1 Consola
Dentro de cualquier accin o vista del controlador, puede invocar la consola llamando al
mtodo console .

Por ejemplo, en un controlador:

class PostsController < ApplicationController


def new
console
@post = Post.new
end
end

O en una vista:

<% console %>

<h2>New Post</h2>

Esto har que se renderice console dentro de su vista. No necesita preocuparse por la
ubicacin de la llamada a la consola; No se renderizar en el lugar de su invocacin sino
junto a su contenido HTML.

La consola ejecuta cdigo Ruby puro: Puede definir e instanciar clases personalizadas,
crear nuevos modelos e inspeccionar variables.

Slo se puede renderizar una consola por solicitud. De lo contrario, console-web


generar un error en la segunda invocacin de la consola.

4.2 Inspeccin de variables


Puede invocar las variables de instancia para listar todas las variables de instancia
disponibles en su contexto. Si desea listar todas las variables locales, puede hacerlo con
local_variables .

630
4- Depuracin con la gema web-console

4.3 Ajustes
config.web_console.whitelisted_ips : Lista autorizada de direcciones y redes IPv4 o

IPv6 (valores por defecto: 127.0.0.1/8, ::1 ).


config.web_console.whiny_requests : Registra un mensaje cuando se evita un

renderizado de consola (defaults: true ).

Dado que console-web evala el cdigo Ruby en forma remota en el servidor, no intente
utilizarlo en produccin.

631
5- Depuracin de fugas de memoria

5- Depuracin de fugas de memoria


Una aplicacin Ruby (en Rails o no), puede perder la memoria - ya sea en el cdigo Ruby o
en el nivel de cdigo C.

En esta seccin, aprender cmo encontrar y corregir dichas filtraciones utilizando una
herramienta como Valgrind .

5.1 Valgrind
Valgrind es una aplicacin para detectar fugas de memoria basadas en C y condiciones de
carrera.

Hay herramientas de Valgrind que pueden detectar automticamente muchos problemas de


gestin de memoria y de subprocesos, y perfilar sus programas en detalle. Por ejemplo, si
una extensin C del intrprete llama a malloc() pero no llama correctamente a free() ,
esta memoria no estar disponible hasta que finalice la aplicacin.

Para obtener ms informacin sobre cmo instalar Valgrind y utilizarla con Ruby, consulte
Valgrind y Ruby por Evan Weaver.

632
6- Plugins para Depurar

6- Plugins para Depurar


Existen algunos plugins de Rails que le ayudarn a encontrar errores y depurar su
aplicacin. Aqu hay una lista de plugins tiles para la depuracin:

Footnotes Cada pgina de Rails tiene notas a pie de pgina que proporcionan
informacin de solicitud y enlace a su fuente a travs de TextMate.
Query Trace Aade el rastreo de origen de consultas a sus logs.
Query Reviewer Este plugin de Rails no slo ejecuta "EXPLAIN" antes de cada una de
sus consultas selectas en desarrollo, sino que proporciona un DIV pequeo en la salida
renderizada de cada pgina con el resumen de advertencias para cada consulta que
analiz.
Exception Notifier Proporciona un objeto de correo y un conjunto predeterminado de
plantillas para enviar notificaciones de correo electrnico cuando se producen errores
en una aplicacin de Rails.
Better Errors Reemplaza la pgina de error de Rails estndar con una nueva que
contiene ms informacin contextual, como el cdigo fuente y la inspeccin de
variables.
RailsPanel Extensin de Chrome para el desarrollo de Rails que pondr fin a su
seguimiento de development.log . Tenga toda la informacin sobre las solicitudes de la
aplicacin Rails en el navegador, en el panel de Herramientas para desarrolladores.
Proporciona informacin sobre db/rendering/total times, lista de parmetros, vistas
renderizadas y ms.
Pry Una alternativa de IRB y una consola de desarrollador de tiempo de ejecucin.

633
XX- Configuracin de aplicaciones de Rails

XX- Configuracin de aplicaciones de


Rails
Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

634
XXI- La lnea de comandos de Rails

XXI- La lnea de comandos de Rails


Despus de leer esta gua, sabr:

Cmo crear una aplicacin de Rails.


Cmo generar modelos, controladores, migraciones de bases de datos y pruebas
unitarias.
Cmo iniciar un servidor de desarrollo.
Cmo experimentar con objetos a travs de un shell interactivo.

635
1- Conceptos bsicos de la lnea de comandos

1- Conceptos bsicos de la lnea de


comandos
Hay algunos comandos que son absolutamente crticos para su uso diario de Rails.
Enumerados el orden de cunto probablemente los usars son:

rails console

rails server

bin/rails

rails generate

rails dbconsole

rails new app_name

Todos los comandos pueden ejecutarse con -h o --help para obtener ms informacin.

Vamos a crear una aplicacin Rails simple para pasar a travs de cada uno de estos
comandos en el contexto.

1.1 rails new


Lo primero que queremos hacer es crear una nueva aplicacin de Rails ejecutando el
comando rails new despus de instalar Rails.

Usted puede instalar la gema de rails escribiendo gem install rails , si usted no la
tiene ya.

$ rails new commandsapp


create
create README.md
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
...
create tmp/cache
...
run bundle install

Rails te preparar con lo que parece una enorme cantidad de cosas para un comando tan
pequeo! Ahora tiene toda la estructura de directorios de Rails con todo el cdigo que
necesita para ejecutar nuestra aplicacin sencilla justo despus de la caja.

636
1- Conceptos bsicos de la lnea de comandos

1.2 rails server


El comando rails server lanza un servidor web llamado Puma que viene incluido con
Rails. Lo usar cuando quiera acceder a su aplicacin a travs de un navegador web.

Sin ms trabajo, el servidor de rails ejecutar nuestra nueva aplicacin de Rails as:

$ cd commandsapp
$ bin/rails server
=> Booting Puma
=> Rails 5.1.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.0.2 (ruby 2.3.0-p0), codename: Plethora of Penguin Pinatas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop

Con slo tres rdenes que digit ya tiene a un servidor Rails escuchando en el puerto 3000.
Vaya a su navegador y abra http://localhost:3000 , ver una aplicacin bsica de Rails en
ejecucin.

Tambin puede utilizar el alias " s " para iniciar el servidor: rails s.

El servidor se puede ejecutar en un puerto diferente mediante la opcin -p . El entorno de


desarrollo predeterminado se puede cambiar mediante -e .

$ bin/rails server -e production -p 4000

La opcin -b enlaza Rails a la direccin IP especificada, por defecto es localhost. Puede


ejecutar un servidor como un daemon pasando una opcin -d .

1.3 rails generate


El comando rails generate usa plantillas para crear un montn de cosas. Correr rails
generate genera por s mismo una lista de generadores disponibles:

Tambin puede usar el alias " g " para invocar el comando del generador: rails g .

637
1- Conceptos bsicos de la lnea de comandos

$ bin/rails generate
Usage: rails generate GENERATOR [args] [options]

...
...

Please choose a generator below.

Rails:
assets
controller
generator
...
...

Usted puede instalar ms generadores a travs del generador de gemas, porciones de


plugins que sin duda se instalar, e incluso puede crear su propio generador!

El uso de generadores le ahorrar una gran cantidad de tiempo mediante la escritura de


cdigo predeterminada, cdigo que es necesario para que la aplicacin funcione.

Hagamos nuestro propio controlador con el generador de controladores. Pero, qu


comando debemos usar? Vamos a preguntar al generador:

Todas las utilidades de la consola de Rails tienen texto de ayuda. Como con la mayora de
las utilidades de *nix , puede intentar agregar --help o -h al final, por ejemplo, rails
server --help .$ bin/rails generate controller

Usage: rails generate controller NAME [action action] [options]

...
...

Description:
...

To create a controller within a module, specify the controller name as a path like
'parent_module/controller_name'.

...

Example:
`rails generate controller CreditCards open debit credit close`

Credit card controller with URLs like /credit_cards/debit.


Controller: app/controllers/credit_cards_controller.rb
Test: test/controllers/credit_cards_controller_test.rb
Views: app/views/credit_cards/debit.html.erb [...]
Helper: app/helpers/credit_cards_helper.rb

638
1- Conceptos bsicos de la lnea de comandos

El generador del controlador est esperando parmetros en la forma de generate


controller ControllerName action1 action2 . Hagamos un controlador Greetings con una

accin de hello , que nos dir algo agradable.

$ bin/rails generate controller Greetings hello


create app/controllers/greetings_controller.rb
route get "greetings/hello"
invoke erb
create app/views/greetings
create app/views/greetings/hello.html.erb
invoke test_unit
create test/controllers/greetings_controller_test.rb
invoke helper
create app/helpers/greetings_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/greetings.coffee
invoke scss
create app/assets/stylesheets/greetings.scss

Qu fue todo esto? Es un montn de directorios en nuestra aplicacin, y cre un archivo


controlador, un archivo de vista, un archivo de prueba funcional, un ayudante para la vista,
un archivo de JavaScript y un archivo de hoja de estilo.

Compruebe el controlador y modifquelo un poco (en


app/controllers/greetings_controller.rb ):

class GreetingsController < ApplicationController


def hello
@message = "Hello, how are you today?"
end
end

A continuacin, la vista, para mostrar su mensaje (en app/views/greetings/hello.html.erb ):

<h1>A Greeting for You!</h1>


<p><%= @message %></p>

Encender el servidor utilizando el servidor de rails.

$ bin/rails server
=> Booting Puma...

La URL ser http://localhost:3000/greetings/hello.

639
1- Conceptos bsicos de la lnea de comandos

Con una aplicacin Rails normal, las URL seguirn generalmente el patrn
http://(host)/(controller)/(action) , y una URL como http://(host)/(controller\)

mostrar la accin index de ese controlador

Rails viene con un generador para los modelos de datos tambin.

$ bin/rails generate model


Usage:
rails generate model NAME [field[:type][:index] field[:type][:index]] [options]

...

Active Record options:


[--migration] # Indicates when to generate migration
# Default: true

...

Description:
Create rails files for model generator.

Para obtener una lista de tipos de campos disponibles para el parmetro type ,
consulte la documentacin del API para el mtodo add_column del mdulo
SchemaStatements . El parmetro index genera un ndice correspondiente para la

columna.

Pero en lugar de generar un modelo directamente (lo que haremos ms adelante),


montaremos un scaffold. Un scaffold en Rails es un conjunto completo de modelo,
migracin de base de datos para ese modelo, controlador para manipularlo, vistas para ver
y manipular los datos y un conjunto de pruebas para cada uno de los anteriores.

Vamos a crear un recurso simple llamado "HighScore" que mantendr un registro de


nuestra puntuacin ms alta en los videojuegos que jugamos.

640
1- Conceptos bsicos de la lnea de comandos

$ bin/rails generate scaffold HighScore game:string score:integer


invoke active_record
create db/migrate/20130717151933_create_high_scores.rb
create app/models/high_score.rb
invoke test_unit
create test/models/high_score_test.rb
create test/fixtures/high_scores.yml
invoke resource_route
route resources :high_scores
invoke scaffold_controller
create app/controllers/high_scores_controller.rb
invoke erb
create app/views/high_scores
create app/views/high_scores/index.html.erb
create app/views/high_scores/edit.html.erb
create app/views/high_scores/show.html.erb
create app/views/high_scores/new.html.erb
create app/views/high_scores/_form.html.erb
invoke test_unit
create test/controllers/high_scores_controller_test.rb
invoke helper
create app/helpers/high_scores_helper.rb
invoke jbuilder
create app/views/high_scores/index.json.jbuilder
create app/views/high_scores/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.coffee
invoke scss
create app/assets/stylesheets/high_scores.scss
invoke scss
identical app/assets/stylesheets/scaffolds.scss

El generador comprueba que existen los directorios para modelos, controladores,


ayudantes, layouts, pruebas funcionales y de unidad, hojas de estilo, crea las vistas, el
controlador, el modelo y la migracin de bases de datos para HighScore (creando la tabla y
los campos high_scores ) Para el recurso, y nuevas pruebas para todo.

La migracin requiere que migremos, es decir, ejecutar algn cdigo Ruby (viviendo en aqui
20130717151933_create_high_scores.rb ) para modificar el esquema de nuestra base de

datos. Qu base de datos? La base de datos SQLite3 que Rails crear para usted cuando
ejecute el comando bin/rails db:migrate . Hablaremos ms acerca de bin/rails en
profundidad ms abajo.

641
1- Conceptos bsicos de la lnea de comandos

$ bin/rails db:migrate
== CreateHighScores: migrating ===============================================
-- create_table(:high_scores)
-> 0.0017s
== CreateHighScores: migrated (0.0019s) ======================================

Hablemos de las pruebas unitarias. Las pruebas unitarias son cdigo que prueba y
hace afirmaciones sobre el cdigo. En las pruebas unitarias, tomamos una pequea
parte del cdigo, digamos un mtodo de un modelo, y probamos sus entradas y
salidas. Las pruebas unitarias son tus amigas. Cuanto antes se haga la paz con el
hecho de que su calidad de vida aumentar drsticamente cuando pruebe
unitariamente su cdigo, mejor. Seriamente. Por favor visite la gua de pruebas para
una mirada en profundidad a las pruebas unitarias.

Veamos la interfaz Rails creada para nosotros.

$ bin/rails server

Vaya a su navegador y abra http://localhost:3000/high_scores , ahora podemos crear


nuevos puntajes altos (55.160 en Space Invaders!)

1.4 rails console


El comando de consola le permite interactuar con su aplicacin Rails desde la lnea de
comandos. En la parte inferior, la consola de rails usa IRB, as que si alguna vez lo usaste,
estars como en casa. Esto es til para probar ideas rpidas con cdigo y cambiar los datos
del lado del servidor sin tocar el sitio web.

Tambin puede usar el alias " c " para invocar la consola: rails c .

Puede especificar el entorno en el que debe funcionar el comando de consola.

$ bin/rails console staging

Si desea probar algn cdigo sin cambiar ningn dato, puede hacerlo invocando rails
console --sandobx

$ bin/rails console --sandbox


Loading development environment in sandbox (Rails 5.1.0)
Any modifications you make will be rolled back on exit
irb(main):001:0>

642
1- Conceptos bsicos de la lnea de comandos

1.4.1 Los objetos app y helper


Dentro de la consola de rails tiene acceso a la aplicacin y las instancias de ayuda.

Con el mtodo app puede acceder a url y los ayudantes de ruta, as como hacer las
solicitudes.

>> app.root_path
=> "/"

>> app.get _
Started GET "/" for 127.0.0.1 at 2014-06-19 10:41:57 -0300
...

Con el mtodo auxiliar es posible acceder a Rails y a los ayudantes de su aplicacin

>> helper.time_ago_in_words 30.days.ago


=> "about 1 month"

>> helper.my_custom_helper
=> "my custom helper"

1.5 rails dbconsole


rails dbconsole calcula la base de datos que utilizas y te deja en cualquier interfaz de

lnea de comandos que uses con ella (y calcula los parmetros de la lnea de comandos
para darle tambin!). Soporta MySQL (incluyendo MariaDB), PostgreSQL y SQLite3.

Tambin puede utilizar el alias " db " para invocar la dbconsole: rails db .

1.6 rails runner


Runner ejecuta cdigo Ruby en el contexto de Rails de forma no interactiva. Por ejemplo:

$ bin/rails runner "Model.long_running_method"

Tambin puede utilizar el alias " r " para invocar al corredor: rails r .

Puede especificar el entorno en el que debe funcionar el comando runner mediante el


modificador -e .

$ bin/rails runner -e staging "Model.long_running_method"

643
1- Conceptos bsicos de la lnea de comandos

Incluso puede ejecutar cdigo ruby escrito en un archivo con runner.

$ bin/rails runner lib/code_to_be_run.rb

1.7 rails destroy


Piensa en destruir como lo contrario de generar. Va a averiguar qu gener hacer, y
deshacer.

Tambin puede usar el alias " d " para invocar el comando destroy: rails d .

$ bin/rails generate model Oops


invoke active_record
create db/migrate/20120528062523_create_oops.rb
create app/models/oops.rb
invoke test_unit
create test/models/oops_test.rb
create test/fixtures/oops.yml

$ bin/rails destroy model Oops


invoke active_record
remove db/migrate/20120528062523_create_oops.rb
remove app/models/oops.rb
invoke test_unit
remove test/models/oops_test.rb
remove test/fixtures/oops.yml

644
2- bin/rails

2- bin/rails
Dado que Rails 5.0+ tiene comandos rake incorporados en los ejecutables, bin/rails es el
nuevo valor predeterminado para ejecutar comandos.

Puede obtener una lista de tareas de bin/rails disponibles, que a menudo dependen de
su directorio actual, escribiendo bin/rails --help . Cada tarea tiene una descripcin, y
debera ayudarle a encontrar lo que necesita.

$ bin/rails --help
Usage: rails COMMAND [ARGS]

The most common rails commands are:


generate Generate new code (short-cut alias: "g")
console Start the Rails console (short-cut alias: "c")
server Start the Rails server (short-cut alias: "s")
...

All commands can be run with -h (or --help) for more information.

In addition to those commands, there are:


about List versions of all Rails ...
assets:clean[keep] Remove old compiled assets
assets:clobber Remove compiled assets
assets:environment Load asset compile environment
assets:precompile Compile all the assets ...
...
db:fixtures:load Loads fixtures into the ...
db:migrate Migrate the database ...
db:migrate:status Display status of migrations
db:rollback Rolls the schema back to ...
db:schema:cache:clear Clears a db/schema_cache.yml file
db:schema:cache:dump Creates a db/schema_cache.yml file
db:schema:dump Creates a db/schema.rb file ...
db:schema:load Loads a schema.rb file ...
db:seed Loads the seed data ...
db:structure:dump Dumps the database structure ...
db:structure:load Recreates the databases ...
db:version Retrieves the current schema ...
...
restart Restart app by touching ...
tmp:create Creates tmp directories ...

Tambin puede utilizar bin/rails -T para obtener la lista de tareas.

2.1 about

645
2- bin/rails

bin/rails about proporciona informacin sobre los nmeros de versin de Ruby,

RubyGems, Rails, los subcomponentes Rails, la carpeta de la aplicacin, el nombre del


entorno actual de Rails, el adaptador de base de datos de la aplicacin y la versin del
esquema. Es til cuando necesitas pedir ayuda, comprobar si un parche de seguridad
puede afectarte, o cuando necesitas algunas estadsticas para una instalacin de Rails
existente.

$ bin/rails about
About your application's environment
Rails version 5.1.0
Ruby version 2.2.2 (x86_64-linux)
RubyGems version 2.4.6
Rack version 2.0.1
JavaScript Runtime Node.js (V8)
Middleware: Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Exec
utor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::Runtime, Rack::Met
hodOverride, ActionDispatch::RequestId, ActionDispatch::RemoteIp, Sprockets::Rails::Qu
ietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware
, ActionDispatch::DebugExceptions, ActionDispatch::Reloader, ActionDispatch::Callbacks
, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Sess
ion::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
Database schema version 20110805173523

2.2 assets
Puede precompilar los activos en app/assets utilizando los recursos bin/rails
assets:precompile y eliminar los recursos compilados ms antiguos utilizando los recursos

bin/rails assets:clean . La tarea assets:clean permite la implementacin de deploys que

an pueden estar enlazando a un assets antiguo mientras se estn construyendo los


nuevos assets.

Si desea borrar completamente los elementos public/assets , puede utilizar los recursos
bin/rails assets:clobber .

2.3 db
Las tareas ms comunes del espacio de nombres db : bin / rails son migrate y create , y
vale la pena probar todas las tareas de bin / rails de migracin ( up, down, redo, reset ).
bin / rails db:version es til cuando se soluciona el problema, dicindole la versin

actual de la base de datos.

Puede encontrar ms informacin acerca de las migraciones en la gua Migraciones.

646
2- bin/rails

2.4 notes
bin/rails notes buscar en tu cdigo los comentarios que comiencen con FIXME,

OPTIMIZE o TODO. La bsqueda se realiza en archivos con extensin .builder, .rb,


.rake, .yml, .yaml, .ruby, .css, .js y .erb para las anotaciones predeterminadas y

personalizadas.

$ bin/rails notes
(in /home/foobar/commandsapp)
app/controllers/admin/users_controller.rb:
* [ 20] [TODO] any other way to do this?
* [132] [FIXME] high priority for next deploy

app/models/school.rb:
* [ 13] [OPTIMIZE] refactor this code to make it faster
* [ 17] [FIXME]

Puede agregar soporte para nuevas extensiones de archivo mediante la opcin


config.annotations.register_extensions , que recibe una lista de las extensiones con su

regex correspondiente para que coincida con ella.

config.annotations.register_extensions("scss", "sass", "less") { |annotation| /\/\/\s*(


#{annotation}):?\s*(.*)$/ }

Si est buscando una anotacin especfica, diga FIXME, puede utilizar bin/rails
notes:fixme . Tenga en cuenta que debe ser en minscula el nombre de la anotacin.

$ bin/rails notes:fixme
(in /home/foobar/commandsapp)
app/controllers/admin/users_controller.rb:
* [132] high priority for next deploy

app/models/school.rb:
* [ 17]

Tambin puede usar anotaciones personalizadas en su cdigo y listarlas usando bin/rails


notes:custom especificando la anotacin usando una variable de entorno ANNOTATION.

$ bin/rails notes:custom ANNOTATION=BUG


(in /home/foobar/commandsapp)
app/models/article.rb:
* [ 23] Have to fix this one before pushing!

647
2- bin/rails

Cuando se utilizan anotaciones especficas y anotaciones personalizadas, el nombre


de anotacin (FIXME, BUG etc) no se muestra en las lneas de salida.

De forma predeterminada, las notas de rails se vern en los directorios app, config, db,
lib y test . Si desea buscar en otros directorios, puede configurarlos mediante la opcin

config.annotations.register_directories .

config.annotations.register_directories("spec", "vendor")

Tambin puede proporcionarlos como una lista separada por comas en la variable de
entorno SOURCE_ANNOTATION_DIRECTORIES.

$ export SOURCE_ANNOTATION_DIRECTORIES='spec,vendor'
$ bin/rails notes
(in /home/foobar/commandsapp)
app/models/user.rb:
* [ 35] [FIXME] User should have a subscription at this point
spec/models/user_spec.rb:
* [122] [TODO] Verify the user that has a subscription works

2.5 routes
Las rutas de rails mostrarn todas las rutas definidas, lo que es til para rastrear problemas
de enrutamiento en su aplicacin o para darle una buena visin general de las URL de una
aplicacin con la que est tratando de familiarizarse.

2.6 test
Una buena descripcin de las pruebas unitarias en Rails se proporciona en la Gua
para testing para aplicaciones de Rails

Rails viene con una suite de pruebas llamada Minitest. Rails debe su estabilidad al uso de
testing. Las tareas disponibles en el espacio de nombres test: ayudan a ejecutar las
diferentes pruebas que esperamos escribir.

2.7 tmp
El directorio Rails.root/tmp es, al igual que el directorio * nix/tmp , es el lugar de
almacenamiento de los archivos temporales como archivos de id de proceso y acciones
en cach.

Las tareas tmp: le ayudarn a despejar y crear el directorio Rails.root/tmp :

648
2- bin/rails

rails tmp:cache:clear borra tmp/cache.

rails tmp:sockets:clear borra tmp/sockets.

rails tmp:clear borra todos los archivos de cach y sockets.

rails tmp:create crea los directorios tmp para el cach, sockets y pids.

2.8 Miscellaneous
rails stats es ideal para ver las estadsticas de su cdigo, mostrando cosas como

KLOC (miles de lneas de cdigo) y su cdigo para probar la relacin.


rails secret le dar una clave pseudoaleatoria para usar para su secreto de sesin.

rails time:zones:all lista todas las zonas horarias que Rails conoce.

2.9 Tareas de rake personalizadas


Las tareas de rake personalizadas tienen una extensin .rake y se colocan en
Rails.root/lib/tasks . Puede crear estas tareas de rastreo personalizadas con el comando

bin/rails generate task .

desc "I am short, but comprehensive description for my cool task"


task task_name: [:prerequisite_task, :another_task_we_depend_on] do
# All your magic here
# Any valid Ruby code is allowed
end

Para pasar argumentos a su tarea de rack personalizada:

task :task_name, [:arg_1] => [:prerequisite_1, :prerequisite_2] do |task, args|


argument_1 = args.arg_1
end

Puede agrupar las tareas colocndolas en espacios de nombres:

namespace :db do
desc "This task does nothing"
task :nothing do
# Seriously, nothing
end
end

La invocacin de las tareas se ver as:

649
2- bin/rails

$ bin/rails task_name
$ bin/rails "task_name[value 1]" # entire argument string should be quoted
$ bin/rails db:nothing

Si tiene la necesidad de interactuar con sus modelos, y realizar consultas de base de


datos y as sucesivamente, su tarea debe depender de la tarea de entorno, que
cargar su cdigo de aplicacin.

650
3- La Lnea de Comando Avanzada de Rails

3- La Lnea de Comando Avanzada de


Rails
El uso ms avanzado de la lnea de comandos se enfoca en encontrar opciones tiles
(incluso sorprendentes a veces) en las utilidades, y adaptarlas a sus necesidades y flujo de
trabajo especfico. Aqu estn algunos trucos bajo la manga de Rails.

3.1 Rails con bases de datos y SCM


Al crear una nueva aplicacin de Rails, tiene la opcin de especificar qu tipo de base de
datos y qu tipo de sistema de administracin de cdigo fuente va a utilizar su aplicacin.
Esto le ahorrar unos minutos, y ciertamente muchas pulsaciones de tecla.

Vamos a ver lo que una opcin --git y una opcin --database=postgresql har por
nosotros:

$ mkdir gitapp
$ cd gitapp
$ git init
Initialized empty Git repository in .git/
$ rails new . --git --database=postgresql
exists
create app/controllers
create app/helpers
...
...
create tmp/cache
create tmp/pids
create Rakefile
add 'Rakefile'
create README.md
add 'README.md'
create app/controllers/application_controller.rb
add 'app/controllers/application_controller.rb'
create app/helpers/application_helper.rb
...
create log/test.log
add 'log/test.log'

Tuvimos que crear el directorio gitapp e inicializar un repositorio git vaco antes de que Rails
agregara los archivos creados a nuestro repositorio. Veamos lo que poner en nuestra
configuracin de base de datos:

651
3- La Lnea de Comando Avanzada de Rails

$ cat config/database.yml
# PostgreSQL. Versions 9.1 and up are supported.
#
# Install the pg driver:
# gem install pg
# On OS X with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On OS X with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem 'pg'
#
development:
adapter: postgresql
encoding: unicode
database: gitapp_development
pool: 5
username: gitapp
password:
...
...

Tambin gener algunas lneas en nuestra configuracin de database.yml correspondiente


a nuestra eleccin de PostgreSQL para la base de datos.

La nica captura con el uso de las opciones de SCM es que usted tiene que hacer
primero el directorio de su aplicacin, luego inicializar su SCM, entonces puede
ejecutar el comando rails new para generar la base de su aplicacin.

652
XXII- La Canalizacin de Recursos

XXII- La Canalizacin de Recursos


Esta gua cubre la Canalizacin de Recursos.

Despus de leer esta gua, sabr:

Qu es la canalizacin de activos y qu hace.


Cmo organizar adecuadamente los recursos de su aplicacin.
Los beneficios de la canalizacion de los assets.
Cmo agregar un preprocesador al pipeline.
Cmo empaquetar los assets con una gema.

653
1- Qu es la Canalizacin de Recursos?

1- Qu es el la Canalizacin de
Recursos?
La canalizacin de recursos proporciona un framework para concatenar y minificar o
comprimir los recursos de JavaScript y CSS. Tambin aade la capacidad de escribir estos
recursos en otros lenguajes y preprocesadores como CoffeeScript, Sass y ERB. Permite
que los recursos de su aplicacin se combinen automticamente con recursos de otras
gemas. Por ejemplo, jquery-rails incluye una copia de jquery.js y habilita las funciones
de AJAX en Rails.

La canalizacin de recursos est implementada por la gema sprockets-rails y est


habilitada de forma predeterminada. Puede desactivarlo mientras crea una nueva aplicacin
pasando la opcin --skip-sprockets .

rails new appname --skip-sprockets

Rails aade automticamente las gemas sass-rails , coffee-rails y uglifier a su


Gemfile, que son utilizados por Sprockets para comprimir los recursos:

gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'

El uso de la opcin --skip-sprockets evitar que Rails los agregue a su Gemfile, por lo que
si posteriormente desea habilitar la canalizacin de los recursos, tendr que agregar esas
gemas a su Gemfile. Adems, la creacin de una aplicacin con la opcin --skip-
sprockets generar un archivo config/application.rb ligeramente diferente, con una

instruccin require para el railtie de los sprockets que estar comentado. Tendr que
quitar el operador de comentario en esa lnea para habilitar ms tarde la canalizacin de los
recursos:

# require "sprockets/railtie"

Para establecer mtodos de compresin de recursos, establezca las opciones de


configuracin apropiadas en production.rb - config.assets.css_compressor para su CSS y
config.assets.js_compressor para su JavaScript:

654
1- Qu es la Canalizacin de Recursos?

config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifier

La gema sass-rails se utiliza automticamente para la compresin del CSS si se


incluye en el archivo Gemfile y no se establece ninguna opcin
config.assets.css_compressor .

1.1 Caractersticas principales


La primera caracterstica del pipeline es concatenar los recursos, lo que puede reducir el
nmero de solicitudes que hace un navegador para renderizar una pgina web. Los
navegadores web estn limitados en el nmero de solicitudes que pueden realizar en
paralelo, por lo que un menor nmero de solicitudes puede significar una carga ms rpida
para su aplicacin.

Sprockets concatena todos los archivos JavaScript en un archivo master .js y todos los
archivos CSS en un archivo master .css . Como veremos ms adelante en esta gua,
puede personalizar esta estrategia para agrupar archivos de la forma que desee. En
produccin, Rails inserta una huella digital SHA256 en cada nombre de archivo para que el
archivo sea almacenado en cach por el navegador web. Puede invalidar la memoria cach
alterando esta huella digital, que ocurre automticamente cada vez que cambia el contenido
del archivo.

La segunda caracterstica de la canalizacin de archivos es la minificacin o compresin de


los recursos. Para los archivos CSS, esto se hace mediante la eliminacin de espacios en
blanco y comentarios. Para JavaScript, se pueden aplicar procesos ms complejos. Puede
elegir entre un conjunto de opciones integradas o especificar las propias.

La tercera caracterstica de la canalizacin de archivos es que permite codificar los recursos


a travs de un lenguaje de nivel superior, con la precompilacin hasta los recursos reales.
Los lenguajes admitidos incluyen Sass para CSS, CoffeeScript para JavaScript y ERB para
ambos de forma predeterminada.

1.2 Qu es la huella dactilar y por qu debera


importarme?
La huella dactilar (fingerprint) es una tcnica que hace que el nombre de un archivo
dependa del contenido del archivo. Cuando cambia el contenido del archivo, tambin se
cambia el nombre del archivo. Para contenido que es esttico o cambiado con poca
frecuencia, esto proporciona una manera fcil de saber si dos versiones de un archivo son
idnticas, incluso entre diferentes servidores o fechas de implementacin.

655
1- Qu es la Canalizacin de Recursos?

Cuando un nombre de archivo es nico y basado en su contenido, los encabezados HTTP


se pueden configurar para fomentar cachs en todas partes (ya sea en el CDN, en el ISP,
en equipos de red o en navegadores web) para mantener su propia copia del contenido.
Cuando se actualiza el contenido, la huella digital cambiar. Esto har que los clientes
remotos soliciten una nueva copia del contenido. Esto se conoce generalmente como
anulacin de antememoria.

La tcnica Sprockets usada para la huella dactilar es insertar un hash del contenido en el
nombre, generalmente al final. Por ejemplo, un archivo CSS global.css

global-908e25f4bf641868d8683022a5b62f54.css

Esta es la estrategia adoptada por la canalizacin de recursos de Rails.

La vieja estrategia de Rails era aadir un query string basado en la fecha de cada recurso
vinculado con un helper incorporado. La fuente del cdigo generado se vea as:

/stylesheets/global.css?1309495796

La estrategia query string tiene varias desventajas:

1. No todas las cachs almacenarn de forma fiable el contenido donde el nombre


de archivo slo difiere segn los parmetros de consulta Steve Souders
recomienda, "... evitando una cadena de consulta para recursos en cach". Encontr
que en este caso el 5-20% de las peticiones no sern almacenadas en cach. Las
cadenas de consulta en particular no funcionan en absoluto con algunas CDN para la
invalidacin de cach.

2. El nombre de archivo puede cambiar entre nodos en entornos de varios


servidores.
La cadena de consulta predeterminada en Rails 2.x se basa en el tiempo de
modificacin de los archivos. Cuando los recursos se despliegan en un clster, no hay
garanta de que las marcas de tiempo sean las mismas, lo que dar lugar a que se
utilicen valores diferentes dependiendo del servidor que administre la solicitud.

3. Demasiada invalidacin de cach


Cuando los recursos estticos se implementan con cada nueva versin de cdigo,
cambia el mtime (tiempo de ltima modificacin) de todos estos archivos, forzando a
todos los clientes remotos a buscarlos de nuevo, incluso cuando el contenido de esos
activos no ha cambiado.

La huella dactilar soluciona estos problemas evitando los query string y asegurando que los
nombres de archivo sean consistentes en funcin de su contenido.

656
1- Qu es la Canalizacin de Recursos?

La huella dactilar est habilitada de forma predeterminada para los entornos de desarrollo y
produccin. Puede habilitarlo o deshabilitarlo en su configuracin mediante la opcin
config.assets.digest .

Ms lectura:

Optimize catching
Revving Filenames: don't use querystring

657
2- Cmo utilizar el canalizador de recursos

2- Cmo utilizar el canalizador de recursos


En versiones anteriores de Rails, todos los activos se encontraban en subdirectorios
public , como imgenes, javascripts y hojas de estilo. Con la canalizacin de recursos, la

ubicacin preferida para estos recursos es ahora el directorio app/assets . Los archivos en
este directorio son servidos por el middleware Sprockets.

Los recursos todava se pueden colocar en la herencia public . Cualquier recurso en


public ser servido como recursos estticos por la aplicacin o servidor web cuando

config.public_file_server.enabled est establecido en true . Debe utilizar app/assets

para archivos que deben someterse a algn pre-procesamiento antes de que se sirvan.

En la produccin, Rails precompila estos archivos en public/assets por defecto. Las copias
precompiladas son servidas como recursos estticos por el servidor web. Los archivos de
app/assets nunca se sirven directamente en produccin.

2.1 Recursos especficos del controlador


Cuando genera un scaffold o un controlador, Rails tambin genera un archivo JavaScript (o
un archivo CoffeeScript si la gema coffee-rails est en el archivo Gemfile ) y un archivo
de hoja de estilos en cascada (o archivo SCSS si sass-rails est en el archivo Gemfile)
en ese controlador. Adems, al generar un scaffold, Rails genera el archivo scaffolds.css
(o scaffolds.scss si sass-rails est en el Gemfile).

Por ejemplo, si genera un ProjectsController , Rails tambin agregar un nuevo archivo en


app/assets/javascripts/projects.coffee y otro en app/assets/stylesheets/projects.scss .

De forma predeterminada, estos archivos estarn listos para ser utilizados por su aplicacin
inmediatamente usando la directiva require_tree . Vea Manifest Files and Directives para
obtener ms detalles sobre require_tree .

Tambin puede optar por incluir hojas de estilo especficas del controlador y archivos
JavaScript slo en sus respectivos controladores utilizando lo siguiente:

<%= javascript_include_tag params[:controller] %>

<%= stylesheet_link_tag params[:controller] %>

658
2- Cmo utilizar el canalizador de recursos

Al hacer esto, asegrese de que no est utilizando la directiva require_tree , ya que


resultar en que sus recursos se incluyan ms de una vez.

Al utilizar la precompilacin de activos, deber asegurarse de que los activos del


controlador se precompilan al cargarlos por pgina. De forma predeterminada, los
archivos .coffee y . scss no se precompilarn por s solos. Consulte Precompilacin
de activos para obtener ms informacin sobre cmo funciona la precompilacin.

Debe tener un tiempo de ejecucin soportado por ExecJS para poder utilizar
CoffeeScript. Si est utilizando macOS o Windows, tiene un tiempo de ejecucin de
JavaScript instalado en su sistema operativo. Consulte la documentacin de ExecJS
para conocer todos los tiempos de ejecucin de JavaScript admitidos.

Tambin puede desactivar la generacin de archivos de recursos especficos del


controlador agregando lo siguiente a su configuracin config/application.rb :

config.generators do |g|
g.assets false
end

2.2 Organizacin de los recursos


Los recursos de la canalizacin se pueden colocar dentro de una aplicacin en una de tres
ubicaciones: app/assets , lib/assets o vendor/assets .

app/assets es para recursos propiedad de la aplicacin, como imgenes

personalizadas, archivos JavaScript o hojas de estilo.


lib/assets es para el cdigo de sus propias bibliotecas que no encaja realmente en el

mbito de la aplicacin o las bibliotecas que se comparten entre aplicaciones.


vendor/assets es para recursos pertenecientes a entidades externas, como cdigo

para complementos JavaScript y frameworks CSS. Tenga en cuenta que el cdigo de


terceros con referencias a otros archivos tambin procesados por el componente
Pipeline (imgenes, hojas de estilo, etc.) tendr que volver a escribirse para utilizar
ayudantes como asset_path .

Si se est actualizando desde Rails 3, tenga en cuenta que los activos de lib/assets
o vendor/assets estn disponibles para su inclusin a travs de los manifiestos de la
aplicacin, pero ya no forman parte de la matriz de precompilacin. Consulte
Precompilacin de activos para obtener orientacin.

2.2.1 Rutas de bsqueda

659
2- Cmo utilizar el canalizador de recursos

Cuando se hace referencia a un archivo desde un manifiesto o un helper, Sprockets busca


en las tres ubicaciones de recursos predeterminados para ello.

Las ubicaciones predeterminadas son: los directorios images , javascripts y stylesheets


bajo la carpeta app/assets , pero estos subdirectorios no son especiales: cualquier ruta de
acceso en assets/* ser buscada.

Por ejemplo, estos archivos:

app/assets/javascripts/home.js
lib/assets/javascripts/moovinator.js
vendor/assets/javascripts/slider.js
vendor/assets/somepackage/phonebox.js

Ser referenciado en un manifiesto como este:

//= require home


//= require moovinator
//= require slider
//= require phonebox

Los recursos dentro de subdirectorios tambin se pueden acceder.

app/assets/javascripts/sub/something.js

es referenciado como:

//= require sub/something

Puede ver la ruta de bsqueda inspeccionando Rails.application.config.assets.paths en


la consola de Rails.

Adems de los paths assets/* estndar, se pueden agregar rutas adicionales (totalmente
calificadas) a la canalizacin en config/application.rb . Por ejemplo:

config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")

Las rutas se recorren en el orden en que se producen en la ruta de bsqueda. De forma


predeterminada, esto significa que los archivos de app/assets tienen prioridad y se
enmascaran las rutas correspondientes en lib y vendor .

660
2- Cmo utilizar el canalizador de recursos

Es importante tener en cuenta que los recursos a los que desea hacer referencia fuera
de un manifiesto deben agregarse a la matriz de precompilacin o no estarn
disponibles en el entorno de produccin.

2.2.2 Utilizacin de archivos index


Sprockets utiliza archivos denominados index (con las extensiones correspondientes) para
un propsito especial.

Por ejemplo, si tiene una biblioteca jQuery con muchos mdulos, que se almacena en
lib/assets/javascripts/library_name , el archivo

lib/assets/javascripts/library_name/index.js sirve como manifiesto para todos los

archivos de esta biblioteca. Este archivo podra incluir una lista de todos los archivos
necesarios en orden, o una simple directiva require_tree .

La biblioteca como un todo se puede acceder en el manifiesto de la aplicacin de la


siguiente manera:

//= require library_name

Esto simplifica el mantenimiento y mantiene las cosas limpias permitiendo que el cdigo
relacionado se agrupe antes de su inclusin en otro lugar.

2.3 Enlaces a los recursos


Sprockets no agrega ningn mtodo nuevo para acceder a tus recursos: an debes usar el
conocido javascript_include_tag y stylesheet_link_tag :

<%= stylesheet_link_tag "application", media: "all" %>


<%= javascript_include_tag "application" %>

Si utiliza la gem turbolinks , que se incluye de forma predeterminada en Rails, incluya la


opcin ' data-turbolinks-track ' que hace que los turbolinks comprueben si un recurso se ha
actualizado y si lo carga en la pgina:

<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => "reloa


d" %>
<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %>

En vistas regulares, puede acceder a las imgenes del directorio app/assets/images de la


siguiente manera:

661
2- Cmo utilizar el canalizador de recursos

<%= image_tag "rails.png" %>

Siempre que el canal est habilitado dentro de su aplicacin (y no deshabilitado en el


contexto actual del entorno), este archivo es servido por Sprockets. Si existe un archivo en
public/assets/rails.png es servido por el servidor web.

Alternativamente, una solicitud de un archivo con un hash SHA256 como


public/assets/railsf90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png

se trata de la misma va. La forma en que se generan estos hashes se trata en la seccin in
Development ms adelante en esta gua.

Los Sprockets tambin mirarn a travs de las rutas especificadas en config.assets.paths ,


que incluye las rutas standard de la aplicacin y cualquier ruta aadida por los motores de
Rails.

Las imgenes tambin se pueden organizar en subdirectorios si es necesario y, a


continuacin, se puede acceder especificando el nombre del directorio en la etiqueta:

<%= image_tag "icons/rails.png" %>

Si est precompilando sus recursos (vea in Production ms adelante), enlazar a un


recurso que no existe generar una excepcin en la pgina de llamada. Esto incluye
enlazar a una cadena en blanco. Como tal, tenga cuidado con image_tag y los otros
helpers con los datos suministrados por el usuario.

2.3.1 CSS y ERB


E canalizador de recursos evala automticamente el ERB . Esto significa que si agrega
una extensin erb a un recurso CSS (por ejemplo, application.css.erb ), los helpers
como asset_path estarn disponibles en sus reglas CSS :

.class { background-image: url(<%= asset_path 'image.png' %>) }

Esto escribe la ruta del recurso particular al que se est haciendo referencia. En este
ejemplo, tendra sentido tener una imagen en una de las rutas de carga de activos, como
app/assets/images/image.png , que se referenciara aqu. Si esta imagen ya est disponible

en public/assets como un archivo de huellas dactilares, se hace referencia a esa ruta.

Si desea utilizar una URI de datos, un mtodo para incrustar los datos de imagen
directamente en el archivo CSS, puede utilizar el asistente asset_data_uri .

662
2- Cmo utilizar el canalizador de recursos

#logo { background: url(<%= asset_data_uri 'logo.png' %>) }

Esto inserta una URI de datos correctamente formateados en el CSS fuente .

Tenga en cuenta que la etiqueta de cierre no puede ser del estilo -%> .

2.3.2 CSS y Sass


Cuando se utiliza la canalizacin de recursos, las rutas de acceso a los recursos deben ser
reescritas y sass-rails proporciona los helpers -url y -path (hifenizadas en Sass,
subrayadas en Ruby) para las siguientes clases de recursos: image, font, video, audio,
JavaScript y stylesheet

image-url("rails.png") retorna url(/assets/rails.png)

image-path("rails.png") retorna "/assets/rails.png"

Tambin se puede utilizar la forma ms genrica:

asset-url("rails.png") retorna url(/assets/rails.png)

asset-path("rails.png") retorna "/assets/rails.png"

2.3.3 JavaScript/CoffeeScript y ERB


Si agrega una extensin erb a un elemento JavaScript, hacindo algo como
application.js.erb , puede utilizar el helper asset_path en su cdigo JavaScript:

$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

Esto escribe la ruta del recurso particular al que se est haciendo referencia.

Del mismo modo, puede utilizar el helper asset_path en archivos CoffeeScript con
extensin erb (por ejemplo, application.coffee.erb ):

$('#logo').attr src: "<%= asset_path('logo.png') %>"

2.4 Archivos de Manifiesto y directivas


Sprockets utiliza archivos de manifiesto para determinar qu recursos incluir y servir. Estos
archivos de manifiesto contienen directivas, instrucciones que le dicen a Sprockets qu
archivos debe requerir para crear un nico archivo CSS o JavaScript. Con estas directivas,
Sprockets carga los archivos especificados, los procesa si es necesario, los concatena en

663
2- Cmo utilizar el canalizador de recursos

un solo archivo y luego los comprime (basado en el valor de


Rails.application.config.assets.js_compressor ). Al servir un solo archivo en lugar de

muchos, el tiempo de carga de las pginas se puede reducir considerablemente porque el


navegador realiza menos solicitudes. La compresin tambin reduce el tamao del archivo,
lo que permite al navegador descargarlos ms rpido.

Por ejemplo, una nueva aplicacin de Rails incluye un archivo predeterminado


app/assets/javascripts/application.js que contiene las siguientes lneas:

// ...
//= require jquery
//= require jquery_ujs
//= require_tree .

En los archivos JavaScript, las directivas Sprockets comienzan con //= . En el caso
anterior, el archivo utiliza las directivas require y require_tree . La directiva require se
utiliza para decirle a Sprockets los archivos que desea requerir. Aqu, se requieren los
archivos jquery.js y jquery_ujs.js que estn disponibles en algn lugar de la ruta de
bsqueda de Sprockets. No es necesario proporcionar las extensiones explcitamente.
Sprockets asume que est requiriendo un archivo .js cuando se hace desde dentro de un
archivo .js .

La directiva require_tree le indica a Sprockets que incluya recursivamente todos los


archivos JavaScript en el directorio especificado de salida. Estas rutas deben especificarse
en relacin con el archivo de manifiesto. Tambin puede utilizar la directiva
require_directory que incluye todos los archivos JavaScript slo en el directorio

especificado, sin recursin.

Las directivas se procesan de arriba a abajo, pero no se especifica el orden en que los
archivos estn incluidos en require_tree . Usted no debera confiar en cualquier orden
particular entre ellos. Si necesita asegurarse de que algn JavaScript en particular se
encuentre encima de otro en el archivo concatenado, necesitar requerir primero el archivo
previo en el manifiesto. Tenga en cuenta que la familia de directivas require impide que
los archivos se incluyan dos veces en la salida.

Rails tambin crea un archivo predeterminado app/assets/stylesheets/application.css que


contiene estas lneas:

/* ...
*= require_self
*= require_tree .
*/

664
2- Cmo utilizar el canalizador de recursos

Rails ambos app/assets/javascripts/application.js y


app/assets/stylesheets/application.css independientemente de si la opcin - -skip-

sprockets se usa al crear una nueva aplicacin de Rails. Esto es as ya que usted puede

agregar fcilmente el pipelining del recurso ms adelante si usted lo desea.

Las directivas que funcionan en archivos JavaScript tambin funcionan en las hojas de
estilo (aunque obviamente incluyen hojas de estilo en lugar de archivos JavaScript). La
directiva require_tree en un manifiesto CSS que funciona de la misma forma que con
JavaScript, requiriendo todas las hojas de estilo del directorio actual.

En este ejemplo, require_self se utiliza. Esto coloca el CSS contenido en el archivo (si lo
hay) en la ubicacin exacta de la llamada require_self .

Si desea utilizar varios archivos Sass, debe utilizar la regla Sass @import en lugar de
estas directivas Sprockets. Cuando se utilizan directivas Sprockets, los archivos Sass
existen dentro de su propio mbito, haciendo que las variables o mixins slo estn
disponibles dentro del documento en el que se definieron.

Usted puede hacer el archivo globbing usando tambin @import "*" , e @import "** / *"
para agregar el rbol entero que es equivalente a cmo trabaja require_tree . Revise la
documentacin de sass-rails para obtener ms informacin y advertencias importantes.

Puede tener tantos archivos de manifiesto como necesite. Por ejemplo, el manifiesto
admin.css y admin.js podra contener los archivos JS y CSS que se utilizan para la

seccin admin de una aplicacin.

Se aplican las mismas observaciones sobre las ordenes realizadas anteriormente. En


particular, puede especificar archivos individuales y se compilan en el orden especificado.
Por ejemplo, puede concatenar tres archivos CSS de esta manera:

/* ...
*= require reset
*= require layout
*= require chrome
*/

2.5 Preprocesamiento
Las extensiones de archivo utilizadas en un recurso determinan qu pre-procesamiento se
aplica. Cuando se genera un controlador o un scaffold con el gemset de Rails
predeterminado, se genera un archivo CoffeeScript y un archivo SCSS en lugar de un
archivo normal de JavaScript y de CSS. El ejemplo utilizado anteriormente era un

665
2- Cmo utilizar el canalizador de recursos

controlador denominado " projects ", que generaba un


app/assets/javascripts/projects.coffee y un archivo

app/assets/stylesheets/projects.scss .

En el modo de desarrollo, o si la canalizacin de recursos est deshabilitada, cuando se


solicitan estos archivos, los procesadores proporcionados por las gemas coffee-script y
sass los procesan y luego se envan al navegador como JavaScript y CSS,

respectivamente. Cuando se habilita el canalizador de recursos, estos archivos se procesan


previamente y se colocan en el directorio public/assets para que se puedan servir
mediante la aplicacin de Rails o por el servidor web.

Se pueden solicitar capas adicionales de preprocesamiento aadiendo otras extensiones,


donde cada extensin se procesa de derecha a izquierda. Estos deben usarse en el orden
en que se debe aplicar el tratamiento. Por ejemplo, una hoja de estilo denominada
app/assets/stylesheets/projects.scss.erb se procesa primero como ERB , SCSS y

finalmente se utiliza como CSS . Lo mismo se aplica a un archivo JavaScript:


app/assets/javascripts/projects.coffee.erb se procesa como ERB , luego CoffeeScript y

se utiliza como JavaScript .

Tenga en cuenta que el orden de estos preprocesadores es importante. Por ejemplo, si


llam a su archivo JavaScript app/assets/javascripts/projects.erb.coffee entonces se
procesara con el intrprete de CoffeeScript primero, y no entendera ERB y por lo tanto
tendra problemas.

666
3- In Development

3- In Development
En el modo de desarrollo, los recursos se sirven como archivos separados en el orden en
que se especifican en el archivo de manifiesto.

Este manifiesto app/assets/javascripts/application.js :

//= require core


//= require projects
//= require tickets

Generara este HTML:

<script src="/assets/core.js?body=1"></script>
<script src="/assets/projects.js?body=1"></script>
<script src="/assets/tickets.js?body=1"></script>

El param body es requerido por Sprockets.

3.1 Comprobacin de errores en tiempo de ejecucin


De forma predeterminada, la canalizacin de recursos comprueba los posibles errores en el
modo de desarrollo durante el tiempo de ejecucin. Para desactivar este comportamiento
puede establecer:

config.assets.raise_runtime_errors = false

Cuando esta opcin es true , la canalizacin de los recursos comprobar si todos los
recursos cargados en su aplicacin estn incluidos en la lista config.assets.precompile . Si
config.assets.digest tambin es true , la canalizacin de recursos requerir que todas

las solicitudes de recursos incluyan digests (resmenes).

3.2 Levantar un error cuando un recurso no se encuentra


Si est utilizando sprockets-rails >= 3.2.0 puede configurar lo que sucede cuando se
realiza una bsqueda de recursos y no se encuentra nada. Si desactiva el "asset fallback",
se generar un error cuando no se pueda encontrar un activo.

config.assets.unknown_asset_fallback = false

667
3- In Development

Si "asset fallback" esta habilitado, cuando no se pueda encontrar un recursos, se mostrar


la ruta de acceso y no se producir ningn error. El comportamiento ante un fallo de
recursos est habilitado de forma predeterminada.

3.3 Cmo desactivar los digest


Puede desactivar los digest mediante la actualizacin de
config/environments/development.rb para incluir:

config.assets.digest = false

Cuando esta opcin es true , se generarn los digest para las URL de los recursos.

3.4 Como desactivar el debugging


Puede desactivar el modo de debugging debe incluir en
config/environments/development.rb lo siguiente:

config.assets.debug = false

Cuando el modo de depuracin est desactivado, Sprockets concatena y ejecuta los


preprocesadores necesarios en todos los archivos. Con el modo de depuracin
desactivado, el manifiesto generara en su lugar:

<script src="/assets/application.js"></script>

Los recursos se compilan y se almacenan en cach en la primera solicitud despus de


iniciarse el servidor. Sprockets establece una cabecera HTTP de Cache-Control que se
debe revalidar para reducir la sobrecarga de la peticin en las solicitudes posteriores. En
estas, el navegador obtiene una respuesta 304 (no modificada).

Si alguno de los archivos del manifiesto ha cambiado entre las solicitudes, el servidor
responde con un nuevo archivo compilado.

El modo de depuracin tambin se puede habilitar en los mtodos de ayuda de Rails:

<%= stylesheet_link_tag "application", debug: true %>


<%= javascript_include_tag "application", debug: true %>

La opcin :debug es redundante si el modo de depuracin ya est activado.

668
3- In Development

Tambin puede habilitar la compresin en modo de desarrollo como comprobacin de


sanidad y deshabilitarla a peticin segn sea necesario para la depuracin.

669
4- In Production

4- In Production
En el entorno de produccin, Sprockets utiliza el esquema de huellas dactilares descrito
anteriormente. Por defecto, Rails asume que los recursos se han precompilado y sern
servidos como recursos estticos por su servidor web.

Durante la fase de precompilacin se genera un SHA256 a partir del contenido de los


archivos compilados, e insertado en los nombres de los ficheros a medida que se escriben
en el disco. Estos nombres de fingerprinted son utilizados por los ayudantes de Rails en
lugar del nombre del manifiesto.

Por ejemplo:

<%= javascript_include_tag "application" %>


<%= stylesheet_link_tag "application" %>

Genera algo como esto:

<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" r
el="stylesheet" />

Con el canalizador de recursos las opciones :cache y :concat ya no se utilizan,


borre estas opciones de javascript_include_tag y stylesheet_link_tag .

El comportamiento de las huellas dactilares es controlado por la opcin de inicializacin


config.assets.digest (que por defecto es true ).

En circunstancias normales, la opcin predeterminada config.assets.digest no debe


cambiarse. Si no hay digest en los nombres de archivo y se establecen encabezados
de futuro lejano, los clientes remotos nunca harn refetch a los archivos cuando su
contenido cambia.

4.1 Precompilacin de recursos


Rails viene incluido con una tarea para compilar los manifiestos de recursos y otros archivos
en la canalizacin.

Los recursos compilados se escriben en la ubicacin especificada en


config.assets.prefix . De forma predeterminada, este es el directorio /assets .

670
4- In Production

Puede llamar a esta tarea en el servidor durante la implementacin para crear versiones
compiladas de sus recursos directamente en el servidor. Consulte la siguiente seccin para
obtener informacin sobre cmo compilar localmente.

La tarea es:

$ RAILS_ENV=production bin/rails assets:precompile

Capistrano (v2.15.1 y superior) incluye una receta para manejar esto en el despliegue.
Agregue la siguiente lnea a Capfile :

load 'deploy/assets'

Esto enlaza la carpeta especificada en config.assets.prefix a shared/assets . Si ya utiliza


esta carpeta compartida, tendr que escribir su propia tarea de despliegue.

Es importante que esta carpeta se comparta entre implementaciones para que las pginas
remitidas remotamente que hacen referencia a los recursos compilados antiguos funcionen
durante la vida de la pgina almacenada en cach.

El complemento predeterminado para la compilacin de archivos incluye application.js ,


application.css y todos los archivos que no sean JS/CSS (esto incluir todos los recursos

de imagen automticamente) desde las carpetas app/assets , incluidas las gemas:

[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.ext


name(filename)) },
/application.(css|js)$/ ]

El matcher (y otros miembros del array de precompilacin, vase ms adelante) se


aplica a los nombres de archivos compilados finales. Esto significa que cualquier cosa
que se compila a JS/CSS se excluye, as como archivos crudos JS/CSS; Por ejemplo,
los archivos .coffee y .scss no se incluyen automticamente al compilarlos en
JS/CSS.

Si tiene otros manifiestos u hojas de estilo individuales y archivos de JavaScript para incluir,
puede agregarlos a la matriz de precompilacin en config/initializers/assets.rb :

Rails.application.config.assets.precompile += %w( admin.js admin.css )

Siempre especifique un nombre de archivo compilado esperado que termine con .js
o .css , incluso si desea agregar archivos Sass o CoffeeScript al array de
precompilacin.

671
4- In Production

La tarea tambin genera un archivo .sprockets-manifest-md5hash.json (donde md5hash es


un hash MD5 ) que contiene una lista con todos sus recursos y sus respectivas huellas
dactilares. Esto es utilizado por los mtodos auxiliares de Rails para evitar entregar las
solicitudes de mapeo a Sprockets. Un archivo de manifiesto tpico se ve as:

{"files":{"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442
b.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":4123
83,
"digest":"aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b","integrity"
:"sha256-ruS+cfEogDeueLmX3ziDMu39JGRxtTPc7aqPn+FWRCs="},
"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css":{"l
ogical_path":"application.css","mtime":"2016-12-23T19:12:20-05:00","size":2994,
"digest":"86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18","integrity"
:"sha256-hqKStQcHk8N+LA5fOfc7s4dkTq6tp/lub8BAoCixbBg="},
"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico":{"logic
al_path":"favicon.ico","mtime":"2016-12-23T20:11:00-05:00","size":8629,
"digest":"8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda","integrity"
:"sha256-jSOHuNTTLOzZP6OQDfDp/4nQGqzYT1DngMF8n2s9Dto="},
"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png":{"logi
cal_path":"my_image.png","mtime":"2016-12-23T20:10:54-05:00","size":23414,
"digest":"f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493","integrity"
:"sha256-9AKBVv1+ygNYTV8vwEcN8eDbxzaequY4sv8DP5iOxJM="}},
"assets":{"application.js":"application-aee4be71f1288037ae78b997df388332edfd246471b533
dcedaa8f9fe156442b.js",
"application.css":"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a
028b16c18.css",
"favicon.ico":"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0ed
a.ico",
"my_image.png":"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec
493.png"}}

La ubicacin predeterminada para el manifiesto es la raz de la ubicacin especificada en


config.assets.prefix (' /assets ' por defecto).

Si faltan archivos precompilados en produccin, obtendrs una excepcin


Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError que indica el

nombre de los archivos que faltan.

4.1.1 Far-future caduca el encabezado


Existen recursos precompilados en el sistema de archivos y son servidos directamente por
su servidor web. No tienen cabeceras de futuro por defecto, por lo que para obtener el
beneficio de las huellas dactilares tendr que actualizar la configuracin del servidor para
agregar esos encabezados.

Para Apache:

672
4- In Production

# The Expires* directives requires the Apache module


# `mod_expires` to be enabled.
<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
</Location>

Para NGINX:

location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;

add_header ETag "";


}

4.2 Precompilacin local


Hay varias razones por las que puede que desee precompilar sus recursos localmente.
Entre ellos estn:

Es posible que no tenga acceso de escritura a su sistema de archivos en produccin.


Es posible que est desplegando en ms de un servidor y que quiera evitar la
duplicacin de trabajo.
Es posible que est realizando despliegues frecuentes que no incluyen cambios de
recursos.

La compilacin local le permite asignar los archivos compilados al control de cdigo fuente e
implementarlos de forma normal.

Hay tres advertencias:

No debe ejecutar la tarea de despliegue de Capistrano que precompila los recursos.


Debe asegurarse de que los compresores o minifiers necesarios estn disponibles en
su sistema de desarrollo.
Debe cambiar la siguiente configuracin de la aplicacin:

En config/environments/development.rb , coloque la siguiente lnea:

config.assets.prefix = "/dev-assets"

673
4- In Production

El cambio de prefix hace que los Sprockets usen una URL diferente para servir recurso
en modo de desarrollo y pasen todas las solicitudes a los Sprockets. El prefijo todava est
establecido en /assets en el entorno de produccin. Sin este cambio, la aplicacin servira
los recursos precompilados de /assets en desarrollo y no vera ningn cambio local hasta
que compile los recursos de nuevo.

En la prctica, esto le permitir precompilar localmente, tener esos archivos en su rbol de


trabajo y confiar esos archivos al control de cdigo fuente cuando sea necesario. El modo
de desarrollo funcionar como se esperaba.

4.3 Compilacin en vivo


En algunas circunstancias puede que desee utilizar la compilacin en vivo. En este modo
todas las solicitudes de recursos en el canalizador son manejadas directamente por los
Sprockets.

Para activar esta opcin:

config.assets.compile = true

En la primera solicitud los recursos se compilan y almacenan en cach como se describe en


el entorno de desarrollo anteriormente mencionado y los nombres de manifiesto utilizados
en los helpers se modifican para incluir el hash SHA256.

Sprockets tambin establece el encabezado HTTP Cache-Control a max-age=31536000 . Esto


indica que todas las cachs entre su servidor y el navegador del cliente que este contenido
(el archivo que se sirve) se puede almacenar en cach durante 1 ao. El efecto de esto es
reducir el nmero de solicitudes para este recurso de su servidor; El recurso tiene una
buena probabilidad de estar en la cach del navegador local o en alguna cach intermedia.

Este modo utiliza ms memoria, tiene menor rendimiento que el predeterminado y no se


recomienda.

Si va a implementar una aplicacin de produccin en un sistema sin ningn tiempo de


ejecucin de JavaScript preexistente, puede agregar uno a su archivo Gemfile:

group :production do
gem 'therubyracer'
end

4.4 CDNs

674
4- In Production

CDN es la sigla de Content Delivery Network, diseada principalmente para almacenar en


cach recursos en todo el mundo, de forma que cuando un navegador solicita el recurso,
una copia en cach estar geogrficamente cerca de ese navegador. Si est sirviendo
recursos directamente desde su servidor Rails en produccin, la mejor prctica es usar un
CDN delante de su aplicacin.

Un patrn comn para usar un CDN es configurar su aplicacin de produccin como el


servidor "de origen". Esto significa que cuando un navegador solicita un recurso de la CDN
y hay una falta de cach, se tomar el archivo de su servidor sobre la marcha y luego en la
cach. Por ejemplo, si est ejecutando una aplicacin de Rails en example.com y tiene un
CDN configurado en mycdnsubdomain.fictional-cdn.com , entonces cuando se hace una
solicitud a mycdnsubdomain.fictionalcdn.com/assets/smile.png , el CDN consultar su
servidor una vez en example.com/assets/smile.png y almacenar la solicitud en cach. La
siguiente solicitud al CDN que llega a la misma URL afectar a la copia almacenada en
cach. Cuando el CDN puede servir un recurso directamente, la solicitud nunca toca el
servidor de Rails. Dado que los recursos de un CDN estn geogrficamente ms cercanos
al navegador, la solicitud es ms rpida y, dado que el servidor no necesita dedicar tiempo a
servir recursos, puede centrarse en servir el cdigo de la aplicacin lo ms rpido posible.

4.4.1 Configurar un CDN para servir recursos estticos


Para configurar su CDN, debe ejecutar su aplicacin en produccin en Internet en una URL
disponible pblicamente, por ejemplo example.com . A continuacin, tendr que registrarse
para un servicio de CDN de un proveedor de alojamiento en la nube. Al hacer esto, necesita
configurar el "origen" del CDN para que apunte en su sitio web example.com , consulte a su
proveedor para obtener documentacin sobre cmo configurar el servidor de origen.

El CDN provisto debe darle un subdominio personalizado para su aplicacin como


mycdnsubdomain.fictional-cdn.com (note que fictional-cdn.com no es un proveedor CDN

vlido en el momento de escribir este documento). Ahora que ha configurado su servidor


CDN, debe indicarle a los navegadores que utilicen su CDN para obtener recursos en lugar
de su servidor Rails directamente. Puede hacerlo configurando Rails para configurar su
CDN como el host activo en lugar de usar una ruta relativa. Para configurar su host de
recursos en Rails, debe configurar config.action_controller.asset_host en
config/environments/production.rb :

config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com'

675
4- In Production

Slo es necesario proporcionar el "host", este es el subdominio y el dominio raz, no es


necesario especificar un protocolo o "esquema" como http:// o https:// . Cuando
se solicita una pgina web, el protocolo en el vnculo a su recurso que se genera
coincidir con la forma en que se accede a la pgina web de forma predeterminada.

Tambin puede establecer este valor a travs de una variable de entorno para facilitar la
ejecucin de una copia provisional de su sitio:

config.action_controller.asset_host = ENV['CDN_HOST']

Nota: Usted tendra que configurar CDN_HOST en su servidor a mycdnsubdomain.fictional-


cdn.com para que esto funcione.

Una vez que haya configurado su servidor y su CDN cuando sirva una pgina web que
tenga un recurso:

<%= asset_path('smile.png') %>

En lugar de devolver una ruta tal como /assets/smile.png (los resmenes se dejan fuera
para facilitar la lectura). La URL generada tendr la ruta completa a su CDN.

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

Si el CDN tiene una copia de smile.png lo servir al navegador y su servidor ni siquiera


sabe que fue solicitado. Si el CDN no tiene una copia, tratar de encontrarla en el "origen"
en example.com/assets/smile.png y luego la almacenarla para uso futuro.

Si desea servir slo algunos recursos de su CDN, puede utilizar la opcin personalizada
:host , su helper de recursos, que sobrescribe el valor establecido en

config.action_controller.asset_host .

<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>

4.4.2 Personalizar el comportamiento de cach de CDN


Un CDN funciona almacenando contenido en cach. Si el CDN tiene contenido obsoleto o
malo, entonces lo est perjudicando en lugar de ayudar a su aplicacin. El propsito de esta
seccin es describir el comportamiento general del almacenamiento en cach de la mayora
de los CDN, su proveedor especfico puede comportarse de manera ligeramente diferente.

4.4.2.1 Almacenamiento en cach de la peticin CDN

676
4- In Production

Mientras que un CDN se describe como bueno para el almacenamiento de recursos en


cach, en realidad "cachea" toda la peticin. Esto incluye el cuerpo del recurso, as como
los encabezados. El ms importante es Cache-Control que le dice a la CDN (y a los
navegadores web) cmo almacenar el contenido en cach. Esto significa que si alguien
solicita un recurso que no existe /assets/i-dont-exist.png y su aplicacin de Rails
devuelve un 404, entonces su CDN probablemente almacenar en cach la pgina 404 si
est presente un encabezado vlido de Cache-Control .

4.4.2.2 Depuracin de encabezado CDN

Una manera de comprobar los encabezados que se almacenan en cach correctamente en


su CDN es mediante curl . Puede solicitar los encabezados de su servidor y su CDN para
verificar que son los mismos:

$ curl -I http://www.example/assets/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur

Versus la copia CDN.

$ curl -I http://mycdnsubdomain.fictional-cdn.com/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK Server: Cowboy Last-
Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css
Cache-Control:
public, max-age=2592000
Via: 1.1 vegur
Content-Length: 126560
Accept-Ranges:
bytes
Date: Sun, 24 Aug 2014 20:28:45 GMT
Via: 1.1 varnish
Age: 885814
Connection: keep-alive
X-Served-By: cache-dfw1828-DFW
X-Cache: HIT
X-Cache-Hits:
68
X-Timer: S1408912125.211638212,VS0,VE0

677
4- In Production

Compruebe su documentacin del CDN para cualquier informacin adicional que pueden
proporcionar como X-Cache o para cualquier encabezado adicional que puede agregar.

4.4.2.3 CDNs y el encabezado de control de cach

El encabezado de control es una especificacin de la W3C que describe cmo una solicitud
se puede almacenar en cach. Cuando no se utiliza n CDN, un navegador utilizar esta
informacin para almacenar en cach el contenido. Esto es muy til para los recursos que
no se modifican y que un navegador no necesita volver a descargar el CSS de un sitio web
o JavaScript en cada solicitud. Generalmente queremos que nuestro servidor de Rails diga
a nuestro CDN (y navegador) que el recurso es "pblico", lo que significa que cualquier
cach puede almacenar la solicitud. Tambin comnmente queremos establecer max-age ,
que es la cantidad de tiempo que el cach almacenar el objeto antes de invalidar el cach.
El valor max-age se establece en segundos con un valor mximo posible de 31536000 que
es de un ao. Puede hacerlo en su aplicacin Rails estableciendo

config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}

Ahora, cuando su aplicacin sirva un recurso en produccin, el CDN almacenar el recurso


por un ao. Dado que la mayora de los CDN tambin almacenan en cach los
encabezados de la solicitud, este Cache-Control ser pasado a todos los navegadores
futuros que busquen este recurso, el navegador entonces sabe que puede almacenar este
recurso durante mucho tiempo antes de necesitarlo de nuevo.

4.4.2.4 CDN e Invalidacin de cach basada en URL

La mayora de los CDN almacenarn en cach el contenido de un recurso en funcin de la


URL completa. Esto significa que una solicitud a

http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png

Ser una cach completamente diferente de

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

Si desea establecer un futuro max-age en su Cache-Control (y lo hace), asegrese de que


cuando cambia sus recursos, su cach se invalida. Por ejemplo, al cambiar la cara
sonriente de una imagen de amarillo a azul, desea que todos los visitantes de su sitio
obtengan la nueva cara azul. Cuando se utiliza un CDN con el canal de recursos de Rails
config.assets.digest se establece en true por defecto para que cada elemento tenga un

678
4- In Production

nombre de archivo diferente cuando se cambie. De esta manera no tendr que invalidar
manualmente ningn elemento de su cach. Al utilizar un nombre de recurso nico y
diferente, sus usuarios obtienen el ltimo recurso.

679
5- Personalizacin del Canalizador

5- Personalizacin del Canalizador


5.1 Compresin de CSS
Una de las opciones para comprimir CSS es YUI . El compresor CSS de YUI proporciona
la minificacin.

La lnea siguiente permite la compresin en YUI , y requiere la gema yui-compresor .

config.assets.css_compressor = :yui

La otra opcin para comprimir CSS si tiene instalada la gema sass-rails

config.assets.css_compressor = :sass

5.2 Compresin de JavaScript


Las opciones posibles para la compresin de JavaScript son : closure , :uglifier y
:yui . stos requieren el uso de las gemas closure-compiler , uglifier o yui-

compressor respectivamente.

El Gemfile por defecto incluye uglifier . Esta gema envuelve UglifyJS (escrito para
NodeJS) en Ruby. Se comprime el cdigo mediante la eliminacin de espacios en blanco y
comentarios, acortando los nombres de las variables locales, y la realizacin de otras micro-
optimizaciones, como el cambio de las declaraciones if y else a los operadores
ternarios siempre que sea posible.

La siguiente lnea invoca uglifier para la compresin de JavaScript.

config.assets.js_compressor = :uglifier

Necesitar un tiempo de ejecucin soportado por ExecJS para poder usar uglifier .
Si est utilizando macOS o Windows, tiene un tiempo de ejecucin de JavaScript
instalado en su sistema operativo.

5.3 Servir la versin GZipped de los recursos

680
5- Personalizacin del Canalizador

De forma predeterminada, se generar la versin comprimida de los recursos compilados,


junto con la versin no comprimida de los recursos. Los recursos con Gzip ayudan a
reducir la transmisin de datos a travs del cable. Puede configurar esto configurando el
indicador gzip .

config.assets.gzip = false # disable gzipped assets generation

5.4 Uso de su propio compresor


Los ajustes de configuracin del compresor para CSS y JavaScript tambin pueden tomar
cualquier objeto. Este objeto debe tener un mtodo de compresin que toma una cadena
como nico argumento y debe devolver una cadena.

class Transformer
def compress(string)
do_something_returning_a_string(string)
end
end

Para habilitar esto, pase un nuevo objeto a la opcin config en application.rb :

config.assets.css_compressor = Transformer.new

5.5 Modificacin del assets path


El path publico que Sprockets utiliza por defecto es /assets .

Esto se puede cambiar a otra cosa:

config.assets.prefix = "/some_other_path"

Esta es una opcin prctica si est actualizando un proyecto anterior que no se utiliz a la
canalizacin de recursos y ya utiliza esta ruta o desea utilizar esta ruta de acceso para un
recurso nuevo.

5.6 Encabezados X-Sendfile


El encabezado X-Sendfile es una directiva para que el servidor web ignore la respuesta de
la aplicacin y, en su lugar, sirva un archivo especifico desde el disco. Esta opcin est
desactivada de forma predeterminada, pero se puede habilitar si su servidor lo admite.

681
5- Personalizacin del Canalizador

Cuando se habilita, esto le da la responsabilidad de servir el archivo al servidor web, que es


ms rpido. Eche un vistazo a send_file sobre cmo usar esta funcin.

Apache y NGINX admiten esta opcin, que se puede habilitar en


config/environments/production.rb :

# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache


# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

Si se est actualizando a una aplicacin existente y tiene la intencin de usar esta


opcin, tenga cuidado de pegar esta opcin de configuracin slo en production.rb y
en cualquier otro entorno que defina con comportamiento de produccin (no
application.rb ).

Para ms detalles, eche un vistazo a los documentos de su servidor de produccin: -


Apache-NGINX

682
6- Almacn de cach de recursos

6- Almacn de cach de recursos


De forma predeterminada, Sprockets almacena en cach recursos en tmp/cache/assets en
entornos de desarrollo y produccin. Esto se puede cambiar de la siguiente manera:

config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:memory_store,
{ size: 32.megabytes })
end

Para deshabilitar el almacn de cach de recursos:

config.assets.configure do |env|
env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end

683
7- Agregar recursos a sus gemas

7- Agregar recursos a sus gemas


Los recursos tambin pueden provenir de fuentes externas en forma de gemas.

Un buen ejemplo de esto es la gema jquery-rails que viene con Rails como la gema de
biblioteca JavaScript estndar. Esta gema contiene una clase de motor que hereda de
Rails::Engine . Haciendo esto, Rails es informado de que el directorio para esta gema

puede contener recursos y los directorios app/assets , lib/assets y vendor/assets de


este motor se agregan a la ruta de bsqueda de Sprockets.

684
8- Hacer de su libreria o gema un preprocesador

8- Hacer de su libreria o gema un


preprocesador
Sprockets utiliza procesadores, transformadores, compresores y exportadores para ampliar
la funcionalidad de los Sprockets. Echa un vistazo a extending Sprockets para obtener ms
informacin. Aqu hemos registrado un preprocesador para agregar un comentario al final
de los archivos de text/css ( .css ).

module AddComment
def self.call(input)
{ data: input[:data] + "/* Hello From my sprockets extension */" }
end
end

Ahora que tiene un mdulo que modifica los datos de entrada, es el momento de registrarlo
como un preprocesador para su mime type.

Sprockets.register_preprocessor 'text/css', AddComment

685
9- Actualizacin de versiones antiguas de Rails

9- Actualizacin de versiones antiguas de


Rails
Existen algunos problemas al actualizar desde Rails 3.0 o Rails 2.x. El primero es mover los
archivos de public/ a las nuevas ubicaciones. Consulte la organizacin de activos (vista
arriba) para obtener orientacin sobre las ubicaciones correctas para diferentes tipos de
archivos.

A continuacin evitar duplicar archivos JavaScript. Puesto que jQuery es la biblioteca


predeterminada de JavaScript de Rails 3.1 en adelante, no es necesario copiar jquery.js
en app/assets y se incluir automticamente.

La tercera es la actualizacin de los distintos archivos de entorno con las opciones


predeterminadas correctas.

En application.rb :

# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'

# Change the path that assets are served from config.assets.prefix = "/assets"

En development.rb :

# Expands the lines which load the assets


config.assets.debug = true

Y en production.rb :

# Choose the compressors to use (if any)


config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :yui

# Don't fallback to assets pipeline if a precompiled asset is missed


config.assets.compile = false

# Generate digests for assets URLs.


config.assets.digest = true

# Precompile additional assets (application.js, application.css, and all


# non-JS/CSS are already added)
# config.assets.precompile += %w( admin.js admin.css )

686
9- Actualizacin de versiones antiguas de Rails

En Rails 4 y superiores ya no se establecen valores de configuracin predeterminados para


Sprockets en test.rb , por lo que test.rb ahora requiere la configuracin de Sprockets.
Los valores predeterminados anteriores en el entorno de prueba son: config.assets.compile
= true, config.assets.compress = false, config.assets.debug = false and

config.assets.digest = false .

Lo siguiente tambin se debe agregar a su Gemfile:

gem 'sass-rails', "~> 3.2.3"


gem 'coffee-rails', "~> 3.2.1"
gem 'uglifier'

687
XXIII - Trabajar con JavaScript en Rails

XXIII - Trabajar con JavaScript en Rails


Esta gua cubre la funcionalidad incorporada de Ajax/JavaScript de Rails (y ms); Le
permitir crear aplicaciones ricas en Ajax y dinmicas con facilidad!

Despus de leer esta gua, sabr:

Los fundamentos de Ajax.


JavaScript no intrusivo.
Cmo los helpers incorporados en Rails le ayudarn.
Cmo manejar Ajax en el lado del servidor.
La gema Turbolinks.

688
1- Una introduccin a Ajax

1- Una introduccin a Ajax


Para entender Ajax, primero debes entender lo que hace normalmente un navegador web.

Cuando escribes http://localhost:3000 en la barra de direcciones de su navegador y


pulsa 'Ir', el navegador (su 'cliente') hace una peticin al servidor. Analiza la respuesta y
luego busca todos los recursos asociados, como archivos JavaScript, hojas de estilo e
imgenes. A continuacin, monta la pgina. Si hace clic en un vnculo, realiza el mismo
proceso: busque la pgina, obtenga los recursos, mustrelos todos, mustrele los
resultados. Esto se conoce como el "ciclo de respuesta de solicitud".

JavaScript tambin puede hacer peticiones al servidor y analizar la respuesta. Tambin


tiene la capacidad de actualizar la informacin de la pgina. Combinando estas dos
potencias, un escritor de JavaScript puede hacer una pgina web que pueda actualizar slo
ciertas partes del mismo, sin necesidad de obtener todos los datos de la pgina completa
desde el servidor. Esta es una tcnica poderosa que llamamos Ajax.

Rails se enva con CoffeeScript de forma predeterminada y, por lo tanto, el resto de los
ejemplos de esta gua aparecern en CoffeeScript. Todas estas lecciones, por supuesto, se
aplican a la JavaScript vanila tambin.

Como ejemplo, he aqu un cdigo de CoffeeScript que hace una solicitud de Ajax usando la
biblioteca jQuery:

$.ajax(url: "/test").done (html) ->


$("#results").append html

Este cdigo recupera los datos de " /test ", y luego agrega el resultado al div con un id
results .

Rails proporciona un poco de soporte incorporado para la construccin de pginas web con
esta tcnica. Rara vez tiene que escribir este cdigo usted mismo. El resto de esta gua le
mostrar cmo Rails puede ayudarle a escribir sitios web de esta manera, pero todo se
basa en esta tcnica bastante simple.

689
2- JavaScript no intrusivo.

2- JavaScript no intrusivo.
Rails utiliza una tcnica llamada "JavaScript no intrusivo" para manejar la accin de adjuntar
JavaScript al DOM. Esto generalmente se considera una mejor prctica dentro de la
comunidad frontend, pero ocasionalmente puede leer tutoriales que muestran otras
maneras de hacerlo.

Esta es la forma ms sencilla de escribir JavaScript. Usted puede ver que esto se conoce
como "JavaScript inline":

<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>

Cuando se hace clic, el fondo del enlace se volver rojo. Aqu est el problema: qu ocurre
cuando tenemos un montn de JavaScript que queremos ejecutar en un clic?

<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">


Paint it green</a>

Incmodo, verdad? Podramos extraer la definicin de funcin del controlador de clics y


convertirla en CoffeeScript:

@paintIt = (element, backgroundColor, textColor) ->


element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor

Y luego en nuestra pgina:

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>

Eso es un poco mejor, pero qu pasa si tenemos mltiples enlaces que tienen el mismo
efecto?

<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>


<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>

690
2- JavaScript no intrusivo.

No muy DRY, eh? Podemos arreglar esto usando los eventos en su lugar. Agregaremos un
atributo data-* a nuestro enlace y, a continuacin, enlazaremos un manejador al evento
click de cada enlace que tenga ese atributo:

@paintIt = (element, backgroundColor, textColor) ->


element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor

$ ->
$("a[data-background-color]").click (e) ->
e.preventDefault()

backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)

<a href="#" data-background-color="#990000">Paint it red</a>


<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a
>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

Llamamos a este JavaScript 'no intrusivo' porque ya no estamos mezclando JavaScript en


nuestro HTML. Hemos separado adecuadamente nuestras concerns, facilitando un cambio
futuro. Podemos agregar fcilmente comportamiento a cualquier vnculo agregando el
atributo data . Podemos ejecutar todo nuestro JavaScript a travs de un minimizador y
concatenador. Podemos servir todo nuestro paquete de JavaScript en cada pgina, lo que
significa que se descargar en la carga de la primera pgina y luego se almacenar en
cach en cada pgina despus de eso. Un montn de pequeos beneficios que realmente
se van sumando.

El equipo de Rails le anima a escribir su CoffeeScript (y JavaScript) en este estilo, y usted


podr esperar que muchas bibliotecas tambin sigan este patrn.

691
3- Helpers integrados

3- Helpers integrados
3.1 Elementos remotos
Rails proporciona un montn de mtodos helpers de vista escritos en Ruby para ayudarle a
generar HTML. A veces, usted quiere agregar un poco de Ajax a esos elementos, y Rails le
ayuda en esos casos.

Debido al JavaScript no intrusivo, los "Ajax helpers" de Rails se dividen en dos partes: mitad
JavaScript y mitad Ruby.

A menos que haya deshabilitado el Asset Pipeline, rails-ujs proporciona la mitad de


JavaScript y los helpers de las vistas de Ruby regulares que aaden las etiquetas
adecuadas a su DOM.

Puede leer a continuacin sobre los diferentes eventos que se disparan tratando con
elementos remotos dentro de su aplicacin.

3.1.1 form_with
form_with es un helper que ayuda con la escritura de los formularios. De forma

predeterminada, form_with asume que su formulario utilizar Ajax. Puede inhabilitar este
comportamiento pasando la opcin local :form_with .

<%= form_with(model: @article) do |f| %>


...
<% end %>

Esto generar el siguiente HTML:

<form action="/articles" method="post" data-remote="true">


...
</form>

Tenga en cuenta el data-remote="true" . Ahora, el formulario ser enviado por Ajax en lugar
de por el mecanismo de envio normal del navegador.

Es probable que no quiera sentarse all con un <form> lleno, sin embargo. Es probable que
desee hacer algo despus de un envo exitosa. Para ello, vincule al evento ajax:success .
En caso de error, utilice ajax:error . Echale un vistazo:

692
3- Helpers integrados

$(document).ready ->
$("#new_article").on("ajax:success", (e, data, status, xhr) ->
$("#new_article").append xhr.responseText
).on "ajax:error", (e, xhr, status, error) ->
$("#new_article").append "<p>ERROR</p>"

Obviamente, usted querr ser un poco ms sofisticado que eso, pero es un comienzo.

3.1.2 link_to
link_to es un helper que ayuda a generar enlaces. Tiene una opcin :remote que

puedes usar de esta manera:

<%= link_to "an article", @article, remote: true %>

Que genera

<a href="/articles/1" data-remote="true">an article</a>

Puede vincular los mismos eventos de Ajax como lo hicimos con form_with . He aqu un
ejemplo. Supongamos que tenemos una lista de artculos que se pueden eliminar con un
solo clic. Generaramos un HTML como este:

<%= link_to "Delete article", @article, remote: true, method: :delete %>

Y escribe un CoffeeScript como este:

$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
alert "The article was deleted."

3.1.3 button_to
button_to es un helper que te ayuda a crear botones. Tienes una opcin remote que

puedes llamar as:

<%= button_to "An article", @article, remote: true %>

Esto genera

693
3- Helpers integrados

<form action="/articles/1" class="button_to" data-remote="true" method="post">


<input type="submit" value="An article" />
</form>

Dado que es slo un <form> , toda la informacin sobre form_with tambin se aplica.

3.2 Personalizar elementos remotos


Es posible personalizar el comportamiento de los elementos con un atributo data-remote
sin escribir una lnea de JavaScript. Tu puedes especificar atributos de datos adicionales
para lograr esto.

3.2.1 data-method
Activar hipervnculos siempre da como resultado una solicitud HTTP GET. Sin embargo, si
su aplicacin es RESTful, algunos enlaces son de hecho acciones que cambian datos en el
servidor y deben realizarse con solicitudes que no sean GET. Este atributo permite marcar
tales enlaces con un mtodo explcito como " post ", " put " o " delete ".

La forma en que funciona es que, cuando se activa el enlace, construye un formulario oculto
en el documento con el atributo " action " correspondiente al valor " href " del enlace y el
mtodo correspondiente al valor del data-method y el envio del formulario.

Debido a que el envo de formularios con mtodos HTTP distintos de GET y POST no
es ampliamente aceptado en los navegadores, todos los dems mtodos HTTP se
envan realmente a travs de POST con el mtodo indicado en el parmetro _method .
Rails detecta y compensa automticamente esto.

3.2.2 data-url y data-params


Ciertos elementos de su pgina no se refieren a ninguna URL, pero es posible que desee
que activen llamadas Ajax. Especificar el atributo data-url junto con data-remote
disparar una llamada Ajax a la URL dada. Tambin puede especificar parmetros
adicionales a travs del atributo data-params .

Esto puede ser til para activar una accin en los check-boxes, por ejemplo:

<input type="checkbox" data-remote="true"


data-url="/update" data-params="id=10" data-method="put">

3.2.3 data-type

694
3- Helpers integrados

Tambin es posible definir explcitamente el tipo de datos Ajax mientras se realizan


solicitudes de elementos de datos remotos, a travs del atributo data-type .

3.3 Confirmaciones
Puede solicitar una confirmacin adicional del usuario agregando un atributo de
confirmacin de datos en los vnculos y los formularios. Se mostrar al usuario un dilogo
de confirmacin de JavaScript() que contiene el texto del atributo. Si el usuario decide
cancelar, la accin no tiene lugar.

La adicin de este atributo en los enlaces activar el dilogo al hacer clic y su adicin en los
formularios lo activar al enviar. Por ejemplo:

<%= link_to "Dangerous zone", dangerous_zone_path,


data: { confirm: 'Are you sure?' } %>

Esto genera:

<a href="..." data-confirm="Are you sure?">Dangerous zone</a>

El atributo tambin se permite en los botones de envo de formularios. Esto le permite


personalizar el mensaje de aviso dependiendo del botn que se activ. En este caso, no
debe tener confirmacin de datos en el formulario en s.

La confirmacin predeterminada utiliza un cuadro de dilogo de confirmacin de JavaScript,


pero puede personalizarlo escuchando el evento de confirmacin, que se dispara justo
antes de que aparezca la ventana de confirmacin para el usuario. Para cancelar esta
confirmacin predeterminada, haga que el controlador de confirmacin devuelva false .

3.4 Desactivacin automtica


Tambin es posible desactivar automticamente una entrada mientras el formulario se est
enviando utilizando el atributo data-disable-with . Esto es para evitar doble clic accidental
del usuario, lo que podra dar lugar a duplicar las solicitudes HTTP que el backend no
puede detectar como tal. El valor del atributo es el texto que se convertir en el nuevo valor
del botn en su estado deshabilitado.

Esto tambin funciona para los enlaces con el atributo data-method .

Por ejemplo:

695
3- Helpers integrados

<%= form_with(model: @article.new) do |f| %>


<%= f.submit data: { "disable-with": "Saving..." } %>
<%= end %>

Esto genera un formulario con:

<input data-disable-with="Saving..." type="submit">

696
4- Tratamiento de eventos Ajax

4- Tratamiento de eventos Ajax


Estos son los diferentes eventos que se activan cuando se trata de elementos que tienen un
atributo data-remote :

Todos los manejadores vinculados a estos eventos siempre pasan el objeto de evento como
el primer argumento. La siguiente tabla describe los parmetros adicionales que se pasan
despus del argumento del evento. Por ejemplo, si los parmetros adicionales se enumeran
como xhr , settings , entonces para acceder a ellos, se define su manejador con funcin
( event , xhr , settings )

Extra
Event name Fired
parameters

ajax:before
Antes de todo el negocio ajax, aborta si se
detiene.
Antes de enviar la solicitud, se interrumpe si se
ajax:beforeSend xhr, options
detiene.
ajax:send xhr Cuando se enva la solicitud.

ajax:success
xhr, status, Despus de la finalizacin, si la respuesta fue un
err xito.

ajax:error
xhr, status, Despus de la finalizacin, si la respuesta fue un
err error.
Despus de que la solicitud se ha completado,
ajax:complete xhr, status
no importa el resultado.
Si hay entradas de archivo non-blank, se
ajax:aborted:file elements
interrumpe si se detiene.

4.1 Eventos paralizables


Si detiene ajax:before o ajax:beforeSend devolviendo false del mtodo handler , la
solicitud Ajax nunca tendr lugar. El evento ajax:before tambin es til para manipular
datos de un formulario antes de la serializacin. El evento ajax:beforeSend tambin es til
para agregar encabezados de solicitud personalizada.

Si detiene el evento ajax:aborted:file , el comportamiento predeterminado de permitir que


el navegador enve el formulario por medios normales (es decir, la presentacin no-AJAX)
se cancelar y el formulario no se enviar en absoluto. Esto es til para implementar su
propia solucin de carga de archivos AJAX.

697
4- Tratamiento de eventos Ajax

698
5- Problemas con el servidor

5- Problemas con el servidor


Ajax no es slo client-side , usted tambin necesita hacer un cierto trabajo en el lado del
servidor para apoyarlo. A menudo, la gente hace sus solicitudes Ajax para devolver JSON
en lugar de HTML. Vamos a discutir lo que se necesita para que eso suceda.

5.1 Un ejemplo simple


Imagine que tiene una serie de usuarios que desea mostrar y proporciona un formulario en
esa misma pgina para crear un nuevo usuario. La accin index de su controlador se
parece a esto:

class UsersController < ApplicationController


def index
@users = User.all
@user = User.new
end
# ...

La vista index ( app/views/users/index.html.erb ) contiene:

<b>Users</b>

<ul id="users">
<%= render @users %>
</ul>

<br>

<%= form_with(model: @user) do |f| %>


<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>

El partial app/views/users/_user.html.erb contiene lo siguiente:

<li><%= user.name %></li>

La parte superior de la pgina index muestra los usuarios. La parte inferior proporciona un
formulario para crear un nuevo usuario.

699
5- Problemas con el servidor

El formulario inferior llamar a la accin create del UsersController . Como la opcin


remote del formulario est establecida en true , la solicitud se publicar en el

UsersController como una solicitud de Ajax, buscando JavaScript. Con el fin de atender a

esta solicitud, la accin create de su controlador se vera as:

# app/controllers/users_controller.rb
# ......
def create
@user = User.new(params[:user])

respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.js
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end

Observe el format.js en el bloque respond_to ; Que permite al controlador responder a su


solicitud de Ajax. A continuacin, tiene un archivo de vista correspondiente que genera el
cdigo JavaScript real que se enviar y ejecutar en el lado del cliente en
app/views/users/create.js.erb .

$("<%= escape_javascript(render @user) %>").appendTo("#users");

700
6- Turbolinks

6- Turbolinks
Rails se suministra con la biblioteca Turbolinks, que utiliza Ajax para acelerar el
procesamiento de pginas en la mayora de las aplicaciones.

6.1 Cmo funciona Turbolinks


Turbolinks adjunta un handler de clics a todos los <a> de la pgina. Si su navegador
admite PushState , Turbolinks realizar una solicitud Ajax para la pgina, analizar la
respuesta y reemplazar todo el <body> de la pgina con el <body> de la respuesta. A
continuacin, utilizar PushState para cambiar la URL correcta, preservando la semntica
de actualizacin y darle la URL bonita.

Lo nico que tienes que hacer para activar Turbolinks es tenerlo en tu Gemfile, y poner //=
require turbolinks en tu manifiesto de JavaScript, que usualmente es

app/assets/javascripts/application.js .

Si desea desactivar Turbolinks para ciertos enlaces, agregue un atributo data-


turbolinks="false" a la etiqueta:

<a href="..." data-turbolinks="false">No turbolinks here</a>.

6.2 Eventos de cambios de pgina


Al escribir CoffeeScript, a menudo desea realizar algn tipo de procesamiento tras la carga
de la pgina. Con jQuery, escribiras algo como esto:

$(document).ready ->
alert "page has loaded!"

Sin embargo, como Turbolinks anula el proceso normal de carga de pgina, el evento en el
que se basa no se disparar. Si tiene un cdigo similar, debe cambiar el cdigo para
hacerlo:

$(document).on "turbolinks:load", ->


alert "page has loaded!"

Para ms detalles, incluyendo otros eventos a los que puedes enlazar, consulta el README
de Turbolinks.

701
6- Turbolinks

702
7- Otros Recursos

7- Otros Recursos
Aqu hay algunos enlaces tiles para ayudarle a aprender an ms:

jquery-ujs wiki
jquery-ujs list of external articles
Rails 3 Remote Links and Forms: A Definitive Guide
Railscasts: Unobtrusive JavaScrip
Railscasts: Turbolinks

aqui vamos el sabado 29 de julio

703
XXIV- El proceso de inicializacin de Rails

XXIV- El proceso de inicializacin de Rails


Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

704
XXV- Carga Automatica y recarga de Constantes

XXV- Carga Automatica y recarga de


Constantes
Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

705
XXVI- Almacenamiento en cach con Rails: una visin general

XXVI- Almacenamiento en cach con


Rails: una visin general
Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

706
XXVII- Instrumentacin de Active Support

XXVII- Instrumentacin de Active Support


Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

707
XXVIII- Una gua para perfilar aplicaciones Rails

XXVIII- Una gua para perfilar aplicaciones


Rails
Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

708
XXIX- Uso de Rails para aplicaciones API-only

XXIX- Uso de Rails para aplicaciones API-


only
En esta gua aprender:

Qu proporciona Rails para las aplicaciones API-only


Cmo configurar Rails para iniciar sin ninguna caracterstica el navegador
Cmo decidir qu middleware desea incluir
Cmo decidir qu mdulos utilizar en su controlador

709
1 Qu es una aplicacin API?

1 Qu es una aplicacin API?


Tradicionalmente, cuando la gente deca que utilizaban Rails como "API", significaba
proporcionar una API accesible mediante programacin junto con su aplicacin web. Por
ejemplo, GitHub proporciona una API que puede utilizar desde sus propios clientes
personalizados.

Con la llegada de los frameworks client-side, ms desarrolladores estn utilizando Rails


para construir un back-end que se comparte entre su aplicacin web y otras aplicaciones
nativas.

Por ejemplo, Twitter utiliza su API pblica en su aplicacin web, que se construye como un
sitio esttico que consume recursos JSON.

En lugar de usar Rails para generar HTML que se comunica con el servidor a travs de
formularios y enlaces, muchos desarrolladores estn tratando su aplicacin web como un
cliente API entregado como HTML con JavaScript que consume una API JSON.

Esta gua cubre la creacin de una aplicacin de Rails que sirve recursos JSON a un cliente
de API, incluidos los frameworks del lado del cliente.

710
2 Por qu utilizar Rails para las API de JSON?

2 Por qu utilizar Rails para las API de


JSON?
La primera pregunta que mucha gente tiene al pensar en construir una API de JSON
usando Rails es: "Est usando Rails para renderizar JSON? No debera usar algo como
Sinatra?".

Para APIs muy simples, esto puede ser cierto. Sin embargo, incluso en aplicaciones muy
pesadas en HTML, la mayora de la lgica de una aplicacin vive fuera de la capa de la
vista.

La razn por la que la mayora de la gente utiliza Rails es que proporciona un conjunto de
valores predeterminados que permite a los desarrolladores ponerse en marcha
rpidamente, sin tener que tomar muchas decisiones triviales.

Echemos un vistazo a algunas de las cosas que Rails ofrece fuera de la caja que siguen
siendo aplicables a las aplicaciones API.

Gestionado en la capa middleware:

Reloading: Las aplicaciones de Rails soportan la recarga transparente. Esto funciona


incluso si su aplicacin se vuelve grande y reiniciar el servidor para cada solicitud se
convierte en algo inviable.
Modo de desarrollo: Las aplicaciones de Rails vienen con valores predeterminados
inteligentes para el modo de desarrollo, lo que hace que el desarrollo sea agradable sin
comprometer el rendimiento en tiempo de produccin.
Modo de prueba: Ditto modo de desarrollo.
Logging: las aplicaciones de Rails registran cada solicitud, con un nivel de verbosidad
apropiado para el modo actual. Los logs de Rails en desarrollo incluyen informacin
sobre el entorno de solicitud, consultas de bases de datos e informacin bsica de
rendimiento.
Seguridad: Rails detecta y frustra ataques de spoofing de IP y maneja firmas
criptogrficas en una forma de ataque de sincronizacin. No sabes lo que es un
ataque de spoofing IP o un ataque de tiempo? Puedes buscarlo en internet.
Parameter Parsing: Desea especificar sus parmetros como JSON en lugar de como
una cadena codificada por URL? No hay problema. Rails decodificar el JSON por
usted y lo pondr disponible en params. Quiere usar parmetros anidados de
codificacin de URL? Eso tambin funciona.
GETs condicionales: Rails maneja los encabezados de solicitud de procesamiento
condicionales GET (ETag y Last-Modified) y devuelve los encabezados de respuesta

711
2 Por qu utilizar Rails para las API de JSON?

correctos y el cdigo de estado. Todo lo que necesitas hacer es usar el stale?


Compruebe en su controlador, y Rails manejar todos los detalles del HTTP por usted.
HEAD requests: Rails convertir transparentemente las peticiones HEAD en GET, y
devolver slo los encabezados en la salida. Esto hace que HEAD funcione de manera
fiable en todas las API de Rails.

Si bien obviamente podra construir esto en trminos de middleware de rack existentes, esta
lista demuestra que el stack de middleware de Rails predeterminado proporciona mucho
valor, incluso si est "generando JSON".

Gestionado en la capa Action Pack:

Enrutamiento Resourceful: si est creando una API JSON RESTful, desea utilizar el
enrutador de Rails. El mapeo limpio y convencional de HTTP a controladores significa
no tener que pasar tiempo pensando en cmo modelar su API en trminos de HTTP.
Generacin de URLs: La otra cara del enrutamiento es la generacin de las URLs. Una
buena API basada en HTTP incluye URLs (consulte el GitHub Gist API para ver un
ejemplo).
Encabezado y Redireccin de Respuestas: head: no_content y redirect_to
user_url(current_user) vienen a mano. Claro, puede aadir manualmente los

encabezados de respuesta, pero para qu?


Almacenamiento en cach: Rails proporciona cach de pgina, accin y fragmentos. El
almacenamiento en cach de fragmentos es especialmente til cuando se construye un
objeto JSON anidado.
Autenticacin bsica, de resumen y de token: Rails viene con soporte para tres tipos de
autenticacin HTTP.
Instrumentacin: Rails tiene una API de instrumentacin que activa gestores
registrados para una variedad de eventos, como procesamiento de acciones, envo de
un archivo o datos, redireccin y consultas de base de datos. La carga til de cada
evento viene con informacin relevante (para el evento de procesamiento de acciones,
la carga incluye el controlador, la accin, los parmetros, el formato de solicitud, el
mtodo de solicitud y la ruta completa de la solicitud).
Generadores: A menudo es til generar un recurso y obtener su modelo, controlador,
stubs de prueba y rutas creadas para usted en un solo comando para ajustes
adicionales. Igual para migraciones y otras.
Plugins: Muchas bibliotecas de terceros vienen con soporte para Rails que reducen o
eliminan el costo de configurar y pegar la biblioteca y el framework web. Esto incluye
cosas como reemplazar generadores predeterminados, agregar tareas de Rake y
honrar las opciones de Rails (como el registrador y el back-end de cach).

712
2 Por qu utilizar Rails para las API de JSON?

Por supuesto, el proceso de arranque de Rails tambin encola todos los componentes
registrados. Por ejemplo, el proceso de arranque de Rails utiliza su archivo
config/database.yml al configurar Active Record.

La versin corta es: es posible que no haya pensado en qu partes de Rails siguen siendo
aplicables incluso si se elimina la capa de vista, pero la respuesta resulta ser la mayor
parte.

713
3- La configuracin bsica

3- La configuracin bsica
Si est creando una aplicacin de Rails que ser un servidor API en primer lugar, puede
comenzar con un subconjunto ms limitado de Rails y agregar funciones segn sea
necesario.

3.1 Creacin de una nueva aplicacin


Puede generar una nueva aplicacin de Rails api:

$ rails new my_api --api

Esto har tres cosas principales por usted:

Configura su aplicacin para comenzar con un conjunto de middleware ms limitado de


lo normal. Especficamente, no incluir ningn middleware que resulte til para
aplicaciones de navegador (como el soporte de cookies) de forma predeterminada.
Hace que ApplicationController herede de ActionController::API en lugar de
ActionController::Base . Al igual que con el middleware, esto dejar fuera cualquier

mdulo de Action Controller que provea funcionalidades usadas principalmente por


aplicaciones de navegador.
Configure los generadores para omitir la generacin de vistas, helpers y resources al
generar un nuevo recurso.

3.2 Cambio de una aplicacin existente


Si desea tomar una aplicacin existente y convertirla en una API, lea los pasos siguientes.

En config/application.rb , agregue la siguiente lnea en la parte superior de la definicin


de la clase Application :

config.api_only = true

En config/environments/development.rb , configure config.debug_exception_response_format


para configurar el formato utilizado en las respuestas cuando se producen errores en el
modo de desarrollo.

Para renderizar una pgina HTML con informacin de depuracin, utilice el valor :default .

714
3- La configuracin bsica

config.debug_exception_response_format = :default

Para hacer que la informacin de depuracin mantenga el formato de respuesta, utilice el


valor :api .

config.debug_exception_response_format = :api

De forma predeterminada, config.debug_exception_response_format se establece en :api ,


cuando config.api_only se establece en true .

Por ltimo, dentro de app/controllers/application_controller.rb , en lugar de:

class ApplicationController < ActionController::Base


end

hacer:

class ApplicationController < ActionController::API


end

715
4- Eleccin de middleware

4- Eleccin de middleware
Una aplicacin de API viene con el siguiente middleware de forma predeterminada:

Rack::Sendfile

ActionDispatch::Static

ActionDispatch::Executor

ActiveSupport::Cache::Strategy::LocalCache::Middleware

Rack::Runtime

ActionDispatch::RequestId

ActionDispatch::RemoteIp

Rails::Rack::Logger

ActionDispatch::ShowExceptions

ActionDispatch::DebugExceptions

ActionDispatch::Reloader

ActionDispatch::Callbacks

ActiveRecord::Migration::CheckPending

Rack::Head

Rack::ConditionalGet

Rack::ETag

MyApi::Application::Routes

Consulte la seccin middleware interno de la gua Rack para obtener ms informacin


sobre ellos.

Otros complementos, incluido Active Record, pueden agregar middleware adicional. En


general, estos middleware son agnsticos con el tipo de aplicacin que est construyendo y
tienen sentido en una aplicacin de Rails de slo API.

Puede obtener una lista de todos los middleware en su aplicacin a travs de:

$ rails middleware

4.1 Uso del middleware de cach


De forma predeterminada, Rails agregar un middleware que proporciona un almacn de
cach basado en la configuracin de su aplicacin ( memcache de forma predeterminada).
Esto significa que el cach HTTP incorporado depender de l.

Por ejemplo, utilizando el mtodo stale? :

716
4- Eleccin de middleware

def show
@post = Post.find(params[:id])

if stale?(last_modified: @post.updated_at)
render json: @post
end
end

La llamada a stale? comparar el encabezado If-Modified-Since en la solicitud con


@post.updated_at . Si el encabezado es ms reciente que el ltimo modificado, esta accin

devolver una respuesta "304 no modificada". De lo contrario, mostrar la respuesta e


incluir un encabezado Last-Modified en l.

Normalmente, este mecanismo se utiliza en una base por cliente. El middleware de cach
nos permite compartir este mecanismo de almacenamiento en cach entre los clientes.
Podemos habilitar el almacenamiento en cach de varios clientes en la llamada para
stale? :

def show
@post = Post.find(params[:id])

if stale?(last_modified: @post.updated_at, public: true)


render json: @post
end
end

Esto significa que el middleware de cach almacenar el valor Last-Modified para una
URL en la cach de Rails y agregar un encabezado If-Modified-Since a cualquier
solicitud de entrada posterior para la misma URL.

Piense en ello como el almacenamiento en cach de pginas mediante la semntica HTTP.

4.2 Uso de Rack::Sendfile


Cuando utiliza el mtodo send_file dentro de un controlador de Rails, establece el
encabezado X-Sendfile . Rack::Sendfile es responsable de enviar el archivo.

Si su servidor front-end admite el envo acelerado de archivos, Rack::Sendfile descargar


el trabajo de envo de archivos real al servidor de aplicaciones para el usuario.

Puede configurar el nombre del encabezado que utiliza su servidor front-end con este fin
utilizando config.action_dispatch.x_sendfile_header en el archivo de configuracin del
entorno apropiado.

717
4- Eleccin de middleware

Puede obtener ms informacin sobre cmo utilizar Rack::Sendfile con front-ends


populares en la documentacin Rack::Sendfile .

Estos son algunos valores para esta cabecera para algunos servidores populares, una vez
que estos servidores estn configurados para admitir el envo acelerado de archivos:

# Apache and lighttpd


config.action_dispatch.x_sendfile_header = "X-Sendfile"

# Nginx
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"

Asegrese de configurar su servidor para que admita estas opciones siguiendo las
instrucciones de la documentacin de Rack::Sendfile .

4.3 Uso de ActionDispatch::Request


ActionDispatch::Request#Params tomar los parmetros del cliente en el formato JSON y los

har disponibles en su controlador dentro de los parmetros.

Para usar esto, su cliente necesitar hacer una peticin con parmetros codificados por
JSON y especificar el Content-Type como application/json .

Aqu hay un ejemplo en jQuery:

jQuery.ajax({
type: 'POST',
url: '/people',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
success: function(json) { }
});

ActionDispatch::Request ver el Content-Type y sus parmetros sern:

{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }

4.4 Otros middleware


Rails se enva con un nmero de otros middleware que puede que desee utilizar en una
aplicacin de la API, especialmente si uno de los clientes de su API es el navegador:

Rack::MethodOverride

718
4- Eleccin de middleware

ActionDispatch::Cookies

ActionDispatch::Flash

Para el manejo de sesiones:


ActionDispatch::Session::CacheStore

ActionDispatch::Session::CookieStore

ActionDispatch::Session::MemCacheStore

Cualquiera de estos middleware se puede agregar a travs de:

config.middleware.use Rack::MethodOverride

4.5 Eliminacin del middleware


Si no desea utilizar un middleware que se incluye de forma predeterminada en el conjunto
de middleware solo API, puede quitarlo con:

config.middleware.delete ::Rack::Sendfile

Tenga en cuenta que la eliminacin de estos middlewares eliminar la compatibilidad con


ciertas funciones de Action Controller.

719
5- Seleccin de los mdulos del controlador

5- Seleccin de los mdulos del


controlador
Una aplicacin de API (con ActionController::API ) viene con los siguientes mdulos de
controladores de forma predeterminada:

ActionController::UrlFor : Hace que url_for y ayudantes similares estn disponibles.

ActionController::Redirecting : Soporte a redirect_to .

AbstractController::Rendering y ActionController::ApiRendering : Soporte bsico para

renderizado.
ActionController::Renderers::All: Soporte para renderizar :json y amigos.

ActionController::ConditionalGet : Sporte para stale? .

ActionController::BasicImplicitRender : Se asegura de devolver una respuesta vaca,

si no hay una explcita


ActionController::StrongParameters: Soporte para los parmetros de lista blanca en

combinacin con la asignacin de masa del Active Model.


ActionController::ForceSSL : Soporte para force_ssl .

ActionController::DataStreaming : Soporte para send_file y send_data .

AbstractController::Callbacks : Soporte para before_action y helpers similares.

ActionController::Rescue : Soporte para rescue_from .

ActionController::Instrumentation : Soporte para los hooks de instrumentacin

definidos por Action Controller (consulte la gua de instrumentacin para obtener ms


informacin al respecto).
ActionController::ParamsWrapper : Envuelve el hash de parmetros en un hash

anidado, para que no tenga que especificar elementos raz que envan peticiones
POST por ejemplo.
ActionController::Head : Soporte para devolver una respuesta sin contenido, solo

encabezados

Otros complementos pueden agregar mdulos adicionales. Puede obtener una lista de
todos los mdulos incluidos en ActionController::API en la consola de rails:

720
5- Seleccin de los mdulos del controlador

$ bin/rails c
>> ActionController::API.ancestors - ActionController::Metal.ancestors
=> [ActionController::API,
ActiveRecord::Railties::ControllerRuntime,
ActionDispatch::Routing::RouteSet::MountedHelpers,
ActionController::ParamsWrapper,
... ,
AbstractController::Rendering,
ActionView::ViewPaths]

5.1 Adicin de otros mdulos


Todos los mdulos de Action Controller conocen sus mdulos dependientes, por lo que
puede sentirse libre de incluir cualquier mdulo en sus controladores, y todas las
dependencias sern incluidas y configuradas.

Algunos mdulos comunes que puede agregar:

AbstractController::Translation : Soporte para los mtodos de localizacin y

traduccin de l y t .
ActionController::HttpAuthentication::Basic (or Digest or Token): Soporte para la

autenticacin HTTP bsica, digest o token.


ActionView::Layouts : Soporte para diseos al procesar.

ActionController::MimeResponds : Soporte para respond_to .

ActionController::Cookies : Soporte para cookies, que incluye soporte para cookies

firmadas y cifradas. Esto requiere el middleware de cookies.

El mejor lugar para agregar un mdulo est en su ApplicationController , pero tambin


puede agregar mdulos a controladores individuales.

721
XXX- Descripcin de Action Cable

XXX- Descripcin de Action Cable


Esta seccin no ha sido traducida. Se har una vez terminadas todas las dems
traducciones

722

También podría gustarte