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.
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.
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.
Hotwire aka NEW MAGIC is finally here: An alternative approach to building modern web applications without using much JavaScript by sending HTML instead of JSON over the wire. This includes our brand-new Turbo framework and pairs with Stimulus 2.0 😍🎉🥂 https://t.co/Pa4EG8Av5E
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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á:
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.
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Ayer un amigo me paso un tweet con un screencast que me pareció increíble.
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.
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:
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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í:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
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.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Lo modifico totalmente, uso en su lugar el que se creó cuando armamos el ambiente de Ruby on Rails sin base de datos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
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:
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.