Be reactive on Rails with 100% ruby code

Franco A.
Hashdog Blog
Published in
4 min readSep 8, 2020

I’m not a senior developer or anything like that. I only have some web frameworks experience, especially with Ruby on Rails and when the client requested it, AngularJS and React. Where I feel like a “happy developer” is in Rails, somewhat because of the benefits of ruby, and the same because of the way Rails works. But I must admit that reactivity was something cumbersome to achieve without falling into mixtures. Lately, there have been significant community attempts, such as Stimulus Reflex (Thanks a lot for their contributions and efforts to make the Ruby on Rails Universe a better place) I always had the feeling that although the way to work the reactivity was improving, there was still a screw adjustment.
That is why I was thrilled to find this gem. That is an excellent job from the Motion team. Thanks to them and the entire community. Please visit the repo https://github.com/unabridged/motion

Motion’s promise is attractive: “Motion allows you to build reactive, real-time frontend UI components in your Rails application using pure Ruby.”
Being reactive in Rails writing 100% ruby code, and not stopping being Rails in the attempt, it is priceless. I wanted to clarify that I have nothing against JS and its entire ecosystem. It is just a joy to work with the language that makes me feel more comfortable.

Installing it is very simple and you just have to follow the steps that they detail in their repository so I’m not going to stop at that, except to comment that they rely on another great development such as ViewComponent, a gem of the Github people that will be there in the Rails core since version 6.1.

This combination is a winner, reactive components!

Let’s see an example taken from your documentation:
The “Hello world” of reactivity, a reactive counter button!

When installing Motion we have a generator available to

rails g motion:component <component-name>

We are going to run this command that our component will generate. We will see how we create a folder “components” and within it our files “button_component.rb” where our component logic will be and “button_component.html.erb” where our HTML will be

> rails g motion:component buttonRunning 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

we can render our component in any view we have using the following line

<%= render ButtonComponent.new %>

Let’s put some reactive logic to our component

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

The important thing here is map_motion :add
what will be the union between our HTML component and the method in our ruby ​​component

I am using Bulma for this test but you can use any CSS framework or even none.

Let’s add our HTML to the component

<div>
<span><%= total %></span>
<%= button_tag "Increment", data: { motion: "add" }, class: "button is-small is-info" %>
</div>

Two things to highlight here:
One limitation is that everything in our component template must have a main node, in our case we wrap everything with a simple div, this is because of how Motion works from behind, very similar to how React works.
The other thing to highlight is ´data: {motion: “add”} ´ is the connection with our component in ruby, it is what will trigger the method with the same name.

And that’s all we need, with that we should have a fully functional reactive button and without writing a line of javascript.

Another small example is the classic I write and react, our code could be something like this:

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

And the component on 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

Two other things to highlight here.
By default motion will take the change event as the trigger in input but we can define which event to bind, as seen in the example ´data-motion = “keyup-> update” ´ we are binding keyup Another interesting aspect is that our ruby ​​method receives an ´event´ as a parameter, where we have available data such as the current_target to obtain its value (in this example) or to be able to access its attributes, etc.

Imagine the potential of this using ActiveRecord, CableReady, chartkick, etc …

Thanks for getting here.
This is not intended to be a tutorial or an introduction to Motion. It was to share with you this tool that is undoubtedly going to become an everyday part of my work.
God bless you!

WRITTEN BY

Sergio Sebastián Arriagada

Hashdog RoR developer

--

--