¡Hola! ¡Muy buenas a todos! La semana pasada os presenté la extensión Spree It is a present que creé para Be Bellón y os prometí que esta semana os contaría cómo se hacen estas extensiones. Como lo prometido es deuda, y tomando como ejemplo esta extensión, se avecina un post técnico 😉

Crear una extensión para Spree Commerce

Para crear la extensión Spree It is a present yo me basé en la guía que tiene Spree (en inglés) para crear extensiones. Lógicamente esta guía, aunque es muy completa y está muy bien redactada, no puede cubrir todos los supuestos, por lo que para las cosas que no venían en la guía oficial eché un vistazo a otras extensiones de Spree ya existentes. Y para que tú lo tengas todo en un único sitio te describo aquí el proceso que yo seguí. ¡Vamos allá!

1. Instala Spree Commerce

Lo primero que tienes que hacer es asegurarte de que tienes instalada la gema spree para poder utilizar su comando de creación de extensiones. Presuponiendo que tienes ruby instalado, en un terminal ejecuta:

gem install spree

Este comando ya debería de encargarse de instalar spree y todas sus dependencias.

2. Crea el esqueleto de tu extensión con el generador

La gema de spree incluye un generador de extensiones que te deja prácticamente todo listo para que puedas empezar, de hecho te crea la estructura de la extensión, te incluye una licencia open-source (BSD 3), que básicamente permite todo uso de tu software eximiéndote de responsabilidades en caso de que el mismo cause algún perjuicio, y añade un README muy decente, ¡sólo te harán falta unos retoques en el Gemspec y a programar!

Para utilizar el generador simplemente ejecuta el siguiente comando un un terminal indicando el nombre que quieres darle a la extensión. El caso de spree_it_is a present el comando fue:

spree extension it_is_a_present

El resultado debería de ser algo similar a esto:

Como podrás comprobar el generador crea un directorio con el nombre que has indicado, añadiéndole el prefijo spree_. En nuestro caso spree_it_is_a_present. Entra a tu directorio y abre tu editor favorito:

cd spree_it_is_a_present

E instalamos las dependencias:

bundle install

3. Crea migraciones

Si necesitas que tu extensión persista información en la base de datos, tendrás que añadir una migración.

La guía de extensiones oficial de Spree muestra cómo añadir un campo a un modelo ya existente en Spree.

En nuestro caso, necesitábamos añadir un modelo completamente nuevo que bautizamos como Spree::PresentNote. Puedes añadir una migración como harías en otros proyectos en Rails, en nuestro caso ejecutando el comando:

bundle exec rails g migration CreateSpreePresentNotes recipient_name dedication

Y una vez nuestro fichero de migración ha sido generado podemos retocarlo a nuestro gusto:

Presta atención a un par de detalles aquí. Primero fíjate cómo spree_present_notes referencia a un modelo ya existente de Spree (Spree::Order), aunque aquí se referencia como :order, más adelante veremos qué debemos añadir en el modelo para que Rails sepa a qué modelo nos estamos refiriendo. Y segundo, en otros proyectos de Rails estarás acostumbrado a que tus migraciones hereden de ActiveRecord::Migration. En lugar de eso, haz require 'spree_extension/migration' y haz que tus migraciones hereden de SpreeExtensionMigration. Esto hará que tu extensión funcione en versiones de Rails anteriores a Rails 5.x, (Ver SpreeExtension::Migration).

Estas migraciones se copiaran en la aplicación de Spree que use nuestra extensión cuando ejecuten el comando para instalarla, vamos a ver cómo se hace.

4. Añade e instala tu extensión en tu aplicación de Spree

Cuando antes empieces a probar tu extensión antes sabrás cómo se comporta, además mola ver cómo poco a poco se va haciendo realidad, así que llegados a este punto toca añadir tu extensión a tu aplicación de Spree. En el fichero Gemfile, añade tu extensión indicando el path en el que se encuentra en tu ordenador (Nota: cambia el path según tus necesidades)

gem ‘spree_it_is_a_present’, path: ‘../path/spree_it_is_a_present’ # Use the path where your gem is located in your computer

E instalamos las dependencias

bundle install

Una vez hecho esto instala tu extensión ejecutando el siguiente comando y confirma que quieres ejecutar las migraciones tecleando Yes cuando te lo indique:

bundle exec rails g spree_it_is_a_present:install

5. Añade tus propios modelos…

Esta parte es bastante sencilla, ya que simplemente tienes que añadir el código para tu nuevo modelo y ya está. En este caso añadimos el modelo Spree::PresentNote (app/models/spree/present_note.rb).

Como detalle que no aparece en la guía oficial y que te puede ser de utilidad, fíjate en cómo relaciono Spree::PresentNote con el modelo ya existente Spree::Order.

En este punto puede que quieras añadir factories o fixtures para tus tests, si quieres echarle un vistazo a cómo lo hice yo te dejo por aquí el link al commit.

…y/o extiende otros ya existentes

Una vez que hemos creado el modelo Spree::PresentNote necesitamos asociarlo con Spree::Order desde esta última para permitir la navegación entre objetos. Como Spree::Order es un modelo ya existente, tenemos que extenderlo, para ello crearemos un decorador para Spree::Order utilizando ActiveSupport::Concern en app/models/spree_it_is_a_present/spree/order_decorator.rb 

Como puedes ver, además de añadir la relación, también indicamos que desde ahora Spree::Order aceptará atributos para :present_note. Esto es importante para que podamos persistir la información en el formulario de checkout posteriormente.

Llegados a este punto sería conveniente añadir algunos tests. En mi caso he utilizado RSpec junto con FactoryBot, pero también puedes utilizar otros frameworks.

Lo que más nos interesa es saber si cuando persistimos una Spree::Order esta es capaz de persistir también datos relacionados con una Spree::PresentNote.

Además de esto, también es interesante saber si nuestro nuevo modelo funciona correctamente, sobretodo centrándonos en añadir tests que aunque en otros proyectos me podrían parecer superfluos, en este caso al ser una gema compartida con muchas personas considero que servirán para prevenir futuras regresiones.

6. Muestra tu formulario en el frontend

Lo primero para mostrar algo es tener algo que mostrar. Para ello, creamos un partial que contenga nuestro formulario y lo incrustamos con Deface. El partial sería tal que así:

Modificar vistas con Deface es muy sencillo gracias a que en Spree Commerce se han tomado la molestia de añadir IDs a la mayoría de elementos HTML. Si bien no sé hasta que punto el utilizar Deface seguirá siendo una opción viable en el futuro dado que el equipo que mantiene Spree decidió dejar de incluirla como dependencia. De momento las principales gemas de spree-contrib siguen utilizándola así que yo hice lo mismo. Habrá que estar atentos.

A continuación puedes ver cómo se hace para añadir nuestro partial al formulario de checkout:

7. Persiste los datos del formulario

Muy bien, ya tenemos por un lado los modelos que hemos visto en el paso 5 y también tenemos la vista que acabamos de ver en el paso anterior, ¿Qué nos falta? Efectivamente, el pegamento, el controlador.

El controlador de checkout que persiste instancias de Spree::Order no tiene ni idea de cómo persistir nuestro nuevo modelo Spree::PresentNote. En el paso 5 ya indicamos a Spree::Order que debía aceptar atributos para Spree::PresentNote, pero en algún lugar deberemos pasárselos. Ese lugar será Spree::CheckoutController que es dónde Spree recibe todos los parámetros y persiste los pedidos. Para ello utilizaremos de nuevo el patrón Decorator:

¿Y ahora qué?

Ahora tocaría los últimos pasos, que serían añadir un formulario para el backend y un controlador. Este punto es análogo a los puntos 6 y 7, así que para no extender más este post creo que no merece la pena explicarlo en detalle. Si quieres echarle un vistazo te dejo aquí el enlace al commit de Spree It is a commerce en el que añado esto. El resto es ya simplemente pulir un poco la gema y compartirla con el mundo.

Si has trabajado conmigo ya sabrás que me gusta mantener una historia de commits lo más clara y limpia posible, así que si te interesa ver paso a paso qué fui añadiendo a la gema puedes echarle un vistazo a todo el proceso commit por commit aquí.

¡Y hasta aquí llega el post de hoy! ¡Hasta la semana que viene! ¡Chao!