Añade etiquetas a un modelo con Ruby on Rails

August 29, 2025 12:35 — ruby on rails

Aprovecho que he añadido etiquetas al blog para explicar lo fácil que ha sido hacerlo con Ruby on Rails. Si tienes curiosidad por ver el código en profundidad, puedes ver la PR en Github. Se me fue un poco la mano y acabé añadiendo estilos y tests que faltaban por ahí.

Lo primero son los modelos. La manera fácil de hacerlo es usando has_and_belongs_to_many pero en el futuro quiero poder etiquetar cualquier modelo, así que me he decidido por usar un modelo intermedio en el que en el futuro migraré a una asociación polimórfica (exacto, Yagni).

Los modelos asociados quedan así:

class Entry < ApplicationRecord
  has_many :taggings, dependent: :destroy
  has_many :tags, through: :taggings
end

class Tag < ApplicationRecord
  has_many :taggings, dependent: :destroy
  has_many :entries, through: :taggings
end

class Tagging < ApplicationRecord
  belongs_to :entry
  belongs_to :tag
end

De esta manera es super fácil acceder a las etiquetas desde una entrada (Entry.first.tags) o ver todas las entradas etiquetadas con una determinada etiqueta (Tag.first.entries).

Asociando desde la UI

He creado la sección correspondiente del panel de administración para añadir/editar/eliminar etiquetas.

Al añadir/editar una entrada se muestra una lista de etiquetas con un checkbox al lado para seleccionar las que quiera. Rails hace que esto sea extremadamente simple. Tanto el mostrar la lista de etiquetas como el asociarlas directamente al crear/actualizar una entrada.

Lo primero es actualizar los parámetros permitidos en el controlador. Como se pueden asociar varias etiquetas, esta es la sintaxis a usar:

params.expect(entry: [ :title, ..., tag_ids: [] ])

En el formulario de las entradas he usado este helper que se encarga de mostrar todas las etiquetas con un checkbox para seleccionarlas.

<%= form.collection_checkboxes(:tag_ids, Tag.all, :id, :name) %>

El resultado no es el más bonito del mundo, pero necesito que funcione todo sin Javascript. Tengo algunas ideas para mejorar progresivamente usando turbo-frames y autocompletar, para poder añadir nuevas etiquetas directamente desde el formulario.

El helper acepta diferentes parámetros y hasta bloques, permitiendo personalizar cómo se ven las etiquetas. Por ejemplo, para la barra lateral el código luce tal que así:

<%= form.collection_checkboxes(:tags, Tag.all, :name, :name, include_hidden: false, checked: params[:tags]) do |b| %>
  <%= b.label { b.checkbox + b.text } %>
<% end %>

Montando un blog poco a poco con Ruby on Rails

August 10, 2025 11:25 — blog, ruby on rails

Hace años que dejé de usar CMS en el ámbito personal. Aunque sea más laborioso prefiero montarme algo personalizado. Usualmente utilizo Ruby on Rails.

Ya lo sé. Montar un blog así es como cazar moscas a cañonazos. Podría usar uno de los miles de servicios o CMS que existen por ahí con este propósito. Pero no me apetece.

El código que hace funcionar este blog es público. Lo voy escribiendo en mis ratos libres. Imagino que dista de ser perfecto y puede que no le sea útil a nadie más que a mi. Si te resulta útil o crees que algo se puede mejorar, siéntete libre de abrir una pull request y la revisaré encantado.

¿Cómo probar el blog en un entorno de desarrollo local?

Necesitarás Ruby 3.4.2 instalado en tu máquina.

Clona el repositorio. Puedes hacerlo con el comando: git clone [email protected]:develumpen/blog.git

Entra dentro del directorio y ejecuta bundle install para instalar las dependencias.

Ejecuta bin/dev para correr el servidor de desarrollo. Seguramente se queje de que tienes migraciones pendientes por correr. Si apretando el botón que te aparece en la web no funciona, prueba ejecutando bin/rails db:migrate.

Si todo ha ido bien, deberías poder acceder al blog desde http://localhost:3000

¿Cómo desplegar una copia del código a un entorno de producción?

¿Se dice desplegar? No acostumbro a hablar sobre desarrollo en castellano y escribir deployar me parecía un poco cutre.

Esto es algo personal y hay muchas maneras de hacerlo. Yo utilizo Kamal.

Para no exponer credenciales, he creado un repositorio privado nuevo en Github. Acto seguido, he clonado el repositorio público en un directorio diferente, por ejemplo git clone [email protected]:develumpen/blog.git mi-repositorio-privado.

Esto copiará el contenido del repositorio original en el directorio recién creado mi-repositorio-privado.

Como quiero poder traer los cambios del repositorio original al privado, renombro origin a upstream con el comando git remote rename origin upstream. Imagino que le puedes poner el nombre que te de la gana, pero siempre lo he hecho así.

Lo siguiente es definir el origen del repositorio privado que he creado en Github con git remote add origin xxx, sustituyendo xxx por el repositorio privado. Una vez definido el nuevo origin, subo el código con git push -u origin main.

Ahora ya tengo una copia del código público en mi repositorio privado.

Como he dicho, utilizo Kamal para desplegar a producción. Dentro del repositorio privado, ejecuto kamal init y siguiendo la documentación configuro lo que necesite.

Después de configurar Kamal hay un par de cosas que hay que configurar en la aplicación. La primera es la base de datos. Edito lo que necesito en config/database.yml en la sección production:.

Lo siguiente es regenerar los credenciales. Debí haberlos omitido al subir el código. Como no voy a compartir la clave maestra, los credenciales encriptados del repositorio público son inservibles (dentro no hay nada). Para generar una nueva clave y unos credenciales válidos ejecuto bin/rails credentials:edit. Como uso VSCode, he de modificar un poco el comando : EDITOR=code --wait bin/rails credentials:edit.

Así a grandes rasgos es como lo hago yo. Si necesitas ayuda puedes dejarme un comentario (de momento no, porque no se puede) o escribirme a [email protected].

¿Cómo traer los cambios del repositorio público al repositorio privado?

Todo el desarrollo del blog ocurrirá en el repositorio público. Para traer los cambios al repositorio privado usaré los dos remote que definí un poco más arriba.

Para no arrepentirme en caso de que algo vaya mal, lo primero es crear una nueva rama en el repositorio privado. Seguidamente traigo los cambios con git fetch upstream y los aplico en la nueva rama con git rebase upstream/main. También se puede hacer git merge upstream/main si lo prefieres.

A veces habrá conflictos si en el repositorio privado toco algo que ha cambiado en el público. Se resuelven como conflictos normales de git.

Antes de hacer merge (¿cómo se dice esto en castellano?) despliego la nueva rama para ver que todo ha ido bien.

Conclusión

No es un sistema muy complicado para desarrollar en público y desplegar en privado. Es posible que haya mejores alternativas (por favor, ¡házmelas saber!).

En el pasado implementé un "complicado" sistema de scripts en bash y Ruby pero era un rollo de mantener.

Tengo pensado desarrollar unas cuantas aplicaciones usando este sistema y me gustaría entender si es útil y fácil para otras personas. ¿Cómo lo haces tú?