Sunteți pe pagina 1din 310

Versión 0.

583
1) Temario:
1) Temario:
2) Motivación

¿Qué es Rails?
¿Por qué utilizar ruby on rails para mi aplicación?
Deventajas de utilizar Ruby on Rails
¿Cómo está estructurado este libro?

Sección I
3) Instalando las herramientas

OSX
Linux
En Linux y OSX

4) Estructura de un proyecto en rails


5) Creando nuestra primera página web con rails

Consola secuestrada.
Creando nuestra primera página

Introducción a controllers
Errores de novato
Introducción a las vistas
Cómo convertir esta página en la página principal

Creando nuestra segunda página

Creando la ruta
Creando un método en el controller
Creando la vista.
Agregando un link a una página interna

6) Agregando imágenes a nuestra primera página

Los Assets
El asset path

Incorporando imágenes

El método asset_path
El método image_tag

7) Nuestra primera página con layout.


El Layout
Incorporando CSS
Incorporando Javascript
Incorporando imágenes (u otros assets) dentro del CSS y del JS

¿Qué es la precompilación de assets?


El entorno
La carpeta assets de vendor
Agregando una carpeta nueva al asset_path
Verificando el asset_path
Incorporando tipografías locales

Consecuencias de Sprockets
Incorporando bootstrap.

CDN
Descargando los CSS
Glyphicons

Vistas parciales

Cargando el navbar de bootstrap como una vista parcial

Cargando un layout distinto


Desafío

8) Creando un formulario de registro

El tag form

Revisando los parámetros enviados

Guardando los datos

Creando el primer modelo


Corriendo las migraciones
Rails console

Creando un usuario
Leyendo los usuarios desde la base de datos

Guardando los usuarios dentro de rails


Cambiando la página de inicio
Desafío

9) Formulario 2.0

Creando un proyecto nuevo


Enviando un formulario por POST
MVC in a nutshell
Form y action
El método form_tag
Mostrando los resultados del formulario
Guardando los resultados
Redirect_to
Variables flashs

Mostrando todos los usuarios en la base de datos


Desafío

10) El gemfile
Sección II
11) Deployment en Heroku

Tipos de hosting

Servidor propio
Hosting clásico
VPS
PAAS

Instalando el Toolbelt de heroku


Claves SSH
Repositiorio GIT

Configurando GIT
Creando el repositorio git

Subiendo la aplicación a Heroku

Creando el proyecto en heroku


Setup del proyecto para deployment
Subiendo los cambios a producción
Razones por las que el paso de deployment puede fallar
Revisando errores
Sobre la precompilación de assets
Migrando la base de datos de producción
Cambiando el nombre de la aplicación en Heroku

Desafío

12) Configurando el dominio (o subdominio)

Configurando dominios cuyas autoridades no soportan redireccionamiento tipo CName


Tips de mantención
Entrar a la consola del proyecto en producción
Ver los últimos logs
Dejar una consola con los logs corriendo
Descargando la base de datos de heroku

Desafio

Sección III
13) SQL

PostgreSQL
Instalando PostgreSQL en OSX
Instalando PostgreSQL en Linux
Entrando a PSQL

Insensible a las mayúsculas


Creando usuarios
Cambiando la clave
Dando acceso de superusuario
Listando a todos los usuarios creados en la base de datos.
Creando una base de datos
Listando una base de datos.
Botando una base de datos
Conectándose a una base de datos
Manejo de tablas
Introducción a tipos de datos.
Creando una tabla

Manipulando valores de una tabla

Insertando valores
Leyendo valores de una tabla.
Actualizando valores de una tabla
Borrando datos de una tabla

Modificando una tabla

Agregando una columna


Removiendo una columna

Constraints

Evitando valores nulos


El constraint Unique
La clave primaria
Creando una tabla con primary key

Ordenando los resultados.

Mostrando los resultados ordenados por edad


Mostrando los resultados ordenados por nombre alfabéticamente pero en reverso.
Limitando la cantidad de resultados
Conteo
Select distinct

Distinct y count
Guía de ejercicios

Peliculas

CRUDS
Sorting
Conteo

Productos

14) SQL con más de una tabla

Integridad referencial

Ambiguedad en los nombres

Joins

Tipos de joins
Left Outer Join
Right Outer Join
Full Outer Join

Tipos de relaciones

Relaciones de uno a uno


Relaciones de 1 a n
Relaciones de n a n

La clave foránea
Ejercicios

Shopping
movieDB

Relaciones n a n
Exportando datos

14) Modelando con SQL


Modelando un blog
Ejercicio
Modelando Tinder
Ejercicios de diagrama
Ejercicios de SQL
Modelando un carro de compras
Ejercicio

Sección IV: Backend


15) Rails con PSQL

Instalando postgre en un proyecto existente


Creando un proyecto con postgreSQL
El error de socket

16) Los modelos en rails

Los modelos como mapas a los objetos (ORM)


Creando modelos

Convención de nombres
Creando un modelo vacío
Creando un modelo con diversos campos

Migraciones
El archivo schema.rb
Creando migraciones.

Agregar una columna


Remover una columna
Generadores mágicos
Revirtiendo una migraciones
Enmendando un error.
Cuidado con los branches
Botón de autodestrucción
Destruir un modelo
Agregando un campo extra.

Con helper
Sin helper

Correr las migraciones

Revisar las migración generada


Probar un modelo
Creando una FK con índice.
Getters y setters
El archivo seed
Atributos virtuales
Ejercicio

17) Reglas del negocio

Protegiendo la integridad de los datos


Validando la presencia de un campo
Validando que el campo sea único
Validaciones custom
Valores iniciales
Callbacks
Ejercicios

18) Relaciones entre modelos

Relaciones 1 a 1

Borrando una relación en cascada.

Graficando las relaciones


Relaciones de 1 a n

El método build
Múltiples relaciones de 1 a n

Has many :through

Relaciones n a n

19) Testing unitario de modelos

Los fixtures
Estructura de un test de modelo

Formas de escribir un test


El método assert

Corriendo tests
Cargando los fixtures en los tests

20) Creando un Blog

Construyendo el modelo
Construyendo los tests

Test para modelo sin campo presente


Test para modelo con campo único
Test para probar la relación
Test de pertenencia
Test para diferencias de tiempo

Desafios

21) MVC

Arquitectura REST
Scaffold
Strong parameters

Nota sobre las migraciones

Probando los strong params

22) El archivo de rutas a fondo

Introducción al archivo de rutas

El archivo routes.rb

Rake routes

Rutas sin parámetros

Ruta con controller

Rake routes a fondo

Prefix: El prefijo
Verb, el verbo
URI pattern
URI Controller#Action

El archivo routes a fondo

ruta sin controller


Cambiando el prefijo
Crear una ruta con un parámetro
Rutas anidadas
Crear una ruta que recibe dos parámetro
Query strings

Resources

member vs collection

member
Collection

23) Rutas anidadadas y métodos REST anidados


Introducción a rutas anidadas
Creando un scaffold anidado
Testeando una ruta anidada
Obteniendo y mostrando los resultados.
El detalle del tweet
Formulario para un nuevo tweet
Creando el método create

Manejando el redireccionamiento del create.

22) Relaciones n a n

Implementando relaciones has_and_belongs_to_many

Borrando la asociación:
Testeando la relación

Implementando relaciones con has_many :through

Has_many through vs Has_and_belongs_to_many


Ejercicio practico de uso

24) Haciendo un cloudtag

Creando datos para la nube de tags


Creando el controller y cargando los datos para hacer la nube de tags
Creando la vista con la nube de tags

25) Devise

Empezando con devise


Creando el modelo de devise
Ingreso, registro y salida
login or logout
El objeto current_user
Modificando los formularios de ingresar y registro
Agregando un dato al modelo de usuarios
Bloqueando el acceso

26) Devise avanzado

Agregando el rol de usuario

Agregando el campo
Enums al rescate

Testeando los accesos


Códigos completos:
Tests
Pages Controller
Fixture de usuario

Generando los controllers de devise


Cambiando la página despues de registrarse
Cambiando la página después de ingresar
Configurando action_mailer para enviar correos con gmail
Protegiendo las claves con dot-env
Configurando Heroku para dot-env
Quiz

27) Autorización con CanCanCan

¿Cuándo no utilizar CanCanCan?


Instalando CanCanCan
El árbol de habilidades
Revisión de habilidades
Bloqueo
Manejo de conexiones no autorizadas
Testeando las habilidades
Habilidades basadas en la propiedad
Habilidades en recursos anidados

28) Polimorfismo
29) Subiendo archivos con carrirewave

Instalando carrierwave
Generando el uploader
Probando desde rails console.
Creando una formulario con archivos

30) Amazon S3

Configuración para la gema de carrierwave-aws


IAM
Agregando las claves de entorno a heroku

31) Optimización

n+1 queries en ruby on rails


¿Cuándo ocurre el problema de n+1 queries ?

32) Javascript, jQuery y Turbolinks

Como organizar nuestro javascript:


Turbolinks
Ejemplos de uso

33) Manejo de gráficos

Haciendo los queries


Generando los gráficos

34) Fullcalendar

Agregando el plugin
Cargando el fullcalendar

35) Envió de correos con Action Mailer

Intro
Creando nuestro mailer
Modificando el mailer y como probarlo

Crear un método para dar la bienvenida a quien se registra en nuestra pagina

Enviando el correo usando ActionMailer y Gmail


ActionMailer y Devise

Primera Opción: creando el controlador de devise para el usuario


Segunda Opción: modificando el modelo de usuario

ActionMailer y ActiveJob: deliver_now, deliver_later?

ActiveJob y deliver_later

36) Testings automatizado con Guard

Instalar Guard en nuestro proyecto


Configurar Guard
Configurar Minitest-Reporters
Correr Guard para automatizar el testing

37) Rails y Nginx con Passenger en Ubuntu: Preparando nuestro entorno de producción
(DigitalOcean).

Introducción
Acerca de esta guía.
Convenciones.
Paso 0 – Como acceder a nuestro servidor

Invalid Locale Warning

Configurar el timezone en nuestro servidor


Acerca del usuario Root
¿Cómo ingresar sin tener que ingresar la clave cada vez que nos queremos conectar a nuestro
servidor?
Paso 1 – Instalación de RVM
Paso 2 – Instalación de Ruby y de Rails
Paso 3 – Instalación de Nginx y Passenger

Ahora que tenemos el servidor preparado podemos instalar Nginx con Passenger:

Paso 4 – Habilitando Passenger en Nginx


Paso 5 – Instalación y configuración de Postgres.

Instalación
Creación de un usuario en postgres
Creación de la base de datos

Paso 6 – Crear un Server Block


Paso 7 – Ultimos detalles
Extras
38) Deployment con Capistrano.

Introducción

Acerca de esta guía.


Convenciones.

Paso 0 – La Aplicación
Paso 1 – Añadir Capistrano a nuestra app.
Paso 2 – Preparación de nuestro proyecto
Paso 3 – Tareas personalizadas
Paso 4 – Conectando el servidor con el repositorio

Añadir la llave en Github


Añadir la llave en Bitbucket

Paso 5 – Deploy! Cómo se hace? y qué hace?

Ahora que sabemos cómo y qué hace Capistrano haremos un deploy de nuestra app
2) Motivación

¿Qué es Rails?
Rails en un framework especializado en la construcción de aplicaciones web, su principales ventajas es
que es sólido, rápido y escalable.

La versión actual de Ruby on Rails es 4.2

Rails no es lo mismo que Ruby, Ruby es un lenguaje de programación orientado objetos y Rails es un
conjunto de herramientas construídas en Ruby para la construcción de aplicaciones web.

¿Por qué utilizar ruby on rails para mi aplicación?


Ruby on Rails es impresionantemente efectivo para la construcción de aplicaciones web, con sólo un par
de líneas de código puedes tener una mini aplicación web funcionando, además traer un conjunto de
herramientas permite construir aplicaciones complejas y escalarlas a niveles enterprise con equipos de
programación relativamente pequeños.

Por otro lado también es sorprendemente rápido para la construcción de prototipos, para una persona
entendida en el tema es posible crear un prototipo funcional de un proyecto y dejarlo subido en internet
en sólo un par de horas.

Deventajas de utilizar Ruby on Rails


Es un framework complejo y ruby no es tan masivo como otros lenguajes como PHP por lo que cuesta un
poco más encontrar programadores, sin embargo tiene una muy buena comunidad y es fácil encontrar
documentación y componentes para todo lo que necesitas, además día a día el mundo de la informática
a empezado a reconocer las ventajas de ruby on rails y ha empezado a migrar a este maravilloso
framework.

Otro aspecto a considerar es la curva de aprendizaje de este framework que es bastante dura. Ruby on
Rails tiene muchas componentes que vamos a dicutir a lo largo de este libro y muchas cosas dan la
impresión de funcionan solas y por arte de magia y es por eso que es difícil de aprender pero muy
poderoso cuando se le domina.

Finalmente está el tema del rendimiento, Ruby on Rails es un framework con muchas componentes por lo
mismo tiene un gran footprint, o sea que es pesado, y eso conlleva a que se necesitan servidores más
potentes para atender la misma cantidad de usuarios, por otro lado es bastante más sencillo construir
aplicaciones que en otros framework.
Tanto Ruby como Ruby on Rails están pensandos en la felicidad del programador en lugar de la eficiencia
del programa.

¿Cómo está estructurado este libro?


Este libro cumple dos funciones, en primer lugar es una guía práctica de aprendizaje y en segundo
contiene los pasos necesarios para construir diversos proyectos tipos de los cuales se pueden sacar
varias ideas para emprendimientos.

Este libro se compone en 5 grandes secciones.

Front-end con rails:

Donde aprenderemos todo lo necesario para crear páginas web con rails, y a manejar correctamente el
asset_path, crear pequeños formularios y entender los conceptos básicos de MVC.

Deployment con heroku:

Donde aprenderemos a subir aplicaciones a la web, y aprenderemos a configurar una página .com o
cualquier otro tipo de dominio.

SQL y modelado de bases de datos

Donde aprenderemos a manejar bases de datos en la suficiente medida para entender como funciona
Rails, como realiza las consultas y como optimizarlas.

Back-end con Rails

Donde aprenderemos a construir aplicaciones web con bases de datos y autenticación de usaurios con
controles de acceso y sistemas de pago.

Deployment con Amazon y VPS

Finalmente aprenderemos como subir nuestra aplicación a entornos enterprise como Amazon y VPS y
configurar nuestros propios servidores ocupando NginX.
Sección I
Front-end

No todo lo que brilla es oro


3) Instalando las herramientas

OSX
Uitlizando la versión de ruby que viene instalada de fábrica instalaremos Homebrew, un administrador de
paquetes para OSX.

Para eso abriremos la terminal y pegaremos lo siguiente:

1 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install

Linux
No es necesario instalar ningún administrador de paquetes, al mayoría de las distribuciones viene con
alguno incluído, ubuntu ocupa apt-get

En Linux y OSX
Ahora instalaremos RVM, un programa que nos permite tener instalada diversas versiones de ruby y
actualizarlo sin dolor, existen otros como Rbenv y chruby, las diferencias son menores y sólo afectarán en
la última parte de deployment así que para uniformar el proceso recomendamos a todos instalar RVM.

Correr este comando sólo en OSX

1 brew install rvm

Correr este comando sólo en Linux

1 apt-get install rvm

Es importante leer siempre los logs de la instalación para asegurarnos que haya terminado exitosamente
o poner los comandos que nos pida en caso de que no.

Con RVM instalado y los logs leídos (y asegurado de que todo terminó OK) ahora hay que reiniciar la
terminal, luego podremos correr:
1 rvm install 2.2.3

Nuevamente esperaremos que termine de correr, esto puede demorar ya que necesita compilar los
binarios de ruby. Una vez terminado el proceso debemos verificar que quedó instalado, esto lo podemos
hacer con el comando:

1 rvm list

Eso nos muestra todas las versiones de ruby instaladas con RVM, de las cuales podemos escoger una
con:

1 rvm use 2.2.3

O podemos establecer nuestra versión por defecto con:

1 rvm --default use 2.2.3

Para asegurarnos que estamos sobre la versión correcta debemos correr el comando:

1 ruby -v

Finalmente instalaremos rails con:

1 gem install rails

Las gemas que se instalan dependen de la versión de ruby, si cambiamos la versión tendremos que
instalar rails nuevamente.

Rails trabaja por defecto con SQLite es cuál es suficiente para construir prototipos pero queda corto a la
hora de construir aplicaciones potentes, durante el capítulo de Heroku veremos como instalar la gema de
postgreSQL

Preguntas

1. ¿Qué es RVM?
2. ¿Cómo mostramos todas las versiones que tenemos instaladas de ruby?
3. ¿Cómo cambiamos la versión que estamos ocupando de ruby?
4. ¿Con que motor de base de datos trabaja rails por defecto?
4) Estructura de un proyecto en rails
Archivo Para qué sirve

Gemfile Administrar librerías depencias del proyecto

Gemfile.lock Muestra todas las dependencias, generado a partir del Gemfile

README.rdoc Para documentar el proyecto

Rakefile Para crear tareas rake

app Toda la aplicación, assets, vistas, controllers y modelos van dentro

bin Contiene los binstubs, los cuales son wrappers sobre gemas ejecutables

config Directorio con archivos de configuración de rails, los entornos y la base de datos

config.ru Contiene la configuración de Rack

Continee la información de la base de datos, el archivo de sqlite3 y las


db
migraciones

lib Modulos para la aplicación

log Registro de los errores

public Assets públicos

test Scripts para tests automatizados

tmp Para archivos temporales necearios durante la ejecución de rails

vendor Assets de terceros


5) Creando nuestra primera página web con
rails

Objetivos

1. Aprender a crear un proyecto en rails desde cero


2. Definir la página de inicio de un proyecto rails
3. Conocer el lenguajes de templates erb
4. Crear accesos a nuevas páginas

Rails es un framework MVC, esto quiere decir que divide la información a lo largo de 3 capas principales,
el modelo, la vista y el controlador.

En este capítulo abordaremos las componentes de vista y controlador creando una página estática con
Ruby on Rails.

Para empezar desde el terminal crearemos nuestro primer proyecto una simple página estática pero
sobre rails.

1 rails new landing1

Luego entraremos a la carpeta del proyecto y corremos el servidor.

1 cd landing1
2 rails s

Para verificar que todo funcione abriremos el navegador y entramos a la página localhost:3000
Consola secuestrada.
Mientras esté corriendo el servidor en la consola, esta estará secuestrada.
Esto quiere decir que no la podemos seguir utilizando para lanzar comandos y por lo tanto tenemos que
abrir un tab nuevo para poder seguir trabajando.

Para terminar el servidor, o sea cerrarlo sin cerrar el tab podemos utilizar ctrl + c y eso terminará el
programa devolviendonos el control para poder ejecutar comandos.

Creando nuestra primera página

Introducción a controllers

Para empezar con rails vamos a partir creando una página estática, para eso vamos a crear un controller
que por ahora lo entenderemos como un grupo de páginas.

Por ejemplo, si creamos el controller grupo1, nuestras páginas serían:

localhost:3000/grupo1/inicio
localhost:3000/grupo1/formulario

Tanto los controllers como los modelos y vistas (los cuales veremos despúes) se pueden crear
manualmente o con el generador de rails.

Para crear un controller con el generador tenemos que escribir:

1 rails g controller nombre_grupo pag1 pag2 pag3 ...

Por ejemplo si queremos crear únicamente la página inicial (index), lo hacemos de la siguiente forma:

1 rails g controller pages index

Es convención de rails que los controller siempre tengan un nombre plural, y hace sentido pensando en
que son un grupo de páginas.

Obtendremos como resultado en el mismo terminal:


create app/controllers/pages_controller.rb
route get 'pages/index'
invoke erb
create app/views/pages
create app/views/pages/index.html.erb
invoke test_unit
create test/controllers/pages_controller_test.rb
invoke helper
create app/helpers/pages_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/pages.coffee
invoke scss
create app/assets/stylesheets/pages.scss

Esto nos muestra todos los archivos creados y modificados, por ahora nos interesan sólo dos, el archivo
de rutas route get 'pages/index' y la línea que dice donde se creó la vista
app/views/pages/index.html.erb

Antes de proceder a explicar las vistas y el archivo de rutas, veamos lo creado, para poder hacerlo,
podemos acceder a la página que creada a través de la URL:
http://localhost:3000/pages/index

Esto sólo funcionará si el servidor está corriendo (hay una consola con rails s y si no hay errores en
el código.

Errores de novato
1) No tener el server corriendo

Revisar rails s

2) No haber cargado el server en la carpeta correcta

Volver a la terminal ver donde está corriendo el server

3) Haber creado el controller con nombre en singular

Podemos destruir el controller con rails destroy controller pages

Introducción a las vistas

Actualmente sólo tenemos un título y un párrafo pero aquí es donde podemos empezar a agregar nuestro
contenido.

Para eso podemos abrir el archivo app/views/pages/index.html.erb y modificar el HTML,


incluso podemos escribir ruby, esto lo podemos hacer gracias al lenguaje de template de ruby llamado
erb, por eso el formato del archivo es .html.erb

Para escribir ruby tenemos que hacerlo dentro de las expresiones en <%= %> y <% %>, la primera
expresión se ocupa para mostrar el contenido, el segundo sirve para evaluar, miremos los siguientes
ejemplos:

1 <% a = 2 %>
2 <%= a %>
3 <%= b = 2 %>

Esto mostraría dentro del navegador 2 2 el primer dos correspondiente al valor de 2, y el segundo al
valor de b, pero la primera expresión no se muestre porque <% %> no incluye el signo igual =

¿Esto quiere decir que podemos enviar ruby al navegador del cliente?

No, todo el contenido del archivo será transformado a HTML por rails antes de enviarlo, por lo
que el navegador jamás ve nada relacionado con rails.

Cómo convertir esta página en la página principal


Si con nuestro navegador entramos a la dirección localhost:3000 veremos el aviso de rails, pero
que pasa si queremos que la página que acabamos de crear sea la página principal, para eso tenemos
que modificar el archivo config/routes.rb el cual es el encargado de manejar todos los
enrutamientos.

Dentro del archivo config/routes.rb encontraremos una línea comentada que dice:

1 # root 'welcome#index

Esta línea es un recordatorio de rails que nos dice como podemos definir una página inicial, para utilizarla
vamos a descomentarla (remover el ‘#’) y luego apuntar al controller y la página respectiva, o sea en
nuestro caso debería decir:

1 # root 'pages#index'

Si ahora entramos a localhost:3000 veremos la misma página.

Creando nuestra segunda página


Ya no podemos ocupar le gnerador de controllers puesto que ya tenemos el controller generado y no
queremos sobreescribir los archivos.

Una solución sería ccrear un segundo controller pero no es necesario crear un controller por página.

Para crear una seguna página vamos a:

1. Crear una ruta a la pagína


2. Crear un método en el controller
3. Crear la vista.
4. Agregar el link a la página nueva. (esta parte no es estrictamente necesesaria)

Para nuestra prueba vamos a crear la página about (nosotros)

Creando la ruta

Para agregar la ruta nueva debemos abrir el mismo archivo de rutas que abrimos anteriormente
config/routes.rb dentro de el agregaremos un get, sin los comentarios que vienen por defecto el
archivo de rutas, este se debería ver así:
1 Rails.application.routes.draw do
2 get 'pages/index'
3 get 'pages/about'
4 root 'pages#index'
5 end

Existe un comando que nos permite verificar las rutas creadas, este comando se llama rake routes
y lo tenemos que correr en alguna terminal que no esté secuestrada dentro de la carpeta del proyecto.

Al realizar rake routes, obtendremos:

Prefix Verb URI Pattern Controller#Action


pages_index GET /pages/index(.:format) pages#index
pages_about GET /pages/about(.:format) pages#about
root GET / pages#index

Más adelante de este libro analizaremos más a fondo esta información, por ahora sabemos que es
correcta porque se incluyó dentro del columna que dice URI Patten la página /pages/about , y con
esto terminamos el paso de agregar la ruta de la página.

Si nos hubiésemos saltado este paso, (o comentamos el get ‘pages/about’ que pusismos en el archivo
config/routes.rb e intentamos entrar a la página obtendríamos este error.

Cada vez que tengamos un error de rutas, sabemos que el archivo culpable es routes.rb.

Ahora si intentamos saltarnos el paso 2, y entrar directamente a la página localhost:3000/pages/about


veremos el siguiente error.
The action ‘about’ could not be found for PagesController

O sea nos fala la acción.

Creando un método en el controller

Anteriormente habíamos hablado del controller, pero asumimos que funcionaba de forma mágica, ahora
vamos a tener que modificarlo para agregar una página nueva, para eso abriremos el archivo
app/controllers/pages_controller en el editor, y veremos:

1 class PagesController < ApplicationController


2 def index
3 end

El PagesController, es un controller que hereda de ApplicationController, dentro de el cada método llama


a su respectiva vista, pero lo hace de forma implícita, esta vista se encuentra dentro de
views/pages/index.html.erb

Entonces si ya tenemos una ruta para about el paso siguiente es agregar un método (que es lo mismo
que una cción) dentro de la clase:

1 def about
2 end

Creando la vista.
Con la ruta y el método del controller creado ahora procederemos a crear el archivo
views/pages/about.html.erb y dentro de el podemos agregar lo que queramos.

Finalmente podemos acceder a la página nueva si entramos localhost:3000/pages/about

Agregando un link a una página interna

Rails crea variables para todas nuestras páginas internas, para poder ver como se llaman esas variables
tenemos que ocupar rake routes

Prefix Verb URI Pattern Controller#Action


pages_index GET /pages/index(.:format) pages#index
pages_about GET /pages/about(.:format) pages#about
root GET / pages#index

La columna que dice prefix son los nombres de la variable, bueno casi, es un prefijo porque hay dos,
aquellas que terminan en path y contienen la ruta relativa, y aquellas que terminan en url que contienen la
ruta absoluta.

Si queremos ver el link tenemos que recordar que la variable está en ruby, así que tenemos que imprimirla
en la vista ocupando la zanahoria <%=

1 <%= pages_about_path %>

Otra forma de hacer lo mismo es ocupando el helper link_to de la siguiente forma:

1 link_to "abouts", pages_about_path

El helper se transformará en un <a href=""> apuntando a la página respectiva.

y dentro de la vista de abouts_us podemos agregar un link a la página principal de la siguiente forma:

1 link_to "home", index_about_path

Preguntas

1. ¿Si tenemos un error del tipo el método no existe, donde está el problema?
2. ¿Si tenemos un error del tipo el template falta, donde está el error?
3. ¿Si tenemos un error del tipo la ruta no existe, donde está el error?
4. ¿Qué quiere decir agregar una acción?, en qué archivo va?
5. ¿Para qué sirve el formato .erb?
6. ¿Cuál es la diferencia en <%= %> <% %>?
7. ¿Qué tipo de navegador se necesita para ver un proyecto con ruby on rails?
8. ¿Cuántas páginas puede tener un controller?
9. ¿Qué comando muestra todas las rutas que existen en rails?
10. ¿Cómo se crea un controller con tres páginas?
11. ¿Cómo se destruye un controller?
12. ¿Cuál es la diferencia entre pages_index_path y pages_index_url?
13. ¿Qué hace el método link_to?
6) Agregando imágenes a nuestra primera
página

Objetivos

1. Aprender a incorporar imágenes a un sitio web


2. Introducir el concepto de asset path

Los Assets
Los assets son todas las imágenes, sonidos, tipografías, películas, hojas de estilos y archivos javascript
que insertemos en nuestra página web.

En rails los assets se deberían encontrar divididos en dos carpetas principales, la primera está dentro de
app/assets y la segunda dentro de vendor/assets. La primera sirve para nuestros assets, la segunda para
las librerías de terceros, o sea lo que descarguemos de internet.

El asset path
Cuando uno está empezando a programar en Rails el asset path es la primera gran pesadilla, por no
saberlo manejar bien uno puede demorar horas en simplemente cargar una imagen o un CSS, por lo
mismo vamos a estudiarlo para evitar conflictos futuros.

¿Qué es el asset path?

El asset path es una lista de todos los directorios donde se encuentran los assets, cuando queremos
cargar una imagen o cualquier tipo de assets simplemente le decimos a rails que lo busque dentro del
asset_path

Incorporando imágenes

En una página web normal podemos ingresar imágenes de ruta relativa (dentro de nuestro computador)
simplemente usando el tag:

<img src="imagen-prueba.png">
Pero esto no lo podemos hacer en rails, bueno realmente podemos hacer algo como:

<img src="assets/images/imagen-prueba.png">

Pero, siempre y cuando tengamos una imagen dentro de la carpeta /assets/images/ y funcionará
sólo en el entorno local y fallará cuando queramos correr nuestra aplicación en un servidor, por lo mismo
no lo debemos hacer.

La forma correcta de incluir imágenes en rails es ocupando los métodos image_tag y asset_path

El método asset_path

El método asset_path sirve para todos los tipos de assets así que partiremos explicando este.

Para utilizar asset_path, el cuál es un método de rails, tenemos que utilizar el formato erb, o sea
incorporar la zanahoria <%= %> dentro del tag <img> de HTML o sea quedaría así:

<img src="<%= asset_path 'imagen-prueba.png' %>">

Hay que tener cuidado con un par de cosas, primero, la imagen si tiene que existir dentro de la carpeta
/app/assets/images/, pero además debemos tener mucho cuidado con la sintaxis de asset_path, la
zanahoria (<%= %>), esta debe estar pegada a la primera comilla y el cierre de la zanahoria debe estar
pegada a la segunda, esto para evitar dejar espacios vacíos en el nombre del archivo que no forman
parte del nombre.

El método image_tag

Hay una forma más breve de incluir imágenes, pero no aplica a los otros assets, y esta es ocupando el
método image_tag.

Para incorporar la misma foto que incorporamos previamente sería con:

<%= image_tag 'imagen-prueba.png' %>

image_tag además puede recibir otros parámetros como el id o la clase.

<%= image_tag 'imagen-prueba.png', class:"img", id:"img-1"%>

Una ventaja de ocupar image_tag es que automáticamente genera un atributo alt utilizando el nombre del
archivo.

Obviamente que para que cualquiera de estas soluciones funcione tenemos que estar ocupando la
extensión erb, o sea nuestro archivo debe ser .html.erb (por defecto en ruby)
7) Nuestra primera página con layout.

Objetivos

1. Aprender a utilizar la página maestra


2. Profundizar en el concepto de asset path
3. Descubrir como manejar diversos CSS y javascript en una aplicación rails
4. Entender el proceso de precompilación de assets
5. Aprender a reutilizar código con las vistas parciales
6. Saber cambiar el layout de los controllers y métodos

El Layout
Si observamos detenidamente las vistas nos daremos cuenta que no están completas, no tienen la
estructura típica de un documento HTML, y es así, ¿de adonde sale el título de la página?

Dentro de la carpeta views existe una subcarpeta llamada layouts, estas son las páginas maestras,
podemos tener varias pero por defecto es una sola y esta se llama application.html.erb

Y si modificamos el layout modificaremos todas las otras vistas simultaneamente, probemos que esto sea
cierto. Al abrir el layout veremos lo siguiente:

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Basic</title>
5 <%= stylesheet_link_tag 'application',
6 media: 'all', 'data-turbolinks-track' => true %>
7 <%= javascript_include_tag 'application',
8 'data-turbolinks-track' => true %>
9 <%= csrf_meta_tags %>
10 </head>
11 <body>
12 <%= yield %>
13 </body>
14 </html>

Esta es la página maestra y en ella podemos definir el contenido y estilo que queramos compartir a lo
largo de todas las páginas de nuestra aplicación.

Para probar que así sea cierto vamos agregar el siguiente texto antes de la instrucción yield

1 HOLA !!!
2 Modificando el layout

y luego si abrimos las páginas veremos que en ambas dice el texto

Entonces como cambiamos el estilo de nuestro sitio?, para eso tenemos que agregar CSS

Incorporando CSS
Los archivos CSS son fáciles de incorporar, simplemente debes colocarlo dentro de la carpeta
apps/assets/stylesheets, todos los archivos de ahí se cargaran en cada página gracias a que dentro de
nuestro layout viene incorporada la siguiente línea.

1 <%= stylesheet_link_tag 'application', media: 'all', 'data-


2 turbolinks-track' => true %>

Esa línea lo que hace es cargar el archivo app/assets/stylesheets/application.css el cuál además de un


par de comentarios contiene:

1 *
2 *= require_tree .
3 *= require_self
4 */

En primer lugar hay que aclarar que estas líneas están comentadas al estilo CSS para evitar que la página
web las cargue, puesto que require no es un instrucción de CSS válida, pero en este archivo si
puede haber CSS.

Cabe destacar que este archivo recibe el nombre de manifiesto, y cada una de las líneas
require dentro recibe el nombre de directivas.
Lo siguiente que vemos es require_tree, esta es la línea responsable de cargar recursivamente, tanto
directorios como subdirectorios, todos los CSS dentro de esta carpeta.

En este archivo el orden de carga importa y si fuera necesario establecer un orden de carga este se
puede especificar en este mismo archivo a través de la instrucción require y nombrando los css, por
ejemplo:

1 /* …
2 *= require reset
3 *= require layout
4 *= require chrome
5 *= require_tree .
6 *= require_self
7 */

Mientras que require_self carga el CSS (si es que hay) contenido dentro del archivo application.css en la
posición mencionada, en el ejemplo, primero se cargaría el archivo reset, luego layout y luego chrome y
finalmente todo el resto, si hubiese CSS dentro de este archivo application.css quedaría al final.

Para probarlo vamos a definir un CSS sencillo, al que vamos a llamar style.css, y tiene que estar dentro
de la carpeta app/assets/css

1 body{
2 background-color:#ddd;
3 font-color:#333;
4 width:80%;
5 margin:20px auto;
6 font-family: 'helvetica';
7 }

Si ahora cargamos nuestra página veremos que el fondo es gris, los márgenes y que la tipografía cargó.

La librería encargada de procesar estas directivas y convertir todos los CSS (y otros assets) en
único archivo recibe el nombre de Sprockets

Incorporando Javascript
Con respecto a este punto no nos extenderemos puesto que funciona exactamente igual que los CSS, la
diferencia es que la línea que carga todos los js dentro del layout es:
1 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>

El archivo cargado por defecto es app/assets/javascript/application.js y funciona de la


misma forma que application.css. Dentro del archivo se encuentra lo siguiente:

1 //= require jquery


2 //= require jquery_ujs
3 //= require turbolinks
4 //= require_tree .

O sea rails incluye la librería de jQuery, y además, incluye la librería jquery_ujs que sirve para trabajar con
javascript no intrusivo, además incluye turbolinks del cual hablaremos más adelante.

Incorporando imágenes (u otros assets) dentro del


CSS y del JS
Los assets incorporados dentro de los archivos CSS están sujetos a las mismas condiciones que los que
ya hemos visto, pero para poder ocupar el método asset_path dentro de estos necesitamos que estos
archivos además contengan la extensión .erb.

O sea por ejemplo si tenemos un archivo style.css el cual pone una imagen como background de fondo,
entonces debemos nombrarlo style.css.erb y luego dentro de la url(fondo1.png) cambiarlo por

1 url('<%= asset_path fondo1.png %>')

Es muy importante que nos fijemos bien en la sintaxis de nuestro archivo CSS. Si bien CSS es tolerante a
fallos debido al principio de graceful degradation, la precompilación de archivos CSS no es tan tolerante y
un punto y coma sobrante causara problemas a la hora de enviar nuestro proyecto a producción.

¿Qué es la precompilación de assets?

La precompilación de assets es un proceso por el cual los assets son preparados para producción.

1. Preprocesadores

Transforma los lenguajes a su forma compartible, por ejemplo los archivos coffescript se convierten
en javascript, los archivos SASS se convierte en CSS.

2. Concatenados
Todos los CSS se juntan en un CSS final, todos los JS se juntan en un JS final, esto reduce el número
de requests hechos al servidor traduciéndose en una página más rápida

3. Minificados

Tanto al CSS final como al JS final se le remueven todos los caracteres innecesarios, como por
ejemplo los saltos de líneas y los espacios, eso se traduce en archivos de menor peso y por lo mismo
páginas más rápidas.

4. Fingerprint

Consiste en agregar una secuencia de números para convertir el nombre de un asset en un nombre
único y realizar de forma más sencilla el caching de los assets.

El entorno

En ruby on rails existen distintos entornos de funcionamiento, por defecto son tres, desarrollo, testing y
producción.

En algunas ocasiones es útil agregar un 4º llamado staging.

La razón por la que existen diversos entornos es porque tienen distintos funcionamientos, cuando
trabajamos en nuestro computador quremos ver los errores con el mayor detalle posible, pero cuando
nuestra aplicación está online y la está ocupando una persona externa y sucede algún error queremos
estar seguros de que no expondremos una falla de seguridad de nuestro sistema y por lo mismo en rails
se enconden los errores en modo de producción.

El modo testing sirve para correr pruebas, lo ocuparemos más adelante en este libro.

El modo staging se agrega cuando se quiere un paso previo a producción (a veces con clientes reales, o
a veces solo homologando el ambiente para prueba) y prevenir errores inesperados al dar el paso a
producción.
Cuando uno trabaja en el entorno de desarrollo no se realizan todos los procesos de
precompilación de assets, sólo se realiza el preprocesado para poder utilizar sass y
coffeescript, el resto de los procesos sólo se procesan cuando pasamos al entorno de
producción o si modificamos nuestro entorno de desarrollo para que lo haga.

Los archivos donde se especifica esta configuración se encuentran en config/enviromments, estos


archivos también permiten modificar otros comportamientos como por ejemplo si se deben mostrar los
errores.

Podemos correr rails en modo de producción dentro de nuestro computador cargado el server con el
parámetro -e production

1 rails s -e production

Pero esto nos generará conflictos ya que todavía no tenemos configurado algunos archivos, así que el
intentar cargar la página obtendremos el siguiente error.

Missing `secret_token` and `secret_key_base` for 'production' environment, set the


se values in `config/secrets.yml`

En los capítulo de heroku tanto como en los últimos capítulos de este libro hablaremos con más detalle
del paso a producción.

La carpeta assets de vendor

Rails trae tres carpetas de assets incluídas en el asset_path, hasta el momento sólo hemos mencionado
la que se encuentra dentro de app, pero dentro de la carpeta vendor y dentro de la carpeta lib también es
posible agregar assets.

La idea es que la carpeta app/assets contenga los assets específicos del proyecto, la carpeta vendor
contenga los assets de librerías de terceros, como por ejemplo los de bootstrap, y finalmente la carpeta
lib para otros casos.
Cuando colocamos los assets en otras carpetas el require_tree no es suficiente para cargarlos, ya que
este tree se refiere al de app/assets/stylesheets, entonces además tenemos que mencionarlos dentro del
archivo, veremos un caso específico de esto en el ejemplo de bootstrap.

Agregando una carpeta nueva al asset_path

Es posible agregar nuevas carpetas al asset_path ocupando el archivo /config/initializers/assets.rb

1 Rails.application.config.assets.paths << Rails.root.join('mis_assets', 'assets')

También para hacer más fácil las referencias puedes agregar subcarpetas

1 Rails.application.config.assets.paths << Rails.root.join('mis_assets', 'assets',


2 Rails.application.config.assets.paths << Rails.root.join('mis_assets', 'assets',

Verificando el asset_path

Rails tiene una consola que nos permite verificar este tipo de configuraciones, para entrar a ella dentro de
la carpeta del proyecto tenemos que escribir rails c o rails console y luego podemos
verificar nuestros assets con:
1 Rails.application.assets

Esto nos devolverá una lista con todas las carpetas que se encuentra dentro del asset_path

Si realizamos un cambio dentro del archivo de configuración es necesario salir de la consola y volver a
entrar para poder ver los cambios.

Incorporando tipografías locales

Para incorporar tipografías todavía nos falta aprender una cosa más, a manipular que archivos y que
carpetas son parte del asset_path, para esto vamos a modificar el archivo /config/initializers/assets.rb y
dentro de la clase vamos a agregar lo siguiente:

1 Rails.application.config.assets.paths << Rails.root.join('app', 'assets', 'fonts'


2 Rails.application.config.assets.precompile += %w( *.svg *.eot *.woff)

Finalmente cuando se modifica el archivo application.rb o cualquier initializers debemos reiniciar el


servidor, y con eso ya deberíamos tener acceso a nuestras tipografías.

Consecuencias de Sprockets
Al unirse todos los CSS y cargarse en cada página podemos tener consecuencias indeseadas,
especialmente cuando tenemos plantillas que cargan diversos archivos CSS en cada páginas. Para
enfrentar este problema hay dos posibles soluciones, la primera consiste en darle un namespace a la
página, la segunda en utilizar un layout distinto, ahora abordaremos la solución del namespace y más
adelante en este capítulo estudiaremos como incorporar diversos layouts.

Para dar un namespace a la página lo que debe hacerse es ocupar el hash params dentro de la vista,
este hash contiene información general que se realiza en cada request, una información importante que
tiene es el nombre del controller y del método llamado, estos se guardan bajo las claves de controller y
action y los podemos ocupar en las vistas, en el layout y en el controller.

1 params[:controller]

Utilizando el hash params podemos generar un namepsace a cada vista, simplemente agregado ese dato
a la clase body dentro del layout.
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Namespace Trick :)</title>
5 <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 <%= csrf_meta_tags %>
8 </head>
9
10 <body class="<%= params[:controller]%>">
11 <%= yield %>
12 </body>
13 </html>

De esta forma si cargamos alguna página dentro del controller pages, obtendremos

<body class="pages">

Podemos hacerlo aún más específico utilizando

1 <body class="<%= params[:controller]%> <%= params[:action]%>">


2 <%= yield %>
3 </body>

En ese caso si cargamos el método landing dentro de pages obtendremos

<body class="pages landings">

Queda un paso que en teoría es sencillo, ahora cada CSS que sea específico a una grupo de página debe
tener junto a cada marca un .nombre-controller y cada CSS específico a una sóla página debe contener
un .nombre-controller .nombre-acción

Incorporando bootstrap.
Hay varias formas de incorporar bootstrap, la primera y más sencilla es ocupar el CDN.

CDN

El CDN consiste en 2 CSS, uno el base, y el otro el tema junto con los JS para alguna de las
funcionalidades.
Estos debemos copiarlos dentro de nuesta página maestra en views/layouts/application.html.erb

1 <!-- Latest compiled and minified CSS -->


2 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.c
3
4 <!-- Optional theme -->
5 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme
6
7 <!-- Latest compiled and minified JavaScript -->
8 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"

Pero para copiarlo bien tenemos que tener en cuenta la carga de nuestros CSS, los CSS de bootstrap
debemos cargarlo antes de nuestros CSS para poder modificarlo (o si no bootstrap rescribirá nuestros
cambios) y los JS debemos cargarlo después de la instrucción javascript_include_tag pues esta
instrucción es la que carga jQuery en nuestro sitio y los JS de boostrap depende de jQuery. Entonces
nuestro layout con bootstrap quedaría así:

1
2 <!DOCTYPE html>
3 <html>
4 <head>
5 <title>Basic2</title>
6
7 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.m
8
9 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-t
10
11 <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
12 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
13 <%= csrf_meta_tags %>
14
15 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
16
17 </head>
18 <body>
19
20 <%= yield %>
21
22 </body>
23 </html>

Descargando los CSS

Es posible descargar los CSS de boostrap dentro de la carpeta vendor/assets, tenemos que tener
cuidado de pones los CSS dentro de la carpeta CSS y los JS dentro de la carpeta JS, funciona si los
cruzamos pero la idea es mantener todo ordenado, y si creamos una carpeta nueva tenemos que
agregarla al asset_path

Entonces primero descargamos los archivos de la página oficial:


https://github.com/twbs/bootstrap/releases/download/v3.3.5/bootstrap-3.3.5-dist.zip

Copiaremos los archivos CSS en lugar de los .min puesto que rails tiene la capacidad de minificarlos, o
sea los archivos bootstrap.css y bootstrap-theme.css van en vendor/assets/stylesheets, luego copiamos
el archivo bootstrap.js en vendor/assets/javascript

El último paso que nos queda es mencionar dentro del application.css los archivos bootstrap, puesto
como habíamos explicado previamente la línea require_tree dentro de
app/assets/stylesheets/application.css sólo carga el tree de stylesheets, por lo que debemos hacer el
require de forma explícita.

1 *= require bootstrap
2 *= require bootstrap-theme
3 *= require_tree .
4 *= require_self

De la misma forma debemos hacerlo dentro de app/assets/javascript/application.js

Este debería quedar:

1 //= require jquery


2 //= require jquery_ujs
3 //= require turbolinks
4 //= require bootstrap
5 //= require_tree .

Glyphicons

Este tema puede ser un poco más complejo porque en primer lugar hay que agregar la carpeta fonts
dentro de vendors/assets y copiar los archivos ahí, luego hay que agregar la carpeta fonts al asset_path
en el archivo config/initializers/assets.rb

1 Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'fonts'

En el mismo archivo hay que agregar los nuevos formatos al proceso de precompilación
1 Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'fonts'
2 Rails.application.config.assets.precompile += %w( *.svg *.eot *.woff *.ttf *.woff2)

y hay que reiniciar el servidor.

Pero además tenemos que revisar el archivo css de boostrap y cambiar las referencias desde donde se
cargan los fonts, puesto hay que ocupar el asset_path en vez de las rutas que utiliza.

Para eso con ctrl+f dentro del archivo podemos buscar el nombre de los fonts, en este caso glyphicons-
halflings, y buscar donde se cargan, debería aparecer con url() cerca de la línea 266 encontraremos:

1 src: url('../fonts/glyphicons-halflings-regular.eot');
2 src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'
3 url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),
4 url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-hal

Ahí es donde tenemos que eliminar el ../fonts y luego envolverlo ocupando <%= asset_path
"nombrefuente" %>

Debería quedar así:

1 src: url('<%= asset_path "glyphicons-halflings-regular.eot" %>');


2 src: url('<%= asset_path "glyphicons-halflings-regular.eot?#iefix" %>') format(
3 url('<%= asset_path "glyphicons-halflings-regular.woff2" %>') format('woff2'),
4 url('<%= asset_path "glyphicons-halflings-regular.woff" %>') format('woff'),
5 url('<%= asset_path "glyphicons-halflings-regular.ttf" %>') format('truetype'),
6 url('<%= asset_path "glyphicons-halflings-regular.svg#glyphicons_halflingsregular" %>')

Para probar que funcionó podemos agregar un glyphicon a nuestra vista.

1 <span class="glyphicon-cloud"> hola </span>

Vistas parciales
Una vista parcial es una parte de un archivo HTML que simplemente se pone en otro archivo.

La vistas parciales se caracterizan son archivos .html.erb pero tienen una carecterística especial,
empiezan con el prefijo _ y esto ayuda a los programadores a distinguir que vistas no están completas,
o sea son parciales y son para ser insertadas en otros archivos.
Para cargar una vista parcial sólo tenemos que especificar el nombre de un archivo HTML que empieze
con el prefijo _

1 <%= render 'layouts/navbar' %>

Las vistas parciales sirven mucho en dos casos, desacoplar y mantener ordenadas las vistas y para evitar
repetir código, el primer caso lo estudiaremos en este capítulo, el segundo lo veremos más adelante.

Cargando el navbar de bootstrap como una vista parcial

Si queremos desacoplar una vista, o sea por ejemplo si queremos copiar la barra de navegación de
bootstrap que consisten en más de 40 líneas de código HTML es mejor copiarlas dentro de una vista
parcial y de esa forma no ensuciar el layout, debemos crear un archivo dentro de app/views/layouts que
llamaremos _navbar.html.erb , dentro de el copiaremos la barra de bootstrap que aparece en la
página.
1 <nav class="navbar navbar-default">
2 <div class="container-fluid">
3 <!-- Brand and toggle get grouped for better mobile display -->
4 <div class="navbar-header">
5 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse
6 <span class="sr-only">Toggle navigation</span>
7 <span class="icon-bar"></span>
8 <span class="icon-bar"></span>
9 <span class="icon-bar"></span>
10 </button>
11 <a class="navbar-brand" href="#">Brand</a>
12 </div>
13
14 <!-- Collect the nav links, forms, and other content for toggling -->
15 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
16 <ul class="nav navbar-nav">
17 <li class="active">
18 <a href="<%= landings_efrain_path %>">
19 Efrain <span class="sr-only">(current)</span>
20 </a>
21 </li>
22 <li>
23 <a href="<%= landings_gonzalo_path%>">Gonzalo</a>
24 </li>
25 </ul>
26
27 <form class="navbar-form navbar-left" role="search">
28 <div class="form-group">
29 <input type="text" class="form-control" placeholder="Search" name="color
30 </div>
31 <button type="submit" class="btn btn-default">Submit</button>
32 </form>
33 <ul class="nav navbar-nav navbar-right">
34 <li><a href="#">Link</a></li>
35 <li class="dropdown">
36 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button
37 <ul class="dropdown-menu">
38 <li><a href="#">Action</a></li>
39 <li><a href="#">Another action</a></li>
40 <li><a href="#">Something else here</a></li>
41 <li role="separator" class="divider"></li>
42 <li><a href="#">Separated link</a></li>
43 </ul>
44 </li>
45 </ul>
46 </div><!-- /.navbar-collapse -->
47 </div><!-- /.container-fluid -->
48 </nav>

y finalmente dentro del layout antes del yield escribiremos <%= render ‘layouts/navbar’ %>
Cargando un layout distinto
Cada controller se encarga de cargar el layout, si no se específica uno se carga el layout por defecto, el
cuál es application.html.erb

Dentro del método correspondiente podemos especificar que layout o cargar, o si no queremos ningún
layout también podemos hacerlo

1 def index
2 render layout: false
3 end

Podemos hacer lo mismo a nivel de controller para todos los métodos internos

1 class PagesController < ApplicationController


2 layout false
3 def index
4 end
5
6 def about
7 end
8 end

Para mostrar un layout distinto es la misma idea, supongamos que queremos hacer un layout distinto
para las landings page:

A nivel de acción:

1 def index
2 render 'landings'
3 end

A nivel de controller:

1 class PagesController < ApplicationController


2 layout "landing"
3 def index
4 end
5
6 def about
7 end
8 end
El layout puede tener la estructura que quieras, pero debes recordar el yield o no podras ver el contenido
de la vista específica.

1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Layout Distinto</title>
5 <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 <%= csrf_meta_tags %>
8 </head>
9 <body>
10
11 hola
12 <%= yield %>
13
14 </body>
15 </html>

Desafío
Cargar una plantilla de HTML dentro de rails.

Preguntas

1. ¿Para qué sirve el layout?


2. ¿Qué es el asset_path?
3. ¿Qué hace la línea require\_tree?
4. ¿Cómo se puede especificar el orden de carga de un CSS?
5. ¿Cómo se puede cargar una imágen u otro asset dentro de un CSS?
6. ¿Cómo se puede agregar una nueva carpeta al asset path?
7. ¿Para qué sirven los assets dentro de la carpeta vendor?
8. ¿Para qué sirve la línea require_tree dentro del manifiesto?
9. ¿En qué línea del layout se carga jQuery?
10. ¿Por qué los CSS de bootstrap hay que cargarlos antes de
stylesheet\_link\_tag?
11. ¿Para qué sirve el rails console?
12. ¿Qué son los vistas parciales?
13. ¿Qué características tienen los archivos que son vistas parciales?
14. ¿Cómo se puede cargar una vista parcial?
15. ¿En qué consiste el proceso de minificado?
8) Creando un formulario de registro

Objetivos

1. Introducir el concepto de modelo


2. Aprender a crear un formulario básico

En el capítulo anterior vimos las componentes de vistas y controles de rails, en este capítulo abordaremos
la introducción a modelos creando nuestro primer formulario.

Para repasar vamos a empezar desde cero.

1) Creamos el proyecto

1 rails new primer_form

2) Cargamos el CDN de bootstrap en el layout.


1
2 <!DOCTYPE html>
3 <html>
4 <head>
5 <title>Basic2</title>
6
7 <link rel="stylesheet"
8 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"
9 integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezH
10 crossorigin="anonymous">
11
12 <link rel="stylesheet"
13 href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css
14 integrity="sha384-aUGj/X2zp5rLCbBxumKTCw2Z50WgIr1vs/PFN4praOTvYXWlVyh2UtNUU0KAUhAX
15 crossorigin="anonymous">
16
17 <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
18 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
19 <%= csrf_meta_tags %>
20
21 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
22 integrity="sha512-K1qjQ+NcF2TYO/eI3M6v8EiNYZfA95pQumfvcVrTHtwQVDG+aHRqLi/ETn2uB+1JqwYqVG3LIv
23
24 </head>
25 <body>
26 <div class="container">
27 <%= yield %>
28 </div>
29 </body>
30 </html>

3) Creamos el controller landing con la págian index

1 rails g controller landing index

El tag form
En Ruby on Rails al igual que en HTML podemos agregar formularios utilizando los tags de forms.

Para probarlo construiremos un formulario simple dentro del index de la aplicación que ya tenemos
definida.
1 <form method="">
2 <input name="q">
3 <input type="submit" value="Enviar">
4 </form>

Después de guardar, dentro de la página web deberíamos ver un input, y si lo llenamos veremos que por
defecto nos redirige a la misma página, pero esta cambiará ligeramente, pues al final de la url aparecerá
?q=hola donde q es el nombre del input y el igual el valor que fue pasado.

Revisando los parámetros enviados

Todos los parámetros en rails se pásan a través de un hash llamado params, dentro de la vista podemos
mostrar el contenido de este hash con:

1 <%= params %>

Veremos:

Es rails los hash tienen un pequeña diferencia con los de ruby, en estos los accesos con string o con
símbolos son exactamente iguales, o sea podemos obtener el valor de q usando params[:q] o
params["q"]

Entonces si queremos mostrar sólo el valor del email enviado lo podemos hacer con <%=params[:q]%>

Ahora sacaremos <%=params%> de la vista por que no tiene sentido mostrar los párametros al usuario.
Guardando los datos
Ahora tenemos que lidiar con la base de datos, puesto que para guardar los datos la necesitamos, ruby
on rails viene por defecto funcionando con SQLite3 y para este proyecto será suficiente.

Para crear una tabla de datos y guardar los usuarios necesitamos crear un modelo, en el patrón MVC
cada modelo mapea los datos a su tabla de la base de datos, o sea si creamos el modelo llamado
usuario, se creará una tabla en la base de dato llamada usuarios y el modelo nos ayudará a guardar los
datos sin tener que usar comandos SQL.

Se recomienda escribir el código en inglés, el inflector de Rails viene configurado por defecto
en inglés y por lo tanto puede haber un error en el nombramiento de las tablas al intenta
pluralizar bajo las reglas del español

Creando el primer modelo

Ppdemos crear el modelo de usuarios desde la terminal con un generador.

1 rails g model user email

Donde user es el nombre del modelo y email un campo que tendrá la tabla.

A diferencia de los controllers la convención es nombrar los módelos en singular, puesta esta clase
mapea a un elemento (por ejemplo un usuario) con sus datos en la tabla de la base de datos.

Al correr el generador obtendremos:

invoke active_record
create db/migrate/20151120195902_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml

O sea se genera el modelo User, una migración para crear la tabla users en la base de datos y tests.

Corriendo las migraciones


En rails para modificar la base de datos hay que utlizar una migración, estas son una secuencia de
instrucciones que llevan la base de datos de un estado a otro, las migraciones son un tema complejo que
abordaremos profundemente en el libro, pero por ahora sólo necesitamos saber como correrlas, para eso
vamos a utilizar el comando:

1 rake db:migrate

Obtendremos como resultado:

== 20151120195902 CreateUsers: migrating ======================================


-- create_table(:users)
-> 0.0037s
== 20151120195902 CreateUsers: migrated (0.0039s) =============================

Después de correr la migración el siguiente paso es probar el modelo.

Rails console

Cuando uno crea un modelo lo primero que hacemos es probarlo en la consola de rails, podemos
acceder a ella utilizando rails console o rails c , desde consola de Rails donde podemos
procesar e insertas datos a la base de datos.

Creando un usuario

Cuando uno crea un modelo lo que se crea es una clase con ese nombre, o sea dentro de rails nosotros
ahora podemos instanciar un objeto user con:

1 user = User.new
O lo podemos instanciar directamente con un email

1 user = User.new(email: 'gonzalo@desafiolatam.com')


2 => #<User id: nil, email: "gonzalo@desafiolatam.com", created_at: nil, updated_at: nil>

También podemos cambiar el email del usuario simplemente con:

1 user.email = "nuevoemail@desafiolatam.com"

Estos datos no persisten en la base de datos hasta que los guardemos, eso lo podemos hacer con:

1 user.save

Si todo se realizó correctamente obtendremos:

# (0.6ms) begin transaction


SQL (1.1ms) INSERT INTO "users" ("email", "created_at", "updated_at") VALUES (?
, ?, ?) [["email", "gonzalo@desafiolatam.com"], ["created_at", "2015-11-20 20:26:
21.576528"], ["updated_at", "2015-11-20 20:26:21.576528"]]
(0.8ms) commit transaction
=> true

Y ahora ya tenemos datos en nuestra base de datos.

También podemos guardar un usuario directamente en la base de datos sin instanciarlo, eso lo podemos
hacer con el método .create

1 User.create(email: "creando_usuario_directamente@gmail.com")

Leyendo los usuarios desde la base de datos

Para ver todos los usuarios creados lo podemos hacer con:

1 User.all

Esto gatillará una consulta SQL y nos devolverá un array especial llamado ActiveRecord::Relation pero el
cuál a fin de cuentas es un array.
User Load (0.3ms) SELECT "users".* FROM "users"
=> #, #]>

Como los resultados son una array podemos iterarlo con un .each

1 User.all.each {|u| puts u}

Obtendremos:

1 User Load (0.2ms) SELECT "users".* FROM "users"


2 #<User:0x007fd6332ee5d0>
3 #<User:0x007fd6332e6a38>
4 [#<User id: 1, email: "gonzalo@desafiolatam.com", created_at: "2015-11-20 20:26:21", updated_at:

El array al final se obtiene porque el método .each al terminar de iterar devuelve el arreglo original.

Para salir de rails console tenemos que escribir

1 exit

Guardando los usuarios dentro de rails


Ahora que sabemos lo básico del ActiveRecord podemos guardar el usuario cuando se envíe un
formulario.

Para eso tenemos que ir al controller de landings, y detectar si se enviaron parámetros en el formulario, si
se hizo los guardamos en la base de datos.

1 class LandingsController < ApplicationController


2 def index
3 unless params[:q].blank?
4 User.create(email: params[:q])
5 end
6 end
7 end

El método .blank? revisa si el parámetro q es nulo o vacío, o sea si hay un valor en el formulario que sea
distinto de nulo o vacío guardaremos al usuario con su email en la base de datos.
Cuando se trabaja en la parte de datos, el servidor de rails (rails s) siempre tiene datos útiles, para ver si
funcionó podemos revisarlo y veremos:

Started GET "/landings/index?q=gonzalo%40desafiolatam.com"


for ::1 at 2015-11-20 14:45:46 -0600
Processing by LandingsController#index as HTML
Parameters: {"q"=>"gonzalo@desafiolatam.com"}
(0.5ms) begin transaction
SQL (0.7ms) INSERT INTO "users" ("email", "created_at", "updated_at") VALUES (?
, ?, ?) [["email", "gonzalo@desafiolatam.com"], ["created_at", "2015-11-20 20:45:
46.740908"], ["updated_at", "2015-11-20 20:45:46.740908"]]
(0.6ms) commit transaction
Rendered landings/index.html.erb within layouts/application (0.8ms)
Completed 200 OK in 139ms (Views: 98.5ms | ActiveRecord: 1.9ms)

O sea nos dice que se llamó a la página con el párametro q="gonzalo%40desafiolatam.com y


luego se insertó en la base de datos, lo que implica que está todo OK.

En estos logs debemos tener especial cuidado cuando después de la inserción aparesca rollback
transaction en lugar de commit transaction, eso es un indicador de que la operación falló.

Cambiando la página de inicio


Es posible convertir a cualquier página en la página de inicio, para eso hay que abrir el archivo
config/routes.rb y agregar la línea root 'controller#action' en nuestro caso sería

1 root 'landings#index'

Luego podemos verificar que haya funcionando accediendo con nuestro navegador a
localhost:3000'

También podemos obeservar que al hacer rake routes aparacerá este cambio:

landings_index GET /landings/index(.:format) landings#index


root GET / landings#index

Desafío
Crear un formulario funcional en la plantilla que creamos en el capítulo anterior
Preguntas

1. ¿Cuál es la función del modelos en el patrón MVC de ruby on rails?


2. ¿Que contiene el hash params?
3. ¿Cuál es la relación entre el modelo y una tabla en la base
de datos?
4. ¿Qué son las migraciones?
5. ¿Cómo corremos una migración?
6. ¿Cómo podemos probar que una migración haya sido exitosa?
7. ¿Cómo instanciamos un usuario nuevo?
8. ¿Cómo instanciamos un usuario que tenga email?
9. ¿Cómo guardamos un usuario con su email en la base de datos?
10. ¿Cómo podemos guardar un usuario sin instanciarlo?
11. ¿Cómo podemos ver todos los usuarios creados?
12. ¿Cómo podemos mostrar todos los usuarios que hay en la base
de datos?
13. ¿Cómo podemos entrar a la consola de rails?
14. ¿Cómo podemos salir de la consola de rails?
15. ¿Qué hace el método .blank?
16. ¿Qué implica que aparezca rollback transaction en los logs?
17. ¿Cómo podemos cambiar la página de inicio en rails?
9) Formulario 2.0

Objetivos

1. Introducir el concepto de post


2. Introducir el concepto de MVC

Creando un proyecto nuevo


Antes de trabajar en este proyecto vamos a hacer una copia del anterior, (si tienes experiencia con GIT
puedes hacer un branch), para eso del terminal en una carpeta anterior a la del proyecto podemos hacer:

1 cp -r nombre_proyecto1 nombre_proyecto2

Donde nombre_proyecto1 es el nombre original del proyecto y nombre_proyecto2 es el nuevo.

Una vez dentro nos daremos cuenta si entramos a rails console ` que tenemos los mismos datos,
o sea copiaron los usuarios creados en el proyecto anterior, pero eso no se debe a que ambos utilizen las
misma base de datos, se debe a que sqlite3 es un archivo que está dentro de la carpeta db y nostoros
copiamos, por lo tanto nos trajimos todos los cambios.

Podemos probar que estos es así agregando datos en uno solo de los proyectos y luego revisando en el
otro, veremos que sólo están en uno, esto no ocurrirá posteriormente cuando trabajemos con
PostgreSQL u otro motor de base de datos.

Otro cuidado que tenemos que tener es que no podemos tener corriendo dos servidores de rails en el
mismo puerto, y como el puerto por defecto es el 3000, debemos bajar el servidor de rails del proyecto
anterior con ctrl+c antes de abrir el nuevo.

Y obviamente debemos abrir la carpeta correcta en el editor, entonces antes de seguir:

¿Copiaste los archivos a la carpeta nueva?


¿Cerraste el proyecto en el editor, y abriste el nuevo?
¿Bajaste el servidor del proyecto anterior y abriste el nuevo?

Enviando un formulario por POST


Los formularios pueden ser enviados por diversos métodos, los más comunes son GET y POST

Cuando se envía un formulario por GET los campos aparecen en la URL, esto es muy útil si se quiere
mostrar y poder compartir los resutados, por ejemplo si buscamos algo en google y queremos compartir
los resultados de la búsqueda lo podemos hacer copiando la url y envíandosela a alguien, pero cuando se
quiere enviar información sensible y no dejar un registro de eso en la URL hay que ocupar el método
POST

Para enviar un formulario por post tenemos que indicar en el método que es POST.

1 <form method="POST">
2 <input name="q">
3 <input type="submit" value="Enviar">
4 </form>

Si ahora probamos el formulario veremos un error que ya hemos visto anteriormente.

No route matches [POST] "/landings/index"

Hacer un match a un ruta requiere tanto de la url como el método, no es lo mismo hacer un get a una
página que un post, para probarlo podemos hacer rake routes

Prefix Verb URI Pattern Controller#Action


landings_index GET /landings/index(.:format) landings#index
root GET / landings#index

Sólo tenemos un acceso GET a /landings/index y ningún acceso post.

Para crear una ruta nueva vamos abrir el archivo de rutas en config/routes.rb

MVC in a nutshell

Pero … ¿Por qué necesitamos una URL nueva?, si antes funcionaba con una sola.

El concepto para entender bien MVC es que una URL es una acción y una acción nueva requiere una
URL nueva, o sea para agregar procesamiento al formulario tenemos que agregar una ruta nueva, esa
URL nos llevará a un controller y un método específico y ahí manejaremos la lógica del formulario.

Si bien un if dentro del controller nos permitiría manejar múltiples acciones debemos recordar siempre el
concepto KISS (Keep it simple and stupid) entonces en lugar de manejar la opción de que si el parámetro
viene hacer tal cosas, mejor simplemente separar ambas acciones, una para mostrar el formulario y una
para procesarlo.
Existe otra ventaja en este caso específico, si tuviesemos varios landings distintos probablemente no
haya ninguna razón para manejar estos formularios de registro de forma distinta, de esta forma al tener
acciones separadas evitamos reescribir código.

La ruta nueva puede ser por POST o por GET, la diferencia es que por GET los parámetros se envían a
través de la URL, en cambio POST se pasa en los headers del request, esto lo estudiaremos con más
profundidad en el capítulo de negociación de contenido.

Ahora agregaremos la ruta al método nuevo dentro del archivo routes.

1 post 'pages/receive'

Para confirmar que la ruta fue agregada correctamente correremos el comando rake routes en la
consola, como resultado deberíamos obtener:

Prefix Verb URI Pattern Controller#Action


landings_index GET /landings/index(.:format) landings#index
landings_receive POST /landings/receive(.:format) landings#receive
root GET / landings#index

La ruta nueva no la podemos probar en el navegador ya que eso sería acceder por GET, pero podemos
probarla utilizando el formulario, para eso vamos a necesitar el prefix.

Ya habíamos comentado que podemos referirnos a cualquier página de rails ocupando el prefix + _path o
prefix + _url el primero es una ruta relativa y para que en un formulario podamos dirigir a una página
distinta tenemos que ocupar el atributo action

Form y action

1 <form method="POST" action="<%= landings_receive_path %>" class="form">


2 <label for="email">Email</label>
3 <input name="email">
4 <input type="submit" value="Subscribirse">
5 </form>

El método form_tag

Otra forma de hacer exactamente lo mismo es ocupando el método form_tag de ruby, no es mucho el
código que se ahorra en los casos sencillos, pero no está de sobra saberlo.
1 <%= form_tag landings_receive_path, method: :post, class: 'form' %>
2 <%= label_tag "email", "Email" %>
3 <%= email_field_tag "email" %>
4 <%= submit_tag "Subscribirse" %>

Si ahora intentanmos probar el formulario, obtendremos el siguiente error:

Unknown action
The action 'receive' could not be found for LandingsController

La razón de este error es porque todavía no hemos creado el método de receive, es lo que haremos a
continuación.

Para eso vamos a abrir el controller /app/controllers/landings_controller.rb en el editor de texto, dentro del
archivo crearemos el nuevo método.

Mostrando los resultados del formulario

1 class PagesController < ApplicationController


2 def index
3 end
4
5 def receive
6 render json: params
7 end
8
9 end

Cada método muestra por defecto una vista del mismo nombre por ejemplo index carga la vista dentro de
views/landings/index.html.erb pero hay una excepción. Si se utiliza la instrucción render o redirect_to,
esto nos puede servir para cambiar el comportamiento y en este en lugar de mostrar una vista (o un error
porque todavía no hemos creado la vista) nos ayudaría a mostrar los parámetros del formulario recibido.

1 {
2 utf8: "✓",
3 authenticity_token: "9WFUTrwJgKSCKIKA4lQPsHted6TRWmERCJTY65u+Ix94dFLxPdfY2XzFETBSEVDjU1knMCe
4 email: "gonzalo@desafiolatam.com",
5 commit: "Subscribirse",
6 controller: "landings",
7 action: "receive"
8 }
Guardando los resultados

Si estamos enviando los datos de forma correcta el siguiente paso es guardarlo, ya tenemos el modelo
creado, lo que tenemos que hacer es mover la lógica que teníamos en index a receive, quedaría así:

1 class LandingsController < ApplicationController


2 def index
3
4 end
5
6 def receive
7 User.create(email: params[:email])
8 render json: params
9 end
10
11 end

Se deben manejar los errores en caso de inserció esto lo estudiaremos en un capítulo futuro

Redirect_to

En lugar de mostrar los resultados del formulario después de crearlo podríamos redirigir al landing (o a
otra página al usuario y mostrarle que sus datos fueron guardados con éxito, para eso ocuparemos el
método redirect_to, este recibe dos parámetros, el primero es la URL a la que hay que redirigir al usuario
y el segundo permite enviar mensajes flash, los mensajes flashs son variables que viven durante un sólo
request o sea son para mostrar a la próxima carga de página, ideal para avisos de lo lograste o tal cosa
falló.

1 class PagesController < ApplicationController


2 def index
3 end
4
5 def receive
6 @user = User.new(email: params[:email])
7 @user.save
8 redirect_to root_path, notice: "Te has registrado"
9 end
10 end

Variables flashs
Nos falta una mejora, siempre existe una posibilidad de que la operación de guardado pueda fallar, en
este caso es muy difícil que suceda dada la simplicidad de la operación, pero siempre debemos manejar
y reportar los casos de falla.

1 class PagesController < ApplicationController


2 def index
3 end
4
5 def receive
6 @user = User.new(email: params[:email])
7 if @user.save
8 redirect_to root_path,
9 notice: "Te has registrado"
10 else
11 redirect_to root_path,
12 alert: "No hemos podido registrarte, inténtalo de nuevo"
13 end
14 end
15 end

No basta con crear variables flashs, tenemos que mostrarlas, para eso dentro de la vista maestra, o sea
layouts/application.html.erb mostraremos las variables flash.

Es una práctica común desde el punto de vista de usabilidad mostrar estas alertas bajo la
barra de navegación. Mostrarlas arriba de la barra de navegación haría que se moviera y se
vería raro, mostrarlas muy abajo haría que el usuario no las viera.

1 <% if flash[:notice] %>


2 <p style="color:green"> <%= flash[:notice] %> </p>
3 <% elsif flash[:alert] %>
4 <p style="color:red"> <%= flash[:alert] %> </p>
5 <% end %>

Mostrando todos los usuarios en la base de datos


Para eso vamos crear una ruta nueva que nos permita rescatar todos los emails ingresados en la base de
datos, la llamaremos get_leads, agregamos la ruta al archivo en config/routes.rb

1 get 'landings/get_leads'

Dentro del controller agregaremos el método get_leads y desde ahí obtendremos todos los leads.
1 def get_leads
2 @users = User.all
3 render json:@leads
4 end

Podemos probar entrando a http://localhost:3000/landings/get_leads

User.all devuelve todos las filas de la tabla leads dentro de un array (realmente no es una rray pero eso lo
estudiaremos más adelante), este array lo vamos a guardar dentro de una variable de instancia, lo cual
nos permitirá más adelante mostrar los resultados en una vista pero que por ahora nos limitaremos a
mostrar como si fuera un archivo JSON.

Ahora para terminar este proyecto haremos la vista de get_leads, para eso tenemos que crear dentro de
app/views/pages el archivo get_leads.html.erb pero primero tenemos que decirle al
controller que ocupe la vista y para eso removeremos el render.

1 def get_leads
2 @users = User.all
3 end

Para mostrar los resultados iteraremos todos los leads obtenidos (los users son un array) y por cada uno
de ellos mostraremos el email.

1 <ul>
2 <% @users.each do |u| %>
3 <li> <%= u.email %> </li>
4 <% end %>
5 </ul>

Más adelante en este libro esstudiaremos el concepto de autenticación y control de accesos


para dar acceso restringido a estas secciones exclusivamente a algunos usuarios.

Desafío
Modificar el formulario de la plantilla que hicimos previamente para incorporar un formulario que funcione
por posts, el usuario debe ser notificado cuando ingresó sus datos y se debe crear una página que
permita verificar todos los usuarios ingresados.

Preguntas
1. ¿Cuál es la diferencia entre enviar datos por get o por post?
2. Complete la oración. ¿Una acción nueva requiere de una …
3. ¿Para que sirve el atributo action de los formularios?
4. ¿Cuál es la diferencia entre utilizar el form de HTML y la etiqueta form_tag de
rails?
5. ¿Que hace redirect_to?
6. ¿Cuál es la diferencia entre redirect_to y render ?
7. ¿Qué hace render json: params?
8. ¿Qué son las variables flashs?
9. ¿Cuál es la diferencia entre utilizar <%= @users.each do |u| %> y <%
@users.each do |u| %>?
10) El gemfile

Objetivos

1. Aprender a instalar dependencias en un proyecto rails


2. Manejar las versiones de las dependencias ocupando el gemfile

El gemfile es un archivo que contiene todas las dependencias de un proyecto en Rails estas
dependencias reciben el nombre de gemas, con este archivo especificamos que bibliotecas con sus
respectivas versiones se deben instalar para poder correr el proyecto.

Bundler es un programa que es capaz de leer este archivo e instalar todas las dependencias. para correr
el correr el programa simplemente debemos ejecutar bundle .

Ni Bundler ni el gemfile son exclusivos de rrails, sirven para controlar dependencias de


cualquier proyecto en ruby.

El gemfile de un proyecto en rails comienza así:

1 source 'https://rubygems.org'
2
3 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
4 gem 'rails', '4.2.5'
5 # Use sqlite3 as the database for Active Record
6 gem 'sqlite3'

La primera línea especifica de donde deben ser buscadas las gemas, el resto de las gemas se especifca
con las instrucción gem, luego el nombre de la gema y como parámetro opcional la versión.

Al correr bundle se generará un archivo llamado gemfile.lock, este archivo contiene todas las gemas y
dependencias (otras gemas) de un proyecto.

Tanto el gemfile como gemfile.lock deben ser añadidos al repositorios pues permiten para otros instalar
las mismas libererías de tu proyecto en rails con las versiones específcadas con una sola línea de
comando.

Podemos especificar una versión en el gemfile con un segundo parámetro.


1 gem 'rails', '3.0.0.beta3'

Se puede pedir que una gema sea mayor que una versión con:

1 gem 'rack', '>=1.0'

Se puede pedir que la versión a instalar esté dentro de una versión menor de la gema, (o sea que el
cambio sea sólo en el segundo decimal)

1 gem 'thin', '~>1.1'

Preguntas

1. ¿Para qué sirve el Gemfile?


2. ¿Cuál es la diferencia entre el Gemfile y el Gemfile.lock?
3. ¿Qué hace el comando bundle?
4. ¿Qué significa que un cambio de versión sea menor?
Sección II
Deployment con Heroku
11) Deployment en Heroku

Objetivos

1. Conocer las diferentes alternativas que hay para hacer deployment de una
aplicación
2. Instalar y configurar las herramientas necesarias para hacer deployment a
Heroku
3. Aprender a hacer deployment en Heroku

Se le llama deployment al proceso de subir la aplicación en modo de producción (o sea lista para atender
usuarios reales), mientras que mientras estamos trabajando en nuestros computadores se dice que la
aplicación está en modo de desarrollo.

Esto no es sólo un decir, rails puede detectar el ambiente que está y correr configuraciones dependiendo
del ambiente, por ejemplo en producción podríamos tener funcionando google analytics, mientras que en
desarrollo no.

Además de estar en proudcción una aplicación requiere de dos cosas, un hosting y un dominio, no son lo
mismo y no hay que confundirlos.

El hosting es un computador donde la aplicación estará hosteada (subida y corriendo), el dominio es el


nombre.

Una pequeña aclaración, el dominio no es estrictamente necesario, si tiene un ip fija (cuando pagas por
un hosting usualmente te dan una ip) puedes usar esa ip para entrar a la aplicación, sin embargo tus
clientes o usuarios no van a recordar ese número, es necesario tener un dominio

Tipos de hosting
Hay varias estrategias para subir una aplicación:

Servidor propio

Requiere de gran infraestructura o de warehousing (llevar tu computador a un data center), ambas


opciones son bastante caras, y a menos que dispongas de los recursos, o sea un requistio importante del
cliente y esté dispuesto a pagar por eso se recomineda ignorar.
Hosting clásico

Este es el típico hosting que uno arrienda y viene con Cpanel, por lo general son baratos, antes de
arrendar uno de estos revisa bien los términos y condiciones y en especial que tengan un buen servicio
de soporte y compatibilidad con Rails 4 (muchos no lo tienen)

un hosting clásico puede costar desde 10 dólares al año, generalmente cuestan del orden de 20 dólares
mensuales, dan poco ram, y además el setup que tienen no está optimizado para Rails, usualmente
vienen con Apache y MySQL, pero NginX tiene mejor rendimiento en cuanto a usuarios / costo

Rails trabaja igual de bien con MySQL que con PostgreSQL sin embargo en un hosting clásico estás
limitado a eso al setup que trae, no puedes crear tu configuración propia

VPS

En un VPS te arriendan una máquina virtual, hay de dos tipos arriendas un servidor normal (Linode, Digital
Ocean, Etc) o un con escalamiento automático como Amazon.

El pro de los VPS es que obtienes un muy buen precio y una buena máquina, además puedes realizar el
setup que quieras.

El problema con los VPS es que tienes que realizar el setup de forma manual y puede llegar a ser un buen
trabajo.

PAAS

Existen diversos sistemas PAAS el más famoso es Heroku, de Salesforce, existen otros como Engine
Yard, estos sistema te permiten levantar rápido tu aplicación, además heroku tiene planes gratuitos que
son suficientes para subir tu primer prototipo y mostrarselo a los primeros clientes.

Por lo mismo en este capítulo nos enfocaremos en subir la aplicación a Heroku.

Instalando el Toolbelt de heroku


Primero debemos crearnos una cuenta www.heroku.com y luego descargar el toolbelt, el cual nos
permitirá lanzar comandos a heroku desde nuestro terminal.

El link al toolbelt debería aparecer después de crear la cuenta pero de todas formas puedes descargar el
toolbelt directamente desde toolbelt.heroku.com

Luego abriremos una nueva terminal y dentro de ella escribiremos:


1 heroku login

Claves SSH
Heroku intentará agregar nuestras claves ssh automáticamente, pero para eso supondrá que tu juego de
claves se llama id_rsa si no las tenemos al momento de instalarlo debemos crearlas y luego agregarlas,
para eso debemos ir a la carpeta .ssh dentro de tu carpeta personal y crear un nuevo juego de claves

Revisamos si existe el juego de claves

1 ls ~/.ssh

Las creamos sólo si no existen.

1 ssh-keygen -t rsa

luego podemos agregar las claves SSH automáticamente a heroku con:

1 heroku keys:add

También es posible copiar y pegar el contenido de la clave ssh pública (id_rsa.pub) a través de la interfaz
de heroku en la sección de configuración de cuenta.

Repositiorio GIT

Configurando GIT

1 git config --global user.name "YOUR NAME"


2 git config --global user.email "YOUR EMAIL ADDRESS"

Creando el repositorio git

Primero debemos estar trabajando sobre un repositorio GIT si no es así, lo crearemos con:
1 git init

Subiendo la aplicación a Heroku

Creando el proyecto en heroku

El primer paso es crear un proyecto en Heroku, esto lo podemos hacer a través del panel de control pero
es mucho más sencillo realizarlo directamente con bash en la carpeta del proyecto, los únicos requisitos
son tener GIT configurado y el toolbelt de Heroku instalado.

Entonces sobre un proyecto con repositorio GIT ya inicializado crearemos el proyecto en heroku con el
comando:

1 heroku create

Obtendremos algo similar a lo siguiente

Creating fierce-inlet-1619... done, stack is cedar-14


https://fierce-inlet-1619.herokuapp.com/ | https://git.heroku.com/fierce-inlet-161
9.git
Git remote heroku added

El nombre del subdominio será distinto.

Setup del proyecto para deployment

Ahora debemos cambiar el Gemfile de nuestro proyecto, Aquí hay que remover la línea que dice sqlite3
del gemfile (puesto que heroku no la soporta) y agregar la de postgreSQL y otra que permite obtener los
logs y arreglar algunos problemas de assets en producción.

1 gem 'sqlite3', group: :development


2 gem 'pg', group: :production
3 gem 'rails_12factor', group: :production

Como se hace siempre tras hacer un cambio en el archivo gemfile, debemos correr en el terminal el
comando para actualizar nuestras gemas.
Si no agregamos las gemas, o no hacemos bundle, o no hacemos el commit después obtendremos el
siguiente error.

remote: Gem files will remain installed in /tmp/build_f0e176e4ab15d0046ef15


66d12b46eb5/vendor/bundle/ruby/2.0.0/gems/sqlite3-1.3.11 for inspection.
remote: Results logged to /tmp/build_f0e176e4ab15d0046ef1566d12b46eb5/vendo
r/bundle/ruby/2.0.0/gems/sqlite3-1.3.11/ext/sqlite3/gem_make.out
remote: An error occurred while installing sqlite3 (1.3.11), and Bundler ca
nnot
remote: continue.
remote: Make sure that `gem install sqlite3 -v '1.3.11'` succeeds before bu
ndling.
remote: !
remote: ! Failed to install gems via Bundler.
remote: !
remote: ! Detected sqlite3 gem which is not supported on Heroku.
remote: ! https://devcenter.heroku.com/articles/sqlite3
remote: !
remote:
remote: ! Push rejected, failed to compile Ruby app

Esto se debe a que en Heroku no se puede instalar sqlite.

Opcionalmente también podemos especifica la versión de ruby a ocupar en heroku, si no


especifica se ocupará la 2.0.

Para especificar la versión de ruby debemos escribir la versión dentro del gemfile como en el siguiente
caso:

1 source 'https://rubygems.org'
2 ruby "2.2.3"
3
4 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5 gem 'rails', '4.2.3'

1 bundle

Luego estos cambios deben ser incorporados en el control de cambios de GIT.


1 git add Gemfile
2 git add gemfile.lock

Subiendo los cambios a producción

Primero debemos guardar los cambios realizados en GIT con:

1 git commit -m "cambios hechos"

Donde dentro de las comillas deberías especificar los cambios que se han realizado luego debemos
enviar los cambios a producción, esto es lo que comunmente se llama deployment.

1 git push heroku master

Razones por las que el paso de deployment puede fallar

No guardaste el Gemfile después de las modificaciones


No corriste la línea en bash bundle
No agregaste a GIT el Gemfile (y el Gemfile.lock) después del bundle
Tienes un error en los assets

Revisando errores

Si tienes un error en los assets, tenemos que encontrar el error y corregirlo, causas comunes es un
archivo con una extensión errada, ej algo.csc (en lugar de css).

Para debuggear esto podemos correr la línea:

1 rake assets:precompile RAILS_ENV=Production

No es bueno agregar estos assets al GIT, como vimos previamente esto correrá los preprocesadores
sobre los archivos respectivos, luego concatenara los CSS y JS, luego los minificará y finalmente les
añadirá un fingerprint, los assets finales quedarán dentro de la carpeta public.

Después debemos borrarlos dentro de la misma carpeta, no es una buena práctica agregarlos a GIT.
Sobre la precompilación de assets

La precompilación de assets es un proceso por el cual suceden las siguientes cosas:

Migrando la base de datos de producción

Recuerda migrar la base de datos de producción cada vez que migres la tuya.

1 heroku run rake db:migrate

Cambiando el nombre de la aplicación en Heroku

Si quieres cambiar el nombre de tu dirección entregada por heroku

1 heroku apps:rename nombrenuevo

Desafío
Subir al aplicación que creaste en el capítulo anterior a heroku, el formulario debe quedar funcionando.

Preguntas

1. ¿Para qué sirve Heroku?


2. ¿Cuál es la función del toolbelt?
3. ¿Con que comando podemos crear una aplicación nueva en heroku?
4. ¿Qué motor de base de datos ocupa heroku por defecto?
5. ¿Qué cuidados debemos tener en el gemfile antes de pushear a heroku?
6. ¿Qué es el entorno de producción?
7. ¿Cómo podemos especificar la versión de ruby a utilizar?
12) Configurando el dominio (o subdominio)
Una URL (Uniform Resource Locator) tiene la siguiente estructura

http(s)://subdominio.dominio.dominio nivel superior

Para entenderlo bien vamos a revisarlo en orden de importancia, o sea de derecha a izquierda.

Los dominios de nivel superior (ejemplo los .com, .cl, .mx) son entregados a diversas autoridades a lo
largo del mundo, estas autoridades se dedican a administirar y vender los dominios.

Los dominios son el nombre, es lo que uno le compra a las diversas autoridades a los largo del mundo,
algunas autoridades venden solo el suyo, otras como godaddy venden de múltiples tipos.

El valor de un dominio depende de múltiples factores pero principalmente depende de que tan solicitado
es, hoy en día es casi imposible tomar un dominio de 4 letras, y mucho menos uno que tenga sentido,
dominios de estos tipos se transan por millones de dólares.

Otros dominios pueden costar incluso de 5 dólares con un cupón de descuentos, fatwallet es una buena
página para buscar cupones.

Una vez que tienes el dominio puedes proceder a configurarlo, y puedes agregar todos los subdominios
que desees.

Entonces que es el subdominio?, es lo que antecede al dominio, el más famoso de todos es www, pero
puedes configrar el que quieras.

Una vez comprado el dominio lo importante es configurarlo para realizar un redireccionamiento tipo
Cname, algunos sistemas de los administradores no lo permiten, GoDaddy lo permite, Nic Chile por
ejemplo no, ya veremos como resolver eso más adelante.

En la dirección de redireccionamiento tenemos que ingresar el nombre de la aplicación.

Además de eso en la línea de comando dentro de la carpeta de la aplicación debemos agregar.

1 heroku domains:add nombre_dominio

si quieres agregar el www debes agregar también

1 heroku domains:add www.nombre_dominio

Si la configuración es sólo para un subdominio es posible


1 heroku domains:add nombre_subdominio.nombre_dominio

Para probarlo simplemente entra a la página configurada. Recuerda que la propagación de nombres es
lenta y por lo tanto puede demorar de 1 a 48 horas. (normalmente es una hora).

Configurando dominios cuyas autoridades no


soportan redireccionamiento tipo CName
Es posible lograrlo a través de un plugin de Heroku, hay dos buenos para realizar esto, son técnicamente
lo mismo, PointDNS y Zerigo, ambos tienen planes gratuitos al día de hoy, pero son empresas
tecnológicas y esa situación podría cambiar en el futuro, Heroku es una empresa de buenas prácticas y
no realiza cobros indebidos o sin previo aviso.

La configuración es sencilla:

1. Debes instalar el plugin desde el dashboard de heroku


2. Luego dentro de la página del plugin debes buscar los dns,
3. Utiliza los dns encontrados para ponerlos en la página de la autoridad
4. Debes configurar la página del plugin para que apunte a la aplicación con el nombre respectivo.
5. Igual que cuando configurabas el .com tienes que utilizar heroku domains:add nombre_dominio.cl

Tips de mantención

Entrar a la consola del proyecto en producción

1 heroku run rake console

Ver los últimos logs

muy útil cuando tienes errores en la versión remota pero en la local funciona bien

1 heroku logs
Dejar una consola con los logs corriendo

1 heroku logs -t

Descargando la base de datos de heroku

Es posible descargar la base de datos de heroku a tu computador, y utilizarla como respaldo o, mejor aún
utilizarla para trabajar con datos reales y sin el miedo de destruir datos importantes. El requisito es tener
instalado postgres en el computador, puesto que ese es el sistema de bases de datos que ocupa Heroku.

Paso 1: realizar una copia de la base de datos en amazon.

1 heroku pg:backups capture

Paso 2: recuperar desde amazon (ahí quedan alojadas) la base de datos

1 curl -o latest.dump `heroku pg:backups public-url`

Esto descargará un archivo que se llama latest.dump en la carpeta donde hayas lanzado la línea de
comandos.

Paso 3: cargar la base de datos descargada a tu sistema.

1 pg_restore --verbose --clean --no-acl --no-owner -h localhost -d nombreDeLaBaseDeDatos latest.du

Desafio
Comprar un dominio .com y configurar la aplicación creada en el capítulo anterior para que quede
funcionando con el dominio o con un subdominio.

Preguntas

1. ¿Cuál es la diferencia entre un dominio de nivel superior y un dominio?


2. ¿Qué es y para que sirven los subdominios?
3. ¿Donde se compra un dominio.com?
4. ¿Qué configuración debemos hacer para configurar nuestro .com?
5. ¿Qué significa DNS?
6. ¿Qué tipo de redirección no soporta Heroku?
7. ¿Que podemos hacer para configurar un dominio cuyo servidor de nombre
no es compatible con los redireccionamientos de Heroku?
8. ¿En qué consiste el modo de mantención de Heroku?
9. ¿Cómo podemos ver los logs de Heroku?
Sección III
SQL y bases de datos
13) SQL
SQL es un lenguaje que permite realizar consultas de bases de datos, es del tipo declarativo porque uno
en lugar de especificar el como obtener los resultados uno simplemente pide lo que necesita y SQL lo
devuelve.

Existen diversas implementanciones de SQL, siendo las más famosas MySQL, PostgreSQL y Oracle, hoy
en día MySQL le pertenece a Oracle y a pesar de que algunas implementaciones siguen bajo licencia GPL
también ha incorporado diversas licencias comerciales lo que empujó a diversos miembros de la
comunidad a moverse a PostgreSQL, ahora la principal razón para utilizar Postgre no es el tema de las
licencias si no el soporte de diversos tipos de datos nativos que funcionan muy bien con Ruby on Rails.

PostgreSQL
Rails es un framework agnóstico a la base de datos, esto quiere decir que puede ser configurado con
cualquiera de ella mientras existan los drivers, y existen drivers para todas las bases de datos conocidas.

Al día de hoy PostgreSQL es la mejor opción para trabajar con rails, primero porque viene configurado
con heroku y homologar los entornos de desarrollo con el de producción facilita el desarrollo y pruebas
del software, pero además Postgres incluye diversas funcionales como manejo nativos de array y de
hashs que abordaremos más adelante en este libro.

Instalando PostgreSQL en OSX


Para instalar postgreSQL podemos descargar la aplicación postgreAPP esta automáticamente creará un
usuario con el mismo nombre de usuario del sistema.

Esta forma es fácil de prender y apagar y nos aseguramos de que el servidor no esté corriendo de fondo
gastando recursos en nuestro computador.

Instalando PostgreSQL en Linux


Podemos instalar PostgreSQL en linux utilizando apt-get

1 apt-get install postgre sql-9.4

También es posible instalar la interfaz gráfica de pgadmin http://www.pgadmin.org/


Entrando a PSQL
Una vez instalado y corriendo psql

Ahora necesitamos entrar a PostgreSQL para crear una base de datos. En linux

1 su - postgres

En OSX

1 psql

Para que lo anterior funcione, el servicio debe de estar corriendo, pues en caso contrario obtendremos
un:

1 psql: could not connect to server: No such file or directory


2 Is the server running locally and accepting
3 connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Para entrar a psql tenemos que

1 psql

Insensible a las mayúsculas

Una curiosidad que tiene SQL es que es un lenguaje insensible a las mayúsculas, la convención consiste
en escribir las palabras reservadas de SQL en mayúsculas, y los nombres de las tablas y valores en
minúsculas

Creando usuarios

Luego podemos crear un usuario con:

1 CREATE USER nombre_usuario;


Cambiando la clave

En postgres los usuarios y los roles son lo mismo, podemos cambiar el password de un usuario con:

1 ALTER ROLE x WITH password 'xx';

Dando acceso de superusuario

1 ALTER ROLE nombre_usuario WITH superuser;

Listando a todos los usuarios creados en la base de datos.

Podemos listar a todos los usuarios para ver si los creamos exitosamente.

1 gonzalosanchez=# \du

Role name Attributes Member of

desafio_blog Superuser {}

super_gonzalo Superuser {}

admin Superuser, Create role, Create DB, Replication {}

Creando una base de datos

En SQL las tablas son archivos, archivos que contienen un solo formato y se parecen en cierto sentido a
un excel, sólo que las columnas son fijas.

Cuando creamos una tabla definimos una estructura, cuando insertamos datos no podemos salirnos de
la estructura de la tabla definida, sin embargo en el futuro podemos cambiar la estructura de la tabla
siempre y cuando especifiquemos que hacer con los datos que existen actualmente.

Una base de datos es un conjunto de tablas que pueden o no estar relacionadas entre ellas.

Para crear una base de datos simplemente debemos utilizar el comando


1 CREATE DATABASE nombre_base_de_datos;

Listando una base de datos.

Postgre al igual que otros motores de bases de datos maneja múltiples bases de datos, y cada usuario
del sismte

Para listar todas las bases de datos lo podemos hacer con \l

Botando una base de datos

Para poder borrar una base de datos existente utilizaremos el comando

1 DROP DATABASE nombre_base_de_datos

Por lo mismo en lugar de borrar se dice botar una base de datos, por el drop.

No hay vuelta atrás de este comando, por lo mismo hay que utilizarlo con mucha
responsabilidad y siempre respaldar las bases de datos, especialmente antes de hacer este
tipo de operaciones.

Conectándose a una base de datos

Para poder ver los datos de las diversas tablas que hay dentro de una base de datos necesitamos
primero conectanos a una, eso lo podemos lograr con:

1 \c nombre_base_de_datos

Para poder conectarnos a una base de datos necesitamos estar dentro de Postgres con un usuario que
tenga permisos para poder ver esa base de datos.

Manejo de tablas

Finalmente una vez dentro de la base de datos podemos hacer queries a las tablas, para saber que tablas
hay dentro de la base de datos con

1 \t

Para poder crear tablas, que son archivos con estructuras fijas, primero vamos a tener que entender un
poco más de estas estructuras, y para eso hay que entender que cada columna tiene un nombre y un tipo
de dato.

Columna = Nombre + Tipo de dato

Introducción a tipos de datos.

Los tipos de datos básicos en Postgres 9.4 se dividen en diversas categorías:

Numéricos, Monetarios, de caractéres, binarios, de fechas, booleanos, de enumeración, geométricos e


incluso existen tipos de datos para manejar json, xml, hashs y arrays.

Los detalles exacto de los tipos de datos no los abordaremos en este libro pero pueden ser consultados
en la doumentación oficial de PostgreSQL. http://www.postgresql.org/docs/9.4/static/datatype.html

Partamos creando una tabla con los tipos más simple, integer y varchar

integer, que permite guardar enteros de hasta 4 bytes, o sea números entre -2147483648 to
+2147483647

varchar(n) el cual permite almacenar strings de hasta n caracteres

Creando una tabla

La sintaxis básica para crear tablas es la siguiente:

CREATE TABLE table_name( column1 datatype, column2 datatype, column3 datatype, ….. columnN
datatype );

Ahora con esto nosotros guardaremos datos en una tabla que almacena personas con nombre y edad

1 CREATE TABLE personas(


2 name varchar(64),
3 age integer
4 );
Manipulando valores de una tabla

Insertando valores

Para insertar valores dentro de la tabla podemos hacerlo con el comando INSERT, la sintaxis básica es la
siguiente:

1 INSERT INTO table_name VALUES ('valor_campo1', 'valor_campo2');

Para insertar valores en la tabla persona, insertamos un nombre y una edad.

1 INSERT INTO personas VALUES ('Camila', '26');


2 INSERT INTO personas VALUES ('Gonzalo', '30');

Leyendo valores de una tabla.

Para leer datos de una tabla la instrucción es SELECT, con SELECT nosotros podemos especificar los
valores que queremos o utilizar * para indicar que queremos todos los campos de la tabla.

1 SELECT * FROM personas;

name age

Camila 26

Gonzalo 30

(2 rows)

Si queremos sólo un campo podemos indicarlo

1 SELECT name FROM personas;


name

Camila

Gonzalo

Actualizando valores de una tabla

Para cambiar valores ocuparemos la instrucción update

1 UPDATE personas SET age = 28;

Sorpresivamente obtendremos lo siguiente:

UPDATE 2, o sea se cambiaron 2 valores, si ahora mostramos todas las personas con
SELECT * FROM personas; veremos:

name age

Camila 28

Gonzalo 28

Para cambiar sólo una persona debemos acompañar el update de la instrucción where

1 UPDATE personas SET age = 30 WHERE name = 'Gonzalo'

Debemos cuidar las comillas, estas tienen que ser comillas simples cuando remplazemos valores por
strings, en lugar pueden ir sin comillas cuando los valores sean numéricos.

1 UPDATE personas SET age = 31 WHERE age = 30;

Borrando datos de una tabla

Podemos borrar datos ocupando la instrucción delete, pero debemos tener mucho cuidado de ocupar el
where o borraremos todos los datos.

1 DELETE FROM personas;


DELETE 2 << UPS

ahora para la siguiente prueba vamos a insertar nuevamente los valores.

1 INSERT INTO personas VALUES ('Julian', '55');


2 INSERT INTO personas VALUES ('Fernanda', '19');
3 INSERT INTO personas VALUES ('Paulina', '20');

Podemos borrar bajo la condición de igualdad, pero también podemos borrar bajo otras condiciones,
como en el siguiente ejemplo:

1 DELETE FROM personas WHERE age < 25;

Modificando una tabla


Previamente dijimos que la estructura de una tabla es fija y no puede ser cambiada, pero eso se refiere en
cuanto a que la inserción de datos no puede salirse de la estructura de la tabla, sin embargo en SQL
tenemos una instrucción para cambiar el formato de la tabla, y ese es ALTER

Agregando una columna

1 ALTER TABLE personas ADD COLUMN id VARCHAR(16);

Removiendo una columna

1 ALTER TABLE personas DROP COLUMN id;

Constraints
Los constraints son reglas que creamos para cuidar la integridad operacional de la base datos, o sea que
los datos que tengamos cumplan con las reglas del negocio, algunos son muy obvios como por ejemplo
que el precio o el stock de un producto no puedan ser negativos, otros son exclusivos del negocios, por
ejemplo tener x productos comprados para poder obtener ciertos descuentos.

Creemos la tabla productos para probarlo


1 CREATE TABLE products (
2 name VARCHAR(100),
3 price NUMERIC CHECK (price > 0)
4 );

Si intentamos ingresar un producto con precio negativo obtendremos:

1 ERROR: new row for relation "products" violates check constraint "products_price_check"

Evitando valores nulos

Al momento de crear la tabla, o de alterarla podemos establecer que una columna no pueda tener valores
nulos, esto es muy conveniente para evitar errores de integridad de datos.

1 CREATE TABLE products2 (


2 name VARCHAR(100) NOT NULL,
3 price NUMERIC CHECK (price > 0)
4 );

Si intentamos insertar productos con:

1 INSERT INTO products2 VALUES (NULL, 100);

1 INSERT INTO products2 (price) VALUES (100);

ERROR: null value in column "name" violates not-null constraint DETAIL: Failing row contains (null, 100).

Eso no evita que podamos ingresar valores vacíos

El constraint Unique
1 CREATE TABLE products3 (
2 name VARCHAR(100) UNIQUE,
3 price numeric CHECK (PRICE > 0)
4 );

1 INSERT INTO products3 (name, price) VALUES ('producto 1', 100);


2 INSERT INTO products3 (name, price) VALUES ('producto 1', 100);

Obtendremos primero un insert y luego un error de duplicated key, puesto que el nombre debe ser único.

La clave primaria

La clave primaria es una combinación entre los constraints NOT NULL y UNIQUE, pero además es un
índice, que en este caso permite encontrar de forma rápida los resultados, y además te asegura que sea
único los resultados, esto en la mayoría de los casos hace sentido, por ejemplo todos los autos tienen
una patente la cual es única dentro de cada país, y esta clave permite encontrar todos los datos
referentes a ese auto específico, en la mayoría de los países existe un identificador asociado a las
personas, (RUT, RUN, RFC, nº de seguridad social, etc..)

Para agregar una clave primaria tenemos que alterar la estructura de la tabla puesto que vamos a agregar
un índice que en cierto sentido es como agregar una columna.

1 ALTER TABLE personas ADD PRIMARY KEY (id);

Si hemos ido siguiendo la secuencia de este libro en este momento obtendremos el siguiente error.

ERROR: column "id" contains null values

El error dice que una columna con clave primaria no puede tener valores null y eso tiene mucho sentido
ya que este es un identificador único.

Por lo mismo ahora tenemos que darles id a todas las personas que tengamos en la base de datos, si has
seguido la secuencia debería ser sólo una persona.

1 SELECT * FROM personas;

name age id

Julian 55
1 UPDATE personas SET id=1 WHERE name='Julian';

y ahora si podremos cambiar la tabla personas

1 ALTER TABLE personas ADD PRIMARY KEY (id);

Si todo está bien en lugar de error, obtendremos como output alter table, pero ahora nunca más
podremos ingresar usuarios sin id o con un id que esté repetido.

Por ejemplo, si hacemos un insert ahora con el mismo id

1 INSERT INTO personas VALUES ('Julian', 55, 1);

Obtendremos:

ERROR: duplicate key value violates unique constraint "personas_pkey"


DETAIL: Key (id)=(1) already exists.

Pero si podemos hacerlo si realizamos un INSERT con:

1 INSERT INTO personas VALUES ('Julian', 55, 2);

Creando una tabla con primary key

Es posible crear directamente una tabla con clave primaria, la sintaxis es la siguiente:

1 create table ejemplo1 (


2 columna1 tipo primary key,
3 columna2 tipo
4 )

Ordenando los resultados.


Para esta sección necesitaremos un mini set de datos, para eso borraremos todos los que tenemos e
ingresaremos nuevos.
1 DELETE FROM personas;
2 INSERT INTO personas VALUES ('Francisca', 30, 1);
3 INSERT INTO personas VALUES ('Juan', 31, 2);
4 INSERT INTO personas VALUES ('Javier', 32, 3);
5 INSERT INTO personas VALUES ('Penelope', 28, 4);

Mostrando todos los resultados

1 SELECT * FROM personas;

name age id

Francisca 30 1

Juan 31 2

Javier 32 3

Penelope 28 4

Mostrando los resultados ordenados por edad

Para ordenar por algún criterio tenemos que agregar order by y la columan sobre la cual vamos a ordenar.

1 SELECT * FROM personas ORDER BY age;

name age id

Penelope 28 4

Francisca 30 1

Juan 31 2

Javier 32 3

Mostrando los resultados ordenados por nombre alfabéticamente


pero en reverso.

Esto lo logramos ocupando DESC en la query.


1 SELECT * FROM personas ORDER BY name DESC;

name age id

Penelope 28 4

Juan 31 2

Javier 32 3

Francisca 30 1

(4 rows)

Limitando la cantidad de resultados

Podemos limitar los resultados agregando al query la instrucción LIMIT

1 SELECT * FROM personas LIMIT 1;

Es combinable con otras instrucciones por ejemplo con las de ordenamiento

1 SELECT * FROM personas ORDER BY name DESC LIMIT 1;

Conteo

Aquí la sintaxis es ligeramente distinta, en lugar de seleccionar una tabla en específico seleccionaremos
la cuenta de elementos de esa tabla

1 SELECT COUNT(*) FROM personas;

En este caso obtendremos 4.

Select distinct

Para los siguientes ejemplos necesitamos un par de datos más, para eso vamos a ingresar a otra mujer
llamada Penelope en la base de datos de distinta edad.
Nuestro set de datos quedaría:

1 SELECT * FROM personas;

name age id

Francisca 30 1

Juan 31 2

Javier 32 3

Penelope 28 4

Penelope 30 5

Podemos seleccionar de la base de datos todos los nombres distintos, o sea si hay un nombre repetido
no aparecerá en la respuesta.

1 SELECT DISTINCT(name) FROM personas;

name

Javier

Juan

Penelope

Francisca

Un ejemplo donde esto podría ser muy útil es si queremos extraer de nuestra base de datos cuantas
personas hay de cada país o ciudad (o ambas)

Distinct y count
Podemos combinar distinct y count para contar la cantidad de elementos distintos.

1 SELECT COUNT(DISTINCT(age)) FROM personas;

Con los datos de nuestro ejemplo deberíamos obtener 4, y si hacemos el count sin el distinct deberíamos
obtener 5
Preguntas

1. ¿Qué tipo de lenguaje es SQL?


2. ¿Cómo se entra a SQL?
3. ¿Cómo crear un usuario en psql?
4. ¿Qué comando muestra todas las bases de datos dentro de postgresql?
5. ¿Qué comando muestra todas las usuarios dentro de postgresql?
6. ¿Cuál es la diferencia entre una base de datos y una tabla?
7. ¿Cómo se le da permisos a un usuario para poder modificar una base de
datos?
8. ¿Cómo insertar datos en una tabla existente
9. ¿Qué es la clave primaria?
10. ¿Por qué es importante especificar el where en las instrucciones de update y
delete?
11. ¿Cómo se puede borrar una base de datos?
12. ¿Cómo se puede borrar una tabla?
13. . ¿Cómo se puede obtener la persona de mayor edad de la tabla personas?
14. ¿Qué hace select distinct?
15. ¿Qué son los constraints?

Guía de ejercicios

Peliculas

Completar los queries y poner la consulta SQL repectiva de cada pregunta subirlos a la plataforma de
empieza.

CRUDS

1. Crear la base de datos movies


2. Crear la tabla movie con la clave primaria id y nombre
3. Ingresar la película El Rey León
4. Ingresar la película Terminator II
5. Alterar la tabla películas para agregar el año
6. Cambiar los datos de todas las películas existentes a 1984
7. Borrar la película Terminator II
8. Crear un usuario nuevo en la base de datos
9. Asignarle un rol que sólo permite hacer consultas select (no podrá ingresar)
10. Cambiar de usuario en la base de datos
11. Probar que no puede ingresar una película

Sorting

1. Ingresar 5 películas más, con nombres y años distintos.


2. devolver las primeras 3 películas (ordenadas alfabéticamente)
3. devolver las últimas 3 películas (ordenadas por año)

Conteo

1. Contar la cantidad de películas en la base de datos


2. Contar la cantidad de películas por año.
3. Alterar la tabla para agregar la categoría de la película
4. Agregar categorías a todas las películas existentes
5. Obtener un listado de las categorías (sin repeticiones)

Productos

Completar los queries y poner la consulta SQL repectiva de cada pregunta subirlos a la plataforma de
empieza.

1. Crear la base de datos productos


2. Crear la tabla productos con id, nombre, precio
3. id debe ser la clave primaria, agregar el contraint de que el precio debe ser mayor que cero y el
nombre del producto único.
4. Insertar 10 productos
5. Contar la cantidad de elementos que hay, se deberían obtener 10
6. Contar la cantidad de elementos que hay con precio mayor a 1000
7. Contar la cantidad de elementos que hay con precios distintos
8. Ordenar los productos por precio
14) SQL con más de una tabla

Integridad referencial
La integridad es un concepto asociado a la calidad y validez de los datos, por ejemplo supongamos
como en el caso del ejercicio anterior tenemos películas y categorías pero que pasa si queremos
renombrar una categoría, entonces tendríamos que asegurarnos de cambiar todas las referencias a la
categoría en la tabla películas, en una base de datos pequeñas esto podría no ser un problema pero en
bases de datos grandes es más complejo.

¿Cómo se soluciona el problema de las referencias?

Separando una tabla en dos partes, la primera contiene los datos donde insertaremos la película, la
segunda es una tabla donde insertaremos todas las categorías, y las relacionaremos a través de un
número.

De esta forma sólo tenemos que cambiar el valor dentro de la categoría para cambiar todos los valores
referidos.
1 create table movies (
2 id integer primary key,
3 title varchar(64),
4 category_id integer
5 );
6
7 create table categories (
8 id integer primary key,
9 name varchar(64)
10 );
11
12 INSERT INTO categories VALUES (1, 'Acción');
13 INSERT INTO categories VALUES (2, 'SCI FI');
14 INSERT INTO categories VALUES (3, 'Animación');
15
16 INSERT INTO movies VALUES (1, 'Terminator', 1);
17 INSERT INTO movies VALUES (2, 'Terminator 2', 1);
18 INSERT INTO movies VALUES (3, 'Volver al futuro', 2);
19 INSERT INTO movies VALUES (4, 'Terminator 3', 1);
20 INSERT INTO movies VALUES (5, 'Volver al futuro 2', 2);
21 INSERT INTO movies VALUES (6, 'Tiburón', 1);
22 INSERT INTO movies VALUES (7, 'Akira', 3);
23 INSERT INTO movies VALUES (8, 'Ghost in the Shell', 3);
24 INSERT INTO movies VALUES (9, 'Duro de matar', 1);

Luego podemos juntar ambas tablas ocupando la instrucción SELECT

1 SELECT * FROM movies, categories;

id title category_id id name

1 Terminator 1 1 Acción

2 Terminator 2 1 1 Acción

3 Volver al futuro 2 1 Acción

4 Terminator 3 1 1 Acción

5 Volver al futuro 2 2 1 Acción

6 Tiburón 1 1 Acción

7 Akira 3 1 Acción

8 Ghost in the Shell 3 1 Acción

9 Duro de matar 1 1 Acción

1 Terminator 1 2 SCI FI
2 Terminator 2 1 2 SCI FI

3 Volver al futuro 2 2 SCI FI

4 Terminator 3 1 2 SCI FI

5 Volver al futuro 2 2 2 SCI FI

6 Tiburón 1 2 SCI FI

7 Akira 3 2 SCI FI

8 Ghost in the Shell 3 2 SCI FI

9 Duro de matar 1 2 SCI FI

1 Terminator 1 3 Animación

2 Terminator 2 1 3 Animación

3 Volver al futuro 2 3 Animación

4 Terminator 3 1 3 Animación

5 Volver al futuro 2 2 3 Animación

6 Tiburón 1 3 Animación

7 Akira 3 3 Animación

8 Ghost in the Shell 3 3 Animación

9 Duro de matar 1 3 Animación

El problema es que hacerlo nos da el producto cartesiano entre ambas tablas, o sea todos los datos con
todos los otros, que obviamente no es lo que queremos, para obtener los resultados que buscamos
tenemos que utilizar la instrucción where.

1 SELECT * FROM movies, categories WHERE categories.id = movies.category_id;


id title category_id id name

1 Terminator 1 1 Acción

2 Terminator 2 1 1 Acción

3 Volver al futuro 2 2 SCI FI

4 Terminator 3 1 1 Acción

5 Volver al futuro 2 2 2 SCI FI

6 Tiburón 1 1 Acción

7 Akira 3 3 Animación

8 Ghost in the Shell 3 3 Animación

9 Duro de matar 1 1 Acción

Y estos si son los resultados que queremos.

Ambiguedad en los nombres

Cuando hay campos con los mismos nombres en ambas tablas no podemos ser ambiguos, por ejemplo
si realizamos el query:

1 SELECT * FROM movies, categories WHERE id = category_id;

ERROR: column reference "id" is ambiguous LINE 1: SELECT * FROM movies, categories WHERE id =
category_id;

Es por lo mismo que en la mayoría de los casos tendremos que utilizar tanto el nombre de la tabla como
el nombre del campo.

Joins
Otra forma de lograr resultados similares es utilizando la instrucción JOIN

1 SELECT * FROM movies INNER JOIN categories ON (categories.id = movies.category_id


Tipos de joins

Hay diversos tipos de join y principalmente difieren en como tratar la unión de estos conjuntos cuando los
valores no están asociados.

Para entender bien la diferencia vamos a ingresar una película sin categoría y una categría sin película.

1 INSERT INTO movies VALUES (10, 'Película sin categoría');


2 INSERT INTO categories VALUES (4, 'Categoría sin películas');

Luego si probamos:

1 SELECT * FROM movies, categories WHERE categories.id = movies.category_id;

O si probamos:

1 SELECT * FROM movies INNER JOIN categories ON (categories.id = movies.category_id

En ambos casos no obtendremos resultados ni de la película nueva ni de la categoría nueva.


Left Outer Join

1 SELECT * FROM movies LEFT OUTER JOIN categories ON (categories.id = movies.category_id

id title category_id id name

1 Terminator 1 1 Acción

2 Terminator 2 1 1 Acción

3 Volver al futuro 2 2 SCI FI

4 Terminator 3 1 1 Acción

5 Volver al futuro 2 2 2 SCI FI

6 Tiburón 1 1 Acción

7 Akira 3 3 Animación

8 Ghost in the Shell 3 3 Animación

9 Duro de matar 1 1 Acción

10 Película sin categoría

Right Outer Join

1 SELECT * FROM movies RIGHT OUTER JOIN categories ON (categories.id = movies.category_id


id title category_id id name

1 Terminator 1 1 Acción

2 Terminator 2 1 1 Acción

3 Volver al futuro 2 2 SCI FI

4 Terminator 3 1 1 Acción

5 Volver al futuro 2 2 2 SCI FI

6 Tiburón 1 1 Acción

7 Akira 3 3 Animación

8 Ghost in the Shell 3 3 Animación

9 Duro de matar 1 1 Acción

4 Categoría sin películas

Full Outer Join

1 SELECT * FROM movies FULL OUTER JOIN categories ON (categories.id = movies.category_id

Tipos de relaciones
Recientemente separamos la tabla películas en dos tablas para evitar tener datos repetidos como el
nombre de la película dentro de cada una.

Al tener dos tablas se generó una relación entre ellas, podemos decir que una película tiene una
categoría, pero una categoría puede tener múltiples películas, este tipo de relaciones se llama de uno a
muchos, puesto que a un elemento de una tabla de la base de datos le corresponden varios elemetnos
de la otra tabla.

Hay tres tipos de relaciones famosas.

Relaciones de uno a uno.


Relaciones de uno a muchos.
Relaciones de muchos a muchos.
Relaciones de uno a uno

Las relaciones de uno a uno consisten en dividir un tabla que tiene muchas columnas en dos, para esto
supongamos que estamos construyendo un software de ventas y tenemos una tabla de usuarios.

Donde la tabla tiene:

nombre
edad
direccion
telefono
tarjeta de credito
rut (identificador de la persona)
compañia
direccion compañia
telefono compañia
cargo

En una tabla como esta podemos separar los datos del usuario con los de la compañía, y sólo llenarlos si
la persona tiene compañía, entonces quedaría la tabla usuarios y la tabla de compañías, pero para unirlas
tenemos que hacer una de dos, guardar un id de la compañía en la tabla usuarios o guardar el usuario en
la tabla de compañía, determinar cual es la mejor forma tiene que ver más con el diseño de la aplicación
que con el de la base de datos, pero la pregunta de rigor que nos haríamos es que se guardará primero,
si creamos el usuario y luego la compañía es más sencillo guardar el usuario en la compañía, si creamos
primero la compañía y luego al usuario probablemente sea más sencillo guardar el dato de la compañía
en el usuario.

Hay diversas formas de diagramar las relaciones, usualmente se representa con una flecha cuando tiene
un elemento y con doble flecha cuando tiene más de un elemento.
Relaciones de 1 a n

Estas son las relaciones más frecuentes, normalmente sucede cuando hay una tabla de datos y una tabla
de categoría de esos datos, por ejemplo: persona y pais, o, ciudad o país, pelicula y categoría. En otros
casos sucede cuando también hay un dueño de muchas cosas, por ejemplo un autor y sus posts, o un
autor y sus comentarios.

Relaciones de n a n

Estas relaciones se dan cuando la autoría de algo es compartida, por ejemplo un proyecto puede tener
varios integrantes, pero a su vez un integrante puede tener varios proyectos, o una persona puede tener
permisos para varias secciones, pero cada sección puede ser accedida por varios usuarios.

Estas relaciones son un poco más complejas porque no se pueden representar directamente en un
modelo de datos relacional, porque donde pondríamos los índices?, si los agregamos por el lado de
usuarios un proyecto_id como haríamos para meter varios proyectos?, por otro lado si lo agregamos del
lado del proyecto con usuario_id como haríamos para meter varios usuarios?

La solución es convertir la relación de n a n en dos relaciones de 1 a N.

De esta forma pueden haber cuantas personas quieran trabajando en cuantos proyectos quieran.

La clave foránea
La clave foránea (en ingés Foreign Key o FK) es un índice que permite mantener la integridad referencial
entre dos tablas, principalmente evita que se borre un elemento de una tabla si hay otros que se refieran a
el.
En nuestro ejemplo anterior no podríamos borrar la categoría acción porque hay películas que utilizan esa
categoría.

1 CREATE TABLE movies2 (


2 id integer PRIMARY KEY,
3 category_id integer REFERENCES categories (id)
4 );

Ahora si intentamos ingresar una película con una categoría que no existe, obtendremos:

1 Insert into movies2 values (2, 10);

ERROR: insert or update on table "movies2" violates foreign key constraint "movie
s2\_category\_id\_fkey"
DETAIL: Key (category_id)=(10) is not present in table "categories".

Preguntas

1. ¿Cuál es la diferencia entre los outer y los inner joins?


2. ¿Cuál es la diferencia entre `SELECT * FROM movies, categories;` y `SELECT
* FROM movies, categories WHERE categories.id = movies.category_id;`?
3. ¿Cuál es la diferencia entre SELECT * FROM movies, categories WHERE
categories.id = movies.category_id; e y realizar el mismo select con inner
join?
4. ¿Por qué es importante en el select nombrar la tabla?
5. ¿Para qué sirven las relaciones de 1 a 1?
6. Nombre 5 ejemplos donde sería útil una relación de 1 a n
7. Nombre 5 ejemplos donde sería útil una relación de n a n
8. ¿Para qué sirve la clave foránea?
9. ¿Qué significa FK?

Ejercicios
Shopping

Crear la base de datos shopping101 y dentro de ella.


Crear la tabla compradores, con id y nombre
Ingresar al menos 5 compradores

Crear la tabla país con id y nombre


Ingresar al menos 5 países

Alterar la tabla compradores para agregar la columna pais_id


Agregar un par de usuarios que no tenga paises asignados

Obtener todos los usuarios con todos los paises


Obtener todos los países que no tienen asigando ningún usuario
Obtener la cantidad de usuarios de cada país
Obtener al país con mayor cantidad de usuarios

movieDB

1. Crear la base de datos moviedb ####Relaciones 1 a n


2. Crear la tabla category
3. agregar la columna category_id a movie
4. Ingresar 3 categorías de películas
Acción
Terror
Drama

5. Ingresar las FK para relacionar las películas existentes con las categorías respectivas
6. Obtener todas las películas de la categoría Acción y contarlas
7. Ordenar la tabla de categorías según la cantidad de películas que hay con esas categorías

Relaciones n a n

1. Crear la tabla tags, con una clave primaria id, y el campo tag
2. crear la tabla movie_tags con la clave primaria id, y las claves foraneas tag_id y movie_id
3. Ingresar un grupo de tags
4. Asignar 3 tags a cada películas (puedes ocupar más de un insert)
5. Obtener todas los nombres de las películas con el tag dinosaurios
6. Utilizando joins, devuelve todas las películas con todos sus tags
7. Contar la cantidad de tags que tiene cada película -hint tienes que hacer dos joins en el mismo query
8. Devolver los tags ordenados por mayor uso -hint hacer el join sólo con la tabla intermedia
Exportando datos

1. Exportar todos los datos en un archivo SQL


14) Modelando con SQL

Objetivos

1. Repasar SQL a través de casos prácticos


2. Modelar diversos ejemplos típicos de aplicaciones

El secreto para modelar con SQL es distinguir bien cuales son las tablas y cuales son los atributos de
esas tablas, por ejemplo supongamos que queremos hacer un blog, entonces claramente un blog tiene
artículos, ahora los comentarios son un campo de artículo o una tabla aparte?, el título del artículo es un
campo del blog o no?, todo ese tipo de preguntas tenemos que poder hacernos para poder modelar con
SQL. Es muy importante tener experiencia en esto para trabajar en rails puesto que Rails no modela por
nosotros, sólo nos genera las consultas SQL de forma automática.

Modelando un blog
Lo primero que tenemos que hacer es distinguir a los actores y las acciones principales.

Entonces: - una visita entra y ve artículos - una visita entra al artículo puede leerlo y ver sus comentarios -
una visita se registra y se convierte en un usuario - un editor (el cual es un tipo de usuarios puede crear
articulos nuevos) - un editor puede borrar artículos y comentarios - al borrar un articulo se deben borrar
todos sus comentarios

La pregunta de rigor al modelar es ¿qué infomración necesitamos utilizar? y para poder utilizarla ¿quçe
necesitamos guardar?.

Respecto a nuestro blog tenemos que considerar algunas preguntas y comentarios

¿Vamos a guardar las visitas en la base de datos o no?, quizas no sea útil ya que estas no registran
nada, si queremos medir el acceso de visitas podemos hacerlo utilizando alguna solución como
google analytics y no tenemos la necesidad de meter estos datos en nuestra base de datos.

Los artículos necesitamos guardarlos, porque vamos a mostrar esa información, cada artículo puede
tener un título y una foto.

Los usuarios necesitamos guardarlos, y surge de aquí un tipo de usuario el cual es el editor, por lo
tanto tenemos que guardar el tipo del usuario dentro de la base de datos para consultarlo después.

Hay comentarios, y estos tenemos que guardarlos para poder mostrarlos, y le pertenecen a un
usuario, pero además uno comenta dentro de un artículo, y dentro de un artículo pueden haber varios
comentarios, así que los comentarios le pertenecen a un artículo.

Entonces el modelo quedaría:

Las convenciones son muy importantes, especialmente cuando trabajamos con Rails, una de estas
convenciones es la del nombre de las claves foráneas, el nombre debe ser el nombre de la tabla referida
en singular con un sufijo id.

O sea si la tabla comentario tiene usuarios, la FK debería llamarse usuario_id y en lo posible todos los
nombres en inglés para evitar problemas.
Ejercicio
Crear la blase de datos blogX
Crear un usuario llamado "Julian" del tipo editor
Crear 5 artículos y asociarlos al usuario creado
Crear 3 usuarios más y agregar comentarios a los artículos creados
Mostrar todos los articulos con la cantidad de comentarios de cada uno
Mostrar todos los usuarios con la cantidad de comentarios creados de cada uno
Mostrar los articulos junto con la información del editor.

Modelando Tinder
Tinder es una famosa red social de citas, que tiene usuarios y tu ves personas marcando si te gustas o
no, si ambas se marcan como gustados entonces es un match, ahora hay múltiples formas de modelar
esto, pero analizemos alguna.

La pregunta entonces es, que actores hay, y que información nos interesa guardar.

Claramente necesitamos a los usuarios con sus fotos, en este momento no es importante que los
usuarios se saquen de Facebook porque necesitamos guardarlos de todas formas en nuestra base de
datos.

Por otro lado lo que nos interesa guardar son dos cosas, una los likes, un usuario hace like a otro,
entonces un usuario da y recibe likes, cuando existe un like del usuario uno al dos y existe uno del dos al
uno entonces hacemos match.

En nuestra aplicación puede (o puede que no) importarnos de que likes viene el match, una vez que ya lo
hacemos podemos darnos el lujo de perder esa información, pero si debemos saber entre que usuarios
se hizo match.

Ahora en el funcionamiento de la aplicación, se encuentran personas que están cerca, por lo tanto
tenemos que saber donde están esas personas, para eso agregaremos latitud y longitud, y además no
podemos mostrarle usuarios que ya haya descartado a una persona, por lo que tenemos que guardar la
información de si la interacción fue de like o unlike, para eso renombraremos la tabla like a interacciones
y guardaremos cada interacción entre un usuario uno y otro.
Ejercicios de diagrama
Modificar el diagrama para que el usuario pueda agregar todas las fotos que quiera.
Agregar la tabla de mensajes para guardar los mensajes entre usuarios.

Ejercicios de SQL
Crear las tablas en el modelo
Agregar 10 usuarios
Agregar 4 interacciones positivas
Agregar 4 interacciones negativas
Obtener todos los usuarios con los que hayas tenido una interacción negativa
Obtener todos los usuarios con los que hayas tenido una interacción positiva
Obtener todos los usuarios con los que no hayas tenido una interacción.
Obtener todos los nombres de los matches de un usuario.

Modelando un carro de compras


El carro de compras más básico consiste en un carro donde guardamos los items que queremos y
después procedemos a hacer el chekcout.

Una pregunta interesante que uno debe hacerse es, ¿nos interesa guardar el carro de compras?, es
perfectamente posible que solo nos interese guardar los items comprado y no el carro mismo, muchas
veces el carro solo se guarda en la sesión del navegador.
Como no podemos tener relaciones de n a n romperemos la tabla ocupando una intermedia
Ejercicio
Crear las tablas en la base de datos.
Ingresar 5 items
Ingresar 2 usuarios
Generar una orden de compra con dos items
Obtener todos los items que ha comprado un usuario
Obtener los items más comprados
Sección IV: Backend
15) Rails con PSQL

Objetivos

1. Aprender a crear un proyecto rails con PostgreSQL como motor de base de


datos
2. Aprender a modificar un proyecto rails existente para agregar PostgreSQL
como motor de base de datos

Instalando postgre en un proyecto existente


En ruby todas las componentes se llaman gemas, en este caso instalaremos los drivers de postgre para
ruby utilizando

1 gem install pg

Luego debemos cambiar el archivo de configuración de desarrollo, este se encuentra en el archivo


database.yml.

1 development:
2 adapter: postgresql
3 encoding: unicode
4 database: nombre_base_de_datos
5 pool: 5
6 username: usuario
7 password: password

Los archivo yaml o .yml requieren de una identación perfecta, asegura de agregar espacios de
sobre ni que falten, así como no agregar comillas donde no vayan.

Con el archivo database configurado el siguiente paso es abrir el archivo gemfile, sacar la gema de sqlite3
del entorno de desarrollo y agregar la gema ‘pg’ al entorno de desarrollo, (y de producción si así se
desea)

Luego correremos el comando


1 bundle

y finalmente creamos la base de datos

1 rake db:setup
2 rake db:create

Existen posibilidades de que el usuario puesto en la configuración no tenga permisos para crear bases de
datos, en ese caso debemos o cambiar el usuario de la configuración, o darle acceso a ese usuario a
crear bases de datos, o finalmente crear la base de datos a mano.

Creando un proyecto con postgreSQL


Es posible crear un proyecto directamente ocupando postgreSQL con

1 rails new myapp --database=postgresql

De todas formas necesitaremos abrir el archivo database.yml para configurar nuestra base de datos, sin
embargo no tendremos que abrir el gemfile para remover la gema sqlite3 y agregar la gema pg.

El error de socket
Existe un error muy común al cargar el servidor de rails y abrir la página que dice

could not connect to server: No such file or directory


Is the server running locally and accepting
connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Esto normalmente se debe a que el servidor de postgreSQL no está corriendo.

Preguntas

1. Por qué es necesario configurar el archivo database.yml si creamos el


proyecto con `rails new myapp –database=postgresql`
2. ¿Qué hace rake db:create?
3. ¿Qué hace rake db:setup?
4. ¿Cuál es la diferencia entre instalar postgres en el sistema operativo e instalar
la gema de postgre en el proyecto rials?
5. ¿En qué archivo se configura la base de datos?
16) Los modelos en rails

Objetivos

1. Entender el concepto de modelo como ORM


2. Manejar las migraciones para modificar la base de datos
3. Agregar datos a través de rails console
4. Agregar datos a través del archivo seed
5. Utilizar atributos virtuales

El concepto de modelo en rails puede ser complicado de entender porque es simultaneamente dos
cosas.

En primera opción es un ORM (object relational mapper) lo cuál consiste en conectar elementos de
dentro de una tabla de base de datos con un objeto de alto nivel que nos permite abstraernos del SQL y
trabajar con objetos que saben buscarse, actualizarse y guardarse en la base de datos.

Además los modelos manejan la capa lógica del negocio, o sea se encargan de cuidar la integridad de los
datos a un nivel más abstracto y más fácil de programar que el de SQL

En este capítulo abordaremos los modelos como un ORM, y en el capítulo 16 abordaremos el como
cuidar y validar datos con los modelos.

Los modelos como mapas a los objetos (ORM)


El modelo al ser un mapa del objeto nos permite manipular datos de la base de datos de forma simple,
mientras la clase sirve para mapear la tabla, las instancias del modelo nos sirven para mapear filas.
Veamos un ejemplo, para eso creemos el modelo Task en un proyecto nuevo.

1 rails g model task user:string

Obtendremos:
invoke active_record
create db/migrate/20151130040223_create_tasks.rb
create app/models/task.rb
invoke test_unit
create test/models/task_test.rb
create test/fixtures/tasks.yml

Al crear modelos se generan migraciones que nos permiten crear las tablas de forma automática en la
base de datos, en el caso anterior se generó la migración
db/migrate/20151130040223_create_tasks.rb , podemos correr esos cambios con:

1 rake db:migrate

Luegos gracias a esto obtendremos métodos de clase que nos permiten traer a memoria todos los tasks
que hay en la base de datos, o, encontrar uno en específico, ya sea el primero, el último o por algún
criterio a nuestra voluntad.

Estos los podemos probar directamente en rails console o dentro de nuestro proyecto rails .

1 Task.all #Obtiene todas las tareas en la base de datos


2 Task.find(1) #Busca la tarea con id 1
3 Task.first #Devuelve el primer task de la base de datos
4 Task.last #Devuelve el último task de la base de datos
5 Task.where(user: "Gonzalo") #Busca todas las tareas cuyo usuario sea Gonzalo, esto funciona siem

No es sorpresa que en ninguna de las pruebas anteriores obtendremos resultados distinto de vacío,
puesto que todavía no tenemos datos, par ahacerlo podemos generar instancias de los tasks para
guardar tareas nuevas.

1 t = Task.new(user: "Gonzalo")
2 t.save

O podemos crearlas directamente utilizando el método create

1 Task.create(user: "Gonzalo")

Para actualizar un dato de la base de datos tenemos que traerlo a memoria primero, luego podemos
modificarlo y guardarlo.
1 t = Task.first
2 t.name = "R2D2"
3 t.save

Hay métodos que traen a memoria un objeto, en ese caso ese objeto será de ese tipo, como por ejemplo
t.task será Task, pero los métodos que traen múltiples datos a memoria como .all, o .where nos devuelven
un objeto del tipo ActiveRecord_Relation que no son un array pero se comportan como uno, en términos
de que son iterables.

1 Task.all.each {|t| puts t.user}

Se debe tener cuidado de no confundir los métodos de instancia con los de clase, por ejemplo
no tiene sentido obtener `Task.user` ¿de qué usuario estaríamos hablando? sin embargo si
tiene sentido obtener un usuario de la tabla y preguntar su nombre.

La clave para evitar este error es entender quelos métodos de clase sirven para operar por sobre la tabla,
y en cambio los métodos de instancia sirven para operar sobre una de las filas.

Creando modelos
En rails cada vez que creamos un modelo se crea una migración que nos permite modificar la base de
datos y que existe esa persistencia a nivel de base de datos cuando ingresamos, borramos o
modificamos datos.

Entonces ¿Para qué sirven los modelos?

Para guardar datos y para asegurarnos que esos datos cumplan con reglas, y esas reglas las podamos
programar en ruby y con toda la magia de rails en lugar de SQL

Y ¿para qué sirven las migraciones?

Para cambiar la estrucutra de la base de datos, particularmente sirven para crear, actualizar y borrar
tablas, campos e índices.

Convención de nombres

La convención en Rails es que los modelos se escriben en singular, de ahí el mismo rails lo pluraliza para
escribir el nombre de la tabla en la base de datos.
por eso mismo no debemos escribir estos nombres en español puesto que las reglas no son
las mismas y podemos causarnos problemas.

Creando un modelo vacío

Podemos crear un modelo vacío, (sólo id, created_at, updated_at los modelos son en minúscula, con:

1 rails g model nombre_modelo

Creando un modelo con diversos campos

Para crear el modelo con uno o más campos, pero debemos ocupar un nombre distinto

1 rails g model nombre_modelo2 campo1:tipo_de_dato campo2:tipo_de_dato

si el tipo_de_dato se omite se asumirá que es string.

por ejemplo si creamos un modelo para guardar comentarios, obtendremos:

1 invoke active_record
2 create db/migrate/20151128090136_create_comments.rb
3 create app/models/comment.rb
4 invoke test_unit
5 create test/models/comment_test.rb
6 create test/fixtures/comments.yml

O sea, un archivo de migración, un archivo con el modelo y tests (los cuales cubriremos pronto en un
próximo capítulo)

Podemos agregar todas las columnas que necesitemos a una tabla, hay tablas en empresas
que tienen 50 columnas y no hay problemas con eso, pero tampoco se deben agregar datos
innecesariamente ni mucho menos se debe caer en el error de tener columnas que repitan la
información.

Ahora, dentro de los tipos de datos comunes que ocuparemos, los más freceuntes son:

integer
float (decimales)
boolean (true o false)
string (hasta 255 chars)
text (más de 255 chars)

No podemos tener dos modelos con el mismo nombre

Migraciones
Una migración es un set de cambios para la base de datos, por ejemplo agregar una tabla, cambiar de
nombre una tabla, agregar campos a las tablas, botarlas, etc.

Las migraciones son clave para el desarrollo pues nos ayudan a ordenar el desarrollo de nuestra
aplicación, la base de datos es el esqueleto de una aplicación web y las migraciones nos permiten
generar un control de cambios bien fino y evitar errores por diversas versiones.

Imaginemos el siguiente caso, dos personas, Pedro y Raúl están trabajando en una aplicación web,
Pedro crea un campo nuevo para una tabla y crea el código para manejar ese campo, luego sube al
repositorio los cambios, pero la base de datos está en su computador, por lo tanto sólo sube los cambios
respectivos a lógica del manejo del campo y no notifica que agregó un campo en la base de datos, luego
Raúl descarga los cambios de Pedro y la aplicación deja de funcionar.

Las migraciones resuelven el tipo de problemas que tienen Pedro y Raúl, pues estas son parte del código
y por lo mismo se agregan a los repositorios, gracias a las migraciones podemos compartir de forma
sencilla las aplicaciones y con sólo rake db:migrate estar listos para empezar a trabajar.

El archivo schema.rb
El secreto para entender las migraciones es el archivo db/schema.rb, el cual a esta altura luce así:

1 ActiveRecord::Schema.define(version: 20150819042819) do
2 create_table "tasks", force: :cascade do |t|
3 t.string "task"
4 t.datetime "created_at", null: false
5 t.datetime "updated_at", null: false
6 t.string "user"
7 end
8 end

El schema contiene toda la información respecto al estado actual de la base de datos, la suma de todas
las migraciones construye el schema, y la versión del schema, o sea el número que aparece corresponde
al nombre del archivo de la última migración.

Rails sabe que hay migraciones no corridas con solo comparar el último archivo de las migraciones con el
de la versión del schema.

Creando migraciones.
Al crear modelos se crean migraciones, pero nosotros también podemos crear una migración nueva
utilizando el generador de rails rails g migration nombre_migracion pero esto generará una
migración vacía.

Una migración vacía luce así:

1 class MigracionX < ActiveRecord::Migration


2 def change
3 end
4 end

Dentro del método change nosotros podemos:

Agregar una columna

1 add_column :tabla, :columna, :tipo_de_dato

Por ejemplo podemos agregar el campo completed a la tabla task con:

1 add_column :tasks, :completed, :boolean

Remover una columna

1 remove_column :tabla, :columna

Por ejemplo podemos remover el campo completed a la tabla task con:

1 remove_column :tasks, :completed


Generadores mágicos

Tanto para el añadir columnas como para removerlos existen autogeneradores, Si recuerdan bien ya los
hemos ocupado previamente para agregar el campo usuario a task.

La Lógica del autogenerador es la siguiente, dentro del bash:

1 rails g migration addXAndYAndZToTable x:string y:integer z:float

y el código anterior generará automáticamente:

1 class AddXAndYAndZToTask < ActiveRecord::Migration


2 def change
3 add_column :tasks, :x, :string
4 add_column :tasks, :y, :integer
5 add_column :tasks, :z, :float
6 end
7 end

También existe generador mágico para borrar campos.

1 rails g migration removeXAndYFromTable x:string y:integer

Revirtiendo una migraciones

El pasado no lo debemos modificar, pero podemos, por lo tanto debemos hacerlo con cuidado.

Supongamos que Raúl realizó una migración pero esta tuvo algún error, ya sea por el tipo de dato, o por
un error tipográfico y supognamos que se dio cuenta después de haber corrido el comando
rake db:migrate si Raul todavía no ha compartido sus cambios a través de un push todavía está a
tiempo de enmendar su error.

Enmendando un error.

Como no podemos modificar el schema lo que tenemos que hacer es modificar una de las migraciones,
pero siempre teniendo en mente que la suma de las migraciones debe dar el schema, y no pueden haber
por ahí dos sumas distintas, o sea en todos los códigos las migraciones deben ser las mismas, por eso
sólo vamos a enmendar los errores de esta forma cuando no hayamos compartido nuestros cambios.

Pasos para enmendar el error.


1. Devolver la base de datos a una versión anterior con rake db:rollback esto el schema ahora
apuntará a la versión anterior a la última

2. Modificar la migración y guardar los cambios

3. Correr rake db:migrate

Si los cambios ya fueron enviados entonces lo que tenemos que haces es simplemente avanzar hacia
adelante, o sea crear una nueva migración que remueva la columna mal hecha y que la cree de nuevo, de
esta forma para aplicar los cambios sólo basta rake db:migrate.

Cuidado con los branches

Cuando se está trabajando con sqlite3 en la versión de desarrollo no existen problemas con los branchs
puesto que cada branch tiene su propia copia de la base de datos, pero cuando se ocupa postgreSQL u
otro motor similar existen problemas como el siguiente.

Imaginemos que Raúl ahora está trabajando en un proyecto con PostreSQL, va a implementar una
funcionalidad nueva por lo que hace un branch del código, luego crea una migración y la corre
modificando la base de datos, después se da cuenta que no le gustó como iba y vuelve al branch de
desarrollo, pero al intentar trabajar se da cuenta que la base de datos no se ha devuelto a su forma
original si no que está con los cambios implementados en el branch.

Hay dos formas de resolver esta situación, volver al branch y hacer un rake db:rollback (o tantos como
sea necesario) y volver al branch orginal o …

Botón de autodestrucción

En el peor de los casos siempre se puede empezar de nuevo, esto quiere decir que podemos resetear la
base de datos utilizando la siguiente reseta:

1. rake db:drop
2. rake db:create
3. rake db:migrate
4. rake db:seed

Si quieres saber más sobre migraciones revisa la guía oficial de migraciones en Rails
http://guides.rubyonrails.org/active_record_migrations.html

Es importante siempre respaldar la base de datos porque una vez que la botemos no la
podremos recuperar.

Ahora que ya entendemos lo básico de migraciones podemos volver al modelo y sus campos.

Destruir un modelo

Para destruir un modelo podemos ocupar:

1 rails destroy model nombre_modelo

Agregando un campo extra.

Una vez que hayamos creado un modelo no podemos volver a crear campos para agregar datos, desde
el punto de vista de SQL no tendría sentido crear dos veces las misma tabla, habría que destruir una para
luego crear la otra, por lo mismo podemos destruir el modelo pero si ya tiene código esto no tendría
sentido, entonces lo que hacemos es agregar un campo extra ocupando el generador de rails.

Hay dos opciones, con el helper que se crea la migracion automática o sin el que nos obliga a nosotros a
escribir los comandos para crear las tablas, añadir los campos o hacer lo que queramos hacer.

Con helper

1 rails g migration addCampoToTabla campo:tipo

Por ejemplo:

1 rails g migration addNameToUser name:string

Sin helper

1 rails g migration nombreMigracion

y luego se debe modificar la migración generada.

Correr las migraciones


Todo cambio en el modelo a nivel de agregar columnas o cambiarlas o borrarlas, o crear tablas requiere
de una migración, estos archivos se crearán sólo si seguimos las convenciones, o los podemos crear
nosotros siguiendo las reglas, pero una vez que los archivos estén listos hay que dar el siguiente paso,
que consiste en correr las migraciones.

Todo hacia adelante

1 rake db:migrate

Revisar que la consola no muestre error.

Una versión hacia atras:

1 rake db:rollback

Un número n de versiones hacia atrás

1 rake db:rollback STEP=n

Correr las migraciones hacia arriba o abajo a una versión específica.

1 rake db:migrate VERSION=20150306120002

El número de la versión lo podemos sacar de los nombres de los archivos dentro de la carpeta de
migraciones.

Revisar las migración generada

Todas las migraciones se encuentran dentro de la carpeta db/migrate , las migraciones están
ordenadas por un número que es una fecha invertida por lo tanto las últimas siempre iran al final.

Al correr el comando rake db:migrate se corren todas las migraciones y a partir de ellas se genera
db/schema.rb , no se corran todas desde cero, el archivo schema contiene un número, ese número
corresponde al de la última migración corrida, a partir de ahí rake db:migrate corre todo el resto de las
migraciones y actualiza el archivo schema.

Nunca debemos modificar el archivo schema directamente

Probar un modelo

Una vez creado el modelo debemos probarlo como lo hicimos en capítulos previos.
1 rails c

Y luego dentro del archivo:

1 Modelo.new

Es importante ocupar mayúscula y singular, esas son los reglas para referirse a una clase del tipo Active
Record

Si lo hicimos bien obtendremos una instancia del modelo este mostrará, id, created_at, updated_at y
todos los otros campos que hayas agregado a la base de datos.

En caso de que no haya sido generado obtendremos un error.

Creando una FK con índice.


Si estamos ocupando postgreSQL u otro motor con soporte de índices sobre claves foraneas es posible
de utilizar el generador de rails para crear el campo y el índice simultáneamente, esto se puede lograr con
la palabra clave references

1 rails g model user


2 rails g model company users:references

Veremos que La migración obtenida incluye el índice.

1 class CreateCompanies < ActiveRecord::Migration


2 def change
3 create_table :companies do |t|
4 t.references :users, index: true, foreign_key: true
5
6 t.timestamps null: false
7 end
8 end
9 end

Los índices generan dependencias, por ejemplo ya no podremos borrar un usuario sin borrar o
reasignar la compañía que tiene antes
Getters y setters
Para la siguiente sección vamos a crear un modelo user con nombre y un campo para saber si es
administrador o no.

1 rails g model user name:string admin:boolean

1 u = User.create(name:"Gonzalo", admin:true)

Los modelos automáticamente crean métodos getter y setters para cada uno de los campos de nuestro
objeto.

Por ejemplo podemos obtener el nombre del usuario con u.name y el valor de si es administrador con
u.admin en el caso de los valores booleanos se crea un método getter adicional con un signo de
interrogación al final, entonces es lo mismo escribir u.admin que u.admin?

El archivo seed
El archivo seed sirve para agregar datos de prueba a la base de datos de desarrollo (e incluso podría
servir para poblar una base de datos de producción, como por ejemplo para agregar los administradores
y configuraciones inciales)

Dentro del archivo seed vienen comentado dos ejemplos que se explican de forma bastante clara

1 #cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])


2 #Mayor.create(name: 'Emanuel', city: cities.first)

El primero sirve para crear varias ciudades, el segundo para crear un Mayor para la primera ciudad
(dependiendo de la versión de rails estos ejemplos pueden ser distintos).

La idea es borrar esto y crear tus propios datos de prueba. Ejemplo:

1 Movie.create([{name:"El rey leon"}, {name:"terminator"}])

Dentro del seed es posible agregar ruby:

1 c = Category.create("Disney")
2 Movie.create(name: "El rey león 2", category: c)
Atributos virtuales
Los atributos virutales son atributos que tienen los modelos pero no persisten en la base de datos (no se
guardan), ¿para qué sirven entonces?, pueden tener distintos usos, desde almacenar una variable
temporalmente, hasta servir para hacer un cálculo que no queremos guardar en la base de datos, para
crear atributos virtuales sólo tenemos que crear variables de instancias en el modelo con sus respectivos
getter y setters.

Preguntas

1. ¿Para qué sirven los modelos?


2. ¿Qué es un ORM?
3. ¿Para qué sirven las migraciones?
4. Suponiendo que existe el modelo User con el atributo name

¿Qué hace User.all?


¿Cómo puedo devolver al último de los usuarios guardados
en la base de datos?
¿Cómo podemos develver todos los usuarios de nombre
“Desafio”?
¿Cómo podemos cambiar todos los registros con el nombre “Desafio” por
el nombre “Desafio 2”?

5. ¿De cuál clase heredan los modelos en rails?


6. ¿Para qué sirve el archivo seed.rb?
7. ¿Qué son los atributos virtuales?

Ejercicio
Crea el modelo icecream con el campo sabor (flavour) el cual es un string
Corre las migraciones
Crea 10 helados, al menos 3 deben de ser de sabores distintos, y dos de ellos debe ser de sabor a
chocolate.
Selecciona todos los helados de sabor a chocolate, deberían ser dos
Crea el modelo provider con la columna name
Agrega la columna e índice de FK provider_id a la tabla helados
Corre las migraciones
Crea un proveedor llamado "Proveedor de helados 1"
Modifica todos los helados existentes para que esten asociados al proveedor uno. (solo hay que
cambiar la columna proveedor_id)
haz un rollback
Muestra todos los helados.
17) Reglas del negocio

Objetivos

1. Utilizar el modelo para proteger la integridad de los datos


2. Aprender a validar campos del modelo

Protegiendo la integridad de los datos


En el capítulo de SQL vimos como agregando constraints podiamos proteger datos que cumplieran
ciertas reglas, es posible de rails levantar una migración y crear constraints con un ALTER TABLE pero
también es posible agregar las reglas a nivel de modelo.

Dentro de los modelos podemos validar presencia de campos, que estos sean únicos, que sean mayores
que otros, que la combinación de campos sea única, o que un producto no esté expirado o que una
licitación esté hecha dentro de cierto rango de fechas, este tipo de reglas dependen específicamente de
cada negocio.

Validando la presencia de un campo


En el modelo respectivo, dentro de la clase:

1 validates :nombre_campo, presence: true

Un detalle importante si estamos trabajando en la consola los cambios en el modelo no se


reflejan de forma inmediata, tenemos que salir y volver a entrar de rails console o utilizar el
comando reload!

Validando que el campo sea único


En el modelo respectivo, dentro de la clase:
1 validates_uniqueness_of :nombre_campo

Validaciones custom
Podemos validar un campo o conjunto de campos con las reglas del negocio que queramos, para eso
hay que hacer dos cosas, la primera dentro de la clase debemos agregar un método validate
acompañado de los métodos que queramos ejecutar, ejemplo

1 validate :metodo_custom
2
3 def metodo_Custom
4 if expiration_date.present? && expiration_date < Date.today
5 errors.add(:expiration_date, "can't be in the past")
6 end
7 end

Valores iniciales
Hay dos formas de agregar valores iniciales a un modelo, una es a nivel de base de datos, para lograrlo
tenemos que generar un migracion para agragar una columna con valor inicial o para modificar una
columna y darle valor inicial.

Ejemplos:

1 add_column :users, :notification_email, :boolean, :default => true


2 change_column :player_states, :location, :integer, :default => 0

Callbacks
La segunda forma es utilizando callbacks, los modelos permiten llamar automáticamente métodos en
ciertos momentos, por ejemplo antes de guardarse, de esa forma podemos detectar si no hay valores
ingresados y ponerlos nosotros.

Para hacerlo ocuparemos el método before_save en el modelo respectivo.

1 before_save :metodo1, metodo2

Estos métodos se llamaran en el orden establecido, o sea primero metodo1 y luego metodo2, pero para
que esto funcione debemos definir los métodos, ejemplo:

1 def metodo1
2 self.user = User.first
3 end

Debemos tener mucho cuidado de nunca devolver false dentro de estos métodos pues en es
caso rails asumirá que alguna validación falló y hará rollback del commit, o sea no se
guardarán los datos.

Ejercicios
Copiar todos los códigos a un archivo de texto

Crear un proyecto nuevo de rails, con una configuración nueva de la base de datos
Crear el modelo de posts (sin agregar los usuarios)
Validar que el título del post este presente
Crear 5 artículos desde la consola
Crear una artículo sin título y observar el error obtenido

Crear el modelo de usuarios


Agregar una migración para agregar el usuario a los posts
Modificar el modelo de post para validar que el usuario esté presente
Crear un post sin usuarios para asegurarnos de que la validación esté funcionando

Crear el modelo de comentarios


Validar que el usuario esté presente

Crear el usuario "Usuario por defecto"

Cuando se cree un post sin usuario se debe asignar al usuario por defecto.
Cuando se borre un usuario que sea dueño de posts se deben asignar todos esos post al usuario
"usuario por defecto"

Preguntas

1. ¿Cómo podemos agregar una validación de presencia sobre un atributo del


modelo?
2. ¿Cómo podemos agregar un método custom para validar un modelo?
3. ¿En qué archivo se muestra el estado actual de la base datos?
4. ¿Qué significan los números al lado de los archivos de migraciones?
5. ¿Cómo podemos agregar un campo nuevo a un modelo?
6. ¿Cómo podemos eliminar un campo de un modelo?
7. ¿Que consecuencias habrían de modificar el archivo schema directamente?
8. ¿Para qué sirve dar valores iniciales a un campo del modelo?
9. ¿Cuál es la diferencia de realizar este trabajo a nivel de base de datos y a
nivel de modelo?
10. ¿Cómo se crea una migración para dar un valor incial en el modelo?
18) Relaciones entre modelos

Objetivos

1. Utilizar el modelo para proteger la integridad de los datos


2. Graficar automáticamente los modelos de datos utilizando los modelos de
rails

En SQL podemos relacionar dos tablas haciendo un select a múltiples tablas o haciendo un join, cuando
lo hacemos estamos uniendo dos o más tablas en una resultante.

1 select * from posts, users where posts.post_id = users.id

En rails podemos hacer lo mismo al establecer relaciones, sin embargo debemos especificar el tipo de
relación.

Agregar relaciones en los modelos nos permite movernos de un objeto a sus objetos relacionados de
forma sencilla, los modelos requieren que

Por ejemplo si establecemos una relación de uno a muchos entre los posts y los comentarios podemos
hacer lo siguiente:

1 @post = Post.first
2 @post.comments

Establecer la relación en el modelo nos genera automáticamente métodos que nos permiten obtener los
objetos relacionados de forma sencilla, en este capítulo estudiaremos esas asociaciones y sus ventajas.

Relaciones 1 a 1
Cuando existen relaciones 1 a 1 se dice que uno de los modelos tiene a otro, y el otro le pertence al
primero.

Si tenemos un usuario y una compañía semanticamente hace más sentido decir que la compañía le
pertenece al usuario, y que el usuario tiene una compañia, debemos tratar de ocupar las relaciones
siempre en el sentido semántico, si no se hace muy difícil programar si lo que se lee no tiene sentido en
españo.
Entonces crearemos dos modelos usuario y compañia

1 rails g model user name:string


2 rails g model company name:string user:references

Corremos las migraciones:

1 rake db:migrate

y ahora la parte nueva consiste en agregar las relaciones a los modelos.

En el modelo user.rb agregaremos

1 has_one :company

En el modelo company.rb nos daremos cuenta que ya se encuentra la relación, esto se agregó
automáticamente ya que rails lo dedujo a partir del user:references

1 belongs_to :user

Al igual que cuando agregamos los campos validadores en los modelos, al agregar referencias
debemos reiniciar la consola, despues de hacer cambios en el modelo, lo podemos hacer
ocupando el comando reload!

Debemos tener cuidado de por cual lado va la clave foránea y no invertir el orden por error, por
eso siempre es bueno dibujar el diagrama

Si lo implementamos bien dentro de rails console podemos obtener un usuario a partir de la empresa y
viceversa, de esta forma.

Para probarlo, vamos a crear un usuario y una empresa

1 u = User.new(name:"Rick Hunter")
2 c = Company.new(name:"SDF-1 Macross", user:u)

¿Qué fue lo que obtuvimos al hacer las relaciones? ganamos el obtener métodos getter automáticos para
obtener objetos a partir del otro, ejemplo:
1 u.company
2 => #<Company id: nil, name: "SDF-1 Macross", user_id: nil, created_at: nil, updated_at: nil>
3 c.user
4 #<User id: nil, name: "Rick Hunter", created_at: nil, updated_at: nil>

Borrando una relación en cascada.

Podemos borrar campos en cascada, esto quiere decir que al borrar un usuario se borrará su compañía
automáticamente, esto lo podemos hacer agregando dependent: :destroy en el modelo dentro de la
relación.

1 has_one :company, dependent: :destroy

reiniciar la consola

Para probarlo esta vez si necesitamos guardar los datos, (si no están no tiene sentido borrarlos)

1 u = User.create(name:"Rick Hunter")
2 c = Company.create(name:"SDF-1 Macross", user:u)
3 u.destroy

Veremos que justo después de hacer el destroy obtendremos:

1 2.2.2 :004 > u.destroy


2 (0.1ms) begin transaction
3 SQL (0.2ms) DELETE FROM "companies" WHERE "companies"."id" = ? [["id", 1]]
4 SQL (0.1ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]]
5 (0.9ms) commit transaction
6 => #<User id: 1, name: "Rick Hunter", created_at: "2015-11-30 05:26:26", updated_at: "2015-11-3

Donde del SQL claramente se distinguen dos borrados, primero el de la compañía asociada y luego el de
el usuario que se intenta destruir.

Graficando las relaciones


Existe una gema en rails que permite generar los diagramas de clases del modelo de datos.
Esta gema requiere de que tengamos instalado graphviz en nuestro computador, esto se puede lograr
con:

En OSX

1 brew install graphviz

En Ubuntu

1 sudo apt-get install graphviz

1 gem 'railroady', group: :development

y finalmente bundle.

Luego podemos generar el diagrama de modelos corriendo la línea.

1 railroady -M | dot -Tsvg > doc/models.svg

O podemos generar todos los diagramas con la línea.

1 rake diagram:all

Los diagramas se guardan en la carpeta doc del proyecto rails.

Ahora podemos diagramar al relación que creamos en el proyecto anterior.

Se necesita la base de datos para generar el documento, por lo mismo es necesario correr las
migraciones antes del comando
User
Models diagram
Date: Nov 28 2015 - 02:15 id :integer
Migration version: 20151128080750 name :string
Generated by RailRoady 1.3.1 phone :string
http://railroady.prestonlee.com created_at :datetime
updated_at :datetime

Company
id :integer
users_id :integer
created_at :datetime
updated_at :datetime

Relaciones de 1 a n
Implementar relaciones 1 a n es muy similar a implementar relaciones 1 a 1, para probarlo vamos a crear
un proyecto nuevo y crear dos modelos, películas y categorías, una categoría tiene muchas películas, una
película le pertenece a una categorías

1 rails g model category name:string


2 rails g model movie name:string category:references

luego

1 rake db:migrate

Una cosa es que en la base de datos existan las relaciones y otra muy distinto es que existan en el
modelo, el modelo es una capa de abstracción por sobre los datos, por lo mismo al igual que en las
relaciones 1 a 1 ahora debemos agregar las relaciones.

para agregar las relaciones debes agregar belongs_to :category al modelo de películas y
has_many :movies al modelo de categoría.

en el modelo category.rb

1 has_many :movies
y luego abriremos el modelo de movies.rb para revisar si se creó la relación, debería aparecer:

1 belongs_to :category

Observar que el has_many viene acompañado con el nombre de la tabla en plural, y es por que son varias
películas y no una, esta es una de las grandes razones porque debemos programar en inglés en lugar de
en español, porque las reglas de inflección (para pasar de singular a plural) no son las mismas en ambos
idiomas.

Para probarlo tenemos que entrar a rails console y luego crear una categoría y una película.

1 c = Category.create(name: "Children and Family")


2 m = Movie.create(name:"Hercules", category: c)

Al igual que en las relaciones 1 a 1 podemos hacer

1 m.category

Si observamos el diagrama veremos que es similar al de 1 a 1 pero en este caso vemos varias flechas
para representar que la relación es de 1 a n.

Lo nuevo en este tipo de relación es que categoría no tiene una película, tiene muchas, por lo mismo el
getter para obtener todas las películas es en plural.
1 c.movies
2 #=> #<ActiveRecord::Associations::CollectionProxy [#<Movie id: 1, name: "Hercules", category_id:

El método build

Este método es parecido al new pero permite automaticamente un objeto hijo

1 m = c.movies.build(name:"Wall-E")

De esta forma automáticamente el objeto nuevo tiene como clave foránea la id del padre, debemos tener
cuidado porque aún cuando el objeto no esté guardado en la base de datos si mostramos ahora todas las
películas veremos esta nueva, para guardarlo en la base datos es el mismo proceso.

1 m.save

Múltiples relaciones de 1 a n

Son muy pocas las aplicaciones que tengan unicamente una o dos tablas, una aplicación pequeña puede
tener entre 5 y 10 y una grande puede tener cientos, muchas veces existen relaciones entre varias de
estas simultáneamente.

En el caso típico de un blog tenemos usuario, comentarios y posts, ya estudiamos un posible modelo
para esto en el capítulo 13, y era:
Generamos los tres modelos:

1 rails g model user name:string


2 rails g model post content:text user:references
3 rails g model comment content:text post:references user:references

Corremos las migraciones con rake db:migrate agregamos las relaciones en los respectivos
modelos.

En el modelo de usuario agregamos:

1 has_many :comments
2 has_many :posts

En el modelo de posts:

1 has_many :comments
2 belongs_to :user

En el modelo de comments:
1 belongs_to :user
2 belongs_to :comment

Podemos rescatar los post de un usuario de la misma forma que hicimos previamente, pero para realizar
las pruebas vamos a tener que agregar algunos datos, lo podemos hacer en el archivo seed.rb o
directamente en la consola.

1 u1 = User.create(name:"Usuario 1")
2 u2 = User.create(name:"Usuario 2")
3 u3 = User.create(name:"Usuario 3")
4
5 p1 = Post.create(content:"Articulo 1 del usuario 1", user: u1)
6 p2 = Post.create(content:"Articulo 2 del usuario 1", user: u1)
7 p3 = Post.create(content:"Articulo 3 del usuario 1", user: u1)
8
9 Comment.create(content:"Comentario del usuario 1 en el articulo 1", user: u1, post
10 Comment.create(content:"Comentario del usuario 1 en el articulo 1", user: u1, post
11
12 Comment.create(content:"Comentario del usuario 2 en el articulo 1", user: u2, post
13 Comment.create(content:"Comentario del usuario 2 en el articulo 1", user: u2, post
14
15 Comment.create(content:"Comentario del usuario 3 en el articulo 1", user: u3, post
16 Comment.create(content:"Comentario del usuario 3 en el articulo 1", user: u3, post
17
18 Comment.create(content:"Comentario del usuario 2 en el articulo 1", user: u2, post
19 Comment.create(content:"Comentario del usuario 3 en el articulo 2", user: u3, post

Para ingresar los datos ahora debemos escribir rake db:seed

1 user = User.first
2 user.posts

Tambien podemos obtener todos los comentarios con user.comments pero que pasa si lo que
necesitamos es un poco más complejo, por ejemplo como podríamos obtener todos los comentarios que
ha recibido en todos sus posts un usuario.

Intuitivamente podríamos decir user.posts.comments pero sería un error, puesto que user.posts es un
active record association, es como un array de posts y este no tiene un método .comments

Podemos seleccionar primero todos los posts de ese usuario y luego buscar dentro de estos los
comentarios.
1 comments = []
2 user.posts.each {|p| comments << p.comments }

El problema es que ahora comments es un array y no podemos realizar filtros sucesivos o utilizar otros
métodos del activerecord

Como solución podemos utilizar dos joins, o sea unimos la tabla de comments con la de post, y la de
post con la de usuarios.

1 Comment.joins(:post => :user)

Esto se transforma en una consulta sql con:

1 SELECT "comments".* FROM "comments" INNER JOIN "posts" ON "posts"."id" = "comments"

pero hay una forma mucho más sencilla, que es utilizando hasmanythrough

Has many :through

Para llegar a una tercera tabla a través de una segunda podemos utilizar has_many :through en el
modelo, la sintaxis es la siguiente

1 has_many :tabla_final, through: :tabla_intermedia

Ahora en el caso de nuestro modelo de usuarios ya tenemos una relación llamada comentarios, por lo
que no podemos crear otra igual, pero en este caso podemos crear una relación con nombre, para eso
especificamos el nombre de la relación y el destino, ya que no lo puede calcular sólo.

1 has_many :received_comments, through: :posts, source: :comments

Ahora si recargamos la consola, podemos utilizar user.received_comments y obtendremos todos los


comentarios que ha recibido un usuario.

Relaciones n a n
Existen dos formas de implementar relaciones de n a n en rails, la primera es ocupando el has_many
:through, o sea dado que tenemos 3 modelos, donde uno de los
Preguntas

1. ¿Para qué sirve relacionar los modelos?


2. ¿Por qué es necesario agregar una columna en la base de datos para hacer
la asociación?
3. ¿Cuál es la converción respecto a la columan de asociación?
4. ¿Cuál es la diferencia entre una relación de 1 a 1 y una de 1 a n?
5. ¿Cómo se puede agregar nombre a una relación?
6. ¿Para que sirve el método .build?
7. ¿Cómo se pueden borrar en cascada los elementos de una relación?
19) Testing unitario de modelos

Objetivos

1. Entender el concepto de testing unitario


2. Aprender a correr tests automatizados
3. Mejorar la calidad de código
4. Entender como funcionan los fixtures

Los modelos son el esquelo de la aplicación y por lo tanto es muy importante que desarrollemos tests
automatizados para probar nuestra lógica del negocio.

En este capítulo aprenderemos a crear set de datos y correr pruebas para probar que nuestros modelos
fallan y decimos que debemos probar que las cosas fallan, porque la lógica del testing nace del intentar
hacer fallar algo.

Si intentamos demostrar que algo funciona probablemente solo incluiremos algunas pruebas necesarias y
no demostraremos que nuestro código es sólido y lo que es peor obviaremos aquellas que son más
importantes y representan un riesgo para nuestra aplicación.

Dentro de un proyecto rails todos los test se encuentran en la carpeta test de nuestra aplicación, dentro
de ella encontraremos subcarpetas para distintos tipos de tests, en este capítulo utilizaremos sólo dos de
ellas, models y fixtures los cuales sirven para hacer unit testing de los modelos.

Los test corren en un entorno especial llamado testing y no comparten los datos con la base de datos de
desarrollo o producción.

Los fixtures
Los fixtures son los archivos con los datos que se cargaran en la base de datos en cada prueba. Cuando
creamos un modelo se crea automáticamente un fixture para ese modelo, los fixtures están en formato
yaml por lo que su identación es clave.
1 one:
2 content: MyText
3 post_id:
4 user_id:
5
6 two:
7 content: MyText
8 post_id:
9 user_id:

Un secreto para no tener problemas con la identación es cuidar las tabulaciones, son críticas
en YML, utilizar dos espacios, en lugar de tabs, esto requiere configurar el editor.

Por defecto los fixtures incluyen valores para todos los campos que fueron creados con el generador de
rails al momento de generar el modelo, si los valores los agregamos después con una migración no
aparecerán aquí, pero los podemos agregar manualmente.

También hay que destacar que los fixtures no incluyen el campo id, pero podemos agregarlo, esto es
especialmente útil a la hora de probar relaciones.

Estructura de un test de modelo


Cada archivo de test viene ya con la estructura, la cual consiste en la clase que hereda de los TestCase y
dentro de esta van todos los pruebas que definiremos para nuestro modelo.

Formas de escribir un test

Hay dos formas de escribir los tests dentro de este archivo, la primera es creando métodos que empiezen
con test_

1 def test_the_truth
2 assert true
3 end

La segunda es de la siguiente forma:

1 test "the truth" do


2 assert true
3 end
Esta última forma es la más utilizada puesto que es más clara, pero cualquiera de las dos sirve y cumple
el mismo propósito

El método assert

Los asserts que vimos en las líneas anteriores son afirmaciones, con un assert le estamos diciendo a ruby
que una cosa tiene que ser de una forma y si no cumple, que nos avise.

Entonces con estos asserts podemos hacer pruebas como determinar si un objeto con ciertos dados
puede ser guardado con éxito en la base de datos.

Cuando ejecutemos los tests obtendremos un reporte por cada assert que no haya cumplido.

Un ejemplo práctico es evaluar si uno modelo se guardó o no

1 assert post.save?

Existen diversos tipos de asserts, estos vienen de la librería de minitest


https://github.com/seattlerb/minitest Algunos de los más usados son:

Assertion Purpose

assert( test, [msg] ) Asegura que el resultado sea verdad.

assert_not( test, [msg] ) Asegura que el resultado sea falso.

assert_equal( expected, actual, [msg] ) Asegura que el primer parámetro sea igual al segundo.

assert_not_equal( expected, actual, Asegura que el primer parámetro sea distinto al


[msg] ) segundo.

assert_nil( obj, [msg] ) Asegura que el objeto sea nulo.

assert_not_nil( obj, [msg] ) Asegura que el obejto no sea nulo.

assert_empty( obj, [msg] ) Asegura que el objeto esté vacío.

assert_not_empty( obj, [msg] ) Asegura que el objeto no esté vacío.

Asegura que un objeto esté en una colección de


assert_includes( collection, obj, [msg] )
datos.

assert_not_includes( collection, obj, Asegura que el objeto no esté en la colección de


[msg] ) datos-

Cada test contiene uno de los assets vistos en la tabla o incluo más de uno, la lógica del testing unitario
es probar una sola cosa a la vez lo que suele traducirse a hacer sólo un assert por test pero esto no es
estrictamente necesario, lo que si es importante es aislar los casos de pruebas, en los modelos se suele
hacer un assert por método

Corriendo tests
Todos estos tests se correran cuando ejecutemos el comando rake en la terminal dentro de la carpeta
del proyecto, al correrlo en un proyecto nuevo (o donde no tengamos tests definidos, obtendremos:

1 # Running:
2
3 Finished in 0.007509s, 0.0000 runs/s, 0.0000 assertions/s.

Cargando los fixtures en los tests


Si tenemos un test del tipo:

1 test "probar que el usuario sea valido" do


2 assert user.valid?
3 end

¿de donde sacamos el valor de user?, la respuesta es de los fixtures, para poder utilizar un fixture en un
test lo hacemos de la siguiente forma:

1 test "probar que el usuario sea valido" do


2 user = users(:one)
3 assert user.valid?
4 end

El método setup permite crear un método comun para todos los otros que se carga antes que cualquier
otra cosa, y sirve casi exclusivamente para cargar los fixtures, si todos los metodos cargan fixtures
distintos no tiene sentido, pero en muchos casos un fixture se comparte en todos o casi todos los
métodos, en ese caso es muy útil para evitar repetir código.

El método setup va así:


1 require 'test_helper'
2
3 class UserTest < ActiveSupport::TestCase
4 def setup
5 @user = users(:one) #cargamos el fixture
6 end
7
8 test "probar que el usuario sea valido" do
9 assert @user.valid?
10 end

En la línea 5 se ve que escribimos @user en lugar de user, debemos ocupar variables de instancias, si
fueran locales no podriamos compartirlas entre los distintos métodos.

Preguntas

1. ¿Por qué los tests los debemos escribir con la lógica de fallar?
2. ¿Qué son los fixtures?
3. ¿En qué consisten los tests unitarios?
4. ¿En qué entorno corren los tests?
5. ¿Cómo utilizamos un fixture dentro de un test?
6. ¿Cómo podemos correr los tests?
7. ¿Cuál es la diferencia entre escribir `def test_insert` y test “insert” do?
8. ¿Qué significa assert?
9. ¿Cuántos assert pueden haber en un test?
10. ¿Para qué sirve el método valid?
11. ¿Cuál es la diferencia entre assert assert_equal?
12. ¿Cómo se corren los tests?
13. ¿Cómo se cargan los fixtures en un test?
14. ¿Para qué sirve el método setup en los tests?
15. ¿Por qué el método setup se ocupan variables de isntancias en lugar de
locales?
20) Creando un Blog

Construyendo el modelo
En este capítulo y en los próximos construiremos un blog, por ahora utilizaremos como base el modelo
que hemos generado en capítulos anteriores del blog para realizar los tests del modelo.

Entonces y a modo de repaso, construimos el proyecto desde cero.

1 rails new blog_with_testing --database=postgresql

Corremos rake db:migrate para crear el archivo schema y para probar si tenemos algún conflicto con la
configuración de la base de datos

1 rake db:migrate

Luego creamos los modelos, por ahora todo campo que no sea un fk, será un string, esto nos ayudará a
demostrar un test pronto.
1 rails g model user name email role
2 rails g model post title content user:references
3 rails g model comment content user:references post:references

Construyendo los tests


Los test del modelo consisten principalmente en lógica del negocio, entonces necesitamos saber que
vamos a validar antes de hacer los test

Partiremos validando que un usuario no se pueda crear un usuario sin email

Test para modelo sin campo presente

Para evitar errores de cualquier tipo primero tenemos que partir de fixtures válidos ya que estos se cargan
directamente en la base de datos. Si vamos a crear una regla de negocio que valida la existencia de un
email y luego creamos usuarios sin email vamos a tener problemas.

1 one:
2 name: MyString
3 email: MyString
4 role: MyString
5
6 two:
7 name: MyString
8 email: MyString
9 role: MyString

La lógica de la validación de la existencia de un campo siempre es similar, utilizamos un fixture válido,


anulamos el campo que debería ester presente, esperamos que eso sea inválido.

1 require 'test_helper'
2
3 class UserTest < ActiveSupport::TestCase
4 test "user should have an email" do
5 user = users(:one)
6 user.email = nil #decimos que no tiene email
7 assert_not user.valid?, "el usuario no puede no tener email"
8 end
9
10 end

O, utilizando el método setup (es exactamente lo mismo):


1 def setup
2 @user = users(:one) #cargamos el fixture
3 end
4
5 test "user should have an email" do
6 @user.email = nil #decimos que no tiene email
7 assert_not @user.valid?
8 end

Al correr los tests con rake obtendremos:

1) Failure:

UserTest#test_user_should_have_an_email [/Users/gonzalosanchez/Proyectos/clases/bo
otcampv4/modelo/blog_with_testing/test/models/user_test.rb:10]:
Expected true to be nil or false

Además a los métodos assert les podemos pasar un parámetro más con el mensaje.

1 assert_not user.valid?, "user must have an email"

De esta forma al correr los test obtendremos algo más específico .

UserTest#test_user_should_have_an_email [/Users/gonzalosanchez/Proyectos/clases/bo
otcampv4/modelo/blog_with_testing/test/models/user_test.rb:10]:
user must have an email

Ahora, nosotros hicimos un test que dice que un usuario no debería ser valido si el email es nulo,
entonces al fallar nos está diciendo que hay usuarios con email nulo, por lo tanto nuestro código no lo
está validando, y eso es obvio porque no lo hemos implementado.

¿Cómo lo arreglamos?

Agregando una validación de presencia en el model de usuario al campo email, entonces en el modelo de
usuario:

1 validates :email, presence: true

Si ahora corremos los tests veremos:


Running:
.
Finished in 0.071091s, 14.0665 runs/s, 14.0665 assertions/s.

Es normal que los test fallen, que fallen significa que una funcionalidad no está implementada o está mal
implementada, y eso es genial porque nos permite encontrar problemas en nuestro código y además nos
permite ordenar nuestro trabajo.

A esta filosofía de testear primero y codear después se llama Test Driven Develpment o TDD

Test para modelo con campo único

Ahora si se pide que el mail sea unico, ¿qué necesitamos?

La respuesta es otro test

1 test "user cant have a duplicatd email" do


2 u = User.new(email: @user.email)
3 assert_not u.valid?, "user with email #{u.email} is repeated"
4 repeated "
5 end

¿Por qué en esta ocasión no ocupamos un fixture?, nuevamente, el secreto para sobrevivir al testing es
que todos los fixtures deben ser válidos porque estos se cargan directamente a la base de datos, y si uno
de ellos parte rompiendo un regla ya tendremos probemas.

Entonces como probamos que esté repetido?, creamos uno nuevo (no lo guardamos todavía) y
verificamos si con los datos que tiene es válido.

Test para probar la relación

Para probar una relación necesitamos dos fixtures, uno para cada elemento de la relación, hay varias
formas de hacer esta prueba, lo que nosotros haremos es crear un usuario y un post, un usuario tiene
muchos posts, por lo mismo podríamos preguntarnos si dentro de todos los posts del usuario se
encuentra el fixture, para eso probaremos con el método assert_includes

Entonces, primero agregamos el id del usuario al fixture de usuario


1 one:
2 id: 1
3 name: MyString
4 email: MyString
5 role: MyString
6
7 two:
8 id: 2
9 name: MyString
10 email: MyString
11 role: MyString

Luego agregamos el id del usuario como user_id al fixture de post

1 one:
2 title: MyString
3 content: MyString
4 user_id: 1
5
6 two:
7 title: MyString
8 content: MyString
9 user_id: 2

Con estos datos estamos diciendo que en nuestra base de datos hay dos usuarios y dos post, y cada
usuario tiene su post, ahora dentro del test veremos si el usuario one efectivamente tiene el post one.

Luego creamos nuestro test.

1 test "user has posts" do


2 assert_includes @user.posts, posts(:one), "user one should have post one"
3 end

Al correr los test obtendres un error en lugar de un failure, los failures suceden cuando el assert no
cumple la espectativa, los errores por diversos motivos, pero en este caso es porque el usuario no tiene el
método posts

UserTest#test_user_has_posts:
NoMethodError: undefined method `posts' for #
test/models/user_test.rb:19:in `block in '

¿Cómo lo arreglamos?, agregando el método post a usuarios, por ahora lo haremos manualmente sin
agregar la relación para demostrar el failure, y devolveremos un arreglo vacío ya que assert_includes
espera como primer parámetro una colección de datos y un array vacío cumple con eso.
model user.rb

1 validates :email, presence: true


2 validates :email, uniqueness: true
3
4 def posts
5 []
6 end
7
8 end

Ahora al correr los tests obtendremos:

Expected [] to include #.

Claro, nosotros esperamos que el usuario tenga dentro del arreglo un post, y un arreglo vacío no tiene
nada adentro, ahora borraremos el método posts y montaremos la relación para arreglarlo.

1 class User < ActiveRecord::Base


2 validates :email, presence: true
3 validates :email, uniqueness: true
4 has_many :posts
5 end

y voila, hemos pasado la prueba porque ahora si se incluye.

Running:

...

Finished in 0.121981s, 24.5939 runs/s, 32.7919 assertions/s.

Test de pertenencia

Ahora queremos hacer el test inverso, o sea asegurarnos que un post le pertenece a un usuario, si existe
el método obtendremos o un usuario en caso de que haya o nil en caso de que no, y error en casa de que
no exista el método, para eso dentro de los posts escribiremos el test.

1 test "post has user" do


2 assert_not_nil posts(:one).user, "Post one should have a user"
3 end
Test para diferencias de tiempo

Validando que un post no se pueda actualizar después de 5 minutos. Primero haremos el test, para eso lo
primero que necesitamos es revisar que el fixture de post incluye la fecha en que fue guardado, como nos
interesa que no se pueda guardar después de 5 minutos crearemos 2, uno hace 6 minutos que no
podemos actualizar y uno de hace 4 minutos que si podremos.

1 require 'test_helper'
2
3 class PostTest < ActiveSupport::TestCase
4 # test "the truth" do
5 # assert true
6 # end
7 test "cant update after 5 minutes" do
8 @post = posts(:one)
9 assert_not @post.valid?
10 end
11
12 test "can update before 5 minutes" do
13 @post = posts(:two)
14 assert @post.valid?
15 end
16
17 end

Al correr los tests veremos que sólo uno de ellos fala, esto se debe a que no existe ninguna validación,
por lo que al intentar guardar el post después de 5 minutos funciona y no debería funcionar.

Ahora tenemos que crear la validación, como no existe una para tiempos tendremos que crear una
custom como aprendimos previamente en el libro.

El modelo de post quedaría así:

1 class Post < ActiveRecord::Base


2 belongs_to :user
3 has_many :comments
4
5 validate :time_limit
6
7 def time_limit
8 delta = Time.now - self.created_at
9 if delta > 5.minutes
10 errors.add(:expiration_date, "can't update")
11 end
12 end
13
14 end
La diferencia de tiempos es simplemente al resta de cuanto tiempo ha pasado desde que se creó y si es
mayor a 5 minutos

Desafios
En un modelo de datos donde hay usuario y cada usuario puede tener hasta 6 fotos, se pide.

Crear los modelos de usuario y fotos


Crear las asociaciones
Asegurar que el usuario tiene la relación con fotos
Asegurar que el usuario puede tener un máximo de 6 fotos
Asegurar que el modelo foto tenga la relación con usuario

En un modelo de datos donde hay articulos, item pedido, orden de compra y usuarios, donde un una
orden de compra se relaciones con muchos artículos y viceversa y una orden de compra le pertenece a
un usuario. se pide:

Crear el modelo de articulo, itempedido, ordencompra y usuario


Establecer las relaciones acorde al enunciado.
Asegurar que un ariculo no pueda estar en un pedido si tiene stock igual a cero
Asegurar que un ariculo no puede ser pedido si tiene stock igual a cero
Asegurar que la orden de compra tiene al menos un item pedido.
Asegurar que el item_pedido le pertenesca a una orden de compra
Asegurar que desde el modelo de usuario se pueda resctar las ordenes de compra.
Asegurar que desde el modelo de usuario se pueda resctar los itemes pedidos
Asegurar que desde el modelo de usuario puede rescatar los articulos a través de las respectivas
relaciones

Preguntas

1. ¿Por qué es bueno que el archivo fixtures contenga únicamente fixtures


válidos sea válido?
2. ¿Qué es TDD?
3. ¿Por qué para probar el test de email hicimos assert_not user.valid? en lugar
de assert user.valid?, ¿no deberíamos revisar que fuera válido?
4. ¿Qué son los errores y cuales son la diferencia con los failures?
5. ¿Por qué en en el caso de la validación de la relación usuario -> posts
obtuvimos un error en vez de un failure? ¿Qué se hizo para resolverlo?
21) MVC
Ruby on Rails es un framework MVC, eso quiere decir que separa la programación en 3 capas lógicas
importantes, modelo, vista y controlador, ya vimos superficialmente estas capas en los capítulos
anteriores, en este vamos a aprender un poco más de ellas e integrarlas.

Para empezar debemos entender el diagrama básico.

Un navegador se conecta a localhost:3000/ruta, lo que está sucediendo ahí es que el navegador se está
conectando con el servidor que está corriendo localmente en nuestro computador, ese mismo que
nosotros levamantos corriendo el comando rails s , es por eso que necesita estar andando para que
podamos entrar a la página localhost:3000.

Cuando el servidor de ruby on rails detecta una conexión, las cuáles desde ahora en adelante llamaremos
request lo que hace es verificar el archivo de rutas, este le indica que controller debe resolver el request y
con que método, dentro de este a veces se hacen llamados al modelo (y a veces no) y luego se mostra la
vista.

La ventaja de esta separación en tres capas es que hace fácil la revisión de un código de un tercero y
coordinar tareas con el equipo de trabajo, como podemos ver en los siguientes ejemplos:
1. Si se necesita actualizar una vista en particular para un diseñador es fácil saber cual archivos es, se
revisa la URL, luego se revisa a que controller y que método redirige y luego busca el archivo dentro
de views con el mismo nombre dentro del método.
2. Si hay una falla dentro de una página que no carga, el primer responsable es el controller, si este no
tiene nada fuera de la normal o está todo ok, revisamos el modelo y luego la vista.
3. Toda la lógica de negocios está en el modelo, si una regla del negocio sólo tendremos que actualizar
este archivo, en caso de que agreguemos un campo o saquemos uno tendremos que revisar la vista
pertintente pero sólo eso.

En resúmen la arquitectura MVC hace fácil la coordinación de la construcción y la mantención de una


aplicación web y es por eso que tan usada en la industria de desarrollo, tanto así que incluso se está
ocupando para el desarrollo de apps no webs.

Arquitectura REST
Rails es un framework MVC, pero hay otro concepto de desarrollo embebido en la lógica de la
construcción de proyectos, y es el de REST.

La idea de REST es definir recursos independientes para construir una aplicación, cada uno de estos
recursos tiene 7 métodos primarios.

1. Index: Muestra todos los elementos de un recurso.

2. Show: Muestra el detaller de un recurso

3. New: Muestra el formulario para crear un nuevo recurso

4. Create: Maneja la lógica de la creación de un nuevo recurso

5. Edit: Muestra el formulario para editar un recurso

6. Update: Maneja la lógica para la actualización de un recurso

7. Delete: Borra un recurso específico.

La forma más fácil de empezar a trabajar con REST en Rails es con el generador de scaffold.

Scaffold
En rails existe una forma de crear todos los métodos rest para el recurso que queramos, es más el
scaffold crea:
1. El modelo (y cada vez que se crea un modelo viene acompañado de la migración para crear la table
en la base de datos)
2. El controller con los métodos REST
3. Las rutas para cada uno de los métodos REST.
4. Las vistas necesaria para cada uno de los métodos rest
5. Tests y fixtures básicos
6. Un archivo SCSS para ingresar sass (una variante de CSS)
7. Un archivo coffeescript (una variante de Javascript)
8. Un archivo de helper para delegar la lógica del controller

Para probarlo creemos un proyecto nuevo, donde haremos una una lista de tareas.

1 rails g scaffold tasks task:string

Como resultado obtendremos:


invoke active_record
create db/migrate/20150815182842_create_tasks.rb
create app/models/task.rb
invoke test_unit
create test/models/task_test.rb
create test/fixtures/tasks.yml
invoke resource_route
route resources :tasks
invoke scaffold_controller
create app/controllers/tasks_controller.rb
invoke erb
create app/views/tasks
create app/views/tasks/index.html.erb
create app/views/tasks/edit.html.erb
create app/views/tasks/show.html.erb
create app/views/tasks/new.html.erb
create app/views/tasks/_form.html.erb
invoke test_unit
create test/controllers/tasks_controller_test.rb
invoke helper
create app/helpers/tasks_helper.rb
invoke test_unit
invoke jbuilder
create app/views/tasks/index.json.jbuilder
create app/views/tasks/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/tasks.coffee
invoke scss
create app/assets/stylesheets/tasks.scss
invoke scss
create app/assets/stylesheets/scaffolds.scss

El último archivo creado scaffolds.scss es una base para mostrar los errores de los formularios y otros
detalles menores, si no es de tu agrado puedes borrarlo o modificarlo.

¿Cómo lo probamos?

Primero tenemos que saber cuales son las rutas, eso lo podemos lograr con rake routes
Prefix Verb URI Pattern Controller#Action
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy

Viendo las rutas descubrimos que para entrar al index de tasks tenemos que entrar a
localhost:3000/tasks

Y si no hemos realizado la migración obtendremos el siguiente error.

Esto se debe a que cada vez a que rails detecta que hay una migración que aún no ha sido corrida, pero
resolverlo simplemente debemos correr el comando rake db:migrate en el terminal.

1 rake db:migrate

Si la migración es correcta, deberíamos obtener:


1 == 20150815182842 CreateTasks: migrating ======================================
2 -- create_table(:tasks)
3 -> 0.0024s
4 == 20150815182842 CreateTasks: migrated (0.0025s) =============================

Luego volvemos a entrar a la página web y podremos ver el index de task.

Desde ahí podemos ingresar tareas nuevas, ver el detalle de cada una, en la misma vista se provee un
link para crear un task nuevo, si utilizamos el inspector de elementos o hacemos hover con el mouse
sobre el link veremos que el link apunta hacia tasks/new.
Para saber exactamente que página es lo compararemos con el resultado de rake routes, de esta forma
sabremos que task/new utiliza el controller tasks con el método new, entonces para saber que acción
se realiza revisaremos el controller y método respectivo.

1 class TasksController < ApplicationController


2
3 # GET /tasks/new
4 def new
5 @task = Task.new
6 end
7
8 end

Si observamos el método veremos que no hay mucha lógica, solo se crea un objeto task vacío que se
ocupará para guardar el tasks

Sin embargo en la vista app/views/tasks/new.html.erb nos encontraremos con sorpresas.

1 <h1>New Task</h1>
2
3 <%= render 'form' %>
4
5 <%= link_to 'Back', tasks_path %>

Render es una instrucción que no habíamos visto en los controllers, pero no en las vistas aunque tiene el
mismo propósito, cargar otro vista, cuando de una vista se carga otra se le denomina vista parcial, para
señalizar que un archivo es una vista parcial se utiliza como prefijo un _, como es el ejemplo del archivo
app/views/tasks/_form.html.erb.

Veamos el archivo generado.

1 <%= form_for(@task) do |f| %>


2 <% if @task.errors.any? %>
3 <div id="error_explanation">
4 <h2><%= pluralize(@task.errors.count, "error") %> prohibited this task from being saved:
5
6 <ul>
7 <% @task.errors.full_messages.each do |message| %>
8 <li><%= message %></li>
9 <% end %>
10 </ul>
11 </div>
12 <% end %>
13
14 <div class="field">
15 <%= f.label :task %><br>
16 <%= f.text_field :task %>
17 </div>
18 <div class="actions">
19 <%= f.submit %>
20 </div>
21 <% end %>

Lo primero que vemos es el helper form_for, es similar al form_tag sólo que este es capaz de determinar
las rutas para guardar o actualizar a partir de un objeto, en cambio en form_tag hay que especificarlas
como lo hicimos en el capítulo previo.

En caso de que al querer guardar un objeto haya un error lo mostraremos, de eso se encargan las líneas 2
a la 12. y entre las líneas 14 a 17 se crea el field task y de la 18 a la 20 el botón de envío.

la ruta del formulario se calcula con el objeto, si el objeto task es nuevo apuntará al metodo create si es
antiguo a update, revisemos el método create.
1 # POST /tasks
2 # POST /tasks.json
3 def create
4 @task = Task.new(task_params)
5
6 respond_to do |format|
7 if @task.save
8 format.html { redirect_to @task, notice: 'Task was successfully created.'
9 format.json { render :show, status: :created, location: @task }
10 else
11 format.html { render :new }
12 format.json { render json: @task.errors, status: :unprocessable_entity }
13 end
14 end
15 end

El método create parte con algo muy interesante Task.new(task_params) esta es la clave para
entender los form_for todos los datos del formulario se envían en un hash, esto lo podemos ver en los
logs de rails server

Started POST "/tasks" for 127.0.0.1 at 2015-08-17 13:22:29 -0500


Processing by TasksController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"PIaNf9h4CoHU/lxBWTSH3Kn9lY/LekE
6fmPH8uCu9B4FZfT0jSb8FGTeD7YfVkOtCdEC1sqTxZc1gxQfpffthQ==", "task"=>{"task"=>"Hell
o !!"}, "commit"=>"Create Task"}
(0.1ms) begin transaction
SQL (0.2ms) INSERT INTO "tasks" ("task", "created_at", "updated_at") VALUES (?,
?, ?) [["task", "Hello !!"], ["created_at", "2015-08-17 18:22:29.437613"], ["upd
ated_at", "2015-08-17 18:22:29.437613"]]
(0.8ms) commit transaction
Redirected to http://localhost:3000/tasks/3
Completed 302 Found in 3ms (ActiveRecord: 1.1ms)

Los parameters son un hash, este hash ya lo hemos ocupado antes cuando dentro de un método del
controller utiizábmaos render json: params en lugar da cargar una vista, lo que estamos haciendo
ahí es mostrar el hash params.

Dentro de este hash existe el key task (que corresponde al recurso) y que a su vez es un hash donde
vienen todos los campos creados y modificados.

Utilizando la misma idea del capítulo anterior podemos mostrar el hash en lugar de
1 def create
2 render json: params
3 end

Si queremos leer el hash dentro del controller lo podemos hacer utilizando params, por ejemplo para
mostrar todos los campos pasados del formulario para ingresar una tarea lo podemos hacer con:

1 def create
2 render json: params[:task]
3 end

Entonces, ¿qué es el task_params creado en el scaffold? @task = Task.new(task_params)


task_params es params[:task] pero limpiado, a este técnica de limpieza se le denomina Strong
parameters

Strong parameters
Veremos en las últimas líneas del task controller el siguiente código.

1 # Never trust parameters from the scary internet, only allow the white list through.
2 def task_params
3 params.require(:task).permit(:task)
4 end

Ahí se define un método task_params, donde dice que del key task solo permiteremos un key dentro, en
este caso se le llama task, pero si hubiese otro, por ejemplo el id de un usuario podríamos obtenerlo
agregándolo a la lista.

1 def task_params
2 params.require(:task).permit(:task, :user_id)
3 end

Todo lo que no esté en esa lista será declarado un parámetro ilegal, y será borrado, ahora para probar
esto agregaremos un campo nuevo a la base de datos, un nombre del responsable de la tarea.

Como el modelo ya está creado no corresponde utilizar rails g model en su lugar crearemos una
migración que agregue el campo a la base de datos. Esto se hace así:

1 rails g migration addUserToTask user:string


Si la migracion fue creada con éxito deberíamos obtener:

invoke active_record
create db/migrate/20150819042641_add_user_to_task.rb

Si abrimos el archivo creado, deberíamos ver lo siguiente

1 class AddUserToTask < ActiveRecord::Migration


2 def change
3 add_column :tasks, :user, :string
4 end
5 end

Si add_column no aparece probablemente se debe a que no alternaste la minúsculas y mayúsculas en


addUserToTask correctamente, la notación utilizada es de lower camel case.

Si el archivo es igual al mostrado entonces procedemos a correr la migración, ahí es cuando realmente se
modifica la base de datos.

1 rake db:migrate

Obtendremos como resultado:

== 20150819042819 AddUserToTask: migrating ===================================


=
-- add_column(:tasks, :user, :string)
-> 0.0015s
== 20150819042819 AddUserToTask: migrated (0.0016s) ==========================
=

Nota sobre las migraciones

Las migraciones son el medio por el cual modificamos la base de datos, jamás deberíamos modificar la
base de datos sin una migración, como queremos agregar usuarios responsables a la tarea haremos justo
eso, crear una migración que agregue un usuario a la tarea, donde el usuario es un string.

Discutiremos más profundamente el tema de las migraciones en un próximo capítulo, si te interesa tener
más información puedes revisar la documantación oficial de migraciones
Probando los strong params
Ahora tenemos un nuevo campo user, vamos a probar los strong params agregando el campo user al
formulario pero sin agregarlo a los strong params. Para eso abriremos el archivo
app/views/tasks/_form.html.erb y dentro agregaremos el nuevo field.

1 <%= form_for(@task) do |f| %>


2 <% if @task.errors.any? %>
3 <div id="error_explanation">
4 <h2><%= pluralize(@task.errors.count, "error") %> prohibited this task from being saved:</
5
6 <ul>
7 <% @task.errors.full_messages.each do |message| %>
8 <li><%= message %></li>
9 <% end %>
10 </ul>
11 </div>
12 <% end %>
13
14 <div class="field">
15 <%= f.label :task %><br>
16 <%= f.text_field :task %>
17 </div>
18
19
20 <div class="field">
21 <%= f.label :user %><br>
22 <%= f.text_field :user %>
23 </div>
24
25
26 <div class="actions">
27 <%= f.submit %>
28 </div>
29 <% end %>

Recargaremos la página y llenaremos el formulario.


Para probar si funcionó abriremos desde el bash la consola rails

1 rails c

Adentro buscaremos el último task para ver si fue ingresado correctamente.

1 Task.last

Task Load (0.7ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DES
C LIMIT 1
=> #
2.2.2 :002 >

La razón del por qué user es nil, la podemos encontrar en los logs de rails server (en el tab secuestrado)
Started POST "/tasks" for 127.0.0.1 at 2015-08-18 23:43:40 -0500
Processing by TasksController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"iGFUq2LShLo5NZV+Cjd5dmtnnz4
8MbSderUtyRDdAeCxgi0gN4xyL4kVxolMVb0Hy0sIZz3YMDAxVf4kVYQYew==", "task"=>{"task"=>"
Tarea 1", "user"=>"Gonzalo"}, "commit"=>"Create Task"}
Unpermitted parameter: user
(0.2ms) begin transaction
SQL (1.6ms) INSERT INTO "tasks" ("task", "created_at", "updated_at") VALUES
(?, ?, ?) [["task", "Tarea 1"], ["created_at", "2015-08-19 04:43:40.411793"], ["
updated_at", "2015-08-19 04:43:40.411793"]]
(0.8ms) commit transaction
Redirected to http://localhost:3000/tasks/1
Completed 302 Found in 30ms (ActiveRecord: 2.6ms)

Podemos ver una línea que dice Unpermitted parameter: user, y la razón es sencilla, user no está dentro
de los strong parameters, dentro del controller de tasks tendremos que agregar:

1 params.require(:task).permit(:task, :user)

y luego si podremos crear tasks con usuarios.

Preguntas

1. ¿Cuales son las ventajas de la separación de capas MVC en un apliación


web?
2. ¿Cuál es la diferencia entre crear un scaffold y un controller?
3. ¿Cuáles son los 7 métodos de la arquitectura REST en Rails?
4. ¿Qué es una vista parcial?
5. ¿Cuál es la diferencia entre form\_for y form\_tag?
6. ¿Cuál es la diferencia entre task_params y params[:task]
7. ¿Para qué sirven los strong params?
22) El archivo de rutas a fondo

Introducción al archivo de rutas

El archivo routes.rb

El archivo routes.rb que se encuentra dentro de la carpeta config contiene todas las URL que serán
accesibles por los usuarios del sistema, si la URL no está especificada aquí entonces simplemente no
será accesible.

El archivo routes se encarga de mapear la ruta con un controller y un método dentro de ese controller, de
esta forma cada URL corresponde a una acción que se encuentra definida dentro de un método de
instancia del controller especificado.

Entonces la página /controller_x/pages apuntará al método pages dentro del controller


controller_x

Es posible también que el nombre de la ruta no corresponda con el nombre del método, o omitir el
nombre del controller, eso lo veremos más adelante.

Otra cosa que puede hacer el archivo routes es pasar parámetros, los parámetros en la url son unas
especies de variables, de esa forma el programador puedes especificar la página
/controller_x/pag/20 y eso llamar al método pag asigando el valor 20 a una variable.

Rake routes

rake es un programa de administración de tareas (parecido a ANT), por ejemplo rake db:mgirate para
migrar la base de datos, en particular la tarea importante que utilizaremos respecto a las rutas es rake
routes, esto nos mostrará todas las rutas existentes en el sistema. Veremos más detalles de Rake routes
después de crear nuestra primera ruta sin parámetros.

Rutas sin parámetros

El caso más común para crear rutas es el de sin parámetros, estas son útiles cuando queremos páginas
estáticas o incluso cuando queremos obtener todos los elementos de un recurso, por ejemplo todos los
post, otro caso útil por ejemplo es el perfil del usuario conectado actualmente, por otra parte obtener el
perfil de un usuario específico requeriría el id de ese usuario.

Las rutas sin parámetros pueden ser con controller (la forma más habitual) y sin el controller, no tiene
ventajas omitir el controller pero hay personas que lo prefieren por estetica de la URL
Ruta con controller

ejemplo: localhost:3000/controller\_x/about\_us

Para crear una ruta especificando el controller debemos agregar al archivo routes una nueva dirección de
la siguiente forma: get 'controller/accion' , como por ejemplos

1 get 'pages/about_us'

para probar que esto funcione en el terminal dentro del mismo proyecto escribiremos rake routes

Rake routes a fondo


Después de digitar el siguiente comando en la consola

1 rake routes

Obtendremos lo siguiente: (Esto es un proyecto nuevo, si lo hacemos en otro probablemente


obtendremos muchas más rutas)

Prefix Verb URI Pattern Controller#Action

pages_about_us GET /pages/about_us(.:format) pages#about_us

Pero, ¿Qué es cada columna?

Prefix: El prefijo

El prefix muestra una variable que se creará para referirnos a esa página, es un prefijo porque hay dos
sufijos posibles, que son _url y _path, el primero muestra la ruta absoluta y el segundo la ruta relativa.

De esta forma si nosotros dentro de una vista de rails imprimimos <%= pages_about_us_url %>
obtendremos la dirección a esa página.

Verb, el verbo

Los request dentro de HTTP se hacen en conjunto con un verbo, en la mayoría de los casos este verbo es
GET, pero también existen otros como, POST, PUT, PATCH, DELETE, etc.
El verbo depende del llamado que se haga, por ejemplo cuando nosotros ingresamos una url en el
navegador automáticamente estamos haciendo un GET, pero cuando enviamos un formulario lo más
probable es que estemos haciendo un POST.

Ahora, para que una ruta sea válida tienen que suceder dos cosas, la primera es que la url tiene que estar
habilitada, la segunda es que el verbo tiene que correponder a la url. En el caso anterior si intentamos
hacer un POST a localhost:3000/pages/about_us entonces obtendremos que la ruta no existe, puesto
que nuestra ruta habilitada es GET

Ahora si dentro del archivo routes cambiamos el get por post, al hacer rake routes obtendremos:

Prefix Verb URI Pattern Controller#Action

pages_about_us POST /pages/about_us(.:format) pages#about_us

URI pattern

el URI pattern es la dirección URL en conjunto con el formato, ejemplo .html, .json, .xml, .js. Los
controllers en RAILS por defecto responden a múltiples formatos pero es posible limitarlos a uno o sólo
algunos.

URI Controller#Action

finalmente la última columna indica quien es el controller y el método responsable cuando se carga una
página web, esto es muy útil para debuggear errores pues nos indica en que parte del código esté
probablemente el problema.

Ahora que ya entendemos más sobre rake routes podemos proceder a ver aplicaicones interesantes del
archivo routes.

El archivo routes a fondo

ruta sin controller

primero aclarar que una ruta sin controller no quiere decir que no exista un controller, es sólo que no
aparece en la URL.

por ejemplo: localhost:3000/about_us


Para obtener una ruta sin controller tenemos que especificar dentro del archivo routes cuál es el controller
de la siguiente forma:

1 get 'about_us' => 'pages#about_us'

Luego con rake routes obtendremos lo siguiente:

Prefix Verb URI Pattern Controller#Action

about_us GET /about_us(.:format) pages#about_us

Claramente para que esta página funcione tiene que existir el controller pages con un método llamado
about_us y la respectiva vista.

Cambiando el prefijo

Es posible cambiar el prefix de una url utilizando el símbolo :as cuando definimos la ruta, ejemplo:

1 get 'pages/about_us', as: "hola"

Al hacer rake routes nos dará como resultado:

Prefix Verb URI Pattern Controller#Action

hola GET /pages/about_us(.:format) pages#about_us

No es conveniente cambiar el nombre del prefix arbitrariamente, pero en algunos casos rails no será
capaz de distinguir el prefix de forma automática y en esos casos lo necesitaremos.

Crear una ruta con un parámetro

Una ruta con parámetro se ve así: localhost:3000/user/:id

Para crear una ruta con parámetros tenemos que hacer dos cosas, la primera es especificar que la ruta
tiene un parámetro y nombrarlo, esto lo hacemos así: get 'user/:id' la segunda parte consiste en
especificar el controller como lo hicimos previamente, ya que la ruta no tiene la misma estructura que
antes del tipo controller/pages ahora hay que especificar el controller, uniendo quedaría así:
1 get 'user/:id' => 'users#profile'

al realizar rake routes nos daremos cuenta que en esta ocasión el prefix desapareció, como no sabía cuál
era el controller y el método, menos aún tiene la capacidad de autodeterminar un prefijo, entonces
recurrimos a especificarlo con el símbolo :as

Prefix Verb URI Pattern Controller#Action

profile GET /user/:id(.:format) users#profile

Dentro de las vistas y controllers de rails ahora debmeos pasar el parámetro id, esto es sencillo, en lugar
de utilar profile_path utilizaremos profile_path(id: 5)

Para utilizar este parámetro dentro del controller o dentro de una vista podemos utilizar el hash params de
la siguiente forma

1 params[:id]

Luego este id lo podemos utilizar para buscar registros en nuestra base de datos o para cualquier función
que estimemos conveniente.

Rutas anidadas

Es posible anidar una ruta, o sea una subruta que depende de una ruta padre. Veamos un ejemplo para
entenderlo mejor:

Supongamos que tenemos una red social y queremos obtener todos los matches de un usuario en
específico, pero la ruta user/:id ya la estamos ocupando para mostrar el perfil, podríamos crear otra
que diga matches/:id pero luego matches también apuntaría al controller de user y eso ya empieza
a causar confusión, entonces una forma de solucionarlo de forma que quede limpio es anidando la ruta
para que resulte en user/:id/matches

Esa ruta la podemos especificar así:

1 get 'user/:id/matches' => 'users#matches', as: "matches"

Crear una ruta que recibe dos parámetro

Los parámetros dentro de las rutas tienen nombres, por ejemplo en el caso anterior estabamos
nombrando a la variable :id, pero ¿qué sucede si queremos utilizar una segundo parámetro, por ejemplo
para ver el detalle del match de un usuario en específico. Eso lo podemos implementar de la siguiente
forma:

1 get 'user/:user_id/match/:match_id' => 'match#show', as: 'match_show'

Las rutas anidadas y formularios anidados son muy útiles a la hora de construir aplicaciones,
ahondaremos en más detalle en un próximo capítulo.

Query strings

Los querys strings son los parámetros libres que recive una dirección, estos parámetros no se declaran
en el archivo routes.rb, si no que los pasa el usuario cuando realiza un request.

Todo lo que está a continuación del caracter ? en la url son query strings, seguramente los has visto
cuando haces búsquedas en google.

Entonces al pasar una ruta al navegador del tipo localhost:3000/users?sort=ok podemos rescatar tanto el
valor ok simplemente utilizando params[:sort] desde el controller o desde la vista, en este caso
nos devolvería ok

¿Para qué sirven los query strings? principalmente cuando una página puede realizar otras acciones, que
no son críticas en los datos, como por ejemplo paginación, ordenamineto, etc o búsquedas como lo hace
google.

Resources
Hasta el momento hemos trabajado todas las rutas individualmente, pero hay forma de crear rutas REST
de forma automática para un recurso, para eso basta con especificar dentro del archivo routes:

1 resources :nombre_recurso
Prefix Verb URI Pattern Controller#Action

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

user GET /users/:id(.:format) users#show

PATCH /users/:id(.:format) users#update

PUT /users/:id(.:format) users#update

DELETE /users/:id(.:format) users#destroy

En este caso cuando el prefijo no sale mencionado es porque es el mimo que el anterior.

Limitando las rutas con :only y :except

Es posible además limitar las rutas rest generada utilizando los símbolo :only o :except, en el primer caso
las rutas quedan limitadas a las específicadas, y en el segundo son todas menos las especificadas.
Ejemplo:

1 resources :users, only: [:index, :create, :update]

Prefix Verb URI Pattern Controller#Action

users GET /users(.:format) users#index

POST /users(.:format) users#create

PATCH /users/:id(.:format) users#update

PUT /users/:id(.:format) users#update

member vs collection

Además es posible agregar acciones extras manteniendo la estructura REST, para eso existen los
members y los collections.

member
Los bloques member permiten agregar acciones a un elemento específico del recurso, por ejemplo a las
rutas rest de user podríamos agregar el método profile de la siguiente forma:

1 resources :users, only: [:index, :create, :update] do


2 member do
3 get 'profile'
4 end
5 end

Fuera de las rutas de user que vimos previamente, obtendremos una nueva la de profile.

Prefix Verb URI Pattern Controller#Action

profile_user GET /users/:id/profile(.:format) users#profile

Los bloques member hacen más sencillo el agregar rutas nuevas sin tener que especificar con :as y
establecer que controller y que método, pues es el método respectivo del recurso.

Collection

Los bloques de collections son muy similares a los de members, pero con una diferencia, el collection no
tiene un parámetro :id involucrado, pues se utiliza principalmente para generar una acción que involucra a
todos los recursos, o a uno independiente del id.

Por ejemplo crear un método para:

borrar todos los posts, sería collection.


obtener el perfil del usuario logeado, no depende del id de user y por lo tanto sería con collection.
dar un acceso especial a un usuario específico sería member
votar por un post sería member. (requiere del id del post)

Los bloques collection se utilizan de la misma forma que los members

1 resources :users, only: [:index, :create, :update] do


2 collection do
3 get 'profile'
4 end
5 end

Obtendríamos:
Prefix Verb URI Pattern Controller#Action

profile_users GET /users/profile(.:format) users#profile


23) Rutas anidadadas y métodos REST
anidados

Introducción a rutas anidadas


Las rutas anidadas son cuando ponemos una ruta dentro de otra, en el capítulo anterior lo explicamos
brevemenete, en este lo veremos a fondo debido a que son una parte importante y compleja de rails.

Para entender bien la idea detrás de las rutas anidadas debemos reapasar el propósito de la arquitectura
REST, esta sirve para normalizar la manipulación de recursos, o sea utilizar las misma estructura de URL
ya sea para verlos, crearlos, modificarlos o borrarlos.

Si queremos manipular un recurso que depende de otro podemos inventar muchas rutas para
manipularlo, o, podemos ocupar la arquitectura REST y poner este nuevo recurso anidado dentro del
otro.

De esta forma si tenemos comentarios que depende de posts los podemos anidar asi:

1 resources :posts do
2 resources :comments
3 end
Prefix Verb URI Pattern Controller#Action

post_comments GET /posts/:post_id/comments(.:format) comments#index

POST /posts/:post_id/comments(.:format) comments#create

new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new

edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit

post_comment GET /posts/:post_id/comments/:id(.:format) comments#show

PATCH /posts/:post_id/comments/:id(.:format) comments#update

PUT /posts/:post_id/comments/:id(.:format) comments#update

DELETE /posts/:post_id/comments/:id(.:format) comments#destroy

posts GET /posts(.:format) posts#index

POST /posts(.:format) posts#create

new_post GET /posts/new(.:format) posts#new

edit_post GET /posts/:id/edit(.:format) posts#edit

post GET /posts/:id(.:format) posts#show

PATCH /posts/:id(.:format) posts#update

PUT /posts/:id(.:format) posts#update

DELETE /posts/:id(.:format) posts#destroy

Los bloques collection y member tamibén aplican a rutas anidadas, si hay que tener el cuidado de
escoger el recurso completo, por ejemplo si quisieramos agregar un método para votar un post
deberíamos escribir:

1 resources :posts do
2 member do
3 get 'vote'
4 end
5 resources :comments
6 end

y si quisieramos crear uno para votar un comentario, deberíamos agregar:


1 resources :posts do
2 resources :comments do
3 member do
4 get 'vote'
5 end
6 end
7 end

Finalmente si queremos que ambos tengan un método para votos, simplemente combinamos ambos
métodos.

1 resources :posts do
2 member do
3 get 'vote'
4 end
5 resources :comments do
6 member do
7 get 'vote'
8 end
9 end
10 end

Prefix Verb URI Pattern Controller#Action

vote_post GET /posts/:id/vote(.:format) posts#vote

vote_post_comment GET /posts/:post_id/comments/:id/vote(.:format) comments#vote

Es perfectamente posible cambiar los GET por POST.

Creando un scaffold anidado


Vamos a suponer para este ejercicio que tenemos un modelo que tiene usuarios y tweets

Un tweet le pertenece a un usuario y un usuario puede tener muchos tweets.

En el caso de los usuarios lo podemos crear como scaffold o no, pero sólo vamos a utilizar el index y el
show, ya que nos enfocaremos en el recurso anidado, el resto se vio en capítulos anteriores.

1 rails g scaffold user name:string


2 rails g model tweet tweet:string user:references

Luego corremos las migraciones con rake db:migrate


Agregamos las relaciones en los modelos.
Agregamos al menos un usuario desde la consola
y creamos el controller de tweets.

Antes de siqueira generar el método index para los tweets vamos a comenzar creando los tests.

Testeando una ruta anidada


Para testear el acceso a la ruta index, podemos hacer:

1 test "should get index" do


2 get :index
3 assert_response :success
4 end

Pero las rutas anidadas tienen una subruta adentro, o sea el index de los tweets depende de un usuario
específico /users/2/tweets entonces debemos testear asi:

1 test "should get index" do


2 get :index, user_id: 1
3 assert_response :success
4 end

Para evitar errores también debemos agregar id: 1 dentro del fixture de users.

Ahora en ambos casos (aunque sólo el segundo es el correcto) obtendremos el siguiente error:

1) Error:
TweetsControllerTest#test_should_get_index:
ActionController::UrlGenerationError: No route matches {:action=>"index", :control
ler=>"tweets"}
test/controllers/tweets_controller_test.rb:8:in `block in '

Agregamos la ruta:

1 resources :users do
2 resources :tweets
3 end
Corremos los tests nuevamente, obviamente fallará, puesto que todavía no tenemos el método en el
controller. Observemos el error:

1) Error:
TweetsControllerTest#test_should_get_index:
AbstractController::ActionNotFound: The action 'index' could not be found for Twee
tsController
test/controllers/tweets_controller_test.rb:8:in `block in '

Agreguemos el método index al tweet controller, y de paso agreguemos la vista para evitar errores que ya
hemos estudiado.

1 def index
2 end

No habríamos tenido que pasar por esto si hubiésemos creado el controller como
rails g controller tweets index

Si ahora corremos rake, veremos que ya todos los tests pasan, sin embargo si quitamos el user_id del
test veremos que el test deja de funcionar.

1) Error:
TweetsControllerTest#test_should_get_index:
ActionController::UrlGenerationError: No route matches {:action=>"index", :control
ler=>"tweets"}
test/controllers/tweets_controller_test.rb:8:in `block in '

Esto se debe a que la ruta que nosotros creamos es anidada, por lo tanto debemos testear contra una
ruta anidada.

Obteniendo y mostrando los resultados.


En el controller bajo una ruta anidada normalmente generamos n objetos, uno par cada ruta padre y el
objeto (u objetos) del mismo controller.

Para que funcione tenemos que haber establecido las relaciones belongsto y hasmany en el modelo.

1 def index
2 @user = User.find params[:user_id]
3 @tweets = @user.tweets
4 end
y luego dentro de la vista mostramos los tweets con:

1 <% @tweets.each do |t| %>


2 <%= t.tweet %>
3 <% end %>

Es necesario obtener el objeto user?, no, otra opción de hacer lo mismo sería.

1 @tweets = Tweet.where(user_id: params[:user_id])

Pero es limpio, elegante y después nos servirá para hacer los redireccionamientos, además debemos
recordar que los controllers deben tener la menor cantidad de lógica posible, y esto incluye evitar hacer
consultas a la base de datos directamente.

El detalle del tweet


Partamos desde los tests:

1 test "should get show" do


2 get :show, user_id: 1
3 assert_response :success
4 end

la ruta ya está agregada, agreagmos todas las rutas REST, ahora en el controller agregamos el método
show.

1 def show
2 @tweet = Tweet.find(params[:id])
3 end

y finalmente lo mostramos en la vista, se deja de tarea para el lector.

Formulario para un nuevo tweet


Partimos con el test:
1 test "should get new" do
2 get :new, user_id: 1
3 assert_response :success
4 end

Agregamos el método en el controller:

1 def new
2 @user = User.find params[:user_id]
3 @tweet = @user.tweets.build
4 end

La forma que creamos el tweet en el paso anterior puede parecer rara, de hecho es muy común, y es lo
mismo que @tweet = Tweet.new (bueno, casi lo mismo, en ciertas ocasiones, como cuando ocupemos
relaciones polimórficas, veremos la útilidad de ocupar el build)

ya tenemos el controller, veamos la vista:

1 <%= form_for [@user, @tweet] do |f| %>


2 <%= f.text_area :tweet %>
3 <%= f.submit :enviar %>
4 <% end %>

Recordemos que form_for determina automáticamente las rutas para el objeto, pero en este caso las
rutas de tweet están anidadas dentro de user, para lograr que form_for construya las rutas
automáticamente con recursos anidados tenémos que pasarles los recursos en un arreglo. El orden de
los parámetros del arreglos es el mismo que el de anidamiento, de afuera hacia dentro, o sea si es del
tipo users/2/tweets el orden sería [@user, @tweet]

El formulario no funcionará porque no tenemos el método create todavía.

Creando el método create


Primero un test básico, simplemente para determinar si logramos crear el tweet o no, más adelante
veremos el redireccionamiento.

1 test "should create tweet" do


2 assert_difference('Tweet.count') do
3 post :create, tweet: { tweet: "hola, soy un tweet" }, user_id: 1
4 end
5 end
Como se observa el test se hace igual que otros de create, pero al igual que en todo lo anidado debemos
especificar un user_id existente.

Ahora creamos le método

1 def create
2 @tweet = Tweet.new(tweet_params)
3 @tweet.save
4 render nothing: true
5 end

y luego corremos el test.

Manejando el redireccionamiento del create.

No existe un lugar específico a donde debamos redireccionar, depende de los requisitos de la plataforma,
pero un lugar muy común, es al show del objeto padre, en este caso user.

1 test "should create tweet" do


2 assert_difference('Tweet.count') do
3 post :create, tweet: { tweet: "hola, soy un tweet"}, user_id: 1
4 end
5
6 assert_redirected_to user_path(users(:one))
7 end

y ahora tenemos que modificar el método create para lograr la redirección:

1 def create
2 @user = User.find params[:user_id]
3 @tweet = Tweet.new(tweet_params)
4 @tweet.user = @user
5 @tweet.save
6 redirect_to @user
7 end

las líneas 3 y 4 pueden ser sustituídas por:

1 @tweet = @user.tweets.build(tweet_params)

Se deja de tarea crear los métodos de edit y update que son iguales al de new y create. y el de delete que
no presenta mayor dificultad que el show.
22) Relaciones n a n
Tenemos una relación de n a n cuando un elemento de una tabla está relacionados con n elementos de la
otra y viceversa.

Ejemplo tags y películas

Una pelicúla x puede tener el tag acción, y el tag suspenso, y luego el tag suspenso puede a su vez ser
de la película x y de la película y.

En bases de datos relacionlaes no es posible modelar directamente una relación de muchos a muchos,
pero si es posible hacerlo ocupando una tabla intermedia.

La tabla intermedia guarda las referencias a las otras tablas.

Entonces siempre que queramos implementar una relación de muchos a muchos en bases de datos
relacionales necesitaremos 3 tablas.

Ahora para llevar esto a Rails hay dos formas:

1. Ocupando las relaciones has_and_belongs_to_many


2. Ocupando las relacions has_many :through

La gran diferencia entre la primera y la segunda es que la primera no requiere de un modelo intermedio y
la segunda si, ahora no confundir modelo con tabla, en ambos casos se requieren 3 tablas.
Implementando relaciones
has_and_belongs_to_many
Vamos a empezar con un proyecto nuevo, en el vamos a crear dos modelos, el de películas (movies) y el
de los generos.

Primero creamos los modelos.

1 rails g model movie title:string


2 rails g model genre name:string

luego creamos la tabla intermedia, para eso podemos crear una migración y luego crear la tabla
manualmente dentro o, podemos utilizar una ayuda de rails para crear la migración que cree la tabla
automáticamente.

para eso:

1 rails g migration CreateJoinTable movies genres

El archivo de migración generado debería contener algo así:

1 class CreateJoinTable < ActiveRecord::Migration


2 def change
3 create_join_table :movies, :genres do |t|
4 # t.index [:movie_id, :genre_id]
5 # t.index [:genre_id, :movie_id]
6 end
7 end
8 end

Este archivo generado puede cambiar dependiendo de la versión de rails.

Después de la migración, el schema debería contener:

1 create_table "movies_genres", id: false, force: :cascade do |t|


2 t.integer "movie_id", null: false
3 t.integer "genres_id", null: false
4 end

En muchas ocaciones la tabla intermedia no tiene un id como clave primario, puesto que no es necesaria,
además esta tabla nunca la accederemos directamente, siempre buscaremos por movie, o por genero.
Con las tablas hechas y los modelos creados ahora procedemos a establecer las relaciones.

1 class Genre < ActiveRecord::Base


2 has_and_belongs_to_many :movies
3 end

1 class Movie < ActiveRecord::Base


2 has_and_belongs_to_many :genres
3 end

Podemos probar las relaciones desde la consola de rails Movie.new.genres nos debería devolver
un colección vacía, y Genre.new.movies también.

Hagamos otra prueba para explicar como agregar generos a las películas.

1 m = Movie.new(title:"la pelicula de los muchas a muchos")


2 g = Genre.new(name:"Acción")
3 m.genres << g
4 m.save

Obtendremos como resultado:

1 (1.7ms) begin transaction


2 SQL (2.6ms) INSERT INTO "movies"
3 ("name", "created_at", "updated_at")
4 VALUES (?, ?, ?) [["title", "la pelicula de los muchas a muchos"],
5 ["created_at", "2015-10-14 03:47:10.050114"],
6 ["updated_at", "2015-10-14 03:47:10.050114"]]
7 SQL (0.6ms) INSERT INTO "genres" ("name", "created_at", "updated_at")
8 VALUES (?, ?, ?) [["name", "Acción"],
9 ["created_at", "2015-10-14 03:47:10.093517"],
10 ["updated_at", "2015-10-14 03:47:10.093517"]]
11 SQL (0.5ms) INSERT INTO "movies_tags" ("movie_id", "genre_id")
12 VALUES (?, ?) [["movie_id", 1], ["genre_id", 1]]
13 (1.0ms) commit transaction

Observar que se generó una transacción donde se guardan tres cosas simultáneamente, la película, el
genero, y la asociación.

luego podemos rescatar los generos de esa película con m.genres

Borrando la asociación:
¿Cómo podemos borrar la asociación?

Para borrar debemos tener el objeto que queremos borrar, por ejemplo

1 genre = Genre.first
2 movie = Movie.first
3 movie.genres.delete(genre)

Se debe tener el cuidado de no hacer movie.genres[0].delete, pues en ese caso se borraría el tag pero no
la asociación, esto en SQLite es posible pero en PostgreSQL generaría un error.

Testeando la relación

Cómo todo sucende dentro de una transacción podemos probarlo con un simple assert, también
podríamos contar los resultados.

1 test "may have many genres" do


2 3.times do |i|
3 @movie.genres << Genre.create(name: "Genero #{i}")
4 end
5 assert @movie.save
6 end

Implementando relaciones con has_many :through


Existe otra forma distinta de hacer relaciones de muchos a muchos y es con has_many :through, la
implementación es distinta, pero el resultado es idéntico, con ciertas excepciones que discutiremos en
esta sección.

A continuación veremos como hacer la relación con has_many through usando el mismo ejemplo de
películas y genero

1 rails g model movie title:string


2 rails g model genre name:string

Y ahora crearemos un modelo que los una a los otros dos, con:

1 rails g model movie_genre movie:references genre:references


Corremos las migraciones con rake db:migrate y procedemos a crear las relaciones:

Agregamos las relaciones a movie

1 class Movie < ActiveRecord::Base


2 has_many :movie_genres
3 has_many :genres, through: :movie_genres
4 end

Revisamos las relaciones de movie_genre, (deberían haberse agregado solas gracias al generador de
ruby)

1 class MovieGenre < ActiveRecord::Base


2 belongs_to :movie
3 belongs_to :genre
4 end

Agregamos las relaciones a genre

1 class Genre < ActiveRecord::Base


2 has_many :movie_genres
3 has_many :movies, through: :movie_genres
4 end

Entramos a rails c y procedemos a probar las relaciones:

Desde usuario:

1 Movie.new.movie_genres # ó
2 Movie.new.genres

Desde car:

1 Genre.new.movie_genres # ó
2 Genre.new.movies

Al hacerlo obtendremos colecciones vacías, ya que una película nueva no tiene géneros y lo mismo en el
orden inverso. Si en alguno de los casos obtenemos un error, hay que revisar los plurales en los modelos.
1 movie = Movie.new(name:"Avengers")
2 genre = Genre.new(name:"Action")
3 movie.genres << genre
4 movie.save

Obtendremos como resultado:

1 (2.4ms) begin transaction


2 SQL (4.2ms) INSERT INTO "movies" ("title", "created_at", "updated_at")
3 VALUES (?, ?, ?) [["title", "Avengers"], ["created_at", "2015-10-14 04:55:41.129110"
4 ["updated_at", "2015-10-14 04:55:41.129110"]]
5 SQL (0.5ms) INSERT INTO "genres" ("name", "created_at", "updated_at")
6 VALUES (?, ?, ?) [["name", "Action"], ["created_at", "2015-10-14 04:55:41.159226"
7 ["updated_at", "2015-10-14 04:55:41.159226"]]
8 SQL (1.6ms) INSERT INTO "movie_genres" ("movie_id", "genre_id", "created_at",
9 VALUES (?, ?, ?, ?) [["movie_id", 1], ["genre_id", 1], ["created_at", "2015-10-14 04:55:41.16
10 ["updated_at", "2015-10-14 04:55:41.164011"]]
11 (0.9ms) commit transaction
12 => true

Se puede observar que la utilización es la misma y el resultado también, independiente del método
utilizado, entonces ¿Cuándo conviene utilizar uno o el otro?

Has_many through vs Has_and_belongs_to_many

EL principal motivo para utilizar has_many through es cuando se requiere agregar negocio al modelo
intermedio, tanto en el caso de las películas como en el de los vehículos no había una necesidad real,
pero imaginemos por un momento que quisiéramos manejar el porcentaje de películas pertenecientes a
un genero o necesitar validaciones para esa tabla, entonces en ese caso ya convendría trabajar con
has_many through.

Ejercicio practico de uso

Aprovechando la relación creada anteriormente, con has_many :through, implementaremos la asignación


de géneros a una película, para eso crearemos el formulario, método y ruta necesarios.

Primero agregaremos películas y géneros a nuestra base de datos, para eso modificaremos nuestro
archivo seed
1 genres = []
2
3 genres << Genre.create(name: 'Action')
4 genres << Genre.create(name: 'Comedy')
5 genres << Genre.create(name: 'ScyFy')
6 genres << Genre.create(name: 'Horror')
7 genres << Genre.create(name: 'Drama')
8 genres << Genre.create(name: 'Adventure')
9 genres << Genre.create(name: 'Thriller')
10 genres << Genre.create(name: 'Documental')
11 genres << Genre.create(name: 'Musical')
12 genres << Genre.create(name: 'Animated')
13
14 movies = []
15
16 (1..50).each do |m|
17 movies << Movie.create(title: "Pelicula #{m}")
18 end

Luego crearemos el método index y show de películas, para eso primero crearemos el controller con el
método index y show desde el generador con:

1 rails g controller movies index show

Cambiaremos el archivo de rutas, las rutas generadas por:

1 resources :movies, only [:index, :show]

Luego dentro del método show del controller de movies, obtendremos la película y los generos,
necesitamos ambas para crear nuestro formulario de accesos

1 class MoviesController < ApplicationController


2 def show
3 @movie = User.find params[:id]
4 @genres = Genre.all
5 @movie_genres = @movie.genres
6 end
7 end

y ahora creamos un formulario sencillo, que apunte a la misma página, para probarlo.
1 <h2>Generos</h2>
2 <%= form_tag @movie, method: :get do %>
3 <table>
4 <thead>
5 <tr>
6 <th>Genero</th>
7 <th>Selecionar</th>
8 </tr>
9 </thead>
10 <tbody>
11 <% @genres.each do |genre| %>
12 <% if @movie_genres.include? genre %>
13 <tr>
14 <td><%= genre.name %></td>
15 <td><%= check_box_tag 'genres_ids[]', genre.id, 'Yes' %></
16 </tr>
17 <% else %>
18 <tr>
19 <td><%= genre.name %></td>
20 <td><%= check_box_tag 'genres_ids[]', genre.id %></td>
21 </tr>
22 <% end %>
23 <% end %>
24 </tbody>
25 </table>
26 <%= submit_tag 'Guardar' %>
27 <% end %>

El formulario se vería así:


Antes de avanzar analicemos el código del formulario, primero ponemos como ruta en el formulario la
misma página show (después podemos crear una distinta, esto es para probar), y pasamos como método
get para no tener que crear una ruta nueva
Luego dentro del body de la tabla iteramos sobre todos los géneros, por cada uno de ellos mostramos el
nombre del genero y luego un checkbox. Preguntamos si el genero ya esta asignado a la película y si es
así el checkbox se vera seleccionado (la opción 'Yes' ) y si no se mostrara normal.

El checkbox por defecto recibe el nombre de un atributo, y eso nos permite pasar un campo como true o
false. Aviso: cuando el checkbox no está marcado no se envía, que definitivamente no es lo mismo a que
se envíe con el valor cero.

Pero en este caso nosotros queremos pasar un grupo de géneros asociados a esta película, para eso
ocupamos el arreglo.

1 <%= check_box_tag "genres_ids[]", genre.id %>

Cuando un elemento del arreglo esta marcado se agregará a la lista con su id respectivo.

Al enviar el formulario obtendremos el siguiente output de rails server

Started GET "/movies/4?utf8=%E2%9C%93&genres_ids%5B%5D=3&genres_ids%5B%5D=6&genres


_ids%5B%5D=9&commit=Guardar" for ::1 at 2015-10-14 01:43:01 -0500
Processing by MoviesController#show as HTML
Parameters: {"utf8"=>"✓", "genres_ids"=>["3", "6", "9"], "commit"=>"Guardar", "i
d"=>"4"}
User Load (0.1ms) SELECT "movies".* FROM "users" WHERE "movies"."id" = ? LIMIT
1 [["id", 4]]
Section Load (0.2ms) SELECT "genres".* FROM "genres"
Rendered movies/show.html.erb within layouts/application (2.1ms)
Completed 200 OK in 101ms (Views: 98.9ms | ActiveRecord: 0.4ms)

La parte más importante que tenemos que observar es la de parameters

1 Parameters: {"utf8"=>"✓", "genres_ids"=>["3", "6", "9"], "commit"=>"Guardar", "id"=>"4"}

Ahí vemos que efectivamente los ids están en un arreglo, ahora simplemente hay que buscar esos
géneros y asignarlos a la película, pero para eso agregaremos una ruta nueva, set_genre

Antes tenemos que preguntarnos en que controller tiene que ir este método. ¿En uno nuevo?, ¿En el de
géneros?, ¿En el de las películas?, en este caso lo que queremos hacer es asignar géneros a una película
por lo que tenemos que trabajar sobre el controller de películas, movies. Y no solo necesitamos una ruta
que nos lleve al método, sino que también envíe el id de la película actual para poder trabajar en ella, para
eso hacemos uso de una ruta de tipo member en movies
1 resources :movies do
2 member do
3 post 'set_genre'
4 end
5 end

Luego con rake routes veremos obtendremos lo siguiente:

1 Prefix Verb URI Pattern Controller#Action


2 set_genre_movie POST /movies/:id/set_genre(.:format) movies#set_genre

Cambiamos nuestro formulario y lo dejamos como:

1 <%= form_tag set_genre_movie_path(@movie)%>

Tenemos que pasar la película (@movie) como opción a la ruta del formulario porque se tiene que enviar el
id de esta.

Ahora creamos el método en el controller donde procesaremos el formulario.

1 def set_genre
2 if params.key?(:genres_ids) && !params[:genres_ids].empty?
3 @genres = Genre.find(params[:genres_ids])
4 @movie.genres = @genres
5 else
6 @movie.genres.clear
7 end
8
9 redirect_to @movie
10 end

Lo primero que hacemos comprobar que el parámetro que vamos a usar, en este caso :genres_ids ,
existe y no esta vacío, después buscamos los géneros y las guardamos en un arreglo, y por ultimo le
asignamos los géneros al usuario, en este paso se eliminan los géneros que ya se desmarcaron al usuario
y se asignan las que se marcaron, en el caso de ya estar asignadas no hace nada. Si el parámetro no
existe o esta vacío, borramos todas los géneros asignados a la película.

y si probamos el formulario y observamos los resultados veremos dentro del POST


1 Started POST "/users/4/give_access" for ::1 at 2015-10-14 01:57:10 -0500
2 Processing by UsersController#give_access as HTML
3 Parameters: {"utf8"=>"✓", "authenticity_token"=>"+VsY2A3FZZDJzELf==",
4 "genros_ids"=>["4", "7"], "commit"=>"Guardar", "id"=>"4"}
5 User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ?
6 LIMIT 1 [["id", 4]]
7 Section Load (0.2ms) SELECT "genres".* FROM "genres" WHERE "genres"."id" IN (4
8 (1.5ms) begin transaction
9 SQL (1.8ms)
10 INSERT INTO "movie_genres" ("movie_id", "genre_id", "created_at", "updated_at")
11 VALUES (?, ?, ?, ?) [["movie_id", 4], ["genre_id", 4],
12 ["created_at", "2015-10-14 06:57:10.224260"], ["updated_at", "2015-10-14 06:57:10.224260"
13 SQL (0.1ms)
14 INSERT INTO "movie_genres" ("movie_id", "genre_id", "created_at", "updated_at")
15 ALUES (?, ?, ?, ?) [["movie_id", 4], ["genre_id", 7],
16 ["created_at", "2015-10-14 06:57:10.238410"], ["updated_at", "2015-10-14 06:57:10.238410"
17 (0.7ms) commit transaction
18 (0.0ms) begin transaction
19 (0.1ms) commit transaction
20 Redirected to http://localhost:3000/users/4
21 Completed 302 Found in 180ms (ActiveRecord: 4.8ms)

Lo ultimo que haremos sera crear una validación en el modelo moviegenres para que la combinación
entre movieid y genre_id sea única y así evitar entradas duplicadas

1 class MovieGenre < ActiveRecord::Base


2 belongs_to :movie
3 belongs_to :genre
4
5 validates :movie_id, uniqueness: { scope: :genre_id }
6 end

Podemos complicarlo aún más, y crear un nuevo formulario dentro de index, pero aquí para dar acceso a
todos los usuarios simultáneamente.

para eso hacemos un método de controller sencillo:

1 def index
2 @users = User.all
3 @sections = Section.all
4 end

y luego haremos nuestro primer intento de vista, para eso dentro de la vista views/users/index.html
crearemos una tabla donde en la cabecera estén los usuarios y en el eje de las columnas la sección, la
intersección de ambas será el acceso.
1 <table>
2 <caption>Accesos</caption>
3 <thead>
4 <tr>
5 <th> Sección </th>
6 <% @users.each do |u| %>
7 <th><%= u.name %></th>
8 <% end %>
9 </tr>
10 </thead>
11 <tbody>
12 <% @sections.each do |s| %>
13 <tr>
14 <td> <%= s.name %> </td>
15 <% @users.each do |u| %>
16 <td><%= u.name %></td>
17 <% end %>
18 </tr>
19 <% end %>
20 </tbody>
21 </table>

Como resultado obtendremos lo siguiente:

Ahora el siguiente paso, cambiemos el nombre repetido en cada fila por un checkbox, ese checkbox
deberá guardar información del usuario y de la sección.
1 <%= form_tag users_path, method: :get %>
2 <table>
3 <caption>Accesos</caption>
4 <thead>
5 <tr>
6 <th> Sección </th>
7 <% @users.each do |u| %>
8 <th><%= u.name %></th>
9 <% end %>
10 </tr>
11 </thead>
12 <tbody>
13 <% @sections.each do |s| %>
14 <tr>
15 <td> <%= s.name %> </td>
16 <% @users.each do |u| %>
17 <td><%= check_box_tag "user_sections_ids[#{u.id}][#{s.id}]" %></td
18 <% end %>
19 </tr>
20 <% end %>
21 </tbody>
22 </table>
23
24 <%= submit_tag "enviar" %>

Se vería así:
Aquí todo es bastante parecido excepto 1 cosa, el checkbox, analicemos esto:

1 <%= check_box_tag "user_sections_ids[#{u.id}][#{s.id}]" %><

En el caso anterior teníamos un sólo usuario, y estabamos enviando las secciones asociadas a ese
usuario dentro de un arreglo, ahora tenemos un arreglo de usuarios, dentro de cada uno de ellos hay un
arreglo con las secciones.

Al enviar el formulario veremos que los parámetros son:

1 Parameters: {"utf8"=>"✓",
2 "user_sections_ids"=>{"5"=>{"1"=>"1", "9"=>"1"}, "7"=>{"7"=>"1"}},
3 "commit"=>"enviar"}

O sea recibimos un hash con los usuarios que recibieron accesos, en este ejemplo aquellos con id 5 y 7 y
cada uno de estos apunta un hash con las secciones a las cuales recibieron acceso, el 5 recibió acceso a
la 1 y a la 8, el 7 sólo a la 1.

Para procesar esto ocuparemos un método distinto, como es para todos los usuarios usaremos un
bloque collection.

el archivo de rutas quedaría:

1 resources :users do
2 member do
3 post 'give_access'
4 end
5 collection do
6 post 'give_accesses'
7 end
8 end

y el nuevo prefix obtenido con rake routes sería: give_accesses_users, por lo que el formulario de
index debería quedar:

1 <%= form_tag give_accesses_users_path %>

y finalmente hacemos el método give_accesses dentro del controller de usuarios:

1 def give_accesses
2 end
Dentro del controller lo importante es primero rescatar los usuarios que reciben los permisos.

1 def give_accesses
2 users_and_sections = params[:user_sections_id]
3 users_and_sections.each do |u|
4 # user_hash contiene el user_id y el hash con las secciones
5 user = User.find(u[0])
6 sections = Section.find(u[1].keys)
7 user.sections << sections
8 user.save
9 end
10 redirect_to users_path
11 end
24) Haciendo un cloudtag
En este tutorial crearemos un cloudtag, o nube de tags con ruby on rails igual la que se ilustra en la foto,
para eso ocuparemos el plugin de jqcloud.

Para este ejercicio crearemos un proyecto nuevo:

1 rails new tagandposts

1 rails g model post title content:text


2 rails g model tag tag
3 rails g model tagpost tag:references post:references

Establecemos las relaciones dentro del modelo de tag

1 class Tag < ActiveRecord::Base


2 has_many :tagposts
3 has_many :posts, through: :tagposts
4 end

Establecemos las relaciones dentro del modelo de post

1 class Post < ActiveRecord::Base


2 has_many :tagposts
3 has_many :tags, through: :tags
4 end

Las relaciones dentro del modelo tagpost se crearon automáticamente, ya que utilizamos el generador de
rails con references.

Creando datos para la nube de tags


Luego crearemos algunos datos para nuestro proyecto

Dentro del archivo seeds vamos a agregar algunos datos


1 p = Post.new(title:"Aprendiendo a programar", content:"Lorem Ipsum ...")
2 p.tags << Tag.new(tag:"Programación")
3 p.save
4
5 p = Post.new(title:"Introducción a ruby on rails", content:"Lorem, ruby, rails")
6 p.tags << Tag.new(tag:"Programación")
7 p.tags << Tag.new(tag:"Ruby")
8 p.tags << Tag.new(tag:"Rails")
9 p.save
10
11 p = Post.new(title:"el Patrón MVC", content:"MVC, y lorem ipsum")
12 p.tags << Tag.new(tag:"Programación")
13 p.tags << Tag.new(tag:"Ruby")
14 p.tags << Tag.new(tag:"Rails")
15 p.tags << Tag.new(tag:"MVC")
16 p.save
17
18 p = Post.new(title:"Aprendiendo rails con desafiolatam", content:"Bootcamps lorem ipsum"
19 p.tags << Tag.new(tag:"Bootcamps")
20 p.tags << Tag.new(tag:"Aprender")
21 p.save
22
23 p = Post.new(title:"Lógica para la programación", content:"Logica, tablas de lorem ipsum"
24 p.tags << Tag.new(tag:"Lógica")
25 p.tags << Tag.new(tag:"Programación")
26 p.save
27
28
29 p = Post.new(title:"Escalando rails", content:"rails, tablas de lorem ipsum" )
30 p.tags << Tag.new(tag:"Optimización")
31 p.tags << Tag.new(tag:"Rails")
32 p.save

Luego descargamos el plugin de jQCloude desde https://github.com/mistic100/jQCloud

y copiamos los archivos de jqcloud.css y jqcloud.js en las carpetas respectivas de assets.

Creando el controller y cargando los datos para hacer


la nube de tags
Ahora crearemos el controller

1 rails g controller tags index

Dentro del controller de tags agruparemos los tags por término y los contaremos.
1 class TagsController < ApplicationController
2 def index
3 tags = Tag.group(:tag).count
4 @tags = tags.collect {|x,y| {text: x, weight: y}}
5 end
6 end

Creando la vista con la nube de tags


luego en la vista mostraremos los resultados ocupando un pequeño script.

Vista de tags#index

1 <div class="keywords">
2 </div>
3
4 <script>
5 tags = <%= (@tags.to_json.html_safe) %>
6 console.log(tags)
7 $('.keywords').jQCloud(tags, {
8 width: 500,
9 height: 350
10 });
11 </script>

25) Devise
La autenticación es el proceso de validación de un usuario en un sistema, es perfectamente posible
programar uno desde cero, pero como en la mayoría de los casos estos sistemas son exactamente
iguales en todos los sitios ya existen sistemas muy completos y seguros que puedes incorporar en tu
sitio.

En Ruby on Rails el sistema de autenticación más famoso se llama Devise, y es el que aprenderemos a
ocupar en este capítulo, razones para ocuparlo:

1. Soporte autenticación sobre múltiples modelos


2. Es seguro, encripta las claves automáticamente, no guarda las claves en texto plano.
3. Incorpora herramientas para validación del email (módulo confirmable)
4. Es combinable con otras soluciones como Login con FB, Twitter, Linkedin y todo lo que soporte
OAUTH
5. Los tiempos de sessión son configurables
Empezando con devise
Devise es bastante sencillo de implementar, primero agregamos la gema al gemfile.

1 gem 'devise'

1 bundle
2 rails generate devise:install

Al correr el generador, rails nos mostrará el siguiente mensaje:

1 Some setup you must do manually if you haven't yet:


2
3 1. Ensure you have defined default url options in your environments files. Here
4 is an example of default_url_options appropriate for a development environment
5 in config/environments/development.rb:
6
7 config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
8
9 In production, :host should be set to the actual host of your application.
10
11 2. Ensure you have defined root_url to *something* in your config/routes.rb.
12 For example:
13
14 root to: "home#index"
15
16 3. Ensure you have flash messages in app/views/layouts/application.html.erb.
17 For example:
18
19 <p class="notice"><%= notice %></p>
20 <p class="alert"><%= alert %></p>
21
22 4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:
23
24 config.assets.initialize_on_precompile = false
25
26 On config/application.rb forcing your application to not access the DB
27 or load models when precompiling your assets.
28
29 5. You can copy Devise views (for customization) to your app by running:
30
31 rails g devise:views

Y ahora debemos cumplir con estos 5 puntos: El 1er punto es para poder enviar los emails, estos emails
probablemente sean bloqueados por Gmail u otros sistemas de todas formas así que podemos omitirlo
por ahora.

El segundo punto es para ingresar la página de inicio, esto es necesario para que devise pueda redirigirte
cuando intentes entrar a una URL y no estas autenticado.

El tercer punto es para mostrar los mensajes de que ingresaste con éxito o falló el ingreso da lo mismo
cuál página se use, por lo mismo estos mensajes se ponen en la página maestra.

El punto cuatro es exclusivo para Rails 3.2 y nosotros estamos trabajando con Rails 4, así que no nos
compete.

El último punto permite generar las vistas para login, actualizar contraseñas y muchas otras, si omitimos
este paso igual se van a mostrar estas vistas pero nosotros no las podremos generar.

correr rails g devise:views creará todos estas vistas:

invoke Devise::Generators::SharedViewsGenerator
create app/views/devise/shared
create app/views/devise/shared/_links.html.erb
invoke form_for
create app/views/devise/confirmations
create app/views/devise/confirmations/new.html.erb
create app/views/devise/passwords
create app/views/devise/passwords/edit.html.erb
create app/views/devise/passwords/new.html.erb
create app/views/devise/registrations
create app/views/devise/registrations/edit.html.erb
create app/views/devise/registrations/new.html.erb
create app/views/devise/sessions
create app/views/devise/sessions/new.html.erb
create app/views/devise/unlocks
create app/views/devise/unlocks/new.html.erb
invoke erb
create app/views/devise/mailer
create app/views/devise/mailer/confirmation_instructions.html.erb
create app/views/devise/mailer/reset_password_instructions.html.erb
create app/views/devise/mailer/unlock_instructions.html.erb

Creando el modelo de devise


Una vez con devise instalado tenemos dos opciones, la primera es agregarlo sobre un modelo existente,
la segunda es crear un nuevo modelo deviseable.
¿Cuál opción escoger? Sólo depende de si ya tienes creado el modelo de tus usuarios o no, pero en
ambos casos lo que tenemos que hacer es:

1 rails generate devise user

Es perfectamente posible cambiar el nombre de la tabla y utilizar admin, u otro nombre en lugar de user,
pero es importante el nombre utilizado puesto que devise genera helpers en base a ese nombre, así que
si lo cambias no olvides cuál nombre utilizaste.

al correr el generador del modelo la consola mostrará lo siguiente:

invoke active_record
create db/migrate/20151015141123_devise_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
insert app/models/user.rb
route devise_for :users

En resumen: se generó una migración, el modelo de usuario, test para el modelos de usuarios, los fixtures
para las pruebas y se agrega la ruta devise_for :users

Ahora debes correr las migraciones puesto que devise crea la tabla si no existe o agrega los campos
necesarios si no los tiene.

1 rake db:migrate

y con eso ya terminamos la configuración inicial, ahora para revisar el login y el formulario de registro
debemos entrar a

http://localhost:3000/users/sign_in http://localhost:3000/users/sign_up

recordar que si cambiamos el modelo de user la ruta también debe reflejar el nombre del modelo utilizado
en plurar.
Ingreso, registro y salida
Las acciones claves de devise son:

1. Sign_in
2. Sign_up
3. Sign_out

hay otras como cambiar el password, pero por ahora vamos a abordar las 3 rutas claves, ¿cómo
podemos ver todas la acciones?, con rake routes , a continuación ingreso las tres rutas que nos
permitiran que un usuario se registre, ingrese y luego cierre la sesión.
Prefix Verb Url Controller#method

new_user_registration GET /users/sign_up(.:format) devise/registrations#new

new_user_session GET /users/sign_in(.:format) devise/sessions#new

destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy

¿Cómo podemos dirigir al usuario al formulario de ingreso? fácil, agregamso un link a


new_user_registration_path

1 <%= link_to "Ingresar", new_user_registration_path %>

Para dirigirlo al formulario de creación es igual, pero ocupamos la ruta.

1 <%= link_to "Registrar", new_user_registration_path %>

Para salir debemos ocupar la ruta respectiva, pero además debemos especificar el verbo delete

1 <%= link_to "Salir", destroy_user_session_path, method: :delete %>

o en la forma de HTML

1 <a href="<%= destroy_user_session_path %>"


2 data-method="delete">
3 Salir
4 </a>

login or logout
Es muy común en un sitio web o aplicación web que no se muestre simultaneamente el link a ingresar y
registrar y el link a salir simultáneamente, normalmente se muestra ingresar y registrar (si no has
ingresado) y salir (si ya estas ingresado)

Esto lo podemos lograr ocupando el helper user_signed_in? de devise y un un if y else de ruby.


1 <% if user_signed_in? %>
2 <%= link_to "Salir", destroy_user_session_path, method: :delete %>
3 <% else %>
4 <%= link_to "Ingresar", new_user_registration_path %>
5 <%= link_to "Registrar", new_user_registration_path %>
6 <% end %>

El objeto current_user
Cuando el usuario se registra o ingresa se inicia una sesión, las sesiones en rails son un hash que permite
identificar al usuario y guardar junto con el información a nuestra voluntad.

La session no queda guardada en la base de datos, si no en las cookies del navegador, la entidad de rails
encargada de manejar las sesiones es el ActionDispatch::Session::CookieStore.

Por que son importantes las sesiones en el contexto de devise?, porque sirven para guardar al usuario
actual, ¿cómo lo hace?

el método current_user busca si el usuario está en la sesión, si no lo encuentra lo busca en la base de


datos.

Gracias a esa definición, nosotros podemos mostrar información del usuario logeado, por ejemplo si
quisieramos mostrar el email sería:

1 <%= current_user.email %>

El logout destruye el objeto current_user.

Modificando los formularios de ingresar y registro


Para modificar los formularios lo primero que debemos hacer (sólo si no lo hicimos previamente) es
generar la vistas,

1 rails g devise:views

Con las vistas generadas ahora podemos modificarlas utilizando HTML. Las vistas generadas se
encuentran en: views/devise/ ahí encontraremos varias carpetas, el login está dentro de
views/devise/sessions/new y el sign_up dentro de views/devise/registrations/new

Dentro de estas vistas encontraremos un formulario que es ligeramente distinto a lo que hemos vistos
hasta ahora.

1 <%= form_for(resource, as: resource_name, url: registration_path(resource_name))

Este fomulario de devise es compatible con múltiples recursos simultaneamente, o sea podemos tener
múltiples modelos de usuarios (obviamente con distintos nombres) y todos con devise.

Además la mayoría de las vistas de devise incluyen un render a la vista parcial de links

1 <%= render "devise/shared/links" %>

Agregando un dato al modelo de usuarios


El primer paso ya lo hemos realizado previamente, consiste en generar una migración para agregar el
dato, para probarlo agregaremos el campo nombre (name) para el usuario.

1 rails g migration addNameToUser name:string


2 rake db:migrate

El segundo paso es agregar el campo al formulario

1 <div class="field">
2 <%= f.label :name %><br />
3 <%= f.text_field :name, autofocus: true %>
4 </div>

Al recargar la página veremos el formulario de registro con el nombre pero si lo llenamos y lo enviamos
veremos en nuestra base de datos que nuestro usuario no tiene nombre, si revisamos en el log de rails
server veremos:

Parameters: {"utf8"=>"✓", "user"=>{"name"=>"Gonzalo", "email"=>"gonzalo@desafiol


atam.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "comm
it"=>"Sign up"}
Unpermitted parameter: name

¿Cómo podemos arreglar un problema de strong parameteres si no tenemos controller de usuario?

Fácil, utilizaremos el super controller, o sea el application controller


1 before_action :configure_permitted_parameters, if: :devise_controller?
2
3 protected
4
5 def configure_permitted_parameters
6 devise_parameter_sanitizer.for(:sign_up) << :name
7 end

El callback nos permite llamar al método configure_permitted_parameters si estamos dentro del controller
de devise, luego el método agrega el campo name a la lista de los parámetros sanitizados. Podriamos
agregar un segundo separandolo con una coma.

Después de este cambio podemos enviar el formulario de nuevo y

INSERT INTO "users" ("email", "encrypted\_password", "name", "created_at", "update


d_at")
VALUES (?, ?, ?, ?, ?)
[["email", "gonzalo2@desafiolatam.com"],
["encrypted\_password",
"$2a$10$k19SMlN60bX4NQXutV3CseToYC/I5TbHeb.6P26N6WzWi0ZXxKa5C"], ["name", "Gonzalo
"],
["created_at", "2015-10-19 04:06:23.324424"],
["updated_at", "2015-10-19 04:06:23.324424"]]

Bloqueando el acceso
Es muy común en un sitio web que un usuario no puede acceder a una página X hasta que ya se haya
logeado, en Devise es posible lograrlo utilizando el callback before_action dentro de un controller que se
desea bloquear.

1 before_action :authenticate_user!

Ahora supongamos que tenemos un controller llamado pages, cuyo único objetivo es mostrar dos
páginas, una es home, la otra es secreto y queremos que la página secreto sea sólo para usuarios
logeados.
1 class PagesController < ApplicationController
2 def home
3 end
4
5 def secreto
6 end
7 end

Podríamos hacer:

1 class PagesController < ApplicationController


2 before_action :authenticate_user!
3 def home
4 end
5
6 def secreto
7 end
8 end

Pero de esta forma limitaríamos todas las páginas y no solo home, para evitar esto podemos agregar
como parámetro al before_action los hashs :only o except de esta forma:

1 class PagesController < ApplicationController


2 before_action :authenticate_user!, only: [:secreto]
3 def home
4 end
5
6 def secreto
7 end
8 end

De esta forma sólo el método secreto de pages dependerá de que el usuario se haya logeado y si el
usuario intenta entrar a http://localhost:3000/pages/secreto sin estar logeado será
redirigido a la login.

Antes de probarlo no te olvides de agregar las páginas al archivo routes.rb

1 get 'pages/home'
2 get 'pages/secreto'

Cuando se crea un sistema de login con Devise todo funciona perfecto salido de la caja excepto el
recuperar contraseñas, la razón es muy sencilla, esto se hace via email y para que rails pueda enviar un
email necesita tener un sender (enviador) configurado, este modulo en rails 4 recibe el nombre de Action
Mailer.
Configurando action_mailer para enviar correos con gmail para hacerlo basta abrir el archivo de
configuración config/application.rb (también es posible ocupar un initializer) y agregar las siguientes líneas
dentro del module y de class Application.

1 config.action_mailer.default_url_options = { :host => 'localhost:3000' }


2 config.action_mailer.delivery_method = :smtp
3 config.action_mailer.perform_deliveries = true
4 config.action_mailer.raise_delivery_errors = true
5 config.action_mailer.default :charset => "utf-8"
6
7 ActionMailer::Base.smtp_settings = {
8 :address => "smtp.gmail.com",
9 :port => 587,
10 :authentication => :plain,
11 :domain => 'gmail.com',
12 :user_name => ENV['email'],
13 :password => ENV['email_password'],º
14 }

Donde dice ENV[‘email’] y password podemos cambiarlas por nuestras claves de email y al reiniciar la
aplicación ya estaría funcionando pero hay un problema grande con hacer eso, estaríamos dejando las
claves del correo electrónico dentro de nuestro código.

Protegiendo las claves con dot-env Dot-env es una gema que nos permite agregar variables de entorno
de forma sencilla a nuestra aplicación, para eso vamos agregar la siguiente gema al gemfile

gem 'dotenv-rails'

Luego tenemos que crear un archivo .env (si, el punto es parte del nombre) dentro de la raíz de nuestro
proyecto, en el vamos a agregar las variables de entorno.

1 email=tuemail@gmail.com
2 email_password=tuppassword

y ya con eso nuestra aplicación permite recuperar las contraseñas del usuario desde el sign_in.

Evitando adjuntar el archivo .env por error al repositorio Ahora debemos de asegurarnos de no adjuntar
este archivo por error cuando hagamos un commit, para eso vamos a abrir el archivo .gitignore (esto sólo
aplica si están ocupando GIT)

/.env

Configurando Heroku para que acepte las variables de entorno Si ocupas Heroku te estarás preguntando
como pasar el archivo .env si no está en el repositorio, el secreto es que no se pasa, vamos a ocupar la
terminal para dar los valores de las variables de entorno.
Entonces desde la terminal dentro de la carpeta del proyecto, escribimos:

1 heroku config:set email=tuemail@gmail.com


2 heroku config:set email_password=tupassword

y ahora si que si, tus claves están seguras y tu aplicación está funcionando con la opción de recuperar
contraseñas.
26) Devise avanzado
Ya hemos cubierto los tópicos básicos de devise, pero todavía hay varias funcionalidades interesante que
veremos en este capítulo.

Agregando el rol de usuario


Lejos una de las funcionalidades más utilizadas es la de múltiples roles, o sea existe un usuario que tiene
acceso a ciertas páginas y otros usuarios que tienen acceso a otras.

Para lograr esto tenemos que agregar un campo rol al usuario para poder distinguirlo y luego customizar
nuestro propio authenticate_user!

Agregando el campo

Podemos hacerlo de varias formas, con un string, con un integer o la mejor forma con un enum.

Los strings son buenos para distinguir el rol pero dejas abierta la posibilidad que en algún momento se
ingrese un rol no definido.

Los integers puedes utilizarlo como 0 para admin, 1 para usuario, etc, y a pesar de que no caen en el
problema anterior de crear un rol usuari en vez de usuario tienen el problema de que no es claro que hace
cada número, te obliga a revisar la documentación y es un causa problema de errores.

Enums al rescate

Los enums permiten combinar lo mejor de estos dos mundos, los strings y los integers.

Para crear un enum agregaremos el rol del usuario como integer en la base de datos.

1 rails g migration addRoleToUser role:integer


2 rake db:migrate

luego en el modelo especificaremos que role es un enum

1 enum role: [ :admin, :editor, :user]


Para revisar cambiar el status de un usuario tenemos métodos como

1 - .admin! (cambia el usuario al rol admin)


2 - .admin? (devuelve true o false dependiendo de si el usuario es admin)

Por ejemplo queremos revisar si el usuario logeado es editor, entonces simplemente:

1 <% if current_user.editor? %>

Para cambiar al primer usuario de la base de datos y darle acceso de admin haríamos

1 User.last.admin!

El problema que tenemos ahora es que los usuarios que ya existen en la base de datos quedan con el
campo role como nil y los nuevos usuarios también, a menos que le demos la opción de elegir el role que
tendrán, lo que es una muy mala idea ya que el administrador del sitio debería ser quien asigne los roles
en la aplicación.

Lo que debiera pasar es que cualquier usuario nuevo se cree con un role estándar, en este caso el de
user, para eso definiremos un valor por defecto al campo role y lo haremos a nivel de base de datos y de
modelo.

A nivel de base de datos: Primero creamos una migración vacía

1 rails g migration addDefaultRoleToUser

Luego modificamos la migración recién creada para dejarla así

1 class AddDefaultRoleToUser < ActiveRecord::Migration


2 def up
3 change_column :users, :role, :integer, default: 2, null: false
4 end
5
6 def down
7 change_column :users, :role, :integer, default: nil
8 end
9 end

Y por ultimo ejecutamos la migración


1 rake db:migrate

Una vez corrida la migración todos los usuarios en la base de datos que tenían role = nil ahora
tendrán el role por defecto, en este caso 2.

Ahora setearemos el defaul a nivel de modelo, para eso en el modelo de usuario user.rb añadimos lo
siguiente:

1 ...
2 before_save :default_role
3
4 def default_role
5 self.role ||= 2
6 end
7 ...

Ahora que ya tenemos roles pasaremos a verificar los accesos, para eso vamos al applicationController y
añadimos lo siguiente al final del archivo antes del ultimo end

1 private
2 def check_admin!
3 authenticate_user!
4 unless current_user.admin?
5 redirect_to root_path, alert: "No tienes acceso"
6 end
7 end

De esta manera el método check_admin! estará disponible en todos nuestros controladores.

Luego en cada uno de los controladores donde queremos que el usuario logueado sea administrador
agregamos lo siguiente.

1 before_action :check_admin!, only: :secreto

Esto es suficiente para sitios chicos donde hay que revisar 3 o 4 páginas contra uno o dos accesos, pero
si escalamos de esta forma nos vamos a ver creando muchos métodos en cada controller para revisar los
accesos, para evitar esto en le próximo capítulo estudiaremos la gema CanCanCan.

Testeando los accesos


El testeo de los accesos es un test funcional, o sea un test de los controllers.
Para testear con Devise primero debemos incluir el helper dentro de la clase del test

1 include Devise::TestHelpers

Luego creamo fixtures para los distintos tipos de usuario en el archivo test/fixtures/user.yml

1 admin:
2 id: 1
3 email: "admin@desafiolatam.com"
4 role: 0
5
6 editor:
7 id: 2
8 email: "editor@desafiolatam.com"
9 role: 1
10
11 user:
12 id: 3
13 email: "user@desafiolatam.com"
14 role: 2

y luego de vuelta en el controller definimos nuestros tests.

Principalmente lo que vamos a testear en este punto es si un usuario tiene acceso a una página estando
logeado o no, o si tiene acceso a el método dado el rol que tiene. Siempre tenemos que realizar los dos
tipos de test, los positivos y los negativos, o sea que no pueda cuando no deba y que pueda cuando
deba.

1 test "unlogged user can get home" do


2 get :home
3 assert_response :success
4 end

y luego probamos nuestro primer test, debería pasar, ahora si obtenemos un error del tipo:

ActionView::Template::Error:
undefined method `authenticate' for nil:NilClass

Es porque no agregamos el ​helpers de devise para los tests. include Devise::TestHelpers

Ahora creemos un test para revisar si un usuario logeado puede entrar a la página home
1 test "logged user can get home" do
2 user = users(:user)
3 sign_in(user)
4 get :home
5 assert_response :success
6 end

Este test será más interesante, que pasa si un usuario no loggeado intenta entrar a una página que no
puede.

1 test "logged user can't get secreto" do


2 get :secreto
3 assert_response :redirect
4 end

Y como probamos que un usuario con un rol específico no tenga acceso, eso lo hacemos con:

1 test "user without privileges can't get secreto" do


2 user = users(:user)
3 sign_in(user)
4 get :secreto
5 assert_response :redirect
6 end

Como mencionamos previamente no es suficiente probar que no tenga acceso, también hay que probar
que la persona correcta si lo tiene.

La gran ventaja de tener test para los tipos de acceso es que si en algún momento llegamos a romper
algo por integrar una nueva funcionalidad podemos detectarlo sin tener que probar manualmente todas
las páginas una a una.

En resument testear accesos nos permite:

Mejorar los tiempos de desarrollo


Bajar los costos de desarrollo
Implementar cambios y mejoras con menor costo.
Y en el caso de pruebas de acceso, asegurarnos que la seguridad del sitio esté OK

Códigos completos:
Tests

1 require 'test_helper'
2
3 class PagesControllerTest < ActionController::TestCase
4 include Devise::TestHelpers
5
6 test "unlogged user can get home" do
7 get :home
8 assert_response :success
9 end
10
11 test "logged user can get home" do
12 user = users(:user)
13 sign_in(user)
14 get :home
15 assert_response :success
16 end
17
18 test "logged user can't get secreto" do
19 get :secreto
20 assert_response :redirect
21 end
22
23 test "user without privileges can't get secreto" do
24 user = users(:user)
25 sign_in(user)
26 get :secreto
27 assert_response :redirect
28 end
29
30 test "admin can get secreto" do
31 admin = users(:admin)
32 sign_in(admin)
33 get :secreto
34 assert_response :success
35 end
36
37 end

Pages Controller
1 class PagesController < ApplicationController
2 before_action :check_user, only: :secreto
3
4 def home
5 end
6
7 def secreto
8 end
9
10 private
11 def check_user
12 authenticate_user!
13 unless current_user.admin?
14 redirect_to root_path, alert: "No tienes acceso"
15 end
16 end
17
18 end

Fixture de usuario

1 admin:
2 id: 1
3 email: "admin@desafiolatam.com"
4 role: 0
5
6 editor:
7 id: 2
8 email: "editor@desafiolatam.com"
9 role: 1
10
11 user:
12 id: 3
13 email: "user@desafiolatam.com"
14 role: 2

Generando los controllers de devise


Al igual que las vistas, los controllers pueden ser generados, y al generarlos tenemos un mejor control del
como funcionan, ya sea por los strong params, o porque queremos realizar alguna acción como enviar un
email al momento del registro.

Para generar los controllers ocuparemos el generador de devise.


1 rails generate devise:controllers users

Al correr el generador observaremos que aparece un mensaje que dice lo siguiente:

1 Some setup you must do manually if you haven't yet:


2
3 Ensure you have overridden routes for generated controllers in your routes.rb.
4 For example:
5
6 Rails.application.routes.draw do
7 devise_for :users, controllers: {
8 sessions: 'users/sessions'
9 }
10 end

Para lograr esto iremos al archivo de rutas y cambiaremos el devise_for :users por

1 devise_for :users, controllers: {


2 sessions: 'users/sessions',
3 registrations: 'users/registrations'
4 }

En el archivo de rutas debemos especificar el remplazo de lo que queramos cambiar.

Una vez corrido el generador veremos que se crean diversos archivos bajo la carpeta user dentro de
controllers.

Los métodos expresados ahí dentro son sencillos, ya que user hereda de RegistrationsController todos
los métodos respectivos se reducen a hacer un llamado a super para llamar al método padre.

Cambiando la página despues de registrarse


Nosotros vamos a descomentar el primer before_filter, o sea configure_sign_up_params

1 class Users::RegistrationsController < Devise::RegistrationsController


2 before_filter :configure_sign_up_params, only: [:create]
3 # before_filter :configure_account_update_params, only: [:update]
4
5 # GET /resource/sign_up
6 # def new
7 # super
8 # end
9
10 # POST /resource
11 # def create
12 # super
13 # end
14
15 # GET /resource/edit
16 # def edit
17 # super
18 # end
19
20 # PUT /resource
21 # def update
22 # super
23 # end
24
25 # DELETE /resource
26 # def destroy
27 # super
28 # end
29
30 # GET /resource/cancel
31 # Forces the session data which is usually expired after sign
32 # in to be expired now. This is useful if the user wants to
33 # cancel oauth signing in/up in the middle of the process,
34 # removing all OAuth session data.
35 # def cancel
36 # super
37 # end
38
39 # protected
40
41 # If you have extra params to permit, append them to the sanitizer.
42 def configure_sign_up_params
43 devise_parameter_sanitizer.for(:sign_up) << :name
44 end
45
46 # If you have extra params to permit, append them to the sanitizer.
47 # def configure_account_update_params
48 # devise_parameter_sanitizer.for(:account_update) << :attribute
49 # end
50
51 # The path used after sign up.
52 # def after_sign_up_path_for(resource)
53 # super(resource)
54 # end
55
56 # The path used after sign up for inactive accounts.
57 # def after_inactive_sign_up_path_for(resource)
58 # super(resource)
59 # end
60 end

Cambiando la página después de ingresar


En la última versión de devise en la que probamos esto no se genera el código de redireccion, pero eso
no quiere decir que no se pueda agregar, para eso dentro del archivo
controllers/users/sessions_controller.rb

Agregaremos al final:

1 protected
2 def after_sign_in_path_for(resource)
3 destino_path
4 end

Donde destino_path es un path válido.

Configurando action_mailer para enviar correos con


gmail
para hacerlo basta abrir el archivo de configuración config/application.rb (también es posible ocupar un
initializer) y agregar las siguientes líneas dentro del module y de class Application.
1 config.action_mailer.default_url_options = { :host => 'localhost:3000' }
2 config.action_mailer.delivery_method = :smtp
3 config.action_mailer.perform_deliveries = true
4 config.action_mailer.raise_delivery_errors = true
5 config.action_mailer.default :charset => "utf-8"
6
7 ActionMailer::Base.smtp_settings = {
8 :address => "smtp.gmail.com",
9 :port => 587,
10 :authentication => :plain,
11 :domain => 'gmail.com',
12 :user_name => ENV['email'],
13 :password => ENV['email_password'],
14 }

donde dice ENV[‘email’] y password podemos cambiarlas por nuestras claves de email y al reiniciar la
aplicación ya estaría funcionando pero hay un problema grande con hacer eso, estaríamos dejando las
claves del correo electrónico dentro de nuestro código.

Protegiendo las claves con dot-env


Dot-env es una gema que nos permite agregar variables de entorno de forma sencilla a nuestra
aplicación, para eso vamos agregar la siguiente gema al gemfile

gem 'dotenv-rails'

luego tenemos que crear un archivo .env (si, el punto es parte del nombre) dentro de la raíz de nuestro
proyecto, en el vamos a agregar las variables de entorno.

1 email=tuemail@gmail.com
2 email_password=tuppassword

y ya con eso nuestra aplicación permite recuperar las contraseñas del usuario desde el sign_in.

Evitando adjuntar el archivo .env por error al repositorio Ahora debemos de asegurarnos de no adjuntar
este archivo por error cuando hagamos un commit, para eso vamos a abrir el archivo .gitignore (esto sólo
aplica si están ocupando GIT)

/.env

Configurando Heroku para dot-env


Configurando Heroku para que acepte las variables de entorno Si ocupas Heroku te estarás preguntando
como pasar el archivo .env si no está en el repositorio, el secreto es que no se pasa, vamos a ocupar la
terminal para dar los valores de las variables de entorno.

Entonces desde la terminal dentro de la carpeta del proyecto, escribimos:

1 heroku config:set email=tuemail@gmail.com


2 heroku config:set email_password=tupassword

y ahora si que si, tus claves están seguras y tu aplicación está funcionando con la opción de recuperar
contraseñas.

Quiz
¿Para qué sirve el objeto current_user?
¿Donde se ingresan los strong parameters de un objeto deviseado?
¿Cómo podemos redireccionar a un usuario después de ingresar por devise?
¿Cómo podemos redireccionar a un usuario después de registrarse por devise?
27) Autorización con CanCanCan
CanCanCan es una gema para el manejo de accesos de usuarios en un sitio, y juega muy bien en
conjunto con Devise.

Hay que tener cuidado a la hora de trabajar con este gema de no confundirla con su versión
anterior llamada CanCan

¿Cuándo no utilizar CanCanCan?


Cuando es un sitio pequeño y sólo se busca controlar acceso entre uno o dos perfiles de usuarios
distintos.

Instalando CanCanCan
Para instalar la gema debemos abrir el gemfile y agregar la gema cancancan.

luego corremos bundle y generamos el árbol de habilidades de cancancan con el generador de rails.

1 rails g cancan:ability

Si lo hicimos bien, obtendremos:

1 create app/models/ability.rb

El árbol de habilidades
El árbol de habilidades es el archivo donde se define que puede hacer y que no puede hacer cada
usuario, este archivo se encuentra dentro de app/model/ability.rb y parte con un par de ejemplos
comentados. CanCanCan espera que exista un método current_user en el controlador, por eso
primero agregamos un sistema de autenticación como devise en primer lugar.
1 def initialize(user) # user es sacado del metodo current_user automaticamante por cancancan
2 # Define abilities for the passed in user here. For example:
3 user ||= User.new # guest user (not logged in)
4 if user.admin?
5 can :manage, :all
6 else
7 can :read, :all
8 end
9 end

La lógica que contiene es suficiente para partir, se entiende que si no hay un usuario logeado igual hay
que pasar por el proceso de autenticación por lo mismo crear el objeto user en la línea 4, luego si es
admin le da permiso para acceder a todo, pero si no sólo tiene permisos de lectura.

El método can recibe dos parámetros, el primero es la acción (método) a la cual estamos estableciendo el
permiso, y el segundo el recurso (Clase) en donde se puede ejecutar esa acción.

Por ejemplo quisiéramos dar acceso completo al recurso películas a los editores, haríamos

1 if user.editor?
2 can :manage, Movie
3 end

por ejemplo quisiéramos dar acceso completo a todos los recursos a los editores, haríamos

1 if user.editor?
2 can :manage, :all
3 end

Si quisiéramos por ejemplo que un usuario solo tuviera acceso a ver el índice podríamos hacer:

1 if user.user?
2 can :index, Movie
3 end

Es posible asignar múltiples acciones simultáneamente con arrays, por ejemplo:

1 if user.user?
2 can [:index, :show], [Movie, Review]
3 end
El método :manage es un alias que se refiere a todas las acciones y el método :all es un alias
para referirse a todos los recursos de la aplicación.

Hay otros cuatro alias que usaremos constantemente para definir y chequear permisos y son:

:read que es un alias para :index y :show


:create que es un alias para :new y :create
:update que es un alias para :edit y :update
:destroy que es un alias para :delete y :destroy

También es posible definir aliases propios siguiendo los principios de la guía oficial
https://github.com/CanCanCommunity/cancancan/wiki/Action-Aliases, pero al menos que tengas
muchos métodos no REST repetidos en cada controller no tiene sentido.

Revisión de habilidades
Una vez que ya tenemos el árbol de habilidades, podemos empezar a ocupar el método can? para
determinar cuando mostrar (o cuando no) un link o cierta información específica a un usuario
dependiendo de su rol.

1 <% if can? :update, @movie %>


2 <%= link_to "Edit", edit_movie_path(@movie) %>
3 <% end %>

o es una línea

1 <%= link_to "Edit", edit_movie_path(@movie) if can? :update, @movie %>

Ocultar el contenido no siempre es suficiente, por ejemplo en el caso de un link no sólo nos interesa que
la persona no pueda ver el link, también nos interesa que no logre entrar cambiando la url.

Bloqueo
La forma de bloquear un recurso a un acceso no deseado es a través del controller, se especifica la
acción loadandauthorize_resource.
1 class MoviesController < ApplicationController
2 load_and_authorize_resource
3
4 def show
5 # @movie is already loaded and authorized
6 end
7 end

load_and_authorize_resource cambia algunas reglas del juego, por ejemplo ya no tenemos que cargar los
recursos en los métodos clásicos para los métodos REST usando el callback before_action .

ejemplo:

1 def index
2 # @movies is already loaded and authorized
3 end

En otros métodos si tenemos que cargarlos.

Manejo de conexiones no autorizadas


El último paso consisten en especificar dentro del applicationController a que página redirigir
en caso de que una persona intente acceder a una página a la que no está autorizada.

1 class ApplicationController < ActionController::Base


2 rescue_from CanCan::AccessDenied do |exception|
3 redirect_to root_url, alert: exception.message
4 end
5 end

Podemos cambiar el mensaje donde se define el alert por uno personalizado.

Testeando las habilidades


Es posible testear las habilidades en la consola, y es el primer lugar en el que deberíamos revisar si algo
no funciona.

El testing de una habilidad siempre tiene la siguiente forma: Seleccionamos un usuario y vemos si tiene
acceso a un recurso.
1 user = User.first
2 ability = Ability.new(user)
3 ability.can?(:create, Movie)
4 ability.can?(:edit, Movie)
5 ability.can?(:destroy, Movie)

Habilidades basadas en la propiedad


Es perfectamente posible limitar el acceso a un recurso a cualquier otra persona que no sea su dueño,
por ejemplo supongamos que en un foro los editores pueden cambiar todo, pero un usuario solo puede
editar o eliminar los post creador por el, este tipo de restricciones son bastante comunes y fáciles de
implementar con cancancan

En el árbol de habilidades se puede especificar el acceso en base a un campo del recurso, por ejemplo:

1 can [:update, :destroy], [Movie, Review], user_id: user.id

Testear este tipo de habilidades requiere especificar de quien es el objeto.

1 ability.can?(:destroy, Review.new(user: user))

Habilidades en recursos anidados


Supongamos un caso donde hayan diversos grupos, y quieres que un usuario tenga acceso a todo
dentro de un grupo, tendríamos un recurso grupo y anidado a un recurso movie, lo otro importante es que
el usuario tenga guardado el group_id

dado eso podemos limitar el acceso con:

1 can :index, Movie, {id: user.group_id}

Luego el recurso (en el controller) también hay que autorizarlo de una forma ligeramente distinta.

El de group sería de la misma forma:

1 load_and_authorize_resource

pero el de Movie sería


1 load_and_authorize_resource :group
2 load_and_authorize_resource :through => :group

Construir tests para este tipo de habilidades es igual que para otros tests de recursos anidados.

1 test "users can't access other groups sections" do


2 sign_in users(:group1)
3 get :index, {group_id: 2}
4 assert_redirected_to root_path
5 end
28) Polimorfismo
En muchas ocasiones nos encontraremos con interacciones entre modelos que se repiten, por ejemplo:

votos con post y votos con comentarios


foto del post y del usuario
tags de diferentes elementos
likes a diferentes tablas como movie y review

Para no tener que implementar las reglas del negocio para cada uno de los elementos y caer en el error
de repetir código existe una técnica llamada polimorfismo que consiste en una interfaz que permite
interactuar entre modelos que se comportan similar.

La interfaz suena a algo muy complejo, pero realmente consiste en dos campos de la base de datos que
permiten relacionar con el id del objeto y otro para guardar el tipo de objeto.

La interfaz se agrega sobre el modelo común, por ejemplo si queremos implementar likes de usuarios
sobre movies y sobre reviews la interfaz la haríamos sobre likes. Agregando la interfaz:

Creando el modelo like desde cero:

1 rails g model like user:references likable:references{polymorphic}

Convirtiendo el modelo like a polimórfico si este ya existe

1 rails g migration addLikableToLike likable:references{polymorphic}

Esto generara la migración:

1 class AddLikeableToLike < ActiveRecord::Migration


2 def change
3 add_reference :likes, :likable, polymorphic: true, index: true
4 end
5 end

y ahora marcamos nuestro modelo de like como polimórfico y lo asociamos al usuario

1 belongs_to :user
2 belongs_to :likable, polymorphic: true
Los modelos que interactúan con la interfaz deben saber que lo están haciendo, para eso agregaremos al
modelo de movie y al de review la relación con

1 has_many :likes, as: :likable


2 has_many :user_likes, through: :likes, source: :user

Y al modelo de user también agregamos la relación

1 has_many :likes
2 has_many :movie_likes, through: :likes, source: :likable, source_type: ‘Movie’
3 has_many :review_likes, through: :likes, source: :likable, source_type: ‘Review’

Si queremos validar que un usuario pueda hacer like una sola vez a una movie o review, agregamos la
siguiente validación en el modelo like:

1 validates :user_id, uniqueness: {scope: [:likable_id, :likable_type]}

Es necesario hacer la comprobación usando los tres campos ya que de otra manera si un usuario hace
like a la movie con id 4 no podrá hacer like al review con id 4.
29) Subiendo archivos con carrirewave
La gema carrierwave es una gema bastante sencilla de ocupar que permite la subida de archivos, exista
otra gema que cumple el mimso propósito llamada paperclip, la configuración es distinta pero ambas
hacen el trabajo.

Instalando carrierwave
El branch master de la gema todavía es experimental, por lo que recomendamos ocupar la
documentación del tag 0.10

Para instalar la gema agregaremos gem carrierwave al gemfile.

Gemfile.rb ~~~ruby gem ‘carrierwave’ ~~~

luego bundle.

El siguiente paso es generar un uploader, un uploader define una estrategia para subir archivos, si
tenemos varios campos que requieran subir fotos, pero todas las fotos reciben el mismo tratamiento
podemos reutilizar el uploader, pero si tenemos distintos tipos de archivos que subir y hay que tratarlos
de forma distitna crearemos uno por estrategia.

Generando el uploader

1 rails generate uploader Avatar

Si revisamos el uploader generado, veremos algo como:


1 class AvatarUploader < CarrierWave::Uploader::Base
2 storage :file
3 end

storage file indica que el archivo se guardará dentro de la aplicación, dentro de la carpeta public, en el
próximo capítulo estudiaremos como subir estos archivos a Amazon S3.

Luego el último paso que nos falta es montar el uploader, para montarlo necesitamos tener un campo del
tipo string para guardar el nombre del archivo.

por ejemplo si tuvieramos un campo llamado photo dentro del modelo, entonces:

1 mount_uploader :photo, AvatarUploader

Probando desde rails console.


Si tenemos un archivo en la raiz del proyecto podemos asignarsela a un objeto así.

1 post = Post.first
2 File.open("nombre_archivo) do |f|
3 post.photo = f
4 end
5 post.save

Creando una formulario con archivos


En primer lugar para que un formulario pueda enviar archivos debe ser del tipo multiparte, podemos
convertir a cualquier formulario en multiparte utilizando html: {:multipart => true}

por ejemplo

1 = form_for @post, html: {:multipart => true} do |f|

luego dentro del formulario para agregar un campo que nos permite utilizar archivos debemos uitlizar

1 f.file_file :photo
30) Amazon S3
Amazon S3 es un sistema de almacenamiento de archivos, es bastante fácil de usar, lamentablemente no
es gratuito pero si es muy barato.

Hay dos formas de subir archivos, con fog y con carrierwave-aws, la ventaja de carrierwave-aws es que
tiene menor footprint y debemos tener cuidado de no sobrecargar nuestra aplicación, sin embargo fuera
de que la configuración dentro de un archivo es ligeramente distinta el resto de nuestra aplicación se
mantiene intacto, sin importar cual de estas dos gemas utilicemos.

Configuración para la gema de carrierwave-aws

1 if Rails.env.test?
2 CarrierWave.configure do |config|
3 config.storage = :file
4 config.enable_processing = false
5 end
6 else
7 CarrierWave.configure do |config|
8 config.storage = :aws
9 config.aws_credentials = {
10 :access_key_id => ENV['aws_access_key_id'], # required
11 :secret_access_key => ENV['aws_secret_access_key'], # required
12 :region => ENV['aws_region'], # optional, defaults to 'us-east
13 # :host => 's3.amazonaws.com'
14 #:host => 's3.example.com', # optional, defaults to nil
15 :endpoint => 'http://s3.amazonaws.com' # optional, defaults to nil
16 }
17 config.aws_bucket = ENV['aws_dir'] # required
18 config.aws_acl = 'public-read'
19
20 config.aws_attributes = {
21 expires: 1.week.from_now.httpdate,
22 cache_control: 'max-age=604800'
23 }
24
25 end
26 end

Separar los entornos no es necesario pero es útil, también es posible que en el entorno de desarrollo no
utilizar amazon para no generar gastos innecesarios, pero de todoas formas deberíamos hacer una
prueba con amazon localmente antes de subir los cambios.

Los valores de aws_access_key_id, aws_secret_access_key, aws_region, aws_dir deben estar definidos


dentro del archivo .env
IAM
I AM es un sistema de permisos de Amazon basado en roles y polizas, su configuración es importante
puesto que impide a una persona externa que obtiene tu clave de un servicio utilizarla para otros. Fuera
de que no debemos agregar nuestra clave al código porque la hace vulnerable, además debemos
configurar estas polizas para limitar el acceso a ámbitos específicos, en este caso nos interesa
únicamente Amazon S3 el cual sirve para guardar y leer archivos

Debemos crear un usuario, y agregar amazon s3 full access.

Agregando las claves de entorno a heroku

1 heroku config:set aws_access_key_id=tu_clave_de_acceso


2 heroku config:set aws_secret_access_key=tu_otra_clave_de_acceso
3 heroku config:set aws_region=region_de_s3
4 heroku config:set aws_dir=nombre_del_bucket

Preguntas

1. ¿Para qué sirve amazon S3?


2. ¿Por qué en heroku no se pueden subir imágenes?
3. ¿Para qué sirve IAM?
4. ¿Por qué motivo debemos agregar las claves de entorno a heroku?
31) Optimización

n+1 queries en ruby on rails


El Active Record de Ruby on Rails tiene muchas cosas geniales, pero algunas de ellas si no las manejas
bien pueden repercutir negativamente en el rendimiento de la aplicación.

Uno de los errores más frecuentes de los desarrolladores novatos de rails es el problema de las N+1
queries.

El problema sucede cuando intentando realizar una sola consulta a la base de datos pero esto gatilla
internamente N+1 consultas, y aunque cuando uno tiene una base de datos con pocos datos el efecto es
casi invisible, a medida de que crece el número de datos y el número de clientes este problema puede
llegar a impactar de forma muy dura en el rendimiento de tu aplicación a tal punto de botarla.

¿Cuándo ocurre el problema de n+1 queries ?


El problema ocurre cuando se itera sobre los hijos de uno de los objetos del modelo, para analizarlo sobre
un caso práctica imaginemos que tenemos 2 modelos, uno de usuario y el otro de pins y cada usuario
tiene muchos pins.

Modelo para el experimento Específicamente el problema se hace visible en la iteración de los resultados.

1 User.all.each do |u|
2 u.pins.each do |p|
3 puts p.name
4 end
5 end

El código que causa el problema es el siguiente:


Ahora cuando uno trabaja bajo un paradigma MVC es raro que uno tenga el código de esta forma, lo más
probable es que el llamado a todos los usuarios se haga en la página index y luego dentro de la vista
usuario se itere sobre los resultados mostrando por ejemplo los 5 mejores pins de cada usuario, pero el
resultado es exactamente el mismo, n+1 llamados a la base de datos.

Para este caso de pruebas se tienen 2 usuarios, Diego y Gonzalo y uno tiene 3 pins y el otro ninguno, y
aquí podemos observar que que se hicieron dos queries a la base de datos en lugar de solo una.

Para solucionarlo en el mismo momento que hacemos el query debemos incluir a los hijos del modelo, en
nuestro caso el query inicial debió haber sido.

1 User.all.includes(:pin)

De esta forma se hace una sola consulta a la base de datos, ahora repitamos el experimento anterior y
comparemos resultados.

Y vemos que ahora solo hay una consulta.


32) Javascript, jQuery y Turbolinks
El uso desparramado de javascript en una aplicación Rails se puede volver difícil de manejar muy
rápidamente si no somos coherentes y ordenados. Lo que necesitamos son algunas técnicas y directrices
que nos ayuden a mantener nuestro javascript organizado en nuestros proyectos. Tampoco queremos
desactivar turbolinks porque, al no saber usarlo, nuestro javascript no se comporta como queremos.

Muchas veces me encuentro con aplicaciones de Rails que tienen javascript por todas las vistas y en
cualquier parte, mezclando estructura y comportamiento en el mismo archivo y cuando pregunto el por
qué lo hicieron así o por qué pusieron ese javascript ahí, la respuesta suele ser que si dejaban el código
en un archivo externo muchas veces el script no funcionaba o su comportamiento era errático, otras
veces era porque no sabían donde debían ponerlo, etc …

Esto es una muy mala practica ya que se hace difícil debuguear la aplicación, esteremos repitiendo
código si tenemos un comportamiento que se repite en todas la paginas, la carga de javascript detiene la
carga del HTML que resulta en paginas lentas que se sientes pesadas.

Como organizar nuestro javascript:


Generalmente, podemos dividir los comportamientos de javascript en las siguientes categorías:

Comportamiento del tipo "always on"


Comportamiento que es provocado por el usuario

A su vez estas pueden dividirse en:

Comportamientos globales que están presentes a través de toda la aplicación.


Comportamientos específicos para una o mas vistas, pero que no son necesarias en toda la
aplicación.

Los comportamientos globales deberían ser requeridos en el manifiesto para que de esa manera se
carguen en toda la aplicación.

Los comportamientos específicos NO deberían estar en el manifiesto y se deberían incluir en las vistas
correspondientes sin olvidar de decirle a Rails que compile nuestros archivos (ver punto 2 paso 3).

Otras veces tendremos comportamientos "always on" específicos para una vista en particular de un
controlador. Para evitar tener que hacer un archivo distinto y usar el archivo javascript correspondiente al
controlador podemos hacer lo siguiente:

En nuestro /app/views/layouts/application.html.erb modificamos la etiqueta <body> y


lo dejamos así:
1 <body class="<%= controller_name %> <%= action_name %>">
2 <%= yield %>
3 </body>

Con esto nuestra etiqueta <body> tendrá dos clases, una sera el nombre del controlador y la otra el
nombre de la acción que llama a la vista. Por ejemplo, en una aplicación donde tenemos posts al acceder
al detalle de un post nuestro <body> quedaría así:

1 <body class="posts show">


2 ...
3 resto del contenido
4 ...
5 </body>

Una vez hecho lo anterior podremos tener un comportamiento especifico a una vista haciendo lo
siguiente en nuestro archivo js:

1 $(document).ready(function() {
2 function someBehavior() {
3
4 ... comportamiento ...
5
6 };
7
8 if ( $('.posts.show').length > 0 ) {
9 someBehavior();
10 }
11 })

Aquí lo importante es el if ( $('.posts.show').length > 0 ) que comprueba si existe algún


elemento que contenga las clases posts y show, si existe ejecutamos la función someBehavior , si no
existe no se ejecutará nada.

Turbolinks
Muchos de nosotros nos hemos dado cuenta, y no de la mejor manera, que cuando Turbolinks está
habilitado en nuestro proyecto (estado por defecto cuando creamos un proyecto), la función
$(document).ready() tiene un comportamiento errático y nuestros js no se vuelven a ejecutar al
cambiar de una pagina a otra y como consecuencia nuestro sitio no funciona como lo esperado. Esto
sucede debido a como turbolinks maneja la carga de las paginas de nuestra aplicación. La función de
turbolinks es hacer que nuestra aplicación se sienta mas rápida y fluida. En vez de dejar que el browser
cargue y recompile los JavaScripts y CSS cada vez que nos cambiamos de pagina, turbolinks mantiene la
pagina actual ‘viva’ y solo remplaza el contenido del <body> o partes de el, y el titulo en el <head> .
Esto quiere decir que no existe una recarga completa de la pagina, por lo que no podremos confiar en
DOMContentLoaded o jQuery.ready() para ejecutar nuestro código. En su lugar turbolinks
dispara eventos en el document que podremos usar para ejecutar nuestro código:

Evento Descripción

page:before-
La pagina esta por cambiar.
change

page:fetch Una nueva página está a punto de ser traída desde el servidor.

page:receive Una página ha sido recibida desde el servidor, pero aún no analizada.

page:before-
Los nodos están a punto de ser cambiados.
unload

page:change Nodos han cambiado. También se ejecuta en DOMContentLoaded.

page:update Se ejecuta en page:change y ajaxSuccess de jQuery.

Un nuevo elemento body se ha cargado en el DOM. No se dispara en sustitución


page:load parcial o cuando una página se restaura desde la caché, a fin de no ejecutarse
dos veces en el mismo body.

page:partial-
Nuevos elementos han sido cargados en el DOM a través de la sustitución parcial
load

page:restore Un elemento body en caché se ha cargado en el DOM.

page:after-
Un elemento se ha eliminado del DOM.
remove

Ejemplos de uso

1 $(document).on('page:fetch', function() {
2 $(".loading-indicator").show();
3 });
4 $(document).on('page:change', function() {
5 $(".loading-indicator").hide();
6 });

Como regla general, todos nuestros scripts asociados a un $(document).ready() deberían ser
modificados para usar $(document).on('page:load') ó
$(document).on('page:change') según sea necesario. Otra opción es hacer uso de la gema
jQuery Turbolinks.

En caso de querer hacer una carga completa al seguir un link –por ejemplo un link a una sección de
administrador que usa otro archivo de base (layout) que carga scripts que no se usan en las otras
secciones– y así evitar tener problemas de que no se carguen estos nuevos scripts, podemos decirle a
rails que no use turbolinks en ese links y así ejecutar una carga completa usando la opción
data-no-turbolink .

Ejemplo:

1 <%= link_to 'Admin', admin_path, data: { no_turbolink: true } %>

Otra cosa importante para que nuestros javascript funcionen correctamente es el orden en el que los
requerimos en el manifiesto, primero siempre jQuery, después las librerías externas, luego nuestros
scripts y al final turbolinks para que sea el ultimo en instalar el manejador del evento click y así no
interferir otros scripts.

1 //
2 //= require jquery
3 //= require jquery_ujs
4
5 //= librerías externas
6 //= nuestros scripts
7
8 //= require turbolinks
9 //

En caso de usar la gema jQuery.turbolinks , esta debe ir antes de


jquery_ujs, para que pueda 'secuestrar' la llamada a
(document). ready()‘o‘ (function() { … })`

1 //
2 //= require jquery
3 //= require jquery.turbolinks
4 //= require jquery_ujs
5
6 //= librerías externas
7 //= nuestros scripts
8
9 //= require turbolinks
10 //

33) Manejo de gráficos


Hay diversas gemas para manejar gráficos, usualmente la parte de gráficos es un javascript donde se
ponen los datos, la parte compleja es recuperar los datos específicos que necesitamos para nuestra
aplicación, en este capítulo veremos como recuperar diversos tipos de datos para generar gráficos de
barra y de tipo pie.

Vamos a necesitar agregar las siguientes gemas para hacer los gráficos

1 gem "chartkick"
2 gem 'groupdate'

Para este capítulo vamos a construir un proyecto para administrar un fundacion donde tenemos donantes
y donaciones.

1 rails g model donor


2 rails g scaffold donations

Haciendo los queries

1 @donations = Donation.where(user_id: current_user.id)


2 unless params[:company_filter].blank?
3 @donations = @donations.eager_load(:donor).where("donors.company = ?", params[:company_filter]
4 end
5
6 @donations_by_company = @donations.eager_load(:donor).group("donors.company").sum(:amount)
7 @donation_by_day = @donations.group_by_day("donations.created_at" ).sum(:amount)

Generando los gráficos

1 <%= javascript_include_tag "//www.google.com/jsapi", "chartkick" %>


2
3 <%= line_chart @donation_by_day %>
4
5 <%= pie_chart @donations_by_company %>
34) Fullcalendar
Creando un calendario de eventos, para este ejemplo vamos a crear un calendario de cumpleaños de
usuarios.

Agregando el plugin

1 <link rel='stylesheet' href='fullcalendar/fullcalendar.css' />


2 <script src='lib/moment.min.js'></script>
3 <script src='fullcalendar/fullcalendar.js'></script>

Cargando el fullcalendar
En el html tenemos que agregar un div, y dentro de este div agregaremos el calendario

1 <div id="calendar">

1 <script>
2 $(document).ready(function() {
3 $('#calendar').fullCalendar({
4 theme: true,
5 events: "#{}"
6 })
7 });
8 </script>

35) Envió de correos con Action Mailer

Intro
Action Mailer nos permite enviar correos desde nuestra aplicación utilizando clases y vistas mailer .
Estos funcionan muy parecido a los controladores en donde un método en la clase mailer tiene una vista
asociada.

Creando nuestro mailer


Como casi todo en rails, existe un generador para crear los archivos mailer necesarios.

1 rails generate mailer UserMailer

Esto creara los siguientes archivos:

1 create app/mailers/user_mailer.rb
2 create app/mailers/application_mailer.rb
3 invoke erb
4 create app/views/user_mailer
5 create app/views/layouts/mailer.text.erb
6 create app/views/layouts/mailer.html.erb
7 invoke test_unit
8 create test/mailers/user_mailer_test.rb
9 create test/mailers/previews/user_mailer_preview.rb

Como pueden ver, se generó un mailer y un directorio en las vistas para este, muy parecido a los
controllers.

Si revisamos el mailer generado podemos ver que un mailer hereda de ActionMailer::Base

1 class UserMailer < ApplicationMailer


2 end

Modificando el mailer y como probarlo


Los mailers son muy similares a los controladores, ellos tienen métodos, llamados ‘acciones’, y usan
vistas para estructurar el contenido. La diferencia es que en ves de generar contenido tipo HTML para ser
mostrado en el browser, se crea un mensaje para ser enviado por correo.

Crear un método para dar la bienvenida a quien se registra en


nuestra pagina

1. Añadir un método llamado welcome_user


1 class UserMailer < ApplicationMailer
2 default from: 'notifications@example.com'
3
4 def welcome_email(user)
5 @user = user
6 @url = 'http://example.com/login'
7 mail(
8 to: @user.email,
9 subject: 'Welcome to My Awesome Site',
10 template_path: 'user_mailer', # opcional
11 template_name: 'welcome_mail') # opcional
12 end
13 end

El método default, que acepta un hash como parámetro: aquí estamos seteando el header from:
para todos los mensajes en esta clase. Si queremos setear el from para todos lo mailer lo hacemos en el
archivo application_mailer El método mail: es donde armamos el mensaje de correo. aquí
estamos pasando a quien :to y el asunto :subject . template_path y template_name
setean la carpeta en donde esta la vista y el nombre de esta respectivamente. Estos son campos
opcionales y solo los agregaremos en caso de tener nombres personalizados o reutilizar una vista

Al igual que en los controllers, todas las variables de instancia definidas en este método estarán
disponibles para ser usadas en la vista.

1. Crear la vista

Primero crearemos una vista HTML para el correo

1 <h1>Welcome to example.com, <%= @user.name %></h1>


2 <p>
3 You have successfully signed up to example.com,
4 your username is: <%= @user.login %>.<br>
5 </p>
6 <p>
7 To login to the site, just follow this link: <%= @url %>.
8 </p>
9 <p>Thanks for joining and have a great day!</p>

Y también crearemos una vista en texto plano, ya que no todos los clientes usan HTML por lo que enviar
las dos opciones es una buena practica.
1 Welcome to example.com, <%= @user.name %>
2 ===============================================
3
4 You have successfully signed up to example.com,
5 your username is: <%= @user.login %>.
6
7 To login to the site, just follow this link: <%= @url %>.
8
9 Thanks for joining and have a great day!

Al tener las dos vistas, action mailer las detectara y enviara un correo de tipo
multipart/alternative

1. Testeando el mailer

En el ambiente de desarrollo podemos usar ActionMailer Preview para testear nuestros correos. Para eso
iremos al archivo correspondiente a nuestro mailer:
test/mailers/previews/user_mailer_preview.rb En el llamaremos a nuestro método
welcome_email y le pasaremos un usuario cualquiera como parámetro

1 class UserMailerPreview < ActionMailer::Preview


2 def welcome_email_preview
3 UserMailer.welcome_email(User.last)
4 end
5 end

Y si ahora vamos a la siguiente url: http://localhost:3000/rails/mailers/user_mailer, tendremos un listado


con los test creados, en este caso solo uno: welcome_email_preview . Si entramos a el veremos el
correo que se enviara en sus dos verisones, HTML y Texto Plano, y podremos revisar si esta todo ok.

Enviando el correo usando ActionMailer y Gmail


Por defecto rails trata de enviar los correos usando el protocolo SMTP. Para poder enviar los correos
configuraremos nuestra aplicación para que use nuestra cuenta de gmail.

Es importante recordar que la información sensible, como por ejemplo nuestro nombre de
usuario y contraseña de gmail, nunca deben ser usados de manera explicita en nuestros
archivos de configuración, ya que existen pequeños programas, llamados [web crawlers]
(https://en.wikipedia.org/wiki/Web_crawler) o web spiders, que se dedican a buscar este tipo
de información sensible. Por lo tanto para usar estos datos los pasaremos de manera implícita
a nuestros archivos de configuración usando variables de entorno con la gema `dotenv-rails`

1. Como implementar y usar variables de entorno


Primero instalamos la gema dot-env en nuestro gemfile:

1 gem 'dotenv-rails', groups: [:development, :test]

Luego creamos un archivo llamado .env en nuestra aplicacion


Antes de cualquier otra cosa agregamos este archivo al .gitignore ya que NO queremos que
git le haga seguimiento y lo suba a github!!!
Dentro de ese archivo creamos las variables a usar:

1 GMAIL_USERNAME=ninombredeusurario
2 GMAIL_PASSWORD=miclave

1 El nombre de la variable puede ser cualquier cosa y puede ir en mayusculas o minúsculas. Es impo

Ahora para poder usar estas variables en algún archivo las llamamos de la siguiente forma:

1 ENV['GMAIL_USERNAME']

Donde GMAIL_USERNAME es el nombre de la variable que queremos usar.

1. Configuración de ActionMailer

Para configurar ActionMailer la mejor opción es usar los archivos de ambiente (como enviroment.rb,
production.rb, development.rb, etc…). Pueden revisar las opciones de configuración aqui.

En nuestro caso usaremos /config/environments/development.rb ya que estamos trabajando


en el ambiente de desarrollo, esta configuración también se debe hacer en
/config/environments/production.rb para cuando estemos en producción.

1 config.action_mailer.raise_delivery_errors = true
2 config.action_mailer.delivery_method = :smtp
3 config.action_mailer.smtp_settings = {
4 address: 'smtp.gmail.com',
5 port: 587,
6 user_name: ENV['GMAIL_USERNAME'],
7 password: ENV['GMAIL_PASSWORD'],
8 authentication: :login,
9 enable_starttls_auto: true
10 }

Para el ambiente de desarrollo recomiendo agregar lo siguiente:


1 config.action_mailer.perform_deliveries = false

Esto para que al hacer pruebas en nuestra aplicación no se envíen los correos.

1. Enviar el correo al usuario

Para usar nuestro mailer y enviar el mensaje cuando se crea un nuevo usuario, lo llamaremos desde el
UserController en el método create que es donde se crea y guarda el usuario. Es importante
que sepan que para que el correo efectivamente se envíe tenemos que terminar de llamarlo con el
método :deliver_now o :deliver_later , la diferencia entre ambos la veremos mas adelante.

1 class UsersController < ApplicationController


2 def create
3 @user = User.new(params[:user])
4
5 respond_to do |format|
6 if @user.save
7 # Tell the UserMailer to send a welcome email after save
8 UserMailer.welcome_email(@user).deliver_later
9
10 format.html { redirect_to(@user, notice: 'User was successfully created.'
11 format.json { render json: @user, status: :created, location: @user }
12 else
13 format.html { render action: 'new' }
14 format.json { render json: @user.errors, status: :unprocessable_entity }
15 end
16 end
17 end
18 end

En el caso de estar creando un nuevo registro, como en este ejemplo, es importante que el envío del
correo se realize una vez que el registro se ha guardado, no antes ya que no queremos enviar el correo y
después al tratar de guardar el registro se presenta un error y este no se guarda.

ActionMailer y Devise
Que pasa si queremos enviar un correo de bienvenida cuando se registra un nuevo usuario si estamos
usando devise? Cuando usamos devise no tenemos un controlador para User como el del ejemplo
anterior, por lo que tendremos que ver que opciones tenemos para poder enviar el correo.

Primera Opción: creando el controlador de devise para el usuario


1. Crear el controlador de devise Solo necesitamos crear el controlador de registros de devise, para
eso corremos el siguiente comando, asumiendo que tenemos un modelo user hecho con devise

1 rails g devise:controllers users -c=registrations

Esto solo creara el controller registrations dentro de la carpeta user

2. Modificar el controller Ahora que tenemos el controller creado lo modificaremos y lo dejaremos así:

1 class Users::RegistrationsController < Devise::RegistrationsController


2 def create
3 super
4 UserMailer.welcome_email(@user).deliver_later unless @user.invalid?
5 end
6 end

3. Decirle a devise que use el controller creado anteriormente En el archivo de rutas ( routes.rb )
le diremos a devise que use este nuevo controller

1 ...
2 devise_for :users, controllers: { registrations: 'users/registrations' }
3 ...

Eso es todo, ahora cuando un usuario se registre se enviara un correo de bienvenida.

Segunda Opción: modificando el modelo de usuario

En esta segunda opción no es necesario crear nada adicional ya que solo modificaremos el modelo de
usuario

1. Modificar el Modelo de usuario Creamos un método privado dentro del modelo de usuario

1 class User < ActiveRecord::Base


2 # ...
3 # código omitido
4
5 private
6
7 def send_welcome_email
8 UserMailer.welcome_email(self).deliver_later
9 end
10 end
2. Ejecutar el método cada vez que se crea un nuevo usuario En el mismo modelo, usaremos un
callback para llamar a el método que nos creamos anteriormente cava des que se cree un nuevo
usuario. Para eso usaremos el callback :after_create

1 class User < ActiveRecord::Base


2 after_create :send_welcome_email
3
4 # código omitido
5 # ...
6 end

Eso es todo en esta segunda opción.

ActionMailer y ActiveJob: deliver_now, deliver_later?


Como se menciono anteriormente, para enviar el correo hay que pasar el método :deliver_now o
:deliver_later a nuestro mailer, en el caso del ejemplo seria así:
UserMailer.welcome_email(user).deliver_now . La diferencia entre estos dos métodos la
podemos deducir de su nombre:

1. deliver_now: (sincrona) envía el correo inmediatamente en el proceso en el que fue llamado, si han
realizado pruebas se habrán dado cuenta que al crear un nuevo usuario este proceso demora un
poco mas, y ¿por qué demora más?, demora más porque ahora cuando se crea un nuevo usuario se
llama al mailer para que envíe el correo, el mailer a su vez tiene que hacer las conexiones con el
servidor de correo y autenticarse para luego enviar el correo, una vez que se termina de enviar el
correo el proceso de creación del usuario puede terminar.
2. deliver_later: (asíncrona) a diferencia de su hermano, este método no envía el correo de manera
inmediata al ser llamado, lo que hace es dejarlo en una cola de trabajo (queue) a la espera de ser
enviado, por lo que el proceso de creación del usuario no demora más ya que no tiene que esperar a
que se envíe el correo para poder terminar su proceso. Si han hecho pruebas con deliver_later se
habrán dado cuenta de que aun así el tiempo de creación del usuario no ha mejorado con respecto a
deliver_now, ¿por qué? La respuesta es ActiveJob

ActiveJob y deliver_later

A partir de la version 4.2 rails trae incorporado el framework ActiveJob para declarar ‘trabajos’ (jobs) y
que estos puedan correr en alguno de los backends que manejan colas de trabajo (queueing). Estos
‘trabajos’ pueden ser cualquier cosa, desde mantenimientos programados, cobros, envío de correos, etc

Los backends que manejan estas colas de trabajo son gemas que podemos integrar en nuestra
aplicación, ejemplo de estas son Delayed Job, Resque, Sucker Punch, Sidekiq, etc … Gracias a
ActiveJob podemos usar cualquiera de estas e incluso cambiar en medio del desarrollo sin tener que
reescribir nuestros ‘trabajos’. Para saber mas sobre ActiveJob pueden leer la guia oficial aqui

El punto que nos interesa a nosotros en esta guía es el uso de ActionMailer y ActiveJob. Gracias a que
estos dos son parte de rails, ActiveJob esta integrado con ActionMailer, por lo que podemos enviar
correos de manera asíncrona en una cola de trabajo utilizando el método deliver_later

1 # Si quieres enviar el correo inmediatamente usa #deliver_now


2 UserMailer.welcome_email(@user).deliver_now
3
4 # Si quieres enviar el correo mediante ActiveJob usa #deliver_later
5 UserMailer.welcome_email(@user).deliver_later

Entonces, ¿por qué, si están integrados, al usar deliver_later se comporta como deliver_now? Por
defecto, cuando no tenemos ningún backend asociado a ActiveJob su comportamiento sera ejecutar los
trabajos de manera ‘inline’, es decir, inmediatamente.

Para poder aprovechar ActiveJob lo que haremos sera integrar un backend, en este caso usaremos
sucker_punch ya que no necesita de muchos pasos para configurarlo.

1. Agregar la gema sucker_punch

1 gem 'sucker_punch'

Como ya saben después de agregar una gema corremos bundle en la terminal

2. Configurar ActiveJob para que use sucker_punch Esto lo haremos en el archivo


/config/environments/production.rb , si queremos hacer pruebas en el modo de
desarrollo tendremos que hacerlo también en /config/environments/development.rb

1 config.active_job.queue_adapter = :sucker_punch

3. NO HAY PASO 3 En serio, esto es todo lo que hay que hacer para poder enviar los correos de forma
asíncrona usando ActiveJob y deliver_later

Si hacen una prueba ahora se darán cuenta que el proceso de crear un usuario ya no demora como
antes y el correo se envía igual!!!
36) Testings automatizado con Guard
En rails es posible automatizar completamente los test ocupando guard.

Uno se preguntaría para que automatizar más, puesto que con la simple instrucción rake se corren
todos los test definidos, pero la gema Guard permite que se corran automatícamente los tests respectivo
cada vez que modificas un archivo de un controller, fixture, modelo o test, y te avisa si producto de la
introducción de alguna mejora rompiste alguna funcionalidad ya existente en el sistema.

Esta guía se ha probado con: * Ruby version 2.1.2 o mayor * Rails version 4.1.5 o mayor * MiniTest version
5.4.0 o mayor

Instalar Guard en nuestro proyecto


Primero vamos a añadir las gemas necesarias a nuestro Gemfile

1 group :development do
2 gem 'guard'
3 gem 'guard-minitest'
4 gem 'minitest-reporters'
5
6 # notificaciones, solo usuarios de Mac OS X 10.8 o mayor
7 gem 'terminal-notifier'
8 gem 'terminal-notifier-guard'
9 end

si van a usar las notificaciones tienen que instalar terminal-notifier con brew (brew install terminal-notifier)

Que hace cada gema?

guard : añade soporte para la herramienta Guard que maneja eventos y modificaciones de
archivos.
guard-minitest : encargara de monitorear los cambios y ejecutar nuestros test usando minitest.
minitest-reporters : nos permite customisar el output de nuestros test y darle color a los
resultados.
terminal-notifier y terminal-notifier-guard : solo para los usuarios de osx 10.8 o
mayor, nos mostrara un mensaje en el centro de notificaciones cuando se ejecuten los test.

Añadimos estas gemas al grupo de desarrollo porque se ejecutarán desde el entorno de desarrollo y no
afectan el entorno de testing.
Configurar Guard
Ahora que tenemos guard instalado en nuestro proyecto tenemos que generar su archivo de
configuración, para esto ejecutamos lo siguiente en la terminal:

1 bundle exec guard init minitest

Esto va a crear un archivo llamado Guardfile en el root de nuestro proyecto. Lo que hace este
archivo es decirle a Guard que archivos tiene que monitorear y que hacer cuando alguno de estos se ha
modificado. En nuestro caso va a monitorear la carpeta app y todo su contenido y llamara los test
correspondientes usando minitest.

Al crear el Guardfile éste viene así:

1 # A sample Guardfile
2 # More info at https://github.com/guard/guard#readme
3
4 guard :minitest do
5 # with Minitest::Unit
6 watch(%r{^test/(.*)\/?test_(.*)\.rb$})
7 watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
8 watch(%r{^test/test_helper\.rb$}) { 'test' }
9
10 # with Minitest::Spec
11 # watch(%r{^spec/(.*)_spec\.rb$})
12 # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
13 # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
14
15 # Rails 4
16 # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
17 # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
18 # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test
19 # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_te
20 # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
21 # watch(%r{^test/.+_test\.rb$})
22 # watch(%r{^test/test_helper\.rb$}) { 'test' }
23
24 # Rails < 4
25 # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
26 # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
27 # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
28 end

Como nosotros estamos usando Rails 4, descomentaremos las líneas 16 a la 22 y borraremos el resto de
las líneas comentadas dejando el archivo así:
1 # A sample Guardfile
2 # More info at https://github.com/guard/guard#readme
3
4 guard :minitest do
5 # with Minitest::Unit
6 watch(%r{^test/(.*)\/?test_(.*)\.rb$})
7 watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
8 watch(%r{^test/test_helper\.rb$}) { 'test' }
9
10 # Rails 4
11 watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb"
12 watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
13 watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/
14 watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[
15 watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}
16 watch(%r{^test/.+_test\.rb$})
17 watch(%r{^test/test_helper\.rb$}) { 'test' }
18 end

¿Qué hace cada línea?

1 guard :minitest do
2 # ...
3 end

Aquí le decimos a Guard que el siguiente bloque se tiene que ejecutar con Minitest. Guard se puede
configurar con multiples plugins que pueden realizar muchas otras cosas.

1 watch(%r{^test/(.*)\/?test_(.*)\.rb$})

Esta línea monitorea todos los archivos .rb en las subcarpetas de test/ y ejecuta el archivo que
se ha modificado.

1 watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }

Esta línea monitorea todos los archivos .rb que se encuentran en el directorio lib y ejecutará el
test correspondiente si es que existe alguno.

1 watch(%r{^test/test_helper\.rb$}) { 'test' }

Esta línea monitorea el archivo test_helper.rb y si hay un cambio ejecuta todos los test.
1 watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }

Esta línea monitorea todos los archivos en el directorio app y ejecuta el test correspondiente. En una
aplicación típica de rails estos serian los models, controllers, helpers y mailers.

1 watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }

Esta línea monitorea el archivo aplication_controller.rb y ejecuta todos los test de


controladores si se modifica.

1 watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{

Esta línea monitorea los controladores y ejecuta el test de integración correspondiente cuando hay
modificaciones.

1 watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]

Esta línea monitorea la carpetas de las vitas de los mailers y ejecuta el test correspondiente cuando hay
modificaciones.

1 watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb"

Esta línea monitorea la carpeta lib y ejecuta el test correspondiente cuando hay modificaciones si es
que lo existe un test asociado.

1 watch(%r{^test/.+_tests\.rb$})
2 watch(%r{^test/test_helper\.rb$}) { "test" }

Estas líneas monitorear todos los archivos terminados en _test.rb y test_helper.rb y si hay
un cambio en alguno de ellos ejecutará el test.

Esta es la configuración básica de nuestro Guardfile y como ven es muy completa. Sin embargo podemos
agregar unos monitores extras, que en lo personal encuentro de bastante utilidad.

Agregaremos las siguientes líneas a nuestro Guardfile:


1 # agregar la siguiente línea al inicio del archivo
2 require 'active_support/inflector'
3
4 guard :minitest do
5
6 # ... código anterior omitido
7
8 # extra tests
9 watch(%r{^app/views/(.+)/.+}) { |m| "test/controllers/#{m[1]}_controllet_test.rb"
10 watch(%r{^test/fixtures/(.+)\.yml}) { |m| "test/models/#{m[1].singularize}_test.rb"
11 watch(%r{^test/fixtures/(.+)\.yml}) { |m| "test/controllers/#{m[1]}_controller_test.rb"
12 end

El detalle de los ajustes adicionales

1 require 'active_support/inflector'

Aquí añadimos la clase inflector de rails para poder ‘singularizar’ los nombres de los fixtures. Esto nos
permite convertir ‘Posts’ en ‘Post’ para cualquier modelo o controlador.

1 watch(%r{^app/views/(.+)/.+}) { |m| "test/controllers/#{m[1]}_controller_test.rb"

Esta línea correra el el test de controlador si cambia alguna de las vistas asociadas.

1 watch(%r{^test/fixtures/(.+)\.yml}) { |m| "test/models/#{m[1].singularize}_test.rb"

Esta línea ejecutará los tests de modelo si los fixtures cambian. Aquí es donde usamos la clase inflector
de rails

1 watch(%r{^test/fixtures/(.+)\.yml}) { |m| "test/controllers/#{m[1]}_controller_test.rb"

Esta línea ejecutará los tests de controlador si los fixtures cambian.

Configurar Minitest-Reporters
Antes de continuar con Guard vamos a configurar nuestro ambiente de testing para que haga uso de
minitest-reporters y así el output de nuestros test se vera mejor y con colores.
1. Decirle a nuestros test que usaremos minitest-reporters

En el archivo test/test_helper.rb añadimos lo siguiente:

1 ENV['RAILS_ENV'] ||= 'test'


2 require File.expand_path('../../config/environment', __FILE__)
3 require 'rails/test_help'
4 require "minitest/reporters" # línea que hay que añadir
5
6 # ... resto del archivo omitido

2. Iniciar minitest reporters y configurarlo

En el mismo archivo que en el paso anterior añadimos lo siguiente:

1 # ... código omitido


2
3 class ActiveSupport::TestCase
4 # ... código omitido
5
6 Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
7 end

La primera parte Minitest::Reporters.use! le dice a nuestro test_helper que usaremos


minitest-reporters.

La segunda parte Minitest::Reporters::SpecReporter.new le dice que estilo de reporte


queremos usar, en este caso SpecReporter.

Para que vean la diferencia:

Sin minitest-reporters

1 Run options: --seed 34387


2
3 # Running:
4
5 ...
6
7 Finished in 0.327633s, 70.2005 runs/s, 109.8791 assertions/s.
8
9 3 runs, 3 assertions, 0 failures, 0 errors, 0 skips

Con minitest-reporters
1 Started with run options --seed 28340
2
3 ProductsControllerTest
4 test_should_get_new PASS (0.19s)
5
6 ProductTest
7 test_should_not_create_product_withoud_description PASS (0.00s)
8
9 UserTest
10 test_user_owns_products PASS (0.02s)
11
12 Finished in 0.34634s
13 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips

Correr Guard para automatizar el testing


Ahora que nuestro Guardfile esta completo solo falta ejecutarlo desde nuestra línea de comando:

1 bundle exec guard

Esto iniciara Guard, analizara el Guardfile y los plugins instalados y carrera nuestros test de manera
automática. Al correr Guard nuestra consola quedara secuestrada por este proceso, al igual que cuando
corremos el servidor de rails. Para salir de Guard escribimos exit

Una vez iniciado Guard veremos un output similar a este en nuestra consola:
1 13:07:02 - INFO - Guard::Minitest 2.4.4 is running, with Minitest::Unit 5.8.2!
2 13:07:02 - INFO - Running: all tests
3 Started with run options --seed 46327
4
5 ProductsControllerTest
6 test_should_show_product PASS (0.18s)
7 test_should_get_edit PASS (0.03s)
8
9 UserTest
10 test_should_not_create_user_withoud_username PASS (0.01s)
11 test_should_not_create_user_without_name PASS (0.00s)
12
13 ProductTest
14 test_should_not_create_product_withoud_description PASS (0.00s)
15 test_price_should_be_a_float PASS (0.00s)
16
17 Finished in 0.31300s
18 6 tests, 6 assertions, 0 failures, 0 errors, 0 skips
19
20 13:07:05 - INFO - Guard is now watching at '/Users/Username/Path/To/Project'
21 [1] guard(main)>

Con guard cualquier cambio que hagamos en los archivos monitoreados gatillará los test relevantes y se
ejecutarán automáticamente, y si estas en OSX e instalaste terminal-notifier recibirás una notificación.

Si quieres forzar la ejecución de los test, simplemente presiona enter en el prompt de Guard
( [1] guard(main)> ) en la consola.

Eso es todo, recuerda que para salir del prompt de Guard (y obviamente del testing automatizado) escribe
exit en él.

Eso es todo. No te olvides de siempre usar testing cuando desarrolles un proyecto!


37) Rails y Nginx con Passenger en Ubuntu:
Preparando nuestro entorno de producción
(DigitalOcean).

Introducción
Ruby on Rails (RoR) es un framework de desarrollo que le entrega a los desarrolladores una fácil y rápida
herramienta para crear aplicaciones web, y Nginx es un servidor web ligero de alto rendimiento. Estos
dos programas puedes ser configurados fácilmente para que trabajen en conjunto en un VPS (Virtual
Private Server) con Phusion Passenger.

Pushion Passenger es un servidor web y de aplicaciones, diseñado para integrarse con Nginx o Apache.
Originalmente creado para aplicaciones hechas con RoR, lo que hace que sea la recomendada por la
comunidad de RoR, ademas de ser estable, rápido y escalable.

*Otros servidores de aplicaciones son Unicorn y Puma.

Acerca de esta guía.


Esta guía esta pensada como un recetario para configurar y preparar nuestro servidor utilizando Nginx,
Passenger, RVM, Ruby, Rails y Postgres; y se espera que ya tengan creada su maquina virtual (VPS) con
Ubuntu 14.04 y 15.04 1 en DigitalOcean o Linode.

Se espera que el lector de esta guía sepa como utilizar la terminal y tenga conocimiento de al menos
comandos básicos de este. Así como también se espera sepan usar postgres.

Convenciones.
1. Los términos servidor, server o VPS hacen referencia a su maquina virtual.
2. El termino local hace referencia a su computador.
3. Para determinar el entorno en que tenemos que ejecutar los comandos y en cual estaremos
trabajando en cada sección se usara: local para la maquina local o servidor para la maquina virtual.
4. Se usara para los ejemplos la IP: 111.11.111.11, esta tienen que ser remplazada por la ip de su
maquina virtual.
5. Para mostrar las instrucciones a ejecutar en el terminal se antepondrá el signo $ , que es la
representación de su línea de comandos que esta lista para recibir una instrucción (no hay que
tipearlo), y tendrán el siguiente estilo:
1 $ gem install postgres

*Cada línea que empieza con $ se ejecuta por separado.

Para mostrar las respuestas, errores o advertencias que nos arroja el terminal al ejecutar una
instrucción no se antepone el signo $ y tendrán el siguiente estilo:

1 Agent pid 32877

6. Se espera que tengan una llave ssh creada.

Paso 0 – Como acceder a nuestro servidor


local

Para acceder a nuestro VPS usaremos el Terminal como interface de conexión mediante SSH.

En nuestra terminal:

1 $ ssh root@111.11.111.11

La primera vez que nos tratemos de conectar se nos mostrara un mensaje como este:

1 The authenticity of host 'xx.xx.xx.xx (xx.xx.xx.xx)' can't be established.


2 ECDSA key fingerprint is 79:95:46:1a:ab:37:11:8e:86:54:36:38:bb:3c:fa:c0.
3 Are you sure you want to continue connecting (yes/no)?

Este nos advierte que no se puede establecer la autenticidad del host y si queremos conectarnos de
todas formas. Obviamente le decimos que Yes.

Sabremos que estamos iniciados porque veremos algo como esto en nuestra terminal servidor

1 $ root@ip-111-11-111-11:~$

Esto no indica que estamos logueados en la maquina con ip 111.11.111.11 en el usuario root, todo lo
que escribamos ahora se ejecutará en el servidor.
Para desconectarnos del servidor y "volver" a nuestra maquina local escribimos lo siguiente:

servidor

1 $ exit

Invalid Locale Warning

servidor

En algunos casos puede ser que al entrar a su maquina les muestre esta advertencia:

1 WARNING! Your environment specifies an invalid locale.


2 This can affect your user experience significantly, including the
3 ability to manage packages.

O que al instalar un paquete les muestre esto:

1 perl: warning: Setting locale failed.


2 perl: warning: Please check that your locale settings:
3 LANGUAGE = "en_US:en",
4 LC_ALL = (unset),
5 LC_MESSAGES = "en_US.UTF-8",
6 LANG = "en_US.UTF-8"
7 are supported and installed on your system.
8 perl: warning: Falling back to the standard locale ("C").
9 locale: Cannot set LC_CTYPE to default locale: No such file or directory
10 locale: Cannot set LC_MESSAGES to default locale: No such file or directory
11 locale: Cannot set LC_ALL to default locale: No such file or directory

Este es un error que tenemos que arreglar o si no tendremos problemas con los paquetes a instalar, sobre
todo con Postgres. Para arreglarlo hacemos lo siguiente:

1. Primero generamos el locale que no esta definido:

1 $ sudo locale-gen "en_US.UTF-8"

2. Reconfiguramos lo locales:

1 $ sudo dpkg-reconfigure locales


Solo si por alguna razón con los comandos anteriores no se arregla el error haremos lo siguiente:

1. Abrimos el archivo environment con algún editor (vim, nano, emacs, etc):

1 $ sudo vim /etc/environment

2. Agregamos las siguientes líneas:

1 LC_ALL=en_US.UTF-8
2 LANG=en_US.UTF-8

Ahora ya no deberíamos tener ese error al instalar un paquete

Configurar el timezone en nuestro servidor


¿Por qué? Cuando revisamos nuestros logs, estos tienen la marca de tiempo en GMT y no en nuestra
zona horaria, lo que hace que revisar nuestros logs sea un poco mas difícil.

Para configurar el timezone del servidor a nuestra zona horaria local haremos lo siguiente:

1. Revisaremos el timezone del servidor:

1 $ date

Esto imprime la fecha, hora y zona horaria de nuestro servidor:

1 Mon Aug 31 16:52:18 MST 2015

O podemos revisar solo la zona horaria, así:

1 $ more /etc/timezone

Que imprimira el timezone del servidor:

1 US/Arizona

1. Si el timezone del servidor no corresponde a nuestra zona horaria ejecutamos lo siguiente:


1 sudo dpkg-reconfigure tzdata

y seguimos las instrucciones en pantalla:

Una vez elegida nuestra zona horaria, podemos volver a comprobar ejecutando los comandos del paso
anterior.

Acerca del usuario Root


servidor

En linux el usuario root es el usuario administrador y tiene demasiados privilegios. Debido a esto se
recomienda no usarlo de manera regular, ya que por accidente podemos hacer cambios destructivos en
nuestro servidor.

¿Que hacer entonces?

Para evitar lo anterior y seguir las recomendaciones y buenas practicas crearemos un nuevo usuario para
el uso diario, al que le daremos los privilegios necesarios para cuando lo necesitemos.

Dentro de nuestro VPS haremos lo siguiente:

1. Creamos un nuevo usuario, este se llamara deploy. Se nos pedirá crear una contraseña, esta no se
nos puede olvidar, ya que con ella nos tendremos que conectar al VPS y ejecutar comandos sudo, y
opcionalmente se nos pedirá información adicional.

1 $ adduser deploy

(El nombre deploy es un buen nombre para el usuario de deployment, pero no es necesario que sea
este)

2. Añadimos el nuevo usuario al grupo de sudoers. En este paso al agregar al usuario al grupo de los
sudoers le daremos la posibilidad de ejecutar comandos con privilegio de administrador cuando sea
necesario, eso si tendrá que anteponer la palabra sudo (super user) al comando y se le pedirá su
clave.

1 $ gpasswd -a deploy sudo

3. Salimos del servidor…

1 $ exit

4. *… y nunca más entramos como root

1 $ ssh deploy@111.11.111.11
*Esta vez se nos pedirá la contraseña que pusimos al crear el usuario.

¿Cómo ingresar sin tener que ingresar la clave cada


vez que nos queremos conectar a nuestro servidor?
local

Para no tener que poner la clave cada vez que queremos conectarnos al servidor y también así evitar
problemas en un futuro al usar Capistrano para hacer deploy, haremos lo siguiente:

Copiamos nuestra llave publica al llavero del usuario deploy.

1 $ ssh-copy-id deploy@111.11.111.11

En esta etapa, que demora un poco, se nos pedirá la clave del usuario deploy para poder copiar la llave
ssh en el llavero del usuario deploy

Solo si el paso anterior falla porque no encuentra el comando ssh-copy-id, lo instalaremos de la siguiente
forma:

En Mac.

1 $ brew install ssh-copy-id

En Linux.

1 $ apt-get install ssh-copy-id

Ahora nos podremos conectar al servidor sin tener usar la contraseña!!!

1 $ ssh deploy@111.11.111.11

Paso 1 – Instalación de RVM


servidor

Ahora que ya podemos entrar a nuestro servidor con un usuario diferente a root, vamos a hacer el primer
paso para configurar nuestro entorno, instalaremos RVM (Ruby Version Manager) el cual nos permitirá
instalar ruby y manejar distintas versiones de éste.

1. Antes de hacer cualquier cosa haremos un update para asegurarnos que todos los paquetes
que bajaremos a nuestro VPS estén al día

1 $ sudo apt-get update

2. Instalación de RVM

1 $ curl -L get.rvm.io | bash -s stable

En este paso nos mostrara un warning y si leemos bien veremos las siguiente líneas:

1 ...
2 gpg: Can't check signature: public key not found
3 Warning, RVM 1.26.0 introduces signed releases and automated check of signatures when GPG sof
4 Assuming you trust Michal Papis import the mpapis public key (downloading the signatures).
5
6 GPG signature verification failed for '/home/deploy/.rvm/archives/rvm-1.26.11.tgz' - 'https:/
7 try downloading the signatures:
8
9 gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
10
11 or if it fails:
12
13 command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
14
15 the key can be compared with:
16
17 https://rvm.io/mpapis.asc
18 https://keybase.io/mpapis

Ahi mismo nos dice que tenemos que descargar la firma para autenticar el paquete antes de instalar, y
eso lo hacemos copiando la que dice:

gpg –keyserver hkp://keys.gnupg.net –recv-keys


409B6B1796C275462A1703113804BB82D39DC0E3

Y la pegamos en el terminal

1 $ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3


después volvemos a ejecutar el instalador de RVM

1 $ curl -L get.rvm.io | bash -s stable

Ahora si se instalara sin problemas RVM. Eso si para poder usarlo tenemos que ‘cargarlo’ a nuestro
terminal.

1 $ source /home/deploy/.rvm/scripts/rvm

(esto solo se hace una sola vez)

3. Ahora le diremos a RVM que instale todas las dependencias que necesita.

1 $ rvm requirements

Paso 2 – Instalación de Ruby y de Rails


servidor

Ahora que tenemos RVM instalado, lo usaremos para instalar Ruby.

1. Le pedimos a rvm que instale la version de ruby que necesitamos

1 $ rvm install 2.2.3

A la fecha de la guía Ruby-2.2.3 es la ultima versión.

1. Ahora le diremos a RVM que use esa version por defecto

1 $ rvm use 2.2.3 --default

2. Ahora nos aseguraremos de que tenemos todos los componentes requeridos por RoR

1 $ rvm rubygems current

Si todo sale bien ahora podremos instalar RoR y otras gemas, pero antes de eso le diremos a nuestra
maquina que no descargue la documentación de las gemas al instalarlas, ya que ellas demoran el
proceso y usan espacio innecesariamente.

1 $ echo "gem: --no-ri --no-rdoc" > ~/.gemrc

Ahora si instalamos RoR

1 $ gem install rails

Por un problema con la ultima version de rubygems y ruby (2.2.3), en el proceso de instalación de Rails
tendremos un problema con la gema nokogiri , para solucionarlo haremos lo siguiente:

1 $ sudo apt-get install libgmp-dev

Después de eso instalamos la gema nokogiri :

1 $ gem install nokogiri

Y por ultimo volvemos a instalar rails:

1 $ gem install rails

Ahora si la instalación se hará correctamente.

Paso 3 – Instalación de Nginx y Passenger


servidor

Una ves que tenemos RVM y Ruby instalaremos Nginx y Passenger, pero antes tenemos que preparar el
servidor.

1. Lo primero que tenemos que hacer es instalar la llave GPG de Phusion Passenger

1 $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 561F9B9CAC40B2F7


2. Después descargaremos un paquete que le dará soporte HTTPS a APT

1 $ sudo apt-get install apt-transport-https ca-certificates

3. Luego añadimos el repositorio de passenger al source list de nuestra maquina para poder
usarlo

Para Ubuntu 14.04

1 $ sudo sh -c "echo 'deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main'

Para Ubuntu 15.04

1 $ sudo sh -c "echo 'deb https://oss-binaries.phusionpassenger.com/apt/passenger vivid main' >

4. Después de agregarla tenemos que cambiar el grupo y permisos para poder hacer un update a
los paquetes disponibles para incluirlo a ellos

1 $ sudo chown root: /etc/apt/sources.list.d/passenger.list


2 $ sudo chmod 600 /etc/apt/sources.list.d/passenger.list
3 $ sudo apt-get update

Ahora que tenemos el servidor preparado podemos instalar Nginx con Passenger:

1 $ sudo apt-get install nginx-full passenger

Y hacemos correr el servidor nginx

1 $ sudo service nginx start

Paso 4 – Habilitando Passenger en Nginx


servidor
Para hacer el siguiente paso pueden usar vim, nano o emacs como editor de texto.

En el archivo de configuración de Nginx (nginx.conf) se tiene que especificar donde esta passenger. Para
eso se tienen que descomentar (quitar el signo # que se antepone) las líneas que empiezan con:

1 # passenger_root ....
2 # passenger_ruby ....

1. Primero abrimos el archivo de configuración de nginx con algún editor. (Nginx se encuentra en
la carpeta /etc del servidor)

1 $ sudo vim /etc/nginx/nginx.conf

2. Ahora descomentamos las líneas antes nombradas y las dejamos asi:

1 passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
2 passenger_ruby /home/deploy/.rvm/wrappers/ruby-2.2.3/ruby;

Ojo que en la segunda línea la version de ruby, que en este caso es ruby-2.2.3, tiene que decir la version
que tienen instalada

Hecho esto guardamos los cambios y cerramos el archivo, y tenemos que reiniciar el servidor nginx para
que aplique los cambios.

1 $ sudo service nginx restart

En caso de tener un problema al reiniciar el server puedes revisar los logs así:

1 $ sudo tail /var/log/nginx/error.log

Paso 5 – Instalación y configuración de Postgres.


servidor

Instalación
Para poder trabajar con bases de datos lo primero que tenemos que hacer es instalar Postgresql en
nuestra maquina virtual junto con algunas dependencias necesarias. Para ello ejecutamos lo siguiente:

1 $ sudo apt-get install postgresql postgresql-contrib libpq-dev

Ahora para entrar al entorno de trabajo de postgres ejecutamos:

1 $ sudo -u postgres psql

El entorno de trabajo de postgres esta indicado con el siguiente prompt: postgres=#

Para salir del entorno postgres y volver a nuestro usuario usamos \q .

Creación de un usuario en postgres

servidor

Ahora crearemos un superusuario en nuestro motor de base de datos, éste sera capas de crear bases de
datos y tendrá todos los privilegios sobre esta, en lo personal utilizo el nombre de la aplicación con
alguna variante o simplemente uso deploy.

Crear el usuario:

1 $ sudo -u postgres createuser -s nombreUsuario

Ahora vamos a asignar un password al usuario de postgres que acabamos de crear (para el password usa
uno seguro que sea distinto al del usuario de la VPS y que no se te olvide).

Primero entramos al entorno de postgres:

1 $ sudo -u postgres psql

Para comprobar que el usuario se creo correctamente los vamos a listar usando \du , si nuestro
usuario se encuentra en el listado es porque se creo correctamente.

Ahora asignamos el password a nuestro usuario:

1 \password nombreUsuario
Se nos pedira ingresar la password y confirmarlo.

Creación de la base de datos

En el entorno de postgres crearemos la base de datos asociada al usuario que creamos en el paso
anterior.

1 CREATE DATABASE nombreBaseDatos OWNER nombreUsuario;

Una ves terminado salimos del entrono de postgres usando \q .

Estos datos, nombre de usuario, contraseña y base de datos de postgres, son los que usaremos para
configurar nuestro archivo database.yml en rails (en la guía de capistrano), por lo que es muy importante
no olvidarlos.

Paso 6 – Crear un Server Block


servidor

Cuando se usa Nginx, los server blocks (similar a los virtual hosts en Apache) se usan para encapsular
los detalles de configuración y servir mas de un dominio en un único servidor.

Ahora veremos como configurar los server blocks en nuestra maquina virtual.

1. Crear la carpeta donde se guardara nuestro proyecto o proyectos en caso de tener mas de uno.

Nginx, por defecto, esta configurado para servir los documentos que están en el siguiente directorio:

1 /var/www/html

Nosotros no usaremos el default ya que es mas fácil trabajar desde nuestro directorio del usuario.
Para eso nos podemos crear una nueva carpeta llamada /www o /apps (En esta guía vamos a
trabajar con la carpeta /www ). En este directorio es donde cada uno de nuestros proyectos tendrá
su propia carpeta, ej:

1 ~/www/example/
2 ~/www/test/
Crear el directorio:

1 $ mkdir -p ~/www/example

**La opción -p le dice a mkdir que cree todos los directorios padres necesarios si estos no existen

Ahora que tenemos creado el directorio vamos a continuar.

2. Crear una pagina de prueba

Vamos a crear una pagina de ejemplo, para tener algo que mostrar al crear el server block. Ya que aun
no subimos un proyecto de rails.

Crear un archivo index.html dentro de nuestro proyecto.

1 $ vim ~/www/example/index.html

Dentro del archivo escribiremos esto:

1 <html>
2 <head>
3 <title>Bienvenidos a Example.com</title>
4 </head>
5 <body>
6 <h1>El server block esta funcionando!</h1>
7 </body>
8 </html>

Ahora guardamos y cerramos el archivo index.html

3. Crear el server block para nuestro proyecto

Ahora que tenemos contenido para servir, necesitamos crear el server block que le ‘dirá’ a Nginx
como hacer esto.

Por defecto Nginx viene con un server block llamado default que usaremos como base para
nuestros propios servers. Este se encuentra en el directorio /etc/nginx/sites-available/ ,
en este directorio creamos los server block que necesitamos. Para hacer esto copiaremos el archivo
default, y el nombre que usaremos es el mismo nombre de la carpeta que nos creamos anteriormente:

1 $ sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example


Ahora abrimos el archivo recién creado:

1 $ sudo vim /etc/nginx/sites-available/example

Y eliminamos todo lo que esta en el y escribimos lo siguiente:

1 server {
2 listen 80 default_server;
3 listen [::]:80 default_server ipv6only=on;
4
5 server_name example.com www.example.com;
6
7 passenger_enabled on;
8 rails_env production;
9
10 root /home/deploy/www/example;
11
12 # redirect server error pages to the static page /50x.html
13 error_page 500 502 503 504 /50x.html;
14 location = /50x.html {
15 root html;
16 }
17 }

Importante: todas las declaraciones terminan con un punto y coma ;

Las líneas 2 y 3 le dicen que puerto tiene que escuchar, en el caso de un request del tipo http el
puerto es 80, y con el parámetro default_server le decimos que, en el caso de que se haga
un request a un server_name que no coincide con ninguno de los server block disponibles, se
cargue este server block; Solo uno de nuestros server block puede tener la especificación de
default_server!!!
En la línea 5 seteamos a que requests responderá este server block, en este caso example.com, y
ademas podemos añadir alias, en este caso www.example.com, separados por un espacio.
En la línea 7 activamos passenger para este server block.
En la línea 8 seteamos en ambiente de rails que vamos a ejecutar, en este caso el ambiente de
producción.
En la línea 10 con la directiva root apuntamos al directorio de nuestro proyecto, el path tiene que
ser absoluto. OJO Cuando estamos trabajando con una aplicación de rails la ruta queda
así: root /home/deploy/www/example/current/public;
De la línea 12 a la 16 le estamos diciendo que las paginas de error de servidor apunten a una
pagina estática.

Guardamos el archivo y lo cerramos.

4. Habilitar el server block


Ahora que tenemos nuestro server block creado, tenemos que habilitarlo. Esto se hace creando un
enlace simbólico de nuestro server block, que se encuentra en el directorio
/etc/nginx/sites-available/ , en el directorio /etc/nginx/sites-enabled , este es el
directorio que Nginx lee cuando se inicia el servidor.

Podemos crear los links de la siguiente manera:

1 $ sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/

Ahora nuestro archivo se encuentra habilitado, pero también se encuentra habilitado el archivo
default que usamos para crear nuestro server block y esto nos dará problemas ya que como lo
mencione anteriormente el parámetro default_server solo puede estar en un server block. Para
arreglar esto simplemente eliminamos el enlace simbólico a este:

1 $ sudo rm /etc/nginx/sites-enabled/default

Ahora solo falta reiniciar Nginx:

1 $ sudo service nginx restart

5. Probando nuestro server block

Para probar si todo salió bien, en el navegador vamos a visitar nuestro servidor, como aun no
tenemos un dominio usaremos la ip de la maquina, en el caso de esta guía seria http://111.11.111.11.
Deberíamos ver el mensaje que pusimos en nuestro archivo index.

Paso 7 – Ultimos detalles


servidor

Para que nuestra aplicación RoR funcione bien y en caso de usar capistrano para hacer el deployment,
tenemos que instalar los siguientes paquetes:

1 $ sudo apt-get update


2 $ sudo apt-get install git # necesario para el uso de capistrano
3 $ sudo apt-get install nodejs # obligatorio tener un ambiente js en el VPS
4 $ sudo apt-get upgrade

Con esto ya hemos terminado de configurar y dejar lista nuestra VPS para el deployment de una
aplicación RoR

Extras
Algunos comandos importantes

1 $ ssh deply@ip_del_servidor
2
3 $ sudo apt-get install paquete
4 $ sudo apt-get update
5 $ sudo apt-get upgrade
6
7 $ sudo -u postgres psql
8
9 $ sudo service nginx stop
10 $ sudo service nginx start
11 $ sudo service nginx reload
12 $ sudo service nginx restart
13
14 $ sudo ln -s ruta/original/archivo ruta/destino
15
16 $ sudo tail -n 50 /var/log/nginx/error.log
38) Deployment con Capistrano.

Introducción
Cuando yo estaba aprendiendo Rails, como un novato, no tenía ni idea de cómo llegar de esta cosa que
estaba trabajando en mi máquina de desarrollo a un verdadero servidor web que otras personas pudieran
ver. Todas estas cosas Unix CLI (Command Line Interface o terminal) parecían como magia negra para mí
(probablemente, en parte porque mi terminal es de color negro) y me sentí como que necesitaba un
doctorado en Cirugía Robótica para poder hacerlo. Rails ha hecho tan fácil el desarrollo de aplicaciones!
Seguramente existe una manera de hacer deploy a mis creaciones sin que explote mi cabeza!!!

Bueno, si y no. Servicios como Heroku están tratando de quitar una gran parte de la complejidad a la
hora de hacer deploy una aplicaciones web, y están haciendo un buen trabajo. Pero como yo estaba
tratando de aprender sentí que aquellos servicios no me ayudaban a comprender lo que se estaba
haciendo. Sentía que al menos en la CLI, estaba en control y podia ver con mis propios ojos lo que
estaba pasando (o lo que no estaba pasando). Y así, poco a poco logre comprender un poco mas cómo
mi servidor web funcionaba.

En un principio hice todo manualmente (la copia de archivos, la migración de bases de datos, la
instalación de las gemas, reiniciar servicios, etc). Pero rápidamente me di cuenta de por qué nadie hace
esto!!! En primer lugar, es terriblemente propenso a errores escribir todos los comandos con mis torpes
manos humanas. En segundo lugar, a veces las cosas no funcionaban y yo no sabía por qué, y tenia que
pasar horas averiguando dónde fue que perdí un signo o puse uno de mas. La moraleja de la historia es:
encontrar la manera de hacerlo una vez, y después, guardarlo en un script que se pueda repetir sin
problemas (aparentemente los computadores (ordenadores) son muy buenos haciendo lo mismo una y
otra vez; ¿quién sabe?). Y es por eso que uso Capistrano y Git!

Configuraremos nuestra aplicación de Rails para hacer deploy en el servidor remoto utilizando Capistrano
y Git para que este proceso de implementación sea automatizado, rápido y libre de dolores de cabeza.

Creo que los dos aspectos clave de cualquier proceso de implementación son la velocidad y
consistencia. Velocidad significa que podemos repetir y corregir errores rápido y mantener nuestro código
de producción en sintonía con nuestro código de desarrollo. Consistencia significa que sabemos que va a
hacer lo mismo cada vez, así que no tendremos miedo de hacerlo y estar al día. El uso de un sistema de
control de versiones como Git, junto con las recetas de implementación automatizadas de Capistrano
satisface estos criterios con facilidad.

Acerca de esta guía.

En esta guía usaremos un servidor remoto que tenga Ubuntu, Passenger y Nginx instalados y
configurados, así como también acceso SSH a este. Si no tienes un servidor remoto con los
requerimientos antes mencionados, te recomiendo que sigas la guía "Rails y Nginx con Passenger en
Ubuntu: Preparando nuestro entorno de producción", que disponible para servidores Amazon,
DigitalOcean o Linode.

Se espera que el lector de esta guía sepa como utilizar la terminal y tenga conocimiento de al menos
comandos básicos de este. Así como también se espera sepan usar postgres.

Convenciones.

1. Los términos servidor, server o VPS hacen referencia a su maquina virtual.


2. El termino local hace referencia a su computador.
3. Para determinar el entorno en que tenemos que ejecutar los comandos y en cual estaremos
trabajando en cada sección se usara: local para la maquina local o servidor para la maquina virtual.
4. Se usara para los ejemplos la IP: 111.11.111.11, esta tienen que ser remplazada por la ip de su
maquina virtual.
5. Debes tener Git instalado y tener una cuenta en Github o en Bitbucket. Y saber usarlos.
6. Para mostrar las instrucciones a ejecutar en el terminal se antepondrá el signo $ , que es la
representación de su línea de comandos que esta lista para recibir una instrucción (no hay que
tipearlo), y tendrán el siguiente estilo:

1 $ gem install postgres

*Cada línea que empieza con $ se ejecuta por separado.

Para mostrar las respuestas, errores o advertencias que nos arroja el terminal al ejecutar una
instrucción no se antepone el signo $ y tendrán el siguiente estilo:

1 Agent pid 32877

Paso 0 – La Aplicación
local

Para empezar, vamos a necesitar algo para implementar Capistrano y hacer deploy. Para eso vamos a
crear una aplicación sencilla aquí (el proceso de implementación debe ser más o menos el mismo,
independientemente de lo que esté haciendo su aplicación). Mi objetivo, aquí, es explicar un método muy
simple para la automatización de sus deployments para darle un lugar donde empezar. Quizás esta no es
la manera más rápida o la manera más elegante, pero va a hacer su proceso coherente, y sin duda será
mucho más rápido que hacerlo manualmente. Mi pensamiento es que si funciona, al menos ustedes
pueden darse el tiempo para aprender las técnicas más avanzadas.

Empecemos:

1. Crear una aplicación de tareas (usando Postgres porque eso es lo que he instalado en mi
servidor):

Vamos a crear una pequeña aplicación de tareas con rails y vamos a hacer un scaffold y vamos a
revisar que funcione!

1 $ rails new todoapp -d postgresql


2 $ cd todoapp
3 $ bundle install
4 $ rails g scaffold todo name:string finished:boolean
5 $ rake db:migrate
6 $ rails s

Listo tenemos nuestra aplicación creada, no hace mucho, pero nos servirá para lo que necesitamos.

De ahora en adelante trabajaremos en la carpeta de nuestra app en la terminal

2. Iniciar GIT y el repo en Github

Antes de instalar capistrano, es muy importante que nuestra aplicación este en un sistema de control
de versiones. Para eso vamos a "gittear" nuestra app.

Recuerden que es muy importante , antes de hacer cualquier commit, crear el archivo .gitignore y
añadir los archivos con información sensible a este. Por ejemplo el config/database.yml ,
config/secrets.yml y el .env en caso de estar usando la gema dotenv-rails .

1 $ git init
2 $ git add --all
3 $ git commit -m 'Primer Commit'

Ahora que nuestra app esta "gitteada", tienen que ir a su cuenta en Github o en bitbucket y crear un
repositorio nuevo en donde pushearemos nuestra app, yo los espero aquí mientras tanto… … … Ok
ahora que tenemos nuestro repo creado vamos a configurar nuestra app para linkearla con el. En los
ejemplos usare github!

1 $ git remote add origin git@github.com:username/your-repo-name.git

Una vez linkeado, pushearemos la app para que este disponible en nuestro repo.
1 $ git push -u origin master

Listo, podemos seguir con capistrano!!!

Paso 1 – Añadir Capistrano a nuestra app.


Entonces, ¿qué es Capistrano? Capistrano es una aplicación open-source escrita en Ruby para
automatizar tareas en uno o varios servidores remotos via SSH e incluye un conjunto de flujos de trabajo
de implementación predeterminados.

La instalación de Capistrano es tan fácil como añadir la gema al Gemfile de nuestra aplicación y ejecutar
bundle install. Nosotros no necesitamos Capistrano en el servidor de producción, por lo que la añadimos
bajo el grupo de "desarrollo" del Gemfile. Como referencia, estoy usando la ultima version ‘3.4.0’

1. Abrimos nuestra app en nuestro editor de texto favorito (para mi ese es Sublime Text 3.) y
vamos a editar nuestro archivo gemfile y añadimos lo siguiente:

1 group :development do
2 gem 'capistrano'
3 gem 'capistrano-bundler'
4 gem 'capistrano-rails'
5 gem 'capistrano-rvm'
6 gem 'capistrano-passenger'
7 gem 'capistrano-ssh-doctor'
8 end

Oye, pero ahi añadimos mas de una gema!!!

Tranquilos, ahora voy a explicar que es cada una de ellas.

gem 'capistrano' : es la que nos permitirá instalar capistrano y ejecutar sus tareas.
gem 'capistrano-bundler' : añade la tarea bundler:install a Capistrano, y se
ejecuta automáticamente en el servidor remoto como parte de las tareas que se realizan cuando
hacemos un deploy con capistrano.
gem 'capistrano-rails' : añade 2 tareas especificas a Capistrano, deploy:migrate y
deploy:compile_assets , y se ejecutan automáticamente en el servidor remoto como parte
de las tareas que se realizan cuando hacemos un deploy con capistrano.
gem 'capistrano-rvm' : Asegura que todas las tareas usen la version de Ruby correcta y le
dice a capistrano que use rvm ... do ... para correr rake, bundle, gem y ruby.
gem 'capistrano-passenger' : Añade la tarea passenger:restart , y reiniciará el
servidor passenger después de hacer un deploy con capistrano.
gem 'capistrano-ssh-doctor' : Añade la tarea ssh:doctor , para verificar si las
conexiones mediante ssh están correctas y si no ayudarnos a resolverlas.

2. Ahora el respectivo bundle

Recuerden que estamos trabajando en el directorio de nuestro proyecto

1 $ bundle install

3. Y ahora instalamos Capistrano (o "capify" nuestro proyecto)

1 $ cap install

Esto va a crear los siguientes archivos:

1 !"" Capfile
2 !"" config
3 # !"" deploy
4 # # !"" production.rb
5 # # $"" staging.rb
6 # $"" deploy.rb
7 $"" lib
8 $"" capistrano
9 $"" tasks

En nuestro caso solo queremos tener un solo stage, el de producción, para eso lo instalamos así:
~~~bash $ cap install STAGES=production ~~~

Esto va a crear los siguientes archivos:

1 !"" Capfile
2 !"" config
3 # !"" deploy
4 # # $"" production.rb
5 # $"" deploy.rb
6 $"" lib
7 $"" capistrano
8 $"" tasks

¿Para que tener distintos stages? Sirven por si queremos tener configuraciones especificas para
diferentes deployments de nuestro proyecto.

Listo, nuestro proyecto esta "capify". En la siguiente sección prepararemos nuestro proyecto!
Paso 2 – Preparación de nuestro proyecto
En esta sección vamos a revisar cada archivo que la instalación de capistrano creo, vamos a entender
que hacen y los vamos a editar con nuestras preferencias.

1. Capfile

El archivo Capfile que se encuentra en la raíz de nuestro proyecto es la primera capa de


configuración de capistrano, es decir, contiene la instrucciones iniciales que le dirán a Capistrano que
plugins incluir y que tareas tendremos disponibles para usar. El archivo recién creado se ve así:

1 # Load DSL and set up stages


2 require 'capistrano/setup'
3
4 # Include default deployment tasks
5 require 'capistrano/deploy'
6
7 # Include tasks from other gems included in your Gemfile
8 #
9 # For documentation on these, see for example:
10 #
11 # https://github.com/capistrano/rvm
12 # https://github.com/capistrano/rbenv
13 # https://github.com/capistrano/chruby
14 # https://github.com/capistrano/bundler
15 # https://github.com/capistrano/rails
16 # https://github.com/capistrano/passenger
17 #
18 # require 'capistrano/rvm'
19 # require 'capistrano/rbenv'
20 # require 'capistrano/chruby'
21 # require 'capistrano/bundler'
22 # require 'capistrano/rails/assets'
23 # require 'capistrano/rails/migrations'
24 # require 'capistrano/passenger'
25
26 # Load custom tasks from `lib/capistrano/tasks` if you have any defined
27 Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Como vemos, tenemos varias líneas comentadas por lo que vamos a editar el archivo e incluir los
plugins que necesitamos en nuestro proyecto, estos están relacionados con las gemas que
agregamos al gemfile y limpiar lo que no necesitamos.

El archivo tiene que quedar asi:


1 # Load DSL and set up stages
2 require 'capistrano/setup'
3
4 # Include default deployment tasks
5 require 'capistrano/deploy'
6
7 # Include tasks from other gems included in your Gemfile
8 require 'capistrano/rvm'
9 require 'capistrano/bundler'
10 require 'capistrano/rails'
11 require 'capistrano/passenger'
12 require 'capistrano/ssh_doctor'
13
14 # Load custom tasks from `lib/capistrano/tasks` if you have any defined
15 Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Ahora tenemos todas las tareas extras, de las gemas de capistrano que agregamos, disponibles para
usar al hacer deploy.

2. deploy.rb

El archivo deploy.rb que se encuentra en config/deploy.rb de nuestro proyecto (no


confundir con la carpeta deploy que también esta dentro de config), es donde configuraremos las
variables globales de Capistrano; globales porque afectan a todos los stages que tengamos creados.
Al igual que el archivo Capfile, este viene con contenido por defecto y lo vamos a remplazar por lo
siguiente:

1 # config valid only for current version of Capistrano


2 lock '3.4.0'
3
4 set :rvm_type, :user
5 set :rvm_ruby_version, '2.2.2'
6
7 set :application, 'YourApplicationName'
8 set :deploy_to, "/home/username/#{fetch(:application)}"
9 set :scm, :git
10 set :repo_url, 'git@github.com:your-username/your-repository-name.git'
11 set :branch, 'master'
12
13 set :linked_files, %w{config/database.yml config/secrets.yml .env}
14 set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system publi
15
16 set :keep_releases, 4

Ahora les voy a explicar que es lo que estamos haciendo en este archivo:

lock : seteamos la version de capistrano que estamos usando.


set :rvm_type : le decimos cual es el path de rvm a usar. En nuestro caso el el path desde el
user.
set :rvm_ruby_version : seteamos que version de ruby queremos usar de las que tenemos
instaladas con rvm en el servidor.
set :application : el nombre de nuestra aplicación.
set :deploy_to : la ruta en el servidor donde se hará el deploy. Remplazar username por
"deploy" para digitalocean o linode y "ubuntu" para amazon.
set :scm : que sistema de control de versiones estamos usando, en este caso git.
set :repo_url : la url de nuestro repositorio donde tenemos pusheada la app.
set :branch : el branch que queremos que se use para hacer el deploy. En este caso master.
set :linked_files : listado de archivos que necesitamos que sean persistentes entre cada
deploy mediante links simbólicos, aquí añadimos los archivos config/database.yml ,
config/secrets.yml y .env . Estos archivos TIENEN que estar en el .gitignore
ya que NO los debemos subir a nuestro repo porque que contienen información sensible.
set :linked_dirs : listado de directorios que necesitamos que sean persistentes entre cada
deploy mediante links simbólicos, por ejemplo: para no perder los archivos que se han subido a
nuestra app cuando hacemos un nuevo deploy añadimos public/uploads .
keep_releases : le decimos a capistrano que solo mantenga los ultimos X deploy y borre
todo lo demas. En este caso 4.

Existen más variables que se pueden configurar, pero estas son las que, en la mayoría de los casos,
vamos a necesitar cambiar. Para conocer que otras variables y profundizar en el tema les recomiendo
que lean http://capistranorb.com/documentation/getting-started/configuration/

3. production.rb

Para las configuraciones que son especificas de cada stage, editamos cada uno de los archivos que
tengamos en config/deploy/ . En este caso solo tenemos el stage production, que se encuentra
en config/deploy/production.rb . Al igual que antes el archivo creado viene con contenido
por defecto y lo vamos a remplazar por lo siguiente:

1 set :stage, :production


2 set :rails_env, :production
3
4 server '111.11.111.11', user: 'username', roles: %w{web app db}, primary: true

Aqui va la explicación:

:stage : le damos el nombre a nuestro stage, en este caso producción, que usaremos al hacer
deploy. :rails_env : le decimos a rail que corra en el ambiente que necesitamos, en este caso
producción.
server... : En esta línea le decimos a Capistrano como tiene que acceder a nuestro vps. Le
damos el ip de la maquina y el usuario con el cual conectarse, "deploy" en digitalocean o linode y
"ubuntu" en amazon. La variable :roles le dice a capistrano que el server de PostgreSQL
( db ), el server de Nginx ( web ) y el server de passenger ( app ) corren el la misma maquina. La
opción primary: true le dice a capistrano que este es nuestro server de base de datos primario
y correrá todas las migraciones en este.

Estamos casi listos para hacer deploy, pero antes vamos a añadir unas tareas personalizadas a
capistrano.

Paso 3 – Tareas personalizadas


Por si no lo saben, Capistrano hace mucho de su trabajo con la ayuda de tareas. Por ejemplo, cuando
hicimos cap install lo que hicimos fue invocar una tarea llamada "install" que crea los archivos y
carpetas que hemos estado editando.

Ahora nosotros vamos a crear nuestras propias tareas, para esto crearemos archivos .rake en la
siguiente carpeta lib/capistrano/tasks para cada una de ellas. La primera nos ayudara a setear
algunos archivos en el server, la segunda sera para limpiar nuestros assets en el server y la tercera para
comprobar que nuestro repo esta al día antes de hacer deploy.

1. setup.rake

Para el primer grupo de tareas crearemos el archivo setup.rake en lib/capistrano/tasks


y escribimos lo siguiente:
1 namespace :setup do
2
3 desc "Upload database.yml file."
4 task :database do
5 on roles(:app) do
6 execute "mkdir -p #{shared_path}/config"
7 upload! StringIO.new(File.read("config/database.yml")), "#{shared_path}/config/database
8 end
9 end
10
11 desc "Upload secrests.yml file."
12 task :secrets do
13 on roles(:app) do
14 execute "mkdir -p #{shared_path}/config"
15 upload! StringIO.new(File.read("config/secrets.yml")), "#{shared_path}/config/secrets.y
16 end
17 end
18
19 desc "Upload .env file."
20 task :env do
21 on roles(:app) do
22 execute "mkdir -p #{shared_path}/config"
23 upload! StringIO.new(File.read(".env")), "#{shared_path}/.env"
24 end
25 end
26
27 desc "Seed the database."
28 task :seed do
29 on roles(:app) do
30 within "#{current_path}" do
31 with rails_env: :production do
32 execute :rake, "db:seed"
33 end
34 end
35 end
36 end
37
38 end

Aquí hemos creado cuatro tareas bajo un namespace llamado :setup .

¿Recuerdan que los archivos config/database.yml , config/secrets.yml y .env


están en el .gitignore ? Bueno, esto nos va a traer problemas y errores ya que no estarán
disponibles al hacer deploy, por eso las tres primeras tareas se encargaran de subir estos archivos
directamente al servidor sin pasar por nuestro repo.

La primera tarea es :database . Esta se encarga de subir el archivo


config/database.yml al servidor.
La segunda tarea es :secrests . Esta se encarga de subir el archivo
config/secrets.yml al servidor.
La tercera tarea es :env . Esta se encarga de subir el archivo /.env al servidor.

En el caso de la tarea :env , solo lo usaremos si estamos trabajando con la gema


dotenv-rails , y si es así, esta gema tiene que estar disponible en el ambiente de producción,
ósea, sacarla del grupo development de nuestro gemfile.

La cuarta tares es :seed . Esta se encarga de ejecutar rake db:seed en el servidor si lo


necesitamos.

2. assets.rake

Ahora crearemos el archivo assets.rake en lib/capistrano/tasks y escribimos lo


siguiente:

1 namespace :clean do
2 desc 'Runs rake assets:clobber on server to remove compiled assets'
3 task :assets do
4 on roles(:app) do
5 within "#{current_path}" do
6 with rails_env: :production do
7 execute :rake, 'assets:clobber'
8 execute :touch, release_path.join('tmp/restart.txt')
9 end
10 end
11 end
12 end
13 end

La tarea que hemos creado se llama :assets y esta bajo el namespace :clean , esta tarea lo
que hace es eliminar todos los assets en el servidor en caso de que estos nos estén causando
problemas.

3. deploy.rake

Ahora crearemos el archivo deploy.rake en lib/capistrano/tasks y escribimos lo


siguiente:
1 namespace :deploy do
2
3 desc "Makes sure local git is in sync with remote."
4 task :check_revision do
5 unless `git rev-parse HEAD` == `git rev-parse origin/master`
6 puts "WARNING: HEAD is not the same as origin/master"
7 puts "Run `git push` to sync changes."
8 exit
9 end
10 end
11
12 before :deploy, "deploy:check_revision"
13
14 end

Esta tarea se encargara de revisar si nuestro repo esta al día con los cambios locales antes de hacer
el deploy. Si no es así, se cancelara el deploy y nos dará una advertencia.

Estamos listos con las tareas personalizadas!

Paso 4 – Conectando el servidor con el repositorio


Para este paso es necesario tener el servidor configurado y haber seguido la guía "Rails y Nginx con
Passenger en Ubuntu: Preparando nuestro entorno de producción"

Como ya sabemos, capistrano usa git y un repositorio para automatizar nuestro deployment. Es por esto
que es necesario que nuestro servidor tenga acceso a este repositorio y se pueda autenticar
automáticamente cuando se hace el deployment. Para esto usaremos ssh y deploy keys

*Todos los comando que usaremos a continuacion tienen que ser ejecutados en el servidor remoto (AWS
o DO).

1. Lo primero que tenemos que hacer es revisar si existe alguna llave ssh en nuestro servidor. Para eso
entramos al servidor desde nuestro terminal y ejecutamos:

1 $ ls -al ~/.ssh

Si en el listado tenemos alguna llave publica ssh, del tipo id_rsa.pub o terminado en .pub ,
podemos usarla para la conexión. En caso contrario crearemos una nueva llave.

2. Creación de una llave ssh en nuestro servidor


1 $ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

Es recomendable usar las opciones por defecto como estan, por lo que cuando nos pregunte "en que
archivo queremos guardar la llave" simplemente de damos Enter

1 Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]

Se nos pedirá crear una password

1 Enter passphrase (empty for no passphrase): [Type a passphrase]


2 Enter same passphrase again: [Type passphrase again]

Finalmente se nos mostrara la ‘huella’ o id de nuestra llave ssh. Sera algo parecido a esto:

1 Your identification has been saved in /Users/you/.ssh/id_rsa.


2 Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
3 The key fingerprint is:
4 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db your_email@example.com

3. Copiar la llave publica de nuestro servidor

Para copiar la llave en ubuntu tendremos que installar xclip

1 $ sudo apt-get install xclip

Una vez instalado ejecutamos:

1 $ xclip -sel clip < ~/.ssh/id_rsa.pub

4. Pegar la llave publica en nuestro repositorio como una deploy key

Una deploy key es una llave ssh que se guardara en el repositorio de nuestro proyecto (github o
bitbucket) y permitirá que capistrano se pueda autenticar en el. Esta llave solo esta vinculada con el
repositorio y no con nuestra cuenta. Lo primero que tenemos que hacer es entrar a nuestra cuenta de
Github o Bitbucket e ir al repositorio del proyecto al cual le vamos hacer deployment

Añadir la llave en Github


1 ![Timezone 1](https://dl.dropboxusercontent.com/s/0j63ztcppxkr0am/github-01.png "Github 1")
2
3 ![Timezone 1](https://dl.dropboxusercontent.com/s/9957iuj75a1zw8a/github-02.png "Github 2")
4
5 ![Timezone 1](https://dl.dropboxusercontent.com/s/ol0c5ms7igichqv/github-03.png "Github 3")

Añadir la llave en Bitbucket


Una ves añadidas las llaves a nuestro repositorio estamos listos para continuar.

Paso 5 – Deploy! Cómo se hace? y qué hace?


Ok, tenemos todo listo para seguir, Capistrano instalado y configurado y tareas personalizadas creadas.
Solo nos falta hacer el push a nuestro repo con todos los cambios que hemos echo hasta ahora.

1 $ git add --all


2 $ git commit -m 'Add Capistrano and custom task'
3 $ git push origin master

Listo, ahora si, como hacemos el deploy. Hacer un deploy es tan fácil como escribir esto en la consola en
el root de nuestro proyecto:

1 $ cap production deploy

La instrucción anterior esta compuesta de 3 partes, cap hace referencia a Capistrano,


production es el stage que vamos a usar, y deploy es la tarea a realizar en el stage. Podemos
ejecutar cualquier tarea que tengamos disponible. Para ver cuales son (incluidas las nuestras) ejecutamos
lo siguiente:

1 $ cap -T

Esto nos devolverá un listado de todas las tareas que podemos usar y su descripción; de toda esa lista
las tareas que más usaremos y las que nos interesan son:

1 cap deploy # Deploy a new release


2 cap deploy:check # Check required files and directories exist
3 cap setup:database # Upload database.yml file
4 cap setup:secrets # Upload secrests.yml file
5 cap setup:env # Upload .env file
6 cap setup:seed # Seed the database
7 cap ssh:doctor # Perform ssh doctor

Si ya ejecutaron cap production deploy lo mas probable es que les arrojara un error y no se
completara la tarea. No nos preocuparemos de eso por ahora. Primero vamos a entender que pasa
cuando ejecutamos cap production deploy .

Capistrano utiliza una jerarquía de directorios estrictamente definido en cada servidor remoto para
organizar el código fuente y otros datos relacionados con el deployment. La ruta raíz de esta estructura
es la definida en la variable de configuración: :deploy_to que modificamos en el archivo
config/deploy.rb

Si revisamos la ruta raíz e inspeccionamos los directorios veremos algo como esto:
1 !"" current -> /var/www/my_app_name/releases/20150120114500/
2 !"" releases
3 # !"" 20150080072500
4 # !"" 20150090083000
5 # !"" 20150100093500
6 # !"" 20150110104000
7 # $"" 20150120114500
8 !"" repo
9 # $"" <VCS related data>
10 !"" revisions.log
11 $"" shared
12 $"" <linked_files and linked_dirs>

/releases : cada vez que se hace un deploy un nuevo directorio se creara aquí, y contiene todo el
código de ese deploy.
/current : es un enlace simbólico que apunta al último directorio creado en /releases .
/shared : mantiene los archivos y directorios que son persistentes a lo largo de los deploy.
/repo : contiene un clon de su .git.

Dentro de la carpeta /shared


/shared , encontraremos:

1 $"" shared
2 !"" .env
3 !"" config
4 !"" public
5 !"" log
6 !"" tmp
7 !"" bin
8 !"" bundle
9 $"" vendor

Los que nos interesan son:

.env : el archivo que contendrá nuestras variables privadas.


/config : tendrá nuestro database.yml y secret.yml .
/log : contiene el production.log . Este tendrá todo el historial de nuestra app, no solo del
ultimo deploy.
/public : contiene todos los assets y también la carpeta upload en nuestro caso.

Cuando corremos cap production deploy lo que estamos haciendo es llamar una tarea de
Capistrano llamada deploy, quede manera secuencial invocara otras tareas. Las principales son:

starting : crea la estructura de directorios y comprueba que puede obtener el repo de github.
updating : copia el repo de github a un nuevo directorio en /releases , y añade los links
simbólicos que apuntan a /shared , corre Bundler, las migraciones y compila los assets.
publishing : crea el links simbólico entre /current y el nuevo directorio en /releases .
Solo si no hubo errores en alguna de las tareas anteriores.
finishing : elimina los directorios mas antiguos de /releases .

En caso de que Capistrano se encuentre con un error en el momento de hacer deploy y no termine la
tarea completa, /current siempre apuntara a la ultimo directorio de /releases que estaba
funcionando, de esta manera el sitio siempre estará disponible.

Ahora que sabemos cómo y qué hace Capistrano haremos un


deploy de nuestra app

1. Si es el primer deploy de nuestra app, ejecutaremos las siguientes tareas, una a una, en el
orden en que aparecen a continuación:

1 $ cap production ssh:doctor


2 $ cap production setup:database
3 $ cap production setup:secrets
4 $ cap production setup:env
5 $ cap production deploy

ssh:doctor : comprueba que todas las conexiones mediante ssh estén correctas.
setup:database|secrets| : suben los archivos database y secrets
respectivamente. si falta alguno de ellos el deploy nos dará error.
setup:env : sube el archivo .env , este solo se ejecuta si estamos utilizando la gema
dotenv-rails en nuestro proyecto para definir variables de configuración privadas.
deploy : finalmente ejecutamos deploy.

Si siguieron todos los pasos de esta guía y los de la guía de "Rails y Nginx con Passenger en
Ubuntu…" no deberían tener ningún error. En el caso de que el deploy nos de un error, revisaremos
bien los mensajes que nos da la terminal y buscaremos en google alguna respuesta.

2. Para los siguientes deploys de la misma app, solo ejecutaremos

1 $ cap production deploy

*Sólo en caso de que cambiemos algún dato en database.yml , secrets.yml o .env


volvemos a ejecutar la tarea correspondiente a cada archivo.

Si ahora abrimos nuestro navegador favorito (espero que no sesea Internet Explorer) y escribimos la
dirección IP de nuestro servidor en la barra de direcciones, podremos ver nuestra aplicación; si no la
vemos, no se preocupen. La implementación es difícil y toma un tiempo para asimilar. Si las cosas no
funcionan, lo mejor es comenzar con los registros y googlear cualquier error que encontremos allí.

Pero lo más importante es no desanimarse. Cuando quise configurar mi servidor de producción a


partir de cero la primera vez, me llevó una semana completa (no estoy bromeando) para que
funcionara. Fue frustrante, desalentador, y es la razón por la que me decidí a escribir esta guía
(ademas de que me la pidieron los alumnos :D), porque no quiero que otras personas que pasan por
lo mismo. No tiene por qué ser de esa manera, y espero que no sea así.
I

1. Al momento de escribir esta guía no era compatible con ubuntu 14.10 ↩

S-ar putea să vă placă și