1 2 3 4 5 6 7 8
<script> $(document).ready(function() { $('#calendar').fullCalendar({ theme: true, events: "#{}" }) });
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 2 3 4 5 6 7 8 9
create create invoke create create create invoke create create
app/mailers/user_mailer.rb app/mailers/application_mailer.rb erb app/views/user_mailer app/views/layouts/mailer.text.erb app/views/layouts/mailer.html.erb test_unit test/mailers/user_mailer_test.rb 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 2
class UserMailer < ApplicationMailer 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 2 3 4 5 6 7 8 9 10 11 12 13
class UserMailer < ApplicationMailer default from: '
[email protected]'
def welcome_email(user) @user = user @url = 'http://example.com/login' mail( to: @user.email, subject: 'Welcome to My Awesome Site', template_path: 'user_mailer', # opcional template_name: 'welcome_mail') # opcional end
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 2 3 4 5 6 7 8 9
Welcome to example.com, <%= @user.name %>
You have successfully signed up to example.com, your username is: <%= @user.login %>.
To login to the site, just follow this link: <%= @url %>.
Thanks for joining and have a great day!
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 2 3 4 5 6 7 8 9
Welcome to example.com, <%= @user.name %> =============================================== You have successfully signed up to example.com, your username is: <%= @user.login %>. To login to the site, just follow this link: <%= @url %>. Thanks for joining and have a great day!
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 2 3 4 5
class UserMailerPreview < ActionMailer::Preview def welcome_email_preview UserMailer.welcome_email(User.last) end 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 git le haga seguimiento y lo suba a github!!!
ya que NO queremos que
Dentro de ese archivo creamos las variables a usar: 1 2
GMAIL_USERNAME=ninombredeusurario GMAIL_PASSWORD=miclave
1
El nombre de la variable puede ser cualquier cosa y puede ir en mayusculas o min
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 2 3 4 5 6 7 8 9 10
config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, user_name: ENV['GMAIL_USERNAME'], password: ENV['GMAIL_PASSWORD'], authentication: :login, enable_starttls_auto: true }
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
class UsersController < ApplicationController def create @user = User.new(params[:user]) respond_to do |format| if @user.save # Tell the UserMailer to send a welcome email after save UserMailer.welcome_email(@user).deliver_later format.html format.json else format.html format.json end end end end
{ redirect_to(@user, notice: 'User was successfully created.' { render json: @user, status: :created, location: @user } { render action: 'new' } { render json: @user.errors, status: :unprocessable_entity }
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 2 3 4 5 6
class Users::RegistrationsController < Devise::RegistrationsController def create super UserMailer.welcome_email(@user).deliver_later unless @user.invalid? end 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 3
... devise_for :users, ...
controllers: { registrations: 'users/registrations' }
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 2 3 4 5 6 7 8 9 10
class User < ActiveRecord::Base # ... # código omitido
private def send_welcome_email UserMailer.welcome_email(self).deliver_later end
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 2 3 4 5 6
class User < ActiveRecord::Base after_create :send_welcome_email # código omitido # ... 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 2 3 4 5
# Si quieres enviar el correo inmediatamente usa #deliver_now UserMailer.welcome_email(@user).deliver_now # Si quieres enviar el correo mediante ActiveJob usa #deliver_later 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 2 3 4 5 6 7 8 9
group :development do gem 'guard' gem 'guard-minitest' gem 'minitest-reporters' # notificaciones, solo usuarios de Mac OS X 10.8 o mayor gem 'terminal-notifier' gem 'terminal-notifier-guard' 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 guard instalado en nuestro proyecto proyecto tenemos que generar generar su archivo archivo de configuración, para esto ejecutamos lo siguiente en la terminal:
1
bundle exec exec guard 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# A sample Guardfile # More info at https://github.com/guard/guard#readme guard :minitest do # with Minitest::Unit watch( watch (%r{^test/(.*)\/?test_(.*)\.rb$} %r{^test/(.*)\/?test_(.*)\.rb$}) ) watch( watch (%r{^lib/(.*/)?([^/]+)\.rb$} %r{^lib/(.*/)?([^/]+)\.rb$}) ) { |m| "test/ "test/#{ #{m m[1]}test_ test_#{ #{m m[2]}.rb" .rb" } } watch( watch (%r{^test/test_helper\.rb$} %r{^test/test_helper\.rb$}) ) { 'test' } 'test' } # # # #
with Minitest::Spec watch(%r{^spec/(.*)_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
# # # # # # # #
Rails 4 watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_ watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integrati watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/# watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[ watch(%r{^test/.+_test\.rb$}) watch(%r{^test/test_helper\.rb$}) { 'test' }
# # # # end
Rails < 4 watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" } watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# A sample Guardfile # More info at https://github.com/guard/guard#readme guard :minitest do # with Minitest::Unit watch( watch (%r{^test/(.*)\/?test_(.*)\.rb$} %r{^test/(.*)\/?test_(.*)\.rb$}) ) watch( watch (%r{^lib/(.*/)?([^/]+)\.rb$} %r{^lib/(.*/)?([^/]+)\.rb$}) ) { |m| "test/ "test/#{ #{m m[1]}test_ test_#{ #{m m[2]}.rb" .rb" } } watch( watch (%r{^test/test_helper\.rb$} %r{^test/test_helper\.rb$}) ) { 'test' } 'test' } # Rails 4 watch( watch (%r{^app/(.+)\.rb$} %r{^app/(.+)\.rb$}) ) { |m| "test/ "test/#{ #{m m[1]}_te watch( watch (%r{^app/controllers/application_controller\.rb$} %r{^app/controllers/application_controller\.rb$}) ) { 'test/controllers' 'test/controllers' } } watch( watch (%r{^app/controllers/(.+)_controller\.rb$} %r{^app/controllers/(.+)_controller\.rb$}) ) { |m| "test/integratio watch( watch (%r{^app/views/(.+)_mailer/.+} %r{^app/views/(.+)_mailer/.+}) ) { |m| "test/mailers/ "test/mailers/#{ #{m m watch( watch (%r{^lib/(.+)\.rb$} %r{^lib/(.+)\.rb$}) ) { |m| "test/lib/ "test/lib/#{ #{m m[1] watch( watch (%r{^test/.+_test\.rb$} %r{^test/.+_test\.rb$}) ) watch( watch (%r{^test/test_helper\.rb$} %r{^test/test_helper\.rb$}) ) { 'test' 'test' } } end
¿Qué hace cada línea?
1 2 3
guard :minitest do # ... end
Aquí le decimos a Guard Guard que el siguiente siguiente bloque se tiene que ejecutar ejecutar con Minitest. Guard Guard se puede configurar con multiples plugins que pueden realizar muchas otras cosas.
1
watch( watch (%r{^test/(.*)\/?test_(.*)\.rb$} %r{^test/(.*)\/?test_(.*)\.rb$}) )
Esta línea monitorea todos los archivos se ha modificado.
1
.rb en las subcarpetas de test/ y ejecuta el archivo que
watch( watch (%r{^lib/(.*/)?([^/]+)\.rb$} %r{^lib/(.*/)?([^/]+)\.rb$}) )
{ |m| "test/ "test/#{ #{m m[1]}test_ test_#{ #{m m[2]}.rb" .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( watch (%r{^test/test_helper\.rb$} %r{^test/test_helper\.rb$}) )
{ 'test' 'test' } }
Esta línea monitorea el archivo test_helper.rb y si hay un cambio ejecuta todos t odos los test.
1
watch( watch (%r{^app/(.+)\.rb$} %r{^app/(.+)\.rb$}) )
{ |m| "test/ "test/#{ #{m m[1]}_test.rb" _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( watch (%r{^app/controllers/application_controller\.rb$} %r{^app/controllers/application_controller\.rb$}) ) { 'test/controllers' 'test/controllers' } }
Esta línea monitorea el archivo aplication_controller.rb y ejecuta todos los test de controladores si se modifica.
1
watch( watch (%r{^app/controllers/(.+)_controller\.rb$} %r{^app/controllers/(.+)_controller\.rb$}) )
{ |m| "test/integration/
Esta línea monitorea los controladores y ejecuta el test de integración correspondiente correspondiente cuando hay modificaciones.
1
watch( watch (%r{^app/views/(.+)_mailer/.+} %r{^app/views/(.+)_mailer/.+}) )
{ |m| "test/mailers/ "test/mailers/#{ #{m m[1]
Esta línea monitorea la carpetas de las vitas de los mailers y ejecuta el test correspondiente cuando hay modificaciones.
1
watch( watch (%r{^lib/(.+)\.rb$} %r{^lib/(.+)\.rb$}) )
{ |m| "test/lib/ "test/lib/#{ #{m m[1]}_
Esta línea monitorea la carpeta lib y ejecuta el test t est correspondiente cuando hay modificaciones si es que lo existe un test asociado.
1 2
watch(%r{^test/.+_tests\.rb$} watch( %r{^test/.+_tests\.rb$}) ) watch( watch (%r{^test/test_helper\.rb$} %r{^test/test_helper\.rb$}) ) { "test" "test" } }
Estas líneas monitorear todos los archivos terminados en un cambio en alguno de ellos ejecutará el test.
_test.rb y test_helper.rb y si hay
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 líneas a nuestro Guardfile:
1 2 3 4 5 6 7 8 9 10 11 12
# agregar la siguiente línea al inicio del archivo require 'active_support/inflector' guard :minitest do # ... código anterior omitido # extra tests watch( watch (%r{^app/views/(.+)/.+} %r{^app/views/(.+)/.+}) ) { |m| "test/controllers/#{ "test/controllers/#{m m[1]}_controllet_tes watch( watch (%r{^test/fixtures/(.+)\.yml} %r{^test/fixtures/(.+)\.yml}) ) { |m| "test/models/ "test/models/#{ #{m m[1]. ].singularize singularize} }_t watch( watch (%r{^test/fixtures/(.+)\.yml} %r{^test/fixtures/(.+)\.yml}) ) { |m| "test/controllers/ "test/controllers/#{ #{m m[1]}_controll end
El detalle de los ajustes adicionales
1
require 'active_support/inflector'
singularizar ’ los nombres de los fixtures. Esto nos Aquí añadimos la clase clase inflector de rails para poder ‘ singularizar permite convertir ‘Posts’ en ‘Post’ para cualquier modelo o controlador. controlador.
1
watch( watch (%r{^app/views/(.+)/.+} %r{^app/views/(.+)/.+}) ) { |m| "test/controllers/ "test/controllers/#{ #{m m[1]}_controller_test.rb
Esta línea correra el el test de controlador si cambia alguna de las vistas asociadas.
1
watch( watch (%r{^test/fixtures/(.+)\.yml} %r{^test/fixtures/(.+)\.yml}) ) { |m| "test/models/ "test/models/#{ #{m m[1]. ].singularize singularize} }_test.
Esta línea ejecutará los tests de modelo si los fixtures cambian. Aquí es donde usamos la clase inflector de rails
1
watch( watch (%r{^test/fixtures/(.+)\.yml} %r{^test/fixtures/(.+)\.yml}) ) { |m| "test/controllers/ "test/controllers/#{ #{m m[1]}_controller_t
Esta línea ejecutará los tests de controlador si los fixtures cambian.
Configurar Minitest-Repor Minitest-Reporters ters Antes de continuar con Guard vamos a configurar nuestro 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 2 3 4 5 6
ENV['RAILS_ENV' ENV[ 'RAILS_ENV'] ] ||= 'test' require File File. .expand_path expand_path( ('../../config/environment' '../../config/environment', , __FILE__) require 'rails/test_help' require "minitest/reporters" # línea que hay que añadir # ... resto del archivo omitido
reporters s y configurarlo 2. Iniciar minitest reporter
En el mismo archivo que en el paso anterior añadimos lo siguiente:
1 2 3 4 5 6 7
# ... código omitido class ActiveSupport ActiveSupport: ::TestCase # ... código omitido Minitest: Minitest ::Reporters :Reporters.use .use! ! Minitest Minitest: ::Reporters :Reporters: ::SpecReporter :SpecReporter. .new end
La primera parte Minitest::Reporters.use! le dice a nuestro test_helper que usaremos minitest-reporters. La segunda parte Minitest::Reporters::SpecReporter.new queremos usar, en este caso SpecReporter.
le dice que estilo de reporte
Para que vean la diferencia: Sin minitest-reporte minitest-reporters rs
1 2 3 4 5 6 7 8 9
Run options: --seed 34387 # Running: ... Finished in in 0.327633s, 0.327633s, 70.2005 runs/s, 109.8791 assertions/s. 3 runs, 3 assertions, 0 failures, 0 errors, 0 skips
Con minitest-repor minitest-reporters ters
1 2 3 4 5 6 7 8 9 10 11 12 13
Started with run options --seed 28340 ProductsControllerTest test_should_get_new
PASS (0.19s)
ProductTest test_should_not_create_product_withoud_description
PASS (0.00s)
UserTest test_user_owns_products
PASS (0.02s)
Finished in in 0.34634s 0.34634s 3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
Correr Guard para automatizar el testing Ahora que nuestro nuestro Guardfile esta completo completo solo falta ejecutarlo desde nuestra línea de comando: comando:
1
bundle exec exec guard 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
13:07:02 - INFO - Guard::Minitest 2.4.4 is running, with Minitest::Unit 5.8.2 ! 13:07:02 - INFO - Running: all tests Started with run options --seed 46327 ProductsControllerTest test_should_show_product test_should_get_edit
PASS (0.18s) PASS (0.03s)
UserTest test_should_not_create_user_withoud_username test_should_not_create_user_without_name
PASS (0.01s) PASS (0.00s)
ProductTest test_should_not_create_product_withoud_description test_price_should_be_a_float
PASS (0.00s) PASS (0.00s)
Finished in 0.31300s 6 tests, 6 assertions, 0 failures, 0 errors, 0 skips 13:07:05 - INFO - Guard is now watching at '/Users/Username/Path/To/Project' [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
[email protected]
La primera vez que nos tratemos de conectar se nos mostrara un mensaje como este:
1 2 3
The authenticity of host 'xx.xx.xx.xx (xx.xx.xx.xx)' can't be established. ECDSA key fingerprint is 79:95:46:1a:ab:37:11:8e:86:54:36:38:bb:3c:fa:c0. 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 2 3
WARNING! Your environment specifies an invalid locale. This can affect your user experience significantly, including the ability to manage packages.
O que al instalar un paquete les muestre esto:
1 2 3 4 5 6 7 8 9 10 11
perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: LANGUAGE = "en_US:en", LC_ALL = (unset), LC_MESSAGES = "en_US.UTF-8", LANG = "en_US.UTF-8" are supported and installed on your system. perl: warning: Falling back to the standard locale ("C"). locale: Cannot set LC_CTYPE to default locale: No such file or directory locale: Cannot set LC_MESSAGES to default locale: No such file or directory 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 2
LC_ALL=en_US.UTF-8 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
[email protected]
*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
[email protected]
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
[email protected]
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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
... gpg: Can't check signature: public key not found Warning, RVM 1.26.0 introduces signed releases and automated check of signatu Assuming you trust Michal Papis import the mpapis public key (downloading the GPG signature verification failed for '/home/deploy/.rvm/archives/rvm-1.26.11. try downloading the signatures: gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804 or if it fails: command curl -sSL https://rvm.io/mpapis.asc | gpg --import the key can be compared with: https://rvm.io/mpapis.asc 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 409B6B1796C275462A17031138
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 561F9B9CAC40B
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/passeng
Para Ubuntu 15.04
1
$ sudo sh -c "echo 'deb https://oss-binaries.phusionpassenger.com/apt/passeng
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 2 3
$ sudo chown root: /etc/apt/sources.list.d/passenger.list $ sudo chmod 600 /etc/apt/sources.list.d/passenger.list $ 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 2
# passenger_root .... # 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 2
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini; 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 dat os 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 2
~/www/example/ ~/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 2 3 4 5 6 7 8
Bienvenidos a Example.com El server block esta funcionando!
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/examp
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; server_name example.com www.example.com; passenger_enabled on; rails_env production; root /home/deploy/www/example;
# redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; }
}
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 2 3 4
$ $ $ $
sudo sudo sudo sudo
apt-get update apt-get install git # necesario para el uso de capistrano apt-get install nodejs # obligatorio tener un ambiente js en el VPS 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
$ ssh deply@ip_del_servidor $ sudo apt-get install paquete $ sudo apt-get update $ sudo apt-get upgrade $ sudo -u postgres psql $ $ $ $
sudo sudo sudo sudo
service nginx service nginx service nginx service nginx
stop start reload restart
$ sudo ln -s ruta/original/archivo ruta/destino $ 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 t endremos 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 t ienes 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 sca !old y vamos a revisar que funcione!
1 2 3 4 5 6
$ $ $ $ $ $
rails new todoapp -d postgresql cd todoapp bundle install rails g scaffold todo name:string finished:boolean rake db:migrate 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 2 3
$ git init $ git add --all $ 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
[email protected]: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 2 3 4 5 6 7 8
group :development do gem 'capistrano' gem 'capistrano-bundler' gem 'capistrano-rails' gem 'capistrano-rvm' gem 'capistrano-passenger' gem 'capistrano-ssh-doctor' 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 2 3 4 5 6 7 8 9
!"" Capfile !"" config #
!"" deploy
#
#
!"" production.rb
#
#
$"" staging.rb
#
$"" deploy.rb
$"" lib $"" capistrano $"" 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 2 3 4 5 6 7 8
!"" Capfile !"" config #
!"" deploy
#
#
#
$"" deploy.rb
$"" production.rb
$"" lib $"" capistrano $"" 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# Load DSL and set up stages require 'capistrano/setup' # Include default deployment tasks require 'capistrano/deploy' # # # # # # # # # # # # # # # # # #
Include tasks from other gems included in your Gemfile For documentation on these, see for example: https://github.com/capistrano/rvm https://github.com/capistrano/rbenv https://github.com/capistrano/chruby https://github.com/capistrano/bundler https://github.com/capistrano/rails https://github.com/capistrano/passenger require require require require require require require
'capistrano/rvm' 'capistrano/rbenv' 'capistrano/chruby' 'capistrano/bundler' 'capistrano/rails/assets' 'capistrano/rails/migrations' 'capistrano/passenger'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Load DSL and set up stages require 'capistrano/setup' # Include default deployment tasks require 'capistrano/deploy' # Include tasks from other gems included in your Gemfile require 'capistrano/rvm' require 'capistrano/bundler' require 'capistrano/rails' require 'capistrano/passenger' require 'capistrano/ssh_doctor' # Load custom tasks from `lib/capistrano/tasks` if you have any defined 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# config valid only for current version of Capistrano lock '3.4.0' set :rvm_type, :user set :rvm_ruby_version, '2.2.2' set set set set set
:application, 'YourApplicationName' :deploy_to, "/home/username/#{fetch(:application)}" :scm, :git :repo_url, '
[email protected]:your-username/your-repository-name.git' :branch, 'master'
set :linked_files, %w{config/database.yml config/secrets.yml .env} set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle pub 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 2 3 4
set :stage, :production set :rails_env, :production server '111.11.111.11', user: 'username', roles: %w{web app db}, primary: tru
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
namespace :setup do desc "Upload database.yml file." task :database do on roles(:app) do execute "mkdir -p #{shared_path}/config" upload! StringIO.new(File.read("config/database.yml")), "#{shared_path} end end desc "Upload secrests.yml file." task :secrets do on roles(:app) do execute "mkdir -p #{shared_path}/config" upload! StringIO.new(File.read("config/secrets.yml")), "#{shared_path}/ end end desc "Upload .env file." task :env do on roles(:app) do execute "mkdir -p #{shared_path}/config" upload! StringIO.new(File.read(".env")), "#{shared_path}/.env" end end desc "Seed the database." task :seed do on roles(:app) do within "#{current_path}" do with rails_env: :production do execute :rake, "db:seed" end end end end 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 2 3 4 5 6 7 8 9 10 11 12 13
namespace :clean do desc 'Runs rake assets:clobber on server to remove compiled assets' task :assets do on roles(:app) do within "#{current_path}" do with rails_env: :production do execute :rake, 'assets:clobber' execute :touch, release_path.join('tmp/restart.txt') end end end end 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 2 3 4 5 6 7 8 9 10 11 12 13 14
namespace :deploy do desc "Makes sure local git is in sync with remote." task :check_revision do unless `git rev-parse HEAD` == `git rev-parse origin/master` puts "WARNING: HEAD is not the same as origin/master" puts "Run `git push` to sync changes." exit end end before :deploy, "deploy:check_revision" 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 "
[email protected]"
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 2
Enter passphrase (empty for no passphrase): [Type a passphrase] 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 2 3 4
Your identification has been saved in /Users/you/.ssh/id_rsa. Your public key has been saved in /Users/you/.ssh/id_rsa.pub. The key fingerprint is: 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db
[email protected]
3. Copiar la llave publica de nuestro servidor Para copiar la llave en ubuntu tendremos que installar
1
xclip
$ 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 2 3 4 5
![Timezone 1](https://dl.dropboxusercontent.com/s/0j63ztcppxkr0am/github-01.png ![Timezone 1](https://dl.dropboxusercontent.com/s/9957iuj75a1zw8a/github-02.png ![Timezone 1](https://dl.dropboxusercontent.com/s/ol0c5ms7igichqv/github-03.png
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 2 3
$ git add --all $ git commit -m 'Add Capistrano and custom task' $ 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 2 3 4 5 6 7
cap cap cap cap cap cap cap
deploy deploy:check setup:database setup:secrets setup:env setup:seed ssh:doctor
# # # # # # #
Deploy a new release Check required files and directories exist Upload database.yml file Upload secrests.yml file Upload .env file Seed the database 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 2 3 4 5 6 7 8 9 10 11 12
!"" current -> /var/www/my_app_name/releases/20150120114500/ !"" releases #
!"" 20150080072500
#
!"" 20150090083000
#
!"" 20150100093500
#
!"" 20150110104000
#
$"" 20150120114500
!"" repo #
$""
!"" revisions.log $"" shared $""
/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. /shared , encontraremos: Dentro de la carpeta /shared
1 2 3 4 5 6 7 8 9
$"" shared !"" .env !"" config !"" public !"" log !"" tmp !"" bin !"" bundle $"" 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 2 3 4 5
$ $ $ $ $
cap cap cap cap cap
production production production production production
ssh:doctor setup:database setup:secrets setup:env 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í.