Author: Wilbur Suero

Introduciendo Webpacker

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.

Applying SMACSS to Rails projects

SMACSS is a style guide for organizing CSS in reusable modules. This makes a lot of sense for teams that are collaborating in a big project. How do we apply it in our Rails projects?

SMACSS Principles

SMACSS (Scalable and Modular Architecture for CSS) is a set of rules created and promoted by Jonathan Snook, who also offers a book with the same name. A good part of this book can be found for free on http://smacss.com. If you don’t know what SMACSS is I recomend reading this book, it’s a quick read.

SMACSS basic principles are:

  • Base are default styles of HTML elements (p, a, ul, li …)
  • Layout are styles used generally for layout. Are always prefixed by -l (e.x. l-sidebar)
  • All remaining CSS goes in modules and each goes prefixed with the namespace of the module to which it belongs. example, an alert module could have the following classes .alert, .alert-message, .alert-icon.

These practices are highly encouraged:

  • Don’t use ID in CSS
  • Minimize the deepness of your selectors
  • Use classes to define visual presentation patterns of your elements.

Implementation styleguide of SMACSS on Rails

File Structure

Files must be structured the following way:

app/assets/stylesheets/
├── application.css.scss
├── base.css.scss
├── layout.css.scss
|
├── globals
│   ├── _all.scss
│   ├── _functions.scss
│   ├── _mixins.scss
│   └── _variables.scss
|
└── modules
    ├── alert.css.scss
    └── ...

Filenames In SASS an underscore prefix indicates that it’s a partial which means that the file wont be compiled to a CSS by itself. SMACSS promotes modules that can be reused independently, so it uses partials only for pure SASS constrctors, like variables, functions or mixins declarations.

The files that can be used independently, (ex. modules) must have the double extension .css.scss. Partials on the other hand must start with _ and must end with the .scss extension. From Rails 4.2 partials that have the extension .css.scss are deprecated.

Application Manifest Use Sprockets’ require syntax //= to specify how style show be merged in a sole stylesheet in production.

//= require normalize
//= require ./base
//= require ./layout
//= require_tree ./modules

Globals This directory is for SASS variables, functions and mixins that are really used globally; If you have variables, mixins or functions and are going to be used in a single module, define them in the module itself, don’t clog the Global with rules that are going to be used only once.

Consider combining all global imports in a single file called globals/_all.scss that looks like:

@import "./variables";
@import "./mixins";
@import "./functions";

Then in your base, layout and module stylesheets you can start importing all global styles in each of the files.

@import "globals/all";