Con el siempre cambiante mundo de javascipt, algo que ha sido importante  y que ha estado faltando en Rails es una forma de empaquetar Javascripts, pero desde Rails 5.1 esto cambia gracias a la introducción de la gema Webpacker.

Rails 5.1 viene con Webpack Yarn por medio de la opción –webpacker y con integración, sin cambiar nada, con React, Vue, Angular, Elm y mas que vienen pronto.

Preparación

Antes de comenzar asegúrate de que Ruby, Node Yarn estén instalados en tu máquina.

Ahora abre una terminal, instala Rails 5.1 y crea una nueva app así:

// Última versión 5.1+
gem install rails

rails new webpacker_example --webpack

// para configurarlos con React, Angular o Vue

rails new webpacker_example --webpack=react
rails new webpacker_example --webpack=angular
rails new webpacker_example --webpack=vue

Observa la nueva opción --webpack, esto va a configurar webpack y yarn después de haber creado nuestra aplicación Rails 5.1. Una vez tengamos Webpack y Yarn configurados estamos listos para utilizar Webpack en nuestra aplicación Rails.

Si ya tienes una aplicación Rails 4.2+ puedes instalar webpacker agregándolo a tu Gemfile y posteriormente corriendo:

rails webpacker:install
rails webpacker:install:[react, angular o vue] (opcional)

Puedes ver una lista de los comandos que trae Webpacker corriendo:

bundle exec rails webpacker
bundle exec rails -T

Convenciones

Como todas las cosas en Rails, webpacker también viene con algunas convenciones por defecto. Vamos a verlas una a una:

1- Estructura de carpetas

Heche un vistazo a la carpeta app/javascript Esta será la carpeta que va a contener todo el código Javascript de la aplicación y los puntos de entrada de webpack, también llamados packs. Por defecto están en app/javascript/packs.

La convención aquí es que todos los puntos de entrada de webpack deben estar en la carpeta app/javascript/packs y luego todos los módulos y componentes los podemos orgnizar como querramos en app/javascript.

Por defecto webpacker nos genera un archivo application..js en el folder packs para fines de demostración.

2- Configuración de Webpack

El generador añade configuraciones de Webpack para cada ambiente en la carpeta config/webpack. Estas configuraciones sin hacerles ningún cambio soportan varias características para varios ambientes como code-splitting, asset-fingerprinting y enlazar archivos estáticos como imágenes o stylesheets.

Todas las opciones son personalizables en el archivo config/webpack/paths.yml

3- Live Reloading

Webpacker provee un binstub para correr webpack-dev-server que soporta live code reloading en el ambiente de development. Deberás instalar plugins adicionales a Webpack si quieres características como HMR [Hot Module Replacement]

Las opciones del servidor de development son personalizables en config/webpack/development.server.yml.

Poniéndolo en acción

Ahora vamos a crear un pequeño módulo en Javascript y vamos a ver como funciona.

1- Configuración

Primero vamos a añadir foreman a nuestro Gemfile en el grupo development, que sirve para manejar varios procesos simultaneamente. Luego, creamos dos archivos Procfile y Procfile.dev en el folder raíz.

Procfile

web: bundle exec puma -p $PORT

Procfile.dev

web: bundle exec rails s
# watcher: ./bin/webpack-watcher
webpacker: ./bin/webpack-dev-server

También vamos a configurar un pequeño binstub que nos corra el Procfile.dev, crea un archivo dentro de la carpeta bin llamado server y pega lo siguiente:

#!/bin/bash -i
bundle install
bundle exec foreman start -f Procfile.dev

Quizas debas hacer chmod 777 bin/server en caso de que tengas permiso denegado al correr este binstub. Así que ahora podemos hacer ./bin/server y va a llamar Procfile.dev y va a correr Webpack y Puma como lo hemos intentado.

2- Código

Ahora vamos a agregar nuestro pequeño módulo de prueba, que va a ser un contador. Primero crea una carpeta llamada counter dentro de app/javascript luego dentro de esa carpeta crea dos archivos index.js y counter.js con el siguiente código:

counter.js

// A simple counter example
// The setup will be more complicated in modern apps built using React

const incrementNode = document.getElementById('increment');
const decrementNode = document.getElementById('decrement');
const inputNode = document.getElementById('counter');

const counter = {
  initialize() {
    incrementNode.addEventListener('click', (event) => {
      event.preventDefault();
      const currentValue = inputNode.value;
      inputNode.value = parseInt(currentValue, 0) + 1;
    });

    decrementNode.addEventListener('click', (event) => {
      event.preventDefault();
      const currentValue = inputNode.value;
      if (currentValue > 0) {
        inputNode.value = parseInt(currentValue, 0) - 1;
      }
    });
  }
};

export default counter;

index.js

// Initialize the counter code when DOM is ready
import counter from './counter';

document.addEventListener('DOMContentLoaded', () => {
  counter.initialize();
});

Luego dentro de la carpeta app/javascript/packs crea un archivo llamado counter.js y llénalo con el siguiente código:

// Require or import the counter module
import '../counter';

Muy simple!

3- Vistas

Por último, para agregar el contador a la vista vamos a generar un controller.

rails g controller pages index

Luego dentro de routes.rb configuramos que el root path sea nuestro controller.

root 'pages#index

Finalmente dentro de app/views/pages/index.html.erb page el siguiente código:

<div class="counter-wrapper">;
<h1>Counter</h1>
<form class="’counter’"> 
        <button id="’increment’">Increment</button> 
            <input id="’counter’" name="”counter”" type="”number”" value="’0'" /> 
            <button id="’decrement’">Decrement</button>
    </form>
    </div>

<%= javascript_pack_tag ‘counter’ %>

Fíjate en el helper javascript_pack_tag que va a halar el módulo counter compilado y lo va a referenciar en nuestra app así:

<script src=”http://localhost:8080/counter.js"></script>

4- Corriendo

Abre una terminal en el directorio de la aplicación y corre:

./bin/server

Puedes notar que en la terminal Webpack compila nuestro módulo counter y nos muestra información relevante como el tamaño, etc …

Ahora puedes visitar http://localhost:5000 y vas a ver la aplicación del contador corriendo.