Administrador en Rails (Active Admin)

En este post les voy a mostrar un mini-tutorial de como usar Active Admin para tener un dashboard, con autenticación para administrar recursos.

¿Qué es Active Admin?

Active admin es una gema de Ruby que nos sirve para crear dashboards de administración de modelos que tengamos en Rails. Si bien podríamos decir que haciendo un scaffold de Rails tenemos practicamente un admin listo, esta gema es muchísimo más completa, por ejemplo tiene autenticación con Devise, filtros, export de archivos a CSV, XML, JSON, Scopes para filtrar, indices, etc.

En este tutorial solo vamos a hacer un pequeño proyecto, sin ninguna configuración extra, para mostrar lo rápido y fácil que es crear un dashboard.

Setup del Proyecto

Vamos a crear un proyecto nuevo de Rails, con el clásico ejemplo de Libros y Autores. Lo vamos a llamar Books.

rails new books -d postgresql

Una vez que tenemos nuestro proyecto vamos a crear los modelos y la migración que nos general la relación entre Libro y Autor.

rails generate model book title:string description:text
rails generate model author name:string lastname:string
rails generate migration AddAuthorRefToBooks author:references

En este punto, tenemos que ir a los modelos que se nos crearon y agregar:

En el modelo de Autor: has_many :books, dependent: :destroy

En el modelo de Libros: belongs_to :author

En este punto podemos probar que las migraciones funcionen y podemos abrir una consola de Rails para probar que podemos crear Libros y Autores.

rails db:create db:migrate
rails c
> Author.create(name:'Juan', lastname:'Perez')
> Book.create(title: 'El Libro', description: 'Todo lo que hay que saber', author_id:1)

Agregamos Active Admin

Lo que tenemos que hacer ahora es agregar la gema a nuestro Gemfile. Tambien tenemos que agregar Devise que va a ser la gema que nos maneje la autenticación y es una dependencia de ActiveAdmin.

bundle add activeadmin
bundle add devise

Una vez que tenemos esto, podemos generar el dashboard.

rails generate active_admin:install

Por último ejecutamos las migraciones y también las seeds, ya que tenemos un usuario por defecto para entrar al dashboard

rails db:migrate db:seed

Ahora podemos probar de entrar al dashboard. Ejecutamos el server de Rails.

rails s

Entramos en http://localhost:3000/admin

user: admin@example.com
pass: password
Admin Dashboard

Agregando recursos

Ahora para agregar nuestros recursos para poder administrarlos desde el dashboard

rails generate active_admin:resource Book
rails generate active_admin:resource Author

Si queremos agregar cualquier otro modelo, solo tenemos que cambiar la última parte del comando

rails generate active_admin:resource [Mi-Modelo]

Ahora tenemos que descomentar en los archivos que se autogeneran para permitir que cuando creamos un recurso (Libro o Autor) nos permita enviar los parámetros.

authors.rb
books.rb

Ahora si, ya podemos loguearnos al dashboard y usarlo

Admin Dashboard – Books

Más adelante quizá escriba un post sobre cómo customizar un poco más Active Admin.

Github Actions + Laravel

Hace un tiempo les conté por acá cómo configurar Travis para poder crear un ambiente de integración continua en un proyecto de Laravel.

Y como saben también, hace un tiempo estuve jugando con Github Actions para correr los tests de un proyecto de Rails.

En este post les voy a contar como hacer para configurar Github Actions para ejecutar los tests de Laravel.

El Proyecto

El año pasado en Programación Web II – TUPAR, la materia en la que soy docente, hicimos un proyecto de ejemplo en Laravel, al cual le configuramos Github Actions, para generar un ambiente de Continuous Integration (CI).

El proyecto es muy sencillo, una To Do List, que tiene usuarios comunes y usuarios que son manager. Está todo en este repo de Github.

En cuanto a los tests, tiene bastantes Unit tests y Feature Tests. Tambien agregamos un test de Dusk (browser test) muy sencillo, solo entra y mira el titulo de la página. Este último es solo a efectos de poder ejecutar este tipo de test “visuales” en un ambiente de CI.

¿Qué vamos a configurar?

En principio queremos que corran los unit y feature tests. Luego vamos a crear otro workflow para que corran los tests de Dusk y por último crear uno que nos haga un análisis estático de código.

A su vez, vamos a configurar Codecov, para poder ver visualmente el coverage que tenemos.

Unit Tests y Feature Tests

Si miramos un poco el proyecto, vamos a ver que estamos usando docker-compose para tener varios containers:

  • App: Es donde tenemos PHP
  • Web: El servidor web Nginx
  • Database: La base de datos Postgres
  • Adminer: App para poder acceder a la base.
  • Selenium: Donde vamos a tener Chrome para ejecutar los Dusk Tests

Mirando esto lo primero que vamos a necesitar para correr Unit y Feature tests es una base de datos. Por lo tanto en nuestra acción vamos a necesitar un servicio de Postgres corriendo.

Configurar Postgres

Cuando agregamos el servicio de Postgres, tenemos que configurar el usuario, la password y el nombre de la base de datos. También mapeamos el puerto de Postgres al mismo puerto de la máquina host. De esta forma, donde en el container que genera Github, vamos a poder usar la base de datos.

Un dato importante acá es la variable de entorno DB_HOST. Usen 127.0.0.1, en lugar de localhost. (Alguna vez esto me trajo problemas)

Con respecto al servicio de Postgres se agrega una línea más, que sirve para saber si el servicio está listo para atender requests. No es completamente necesario, pero puede arreglarte algún que otro lío.

Steps

Luego, lo que tenemos que agregar son los steps que vamos a necesitar.

  1. Hacer checkout del codigo, este paso nos va a traer el código al container donde se van a ejecutar los tests.
  2. Crear el .env de Laravel para que la solución funcione. Aca es importante que tengamos un .env.example en nuestro proyecto con todo pre-configurado. Esto nos sirve tanto para usarlo en Github Actions o también para cualquier miembro de nuestro equipo pueda crearse un .env muy rapido y facil. Hay que recordar siempre de hacer los ajustes en ese archivo además de en nuestro .env, ya que este último archivo no se commitea en el repo.
  3. Instalar las dependencias usando composer
  4. Generar una key para que nuestra app funcione. Esta key es la que se usa para todo lo que tenga que ver con encripción en nuestra app.
  5. Actualizamos permisos de carpetas donde van a crearse algunos archivos, para que cualquier usuario pueda escribir.
  6. Ejecutamos los tests
  7. Subimos los resultados a Codecov.

name: Laravel
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
laravel-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: thisisasecretpassword
POSTGRES_DB: tasks_test
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
DB_USERNAME: myuser
DB_PASSWORD: thisisasecretpassword
DB_HOST: 127.0.0.1
steps:
uses: actions/checkout@v2
name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Generate key
run: php artisan key:generate
name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
name: Execute tests (Unit and Feature tests) via PHPUnit
run: vendor/bin/phpunit –coverage-clover=coverage.xml
uses: codecov/codecov-action@v1
view raw laravel.yml hosted with ❤ by GitHub

Dusk Tests

GitHub - JoseVte/laravel-dusk-5.1

Para los Dusk Tests, qué son los que corren en el browser, vamos a tener que agregar algunas partes más.

En primer lugar, tenemos que agregar la variable de entorno APP_URL, para que cuando ejecutemos los tests, Dusk sepa dónde tiene que ir a abrir la app en el browser.

Steps

La primer diferencia con el workflow de la sección anterior es que como tenemos una configuración para ejecutar Dusk localmente, lo que tenemos que hacer es borrarla para que no interfiera con la configuración en Github Actions.

Luego los steps son prácticamente los mismos, se suman estos:

  • Ejecutar las migraciones, esto nos va a permitir que nuestra app funcione correctamente.
  • Actualizar el Plugin de Chrome, con este paso actualizamos el binario que vamos a usar de Chrome.
  • Le damos más permisos a la carpeta de binarios de Dusk para que se puedan ejecutar.
  • Levantamos el server de artisan para poder acceder a nuestra app. Redirigimos toda la salida a /dev/null para que no interfiera con el output de nuestros tests. Es muy importante el & del final, para poder ejecutar en paralelo los siguientes steps.
  • Ejecutamos Chrome, también agregamos el & al final.
  • Ejecutamos los tests de Dusk
name: Dusk Tests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
dusk-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: thisisasecretpassword
POSTGRES_DB: laravel
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
DB_USERNAME: myuser
DB_PASSWORD: thisisasecretpassword
APP_URL: http://127.0.0.1:8000
steps:
uses: actions/checkout@v2
name: Copy .env
run: php -r "copy('.env.example', '.env');"
name: Remove .env.dusk.local
run: rm .env.dusk.local
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Generate key
run: php artisan key:generate
name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
name: Run Migrations
run: php artisan migrate
name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome –version | cut -d " " -f3 | cut -d "." -f1`
name: Change permissions to dusk
run: chmod -R 0755 vendor/laravel/dusk/bin/
name: Run Laravel Server
run: php artisan serve > /dev/null 2>&1 &
name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &
name: Execute dusk tests
run: php artisan dusk
view raw dusk.yml hosted with ❤ by GitHub

Análisis estático de código (Phan)

Por último, podemos crear otro workflow para chequear el estilo de código que tenemos.

Este es mucho más sencillo, porque al ser un análisis estático de código, no necesitamos tener la app corriendo. Solo necesitamos ejecutar Phan

name: Static Code Analysis
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
code-analysis:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v2
name: Install Dependencies
run: composer install -q –no-ansi –no-interaction –no-scripts –no-progress –prefer-dist
name: Run Phan
run: vendor/bin/phan –no-progress-bar –allow-polyfill-parser –output-mode checkstyle | vendor/bin/cs2pr –graceful-warnings –colorize | true
view raw static.yml hosted with ❤ by GitHub

Espero que les sirva y puedan usarlo en sus proyectos Laravel.

Real Time Apps con Hotwire

Durante Diciembre DHH (creador Ruby on Rails, founder de Basecamp) estuvo twitteando mucho sobre algo que estaban desarrollando para su nuevo proyecto HEY y que iban a hacer release a la comunidad. Efectivamente el 22 de Diciembre lo liberaron y dieron a conocer el nombre Hotwire.

https://twitter.com/dhh/status/1341420143239450624

Como siempre en el mundo de la tecnología, todo vuelve y poder hacer server side rendering y mandar HTML “behind the scenes” parece ser lo nuevo.

En esta “nueva movida”, estoy muy de acuerdo con el amigo DHH, mantener todo un backend con toda una lógica, y tener que replicar ciertas cosas en un frontend de React/Angular/Vue o cualquier cosa que se les ocurra, es en muchísimo trabajo. Si bien creo que en ciertos casos está bueno y vale la pena el esfuerzo, muchas de las aplicaciones de hoy en día se podrían simplificar usando este “nuevo” approach.

Ahora si, sin más preámbulos veamos un poco de que se trata

¿Cómo funciona Hotwire?

Pueden leer mucho en la web, y me tomo el atrevimiento de hacer una traducción con mis palabras

Hotwire es un approach alternativo para construir aplicaciones web sin usar mucho Javascript, se envia HTML, en lugar de JSON a través del cable. Esto hace que las páginas carguen más rápido, mantienen el renderizado de los templates del lado del servidor y permite una experiencia de desarrollo más simple y productiva, sin sacrificar velocidad y experiencia asociada a una Single Page App.

Hotwire utiliza 3 cosas Turbo, Stimulus y Strada (al momento de escribir este post Strada aun no esta disponible).

El 80% de las interacciones se hace con Turbo, por lo tanto es el corazón de Hotwire. Si ya tenían experiencia con TurboLinks, es el mismo proyecto que cambió de nombre y está ahora generalizado para formularios. Turbo se usa para hacer stream de actualizaciones parciales de la página sobre WebSockets. Todo esto sin escribir nada de Javascript.

Cuando necesitamos hacer algo personalizado, ahi podemos usar Stimulus. Este es un framework de Javascript para el HTML que ya tenés, por lo que poniendo un poco de código en el HTML que ya tenés, se crean componentes Javascript. Esto no lo vamos a usar en esta prueba que voy a mostrar luego. Probablemente escriba sobre esto más adelante.

Por último Strada, parece que va a estar más relacionado con aplicaciones para dispositivos móviles.

Pueden leer más a fondo cómo funciona en el Handbook.

Manos a la obra

El ambiente de desarrollo

Vamos a usar lo mismo que en el post de Rails + Postgres, pero a nuestro docker-compose le vamos a agregar Redis, que lo necesitamos para configurar Action Cable.

version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
VARIANT: "2.7"
NODE_VERSION: "lts/*"
volumes:
# Update this to wherever you want VS Code to mount the folder of your project
..:/workspace:cached
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db
db:
image: postgres:latest
restart: unless-stopped
volumes:
postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
redis:
image: redis:latest
volumes:
postgres-data:

Además de modificar el archivo database.yml, para conectar a Postgres, tenemos que cambiar cable.yml para que user redis (que es el nombre del servicio en el docker-compose) como url.

development:
adapter: redis
url: redis://redis:6379/1
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://redis:6379/1" } %>
channel_prefix: BeerStyles_production
view raw cable.yml hosted with ❤ by GitHub

El Proyecto

En muchos de los posts que estuve leyendo, para mostrar como funciona Hotwire, se usa un proyecto de un clone de Twitter.

En lugar de eso, a mi me gustaría hacer algo super sencillo que es una aplicación para crear estilos de cervezas.

Para arrancar creamos el proyecto, vamos a usar Postgres.

rails new BeerStyles -d postgresql

Ahora agregamos a nuestro gem file hotwire-rails

bundle add hotwire-rails

Una vez que tenemos esto vamos a configurar Hotwire, para esto tenemos una rake task que nos configura todo y nos pone el boilerplate para usarlo.

rails hotwire:install

Una vez que tenemos esto vamos a crear el scaffold para el estilo.

rails g scaffold Style name:string description:string

Ahora chequeamos que tengamos la configuración del punto anterior en cable.yml y database.yml.

No se olviden de crear la base de datos y correr las migraciones

rails db:create db:migrate

Por último probamos que nuestro proyecto funcione

rails s

Entramos entonces a http://localhost:3100/styles.

Usando Hotwire

Refactor previo

Vamos a modificar un poco lo que nos generó Rails para poder adaptarlo mejor a Hotwire.

Este es el código que nos generó Rails para app/view/styles/index.html.erb

<p id="notice"><%= notice %></p>
<h1>Styles</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @styles.each do |style| %>
<tr>
<td><%= style.name %></td>
<td><%= style.description %></td>
<td><%= link_to 'Show', style %></td>
<td><%= link_to 'Edit', edit_style_path(style) %></td>
<td><%= link_to 'Destroy', style, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Style', new_style_path %>
view raw index.html.rb hosted with ❤ by GitHub

Vamos a hacer un pequeño refactor. Dejamos de usar una tabla y migramos todo el código que muestra cada estilo a su propio template.

Creamos app/view/styles/_style.html.rb

<div class="card m-4">
<div class="card-title">
<%= style.name %>
</div>
<div class="card-body">
<%= style.description %>
</div>
<div class="card-footer bg-transparent border-success">
<%= link_to 'Edit', edit_style_path(style) %>
<%= link_to 'Destroy', style, method: :delete, data: { confirm: 'Are you sure?' } %>
</div>
</div>
view raw _style.html.erb hosted with ❤ by GitHub

Actualizamos app/view/styles/index.html.erb

<p id="notice"><%= notice %></p>
<h1>Styles</h1>
<%= render @styles %>
<%= link_to 'New Style', new_style_path %>
view raw index.html.erb hosted with ❤ by GitHub

Turbo Streams

Para que funcione Hotwire, y exista una comunicación con todos los clientes que están viendo la aplicación, vamos a usar Turbo Streams.

Lo que hacemos es colgarnos de los eventos del modelo create, update y destroy. Cuando algo de eso pasa en el modelo, tomamos alguna acción en el stream.

Vamos a modificar el modelo de Styles app/models/style.rb

class Style < ApplicationRecord
after_create_commit {broadcast_prepend_to "styles"}
after_update_commit {broadcast_replace_to "styles"}
after_destroy_commit {broadcast_remove_to "styles"}
end
view raw style.rb hosted with ❤ by GitHub

Ahora cada vez que pase algo en el modelo, Rails va a enviar un mensaje al canal styles. Por ejemplo, si se crea un nuevo estilo, se va a agregar `brodcast_prepend_to`.

Ahora tenemos que suscribirnos a ese canal, entonces vamos a modificar, app/view/styles/index.html.erb.

En este caso vamos a agregar un turbo_stream_from con el que vamos a establecer la conexión entre el WebSocket y el stream de styles. Después envolvemos la lista de estilos con turbo_frame_tag, esto nos va a permitir parchear el DOM, con lo que se publique en el stream de styles desde el backend

<p id="notice"><%= notice %></p>
<h1>Styles</h1>
<%= turbo_stream_from "styles" %>
<%= turbo_frame_tag "styles" do %>
<%= render @styles %>
<%end%>
<%= link_to 'New Style', new_style_path %>
view raw index.html.rb hosted with ❤ by GitHub

También vamos a actualizar app/view/styles/_style.html.erb envolviendo todo dentro de un nuevo turbo_frame_tag, pero esta vez agregando además el id del estilo.

Es importante usar la función dom_id(style), porque si ponemos sólo style nos va a poner como ID la dirección de memoria de nuestro estilo. Este cambio lo hacemos para poder referenciar cada estilo en el DOM y poder modificarlos o borrarlos.

<%= turbo_frame_tag dom_id(style) do %>
<div class="card m-4">
<div class="card-title">
<%= style.name %>
</div>
<div class="card-body">
<%= style.description %>
</div>
<div class="card-footer bg-transparent border-success">
<%= link_to 'Edit', edit_style_path(style) %>
<%= link_to 'Destroy', style, method: :delete, data: { confirm: 'Are you sure?' } %>
</div>
</div>
<% end %>
view raw _style.html.rb hosted with ❤ by GitHub

Ahora lo que podemos hacer es probarlo, abrimos una consola de Rails en la terminal de VS Code.

rails c

Y desde ahi podemos crear, borrar y editar estilos y ver como sin refrescar el browser se actualizan.

Edicion inline

Para hacer esto, vamos a hacer un pequeño refactor de app/views/styles/edit.html.rb.

Envolvemos nuevamente en un turbo_frame_tag el form, borramos los links que teníamos y creamos un nuevo link de cancel.

<h1>Editing Style</h1>
<%= turbo_frame_tag dom_id(@style) do %>
<%= render 'form', style: @style %>
<%= link_to 'Cancel', styles_url %>
<% end %>
view raw edit.html.rb hosted with ❤ by GitHub

También tenemos que cambiar en el app/controllers/styles_controller.rb, el lugar a donde redirige cuando hacemos una actualización de un estilo. Buscamos el método update y modificamos la redirección en caso de que el update fue exitoso. También actualizamos la rama del if en el caso de que no sea existo, agregando una línea para que nos pueda dar el estilo que se quiso crear y podamos mostrar las validaciones.

def update
respond_to do |format|
if @style.update(style_params)
format.html { redirect_to styles_url, notice: "Style was successfully updated." } # Cambió el redirect_to
format.json { render :show, status: :ok, location: @style }
else
#Agrego esta linea
format.turbo_stream { render turbo_stream: turbo_stream.replace(@style, partial: "styles/form", locals: { style: @style}) }
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @style.errors, status: :unprocessable_entity }
end
end
end

Ahora cuando editamos un estilo se va a desplegar el formulario de actualización en la misma lista de estilos.

Crear nuevos estilos

Para poder crear estilos, vamos a agregar el formulario de creación en el archivo de app/views/styles/index.html.erb (linea 5)

<p id="notice"><%= notice %></p>
<h1>Styles</h1>
<%= turbo_stream_from "styles" %>
<%= render "styles/form", style: @style %>
<%= turbo_frame_tag "styles" do %>
<%= render @styles %>
<%end%>
<%= link_to 'New Style', new_style_path %>
view raw index.html.rb hosted with ❤ by GitHub

Tenemos que hacer algunos cambios en el controller. Hay que crear una variable @style en el método index y también hacer los mismos cambios que hicimos en el método update, ahora en el método create.

def index
@styles = Style.all
@style = Style.new
end
def create
@style = Style.new(style_params)
respond_to do |format|
if @style.save
format.html { redirect_to styles_url, notice: "Style was successfully created." }
format.json { render :show, status: :created, location: @style }
else
format.turbo_stream { render turbo_stream: turbo_stream.replace(@style, partial: "styles/form", locals: { style: @style}) }
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @style.errors, status: :unprocessable_entity }
end
end
end

Ahora si tenemos un CRUD completo, utilizando Hotwire, de esta forma podemos hacer sitios mucho más dinámicos sin necesidad de tener un frontend hecho en Angular, React o agregando toneladas de javascript.

Si quieren ver el código del proyecto se los dejo acá:

ignaciojonas/hotwire-test

Application to Test Hotwire This app is a default Rails example to CRUD beer styles using Hotwire. There is a step by step guide in my blog (Spanish). In order to make this app easily run, you can use VSCode and Remote Containers. After reopen the folder in the container, just excute in the terminal.

Espero que puedan empezar a usar Hotwire en sus proyectos.

Nos leemos!

Github Actions + Rails

Integración y Distribución Continua (CI/CD) con GitHub Actions - Thinking  In Swift

En este post quería mostrarles un poco de la potencia y lo fácil que es usar Github Actions, pero primero…

¿Qué es Github Actions?

Si leemos lo que dice la web nos encontramos con esto:

Automatiza, personaliza y ejecuta tus flujos de trabajo de desarrollo de software directamente en tu repositorio con GitHub Actions. Puedes descubrir, crear y compartir acciones para realizar cualquier trabajo que quieras, incluido CI/CD, y combinar acciones en un flujo de trabajo completamente personalizado.

Pagina de Github Actions

En definitiva nos permite automatizar cosas en nuestro Repo. No solo continuous integration, sino manejo de issues en el repo, darle la bienvenida a los nuevos usuarios, y muchísimas cosas más.

Hubo un hackaton creado en Dev.to para que los devs puedan crear acciones en diferentes categorías. Algunas de las cosas que surgieron fueron muy locas.

Se pueden hacer mil cosas, desde jugar a un juego a mandar un mensaje por telegram. Nosotros lo vamos a usar para tener un ambiente de CI en nuestra aplicación Rails.

¿Qué vamos a hacer?

Vamos a partir de un proyecto de Rails que hice hace algún tiempo que se llama Beer API.

Esta aplicación es una Api muy sencilla que hice para hacer algunas pruebas de diferentes frontends, con diferentes tecnologías, usando la misma API.

Este proyecto tiene corriendo algunos tests de Rspec.

Workflow en Github Actions

Lo primero que tenemos que hacer en nuestro proyecto de Github es hacer click en Actions.

Ahora buscamos en la sección Continuous integration workflows la acción que más nos convenga. En este caso Ruby y hacemos click en Set up this workflow.

Esto nos abre un editor con un archivo pre-creado.

Modifiquemos el workflow

name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
rspec:
name: Rspec
runs-on: ubuntu-latest
services:
postgres:
image: postgres:11
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
5432:5432
options: –health-cmd pg_isready –health-interval 10s –health-timeout 5s –health-retries 5
env:
POSTGRES_HOST: 127.0.0.1
steps:
uses: actions/checkout@v2
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7.2
name: Install Dependencies
run: |
gem install bundler
bundle install
name: Run Migrations
run: bundle exec rails db:create db:migrate RAILS_ENV=test
name: Run Rspec
run: bundle exec rspec
view raw main.yml hosted with ❤ by GitHub

Como yo no necesitaba la estrategia de Matrix, para probar esta api en múltiples versiones de Ruby, borre todo lo relacionado a eso y solo deje una Ruby 2.7.

Lo primero que hice fue cambiar el name por CI (acronimo para Continuous Integration).

Luego en la sección de jobs cree uno que se llama rspec, que tiene el servicio de postgres. Esto lo que va a hacer es levantar un container con Postgres, para que desde el container donde se ejecutan los tests pueda usarlo y ejecutarlos usando una base de datos.

Agregué una sección de options, esto sirve para que los tests ejecuten recién cuando el servicio de la base de datos esté funcionando y no nos fallen los tests porque no se puede conectar. Esto es una configuración que es necesaria para evitar fallas del CI aleatorias.

Luego en la sección env, configuré el host donde va a correr postgres, en este caso 127.0.0.1. En esto tuve algún problema, si no lo ponia o si ponia localhost, por eso les recomiendo agregarlo.

Por último la sección de steps, los primeros son los que vienen por default (el de setup de ruby es un poco distinto porque no uso la matrix) y después agregue:

  • Install Dependencies: Ejecuto bundle install para traer todas las gemas que necesito.
  • Run Migrations: Creo la base de datos y ejecuto las migraciones
  • Run rspec: Ejecuto los tests.

Más Jobs

En este mismo workflow yo podría poner múltiples jobs, en el caso de Beer API, agregué un job que corre Rubocop, para el estilo del código.

Para eso en la sección de jobs agregue uno nuevo que se llama rubocop con la siguiente configuración.

jobs:
rubocop:
name: Rubocop
runs-on: ubuntu-18.04
steps:
uses: actions/checkout@v1
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7.2
name: Install Rubocop
run: |
gem install rubocop
gem install rubocop-rails
gem install rubocop-rspec
name: Run Rubocop
run: rubocop
view raw main.yml hosted with ❤ by GitHub

Para verlo funcionando solo nos queda hacer commit de nuestro archivo de configuración y listo.

Ahora cada vez que se abra un Pull Request, o haga un Merge a main, se van a ejecutar los tests y además el chequeo estático de la sintaxis.

Espero que les sirva para tener mejores prácticas en sus proyectos.

Nos leemos.

Usando Magic Test en Rails

Ayer un amigo me paso un tweet con un screencast que me pareció increíble.

JavaScript is not available.

Wow, I didn’t expect that last video to generate so much response. The truth is, that was just one very small part of what @adampallozzi and I created. Here’s the real thing. We’re calling it Magic Test. 1/2 pic.twitter.com/TiR0PqPgYI

Andrew Culver y Adam Pallozzi crearon la gema Magic Tests, que como se ve en el video, nos puede dar un montón de productividad para crear tests.

Tiene 3 partes que funcionan en conjunto, la primera es la consola, donde ejecutamos los tests, la segunda es el browser, dónde podemos ir haciendo click y la última es el editor donde se va a ir creando nuestro test.

Proyecto de Prueba

Para probar Magic Tests, lo único que hice fue crear un proyecto de Rails 6.1, que usa Postgres y le genere un scaffold de Productos. Siguiendo los siguientes pasos:

rails new testMagicTests -d postgresql
rails g scaffold Products name:string description:string price:integer
rails db:create db:migrate

Por último modifique el archivo routes.rb para que la root_url sea la página de productos. Para eso simplemente agregamos:

root 'products#index'

En este punto podemos probar que nuestra aplicación funcione ejecutando:

rails s

Si accedemos a http://localhost:3000/ deberíamos ver algo como esto

Configurando Magic Tests

Vamos a agregar en nuestro Gemfile, dentro del grupo test la gema magic_tests. A modo de ejemplo les pongo el grupo test completo de mi Gemfile.

group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 3.26'
gem 'selenium-webdriver'
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
gem 'magic_test'
end
view raw Gemfile hosted with ❤ by GitHub

Ahora hacemos bundle install y a continuación vamos a ejecutar el generador de Magic Tests.

rails g magic_test:install

Esto nos va a crear un test en test/system/basics_test.rb.

Una cosa que tenemos que agregar manualmente en views/templates/application.html.rb es

 <%= render 'magic_test/support' if Rails.env.test? %>

Esto no se hace automático, por que es muy dificil de hacerlo, según dicen en el repo de Magic Tests.

El archivo les debería quedar algo así:

<!DOCTYPE html>
<html>
<head>
<title>TestMagicTest</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= render 'magic_test/support' if Rails.env.test? %>
</head>
<body>
<%= yield %>
</body>
</html>

Con esto ya tenemos todo lo que necesitamos, ahora vamos a probar como funciona.

Creando nuestro primer test

Para prepararnos vamos a abrir el archivo test/system/basics_test.rb. en el editor de código que usamos y luego ejecutamos el siguiente comando en la consola.

MAGIC_TEST=1 rails test:system test/system/basics_test.rb

Esto nos va a abrir el browser y en un momento se va a parar la ejecución como si estuviéramos debuggeando. Para ver bien donde estamos parados tenemos que escribir en la consola el comando up y apretar enter.

Ahora vamos al browser y usamos la aplicación haciendo los pasos que queremos que se graben en el test. En mi caso voy a crear un Producto.

Para crear assertions (recuerden que un tests sin assertions no es un test), tenemos un atajo de teclado CTRL + SHIFT + A.

Lo que tenemos que hacer es seleccionar lo que queremos que verificar que aparezca en la pantalla cuando estamos ejecutando el test y luego apretar el atajo. Por ejemplo “Product was successfully created” .

Si quisiéramos podemos seguir probando más cosas y todo lo que hagamos en el browser se va a seguir grabando.

Una vez que terminamos, vamos a la consola, escribimos flush y apretamos enter. Este comando va a tomar todo lo que hicimos en el browser y crear los pasos en el test como por arte de mágia.

Salida en la consola después de ejecutar flush

Y en nuestro test se autocompletó todo 🤯

Ultimos pasos

Solo nos queda borrar las últimas 2 líneas del test. (en la imagen líneas 13 y 14).

La palabra up, creo que queda de cuando la escribimos en la consola para ver donde estábamos. Y magic_test es el método que nos permite colgarnos en el test para poder generar los pasos usando esta gema.

Ahora cada vez que quieras grabar un test o actualizar uno usando esta gema, solo tenés que escribir dentro del test magic_test y en ese punto se va a parar la ejecución para que puedas interactuar con el browser y grabar los pasos que vayas haciendo.

Espero que lo puedan usar en sus proyectos y si bien es una gema muy nueva, ojalá que tenga aceptación en la comunidad.

Nota: No usé el ambiente de Docker que mostré en este post, porque no podríamos estar interactuando con el browser.

Ambientes de Desarrollo Docker (Rails + Postgres)

Vamos a darle vuelta más de tuerca a nuestro ambiente de desarrollo de Ruby on Rails.

Algo más que común, es el uso de una base de datos en una aplicación web. Es algo que en la mayoría de los proyectos vamos a usar.

Como vimos en el post anterior, armamos un ambiente de desarrollo, usando la extension Remote Containers de Visual Studio Code. Pero solo teníamos el container que tiene la aplicación y usábamos SQLite como base de datos.

En este post vamos a ir un paso más y crear un ambiente con múltiples containers. La aplicación y la base de datos. Esto mismo se puede usar en caso que necesitemos Redis, o cualquier otro servicio corriendo en nuestra aplicación.

Al momento de escribir este post hay un pull request en el proyecto de los templates de Remote Containers con una configuración parecida a la que te voy a mostrar.

Probablemente cuando esto se integre al proyecto va a ser más fácil elegir este template en la lista como elegimos el de Ruby on Rails.

Manos a la obra

Para arrancar vamos a necesitar todo lo mismo que les conté en el post anterior.

Ahora, en lugar de elegir el template Ruby on Rails, vamos a elegir Docker from Docker Compose.

Esto nos va a agregar varios archivos, que vamos a cambiar casi todos y borrar algunos.

La carpeta library-scripts la borramos y el resto de los archivos los vamos a modificar.

devcontainer.json

En este archivo lo que cambio es:

  • name: Ponerle un nombre más representativo en este caso Ruby on Rails + Postgres
  • extensions: Poner rebornix.Ruby
  • fowardPorts: Agregar el 3000 que es donde corre Rails.
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/docker-from-docker-compose
{
"name": "Ruby on Rails + Postgres",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"rebornix.Ruby"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3000],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "docker –version",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

Dockerfile

Lo modifico totalmente, uso en su lugar el que se creó cuando armamos el ambiente de Ruby on Rails sin base de datos.

# [Choice] Ruby version: 2, 2.7, 2.6, 2.5
ARG VARIANT=2
FROM mcr.microsoft.com/vscode/devcontainers/ruby:0-${VARIANT}
# Install Rails
RUN gem install rails webdrivers
ARG NODE_VERSION="lts/*"
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install –no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install additional gems.
# RUN gem install <your-gem-names-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
view raw Dockerfile hosted with ❤ by GitHub

docker-compose.yml

En este archivo es donde pasa la magia :).

Vamos a agregar 2 servicios:

  • App: Este es el mismo container que usábamos antes, que está definido en el Dockerfile, acá vamos a correr nuestra aplicación.
  • Db: Este es el container de la base de datos.

Nota: Como te decía más arriba aca podrias poner los servicios que quisieras, Redis, adminer, etc.

version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
VARIANT: "2.7"
NODE_VERSION: "lts/*"
volumes:
# Update this to wherever you want VS Code to mount the folder of your project
..:/workspace:cached
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db
db:
image: postgres:latest
restart: unless-stopped
volumes:
postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
volumes:
postgres-data:

Turn on the engines

Ahora estamos listos para arrancar los motores, nuevamente buscamos en la opciones de Remote Container, Reopen in Container.

Ahora vamos a crear una aplicación de rails, pero usando postgres

rails new myapp --database=postgresql

En el archivo de configuración de la base de datos (config/database.yml), agregamos el host, user y password a la sección default.

default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: db
user: postgres
password: postgres
view raw database.yml hosted with ❤ by GitHub

Nota: Dentro de Docker para comunicarnos con otros servicios, la URL que usamos es el nombre del servicio. En este caso el host que vamos a usar en nuestra aplicación va a ser db.

Probemos la DB

Para poder probar la base de datos, vamos a crear algo en Rails para usarla. Para eso usamos el generator de Rails, para crear un CRUD, en este caso de Clientes.

rails g scaffold Customer name:string address:string

Ahora si , tenemos migraciones, entonces ejecutamos

rails db:create db:migrate

Ahora si podemos entrar a http://localhost:3000/customers y probar nuestra app, creando, editando y borrando clientes.

Espero que les sirva y lo puedan usar.

Nos leemos!

Ambientes de desarrollo en Docker + VSCode

Hace unos meses que estuve probando y usando un poco una extensión de Visual Studio Code que se llama Remote Containers. La verdad que me pareció super simple, cómodo y muy potente.

Remote Containers te ayuda a tener un ambiente de desarrollo para muchísimas tecnologías en 5 minutos.

Para este ejemplo voy a usar una aplicación de Ruby on Rails default.

¿Qué necesitamos?

Para seguir este mini tutorial necesitas tener instalado:

Setup

Para empezar abrimos VSCode, en una carpeta nueva y vamos a la parte de Extensions, ahí tenemos que instalar la extensión Remote Containers.

Una vez instalada, vamos a poder usarla:

  • Haciendo click en el icono de abajo a la izquierda.
  • Abriendo la consola de comandos (Command Palette) en el menú View o con el comando CMD + Shift + p (en Mac).

Crear el container

Sabemos que vamos a estar trabajando en un proyecto de Ruby on Rails, entonces abrimos la consola de comandos y buscamos: Remote-Container: Add development container…

Una vez dentro de ese menu, buscamos Ruby on Rails y luego la versión de Ruby que queramos usar, al momento de este post, la última disponible es 2.7, aunque ya salió Ruby 3.0.

Esto nos va a crear una carpeta con 2 archivos:

  • devcontainer.json: Donde esta toda la configuración del ambiente.
  • Dockerfile: Es la imagen de Docker que vamos a usar.

Ambos archivos estan muy bien documentados y tienen secciones que se pueden descomentar para poder hacer que se instalen gemas adicionales, paquetes de Node, etc.

En principio, lo único que vamos a hacer, es descomentar en devcontainer.json la parte de forwardPorts. Así vamos a poder conectarnos desde el browser (en nuestra máquina) a la aplicación que va a estar corriendo en Docker. Entonces configuramos el puerto 3000 que es el default en las apps de Rails.

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/ruby-rails
{
"name": "Ruby on Rails",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update 'VARIANT' to pick a Ruby version: 2, 2.7, 2.6, 2.5
"VARIANT": "2.7",
"NODE_VERSION": "lts/*"
}
},
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"rebornix.Ruby"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3000],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "ruby –version",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

Probando el ambiente

Ahora vamos a probar el ambiente, para eso, usando la consola de comandos buscamos Remote-Containers: Reopen in Container

Esto nos va a re-abrir Visual Studio Code y nos va a indicar, donde esta el icono de Remote Containers, si estamos viendo el código dentro de un container.

Ahora si, abrimos la terminal (Ctrl + ` o desde el menu Terminal) desde donde vamos a poder ejecutar comandos dentro del container. Por ejemplo podemos ver que versión de Ruby y de Rails tenemos instalado.

Ahora podemos ejecutar, en esa consola, el comando de Rails para crear una app.

rails new MyApp

O si, como yo, preferís que el proyecto quede al nivel de la carpeta que creaste, anda un nivel para arriba y ejecuta.

rails new [nombre-de-la-carpeta]

Una vez que terminó, entramos a la carpeta del proyecto y ejecutamos rails s para poner a andar el web server.

Entramos en nuestro browser a http://localhost:3000 y voilà, nuestra app de Rails está corriendo dentro del container y podemos usarla desde nuestro browser.

El próximo paso será poder configurar el ambiente para que no use SQLite, sino Postgres o alguna otra base de datos. Pero eso es para otro post.

Espero que lo puedan usar en sus proyectos.

Nos estamos leyendo.

Travis CI + Laravel

En una de las materias que doy en la facultad (Programación Web II – TUPAR), estamos continuando un proyecto para los Bomberos de Trenque Lauquen.

Es un proyecto que comenzaron unos alumnos como trabajo de fin de carrera. Es una aplicación web para administrar el cuartel: materiales, vehículos, bomberos, asistencias, promociones de personal, etc. Si bien aún esta en Alpha, es un buen proyecto para aplicar muchísimos de los conceptos de Programación Web.

Este año decidimos enseñar Docker y Laravel, y uno de los conceptos claves que queremos que los alumnos aprendan es la calidad de código, hacer unit test e integration tests, probar un poco de TDD y tener la experiencia de usar un ambiente de Integración Continua.

La idea de este post es poder poner en forma de tutorial como configurar Travis-CI, para hacer el build de una aplicación escrita en Laravel, con tests de unidad y tests de integración.

Laravel y Testing

Laravel es un framework de PHP, según su página web: The PHP Framework For Web Artisans.

Tiene muchas features muy interesantes, como las migrations, el comando artisan, los templates de Blade, Eloquent, y más.

Uno de los puntos fuertes es lo bien integrado que está con la creación de test alrededor de la solución.

Obviamente que para los tests de unidad, usa PHPUnit, que ya está más que probado y es el standard de la industria. Pero para los tests de integración, usa Dusk, que es una herramienta que automatiza los browser tests y esta integrada con Artisan para correr.

Configurando de Travis

Lo primero que vamos a hacer es crear el archivo travis.yml, este es el archivo que le dice a Travis como tiene que crear el container para poder ejecutar los tests de nuestra aplicación.

.env.travis

No Description

Hasta llegar a este archivo terminado tuve unas cuantas luchas. En primer lugar seguí la explicación de la documentación oficial, sin ninguna suerte. Después de buscar mucho en Google, encontré a un post en Japones, en el cual @sutara_lumpur, explicaba como el había hecho para configurarlo y tenia un repo en Github con la solución andando.

Obviamente le agradecí como corresponde:

Ignacio Jonas on Twitter

@sutara_lumpur Thanks for your code on how to configure @travisci to run #lareveldusk.

Vamos a explicar un poco que dice este archivo, las partes que a mi me generaron más dudas, hay algunas partes que se auto-explican.

  • sudo: required : Para poder ejecutar Chrome necesitamos permisos de root en el container.
  • dist: trusty : Como necesitamos ejecutar Chrome, entonces nuestro container tiene que ser un Linux, en este caso Ubuntu Trusty.
  • services : Vamos a necesitar una base de datos para probar nuestra aplicación, en este caso Postgres.
  • addons:  Necesitamos que nuestro container tenga Chrome instalado, para correr los test de integración.
  • install: Ejecutamos composer install para que instale todas las dependencias.
  • before script:
    • Creamos la base de datos, el nombre tiene que coincidir con lo que luego pongamos en el .env.travis.
    • Creamos el archivo .env, copiando el archivo .env.travis con toda la configuración.
    • Generamos la key para que nuestra aplicación Laravel funcione.
    • Migramos la base de datos y además le cargamos los datos. En este punto podemos obviar el –seed, si es que vamos a regenerar la base por cada test que ejecute.
  • script
    • Ejecutamos Chrome y lo ponemos a escuchar en un puerto, en nuestro caso 9222, este puerto tiene que coincidir con el puerto que usemos en DuskTestCase.php
    • Levantamos el server para poder acceder a nuestra aplicación usando artisan, redirigir el output a /dev/null es opcional. De esta forma nos queda mas limpio el log de Travis.
    • Ejecutamos los tests de integración.
    • Ejecutamos los tests de unidad, y generamos el archivo de coverage
  • after_success
    • Creamos el reporte de coverage.

Ya tenemos nuestro archivo de configuración de Travis. Ahora creemos nuestro archivo .env.travis.


APP_ENV=testing
APP_DEBUG=true
APP_KEY=
APP_URL=http://localhost:8000
DB_CONNECTION=pgsql
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=bomberos_test
DB_USERNAME=postgres
DB_PASSWORD=
CACHE_DRIVER=array
SESSION_DRIVER=file
QUEUE_DRIVER=sync
TRAVIS=true
CHROME_HOST=http://localhost:9515

view raw

.env.travis

hosted with ❤ by GitHub

Este archivo, en nuestro caso, es casi una copia de el .env que tenemos en nuestra maquina local. La diferencia son:

  • DB_DATABASE: Ahora apunta a la base de datos que creamos en Travis.
  • DB_USER: Es el usuario de la base de datos del container de Travis.
  • DB_PASSWORD: Es vacío, porque el default password del usuario postgres en Travis es vacío.
  • TRAVIS: Es para indicar que estamos corriendo en Travis, esto me va a servir para hacer algunos cambios en el archivo DuskTestCase.php.
  • CHROME_HOST: Es la URL donde escucha Chrome.

Por último, hacemos un pequeño cambio en el archivo DuskTestCase.php

Este cambio nos ayuda a poder ejecutar los tests tanto en el ambiente de desarrollo como en Travis.


<?php
...
protected function driver()
{
$options_array = env('TRAVIS', false) ? ['–disable-gpu','–headless','–window-size=1100,600'] : [];
$options = (new ChromeOptions)->addArguments($options_array);
return RemoteWebDriver::create(
env('CHROME_HOST', 'http://selenium:4444/wd/hub'), DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
?>

En nuestro caso tenemos un ambiente de desarrollo en Docker, en el cual tenemos los siguientes containers:

  • app: Corremos PHP.
  • web: Corremos el Server (nginx).
  • database: Corremos Postgres.
  • adminer: Corremos adminer, para poder administrar la Base de Datos.
  • selenium: Corremos Chrome y dentro de este container corremos los test de integración.

Para correr los Tests de Integración, tenemos que usar la URL http://selenium:4444/wd/hub y si pasamos las options de headless, disable-gpu, etc. se hace muy lento, al punto de fallar por timeout.

Una vez que subimos estos cambios y configuramos Travis para que integre nuestro repositorios, la aplicación buildea.

 

Y tenemos el reporte de coverage, de lo que cubren los test de unidad. Como vemos es de un 3%, muy poco, pero ahora que lo podemos medir, lo podemos mejorar.

Espero que les sirva, nos estamos leyendo.

Introducción a Angular 2

Hace unas semanas estuve dando, Junto con @nicotourne,  un curso de Introducción a Angular en la UNICEN como propuesta de seminario para la Tecnicatura Universitaria en Desarrollo de Aplicaciones Informáticas.

El curso estuvo bueno y tuvimos  una muy buena retrospectiva al terminarlo, como para poder mejorar en la próxima iteración.

La idea de este post es comentar algunos highlights que descubrí en clases y compartir el material.

Alumnos del Curso

Material

El material esta basado en un ejemplo de una app de una Cervecería. La home page tiene una lista de cervezas y un carrito de compras. Durante las clases fuimos construyendo la lista de cervezas, explicando a la par los conceptos de Angular.

Los temas fueron:

Algo que sumamos, es un branch de cada slide en el repositorio de GitHub para que los alumnos puedan ver qué cambios se van haciendo con cada nuevo concepto.

También durante la clase @nicotourne, fue aportando todo su conocimiento de Angular en producción, dando tips de buenas prácticas y detalles a tener en cuenta cuando hacemos una app un poco mas grande.

Trabajo Final

El trabajo fue agregar algunas features al proyecto:

  • Filtrado en la tabla de cervezas.
  • Hacer un componente que consuma la API de AccuWeather y muestre el clima de Tandil.
  • Agregar un botón que guarde la compra en Firebase
  • Hacer un componente de Contacto que guarde los mensajes en Firebase.
  • Agregar la opción de Agregar Cervezas a la lista.

Highlights

La dinámica de la clase

Dar clases, siguiendo las slides, codeando la solución en vivo en el proyector y que los alumnos puedan seguirlo e ir haciendolo en sus computadoras.

Esto nos permite tener feedback de los alumnos en el momento, validar si se entendió el concepto o no. En base a esto podemos volver a explicar o avanzar con el siguiente concepto.

Branches con las soluciones

En la retrospectiva surgió, como algo muy bueno, poder acceder a solo la porción de código de ese juego de slides. Los alumnos pudieron usarlo para encontrar más rápidamente la solución a los problemas que les surgían en el trabajo final.

Trabajo final, Fork y Pull Requests

Para el trabajo final, los alumnos tuvieron que hacer un Fork del repositorio del curso y al terminar postear el PR.

Esto nos ayudo a corregir mas rápido, y poder agregar comentarios en el mismo código, aprovechando esto como una instancia más de aprendizaje.

Firebase

Wow! Que buenas cosas que están haciendo con Firebase en Google. Agregarlo al curso fue una excusa para aprender un poquito de esta plataforma. Sin dudas es para seguir aprendiendo y usándolo en otros cursos/seminarios o en la materia Web.

A los alumnos les sirvió para ver como una plataforma, que tiene una interfaz REST, es muy fácil de integrar.

Recibida de la primer alumna de TUDAI

Con la finalización de este curso Lucre, fue la primer graduada de la carrera, así que los compañeros y familia aprovecharon a hacer el clásico ritual de tirarle “Con de Todo”. Felicitaciones Lucre!

Comentarios, ideas, Pull Requests, etc. son más que bienvenidos!

Travis CI – Integración Continua fácil!

 

Hace poco estuve trabajando en un proyecto en Ruby, el cual tenía configurado Travis como servicio de integración continua.

Me llamo muchísimo la atención lo sencilla que es la configuración y la buena documentación que tiene. Con un simple archivo .yml y unas pocas líneas tenemos configurado nuestro servidor de integración continua, que incluso puede correr nuestra suite de tests en diferentes versiones de Ruby paralelamente.

large_Mascot-fullcolor-png

Travis tiene soporte para multiples lenguajes, yo solo lo probé con Ruby, pero estoy seguro que voy a seguir haciendo algunas pruebas más con algún otro lenguaje como PHP, C# o Go.

Una última característica genial es que es gratis para proyectos Open Source.

Como funciona Travis CI

Tenemos que tener una cuenta de Github y entrar a travis-ci.org. Autorizamos a Travis a acceder a nuestros repositorios y voilà todo listo.

2016-03-09_23-56-33

Los pasos a seguir son:

  • Seleccionar el repositorio que queremos, haciendo click el botón que lo habilita.
  • Subir a nuestro repositorio el archivo .travis.yml con la configuración.
  • Hacemos un push a nuestro repositorio y automáticamente se ejecuta el primer build.

Travis.yml

Vamos a usar un ejemplo de un proyecto en Ruby.


language: ruby
sudo: false
cache: bundler
rvm:
2.1.0
2.2.1
before_script:
chmod +x build_travis.sh
script: "./build_travis.sh"

view raw

travis.yml

hosted with ❤ by GitHub

Como ven la configuración es sencilla, con las palabras claves nos damos cuenta que significa:

language: Es el lenguaje en el que esta escrita nuestra app. Ejemplo: ruby, go, rust, scala, etc.

sudo: Si vamos a ejecutar la suite como super usuarios. En nuestro caso vamos a hacerlo como usuarios normales.

cache: Los que programamos en Ruby sabemos que hacer un bundle install suele tardar un buen rato, es por eso que con esta opción, podemos tener el cache de la instalación de bundler hasta que nuestro Gemfile cambie, acelerando la ejecución de nuestros tests.

rvm: Esta opción nos permite tener diferentes versiones del lenguaje contra las cuales queremos probar nuestro código. Algo muy bueno de esto, en caso de usar Ruby, es que podemos probar nuestro código contra versiones de jRuby y con diferentes JDKs.

before_script: Podemos ejecutar comandos antes de ejecutar el script de build. En este ejemplo estoy dándole permisos de ejecución al script que va a correr mi build.

script: Es el script (o comandos) para ejecutar el build. En este caso arme un script de bash que contiene solo una linea bundle exec rake spec. Podría incluso escribir esta linea en el este archivo sin necesidad de llamar a un script para hacerlo.

Hay bastante mas posibilidades de configuración y de hacer cosas con Travis, pero en este post pretendo cubrir solo el escenario de correr una suite de test de Rspec.

Configuraciones en la aplicación

Haciendo click en More Options -> Settings accedemos a las configuraciones de Travis.

2016-03-10_00-27-28Podemos ver que tenemos activado el build con cada push a nuestro repositorio. Si nosotros hacemos un push a master o cualquier branch, Travis va a detectarlo y ejecutar un build.

También vemos que esta activado el build pull request. Esta característica es muy útil, ya que podemos estar trabajando en una algo nuevo, enviamos un PR para que nos revisen el código, y podemos asegurarnos que el código que vamos a mergear esta ‘Verde’ y no va a romper el build.

travispr

Vemos también una configuración de limitar jobs concurrentes. Esto nos sirve cuando tenemos tests de integración y estamos accediendo a la misma base de datos o API. No podemos ejecutar tests concurrentemente ya que los datos de prueba no serían consistentes. En este ejemplo no hay necesidad de limitar esto, ya que estoy buildeando una aplicación sencilla de una calculadora.

Por ultimo, si prestamos atención abajo de los botones están las Environment Variables. Estas variables las podemos configurar en el archivo .travis.yml, pero podemos también podemos configurarlas aquí. Podríamos poner en estas variables una conexión a una base de datos o alguna configuración del ambiente. Si es información sensible podemos ocultarla simplemente haciendo que la información no se muestre en el log con el botón al lado de la carga de la variable.

Esta del build en README.md

Un último detalle es que cuando usamos Travis, podemos tener el estado de nuestro build en nuestro repositorio.

Para eso podemos agregar al README.md la línea que nos da travis cuando hacemos click en el estado de nuestro build y seleccionamos Markdown como se puede ver en la siguiente imagen.

markdown

Con lo sencillo que es configurar Travis y si estas haciendo un proyecto open source, ya no hay mas excusas para no tener integración continua.