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 y 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 y 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.