Rails reactivo 100% ruby
Tags: rails, ruby, components, reactive
Published: 2020-09-07 19:02:07

No soy un desarrollador que se considere serior ni nada parecido, solo tengo cierta experiencia en algunos frameworks web, sobre todo con Ruby on Rails y cuando el cliente así lo demandaba, AngularJS y React. Donde me siento un "desarrollador feliz" es en Rails, un tanto por las bondades de ruby, otro tanto por la forma de trabajar de Rails. Pero debo admitir que la reactividad era algo engorroso de lograr sin caer en las mixturas y aunque últimamente salieron grandes intentos de la comunidad como Stimulus Reflex (a los que aplaudo y agradezco inmenzamente por sus aportes y esfuerzos por hacer del Universo Rails un mejor lugar) siempre me quedaba la sensación de que si bien se iba mejorando el cómo trabajar la reactividad aún faltaba un ajuste de tuerca. Es por eso que me puso muy contento encontrar esta [gema](https://github.com/unabridged/motion), un gran trabajo de la gente de Motion. Gracias totales a ellos y a toda la comunidad.

La promeza de Motion es muy atractiva *"Motion allows you to build reactive, real-time frontend UI components in your Rails application using pure Ruby."*
Poder ser reactivos en Rails escribiendo 100% código ruby y no dejar de ser Rails en el intento, para mi no tiene precio. Quería aclarar que no tengo nada en contra de JS y todo su ecosistema, es sólo una alegría poder trabajar con el lenguage que me hace sentir mas cómodo.

Instalarla es muy sencillo y solo hay que seguir los pasos que ellos detallan en su [repositorio](https://github.com/unabridged/motion#installation) así que no voy a detenerme en eso, salvo para comentar que se apoyan en otro gran desarrollo como es [ViewComponent](https://github.com/github/view_component) una gema de la gente de Github que va a estar en el core de Rails desde la version 6.1.

Esta combinación es ganadora, componentes reactivos!

Veamos un ejemplo extraído de su documentación:
El Hola mundo de la reactividad, un boton contador reactivo!

Al instalar Motion tenemos disponible un generador para
```sh
rails g motion:component <component-name>
```
Vamos a correr este comando que nos generará nuestro componente. Veremos como nos crea una carpeta "components" y dentro de ella nuestros archivos "button_component.rb" donde va a estar nuestra lógica de componente y "button_component.html.erb" donde estará nuestro HTML
```sh
> rails g motion:component button

Running via Spring preloader in process 21091
    generate  component
       rails  generate component Button
Running via Spring preloader in process 21096
      create  app/components/button_component.rb
      invoke  test_unit
   identical    test/components/button_component_test.rb
      invoke  erb
      create    app/components/button_component.html.erb
      insert  app/components/button_component.rb
```

podemos renderizar nuestro componente en cualquier vista que tengamos usando la siguiente linea

```ruby
<%= render ButtonComponent.new %>
```

Pongamos un poco de lógica reactiva a nuestro componente

```ruby
class ButtonComponent < ViewComponent::Base
  include Motion::Component

  attr_reader :total

  def initialize(total: 0)
    @total = total
  end

  map_motion :add
  def add
    @total += 1
  end
end

```
lo importante de aquí es el `map_motion :add`
que va a ser la unión entre nuestro componente HTML y el método en nuestro componente ruby

yo estoy usando [Bulma](https://bulma.io/) para esta prueba pero puedes usar cualquier framework CSS o incluso ninguno.

Agreguemos nuestro HTML al componente
```html
<div>
  <span><%= total %></span>
  <%= button_tag "Increment", data: { motion: "add" }, class: "button is-small is-info" %>
</div>
```
Dos cosas para destacar aquí:
Una limitación es, que todo en nuestro template del componente debe tener un nodo principal, en nuestro caso envolvemos todo con un simple div, esto es por como funciona Motion por detrás, muy similar a cómo trabaja React.
La otra cosa para destacar es ´data: { motion: "add" }´ es la conección con nuestro componente en ruby, es lo que va a disparar el metodo con el mismo nombre.

Y eso es todo lo que necesitamos, con eso deberíamos tener un boton reactivo completamente funcional y sin escribir una linea de javascript.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/z8301cq6p0dpjy2ay9vx.gif)

Otro pequeño ejemplo es el clásico escribo y reacciono, nuestro código podría ser algo como esto:

```html
<div>
  <p>My name is <%= name %>  <%= 'ok!' if @is_ok %></p>
  <input type="text" data-motion="keyup->update" class="input">
</div>

```

```ruby
class InputComponent < ViewComponent::Base
  include Motion::Component

  attr_reader :name

  def initialize(name: "")
    @name = name
  end

  map_motion :update
  def update(event)
    new_value = event.current_target.value
    @name = new_value
    @is_ok = new_value == "sebastián"
  end
end

```
Otras dos cosas para destacar aquí.
Por defecto motion tomará como trigger en *input* el evento *change* pero podemos definir qué evento bindear, como se ve en el ejemplo ´data-motion="keyup->update"´ estamos bindeando *keyup*
Otro aspecto interesante es que nuestro método en ruby recibe un ´event´ como parámetro, donde tenemos  disponible datos como el current_target para obtener su valor (en este ejemplo) o poder acceder a sus atributos, etc..

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tf47r6p9n1u26q9l26y6.gif)

Imaginen el potencial de esto usando ActiveRecord, CableReady, chartkick, etc...

Gracias por llegar hasta aquí. Esto no pretende ser un tutorial o una introducción a Motion, solo era compartir con ustedes esta herramienta que seguramente va a pasar a ser cotidiana en mi trabajo.
Dios los bendiga!