A Future for Rails: StimulusReflex
Tags: rails, web, stimulus, ruby
Published: 2020-08-21 22:53:33

---
title: A Future for Rails: StimulusReflex
published: true
description: What's in store for the future of Rails development? What's next for Ruby on Rails using StimulusReflex to build a simple chat application.
tags: rails, web, stimulus, ruby
---
If you've been in the rails community for any length of time, you've probably heard about the desire to stay away from JavaScript.

> "I like JavaScript, but I don't like it that much."
David Heinemeier Hanson, Creator of Ruby on Rails

It's easy to say that most Rails developers are anxious around JavaScript. This led to the rise of Coffeescript, Sprockets, and a whole suite of tools to allow Ruby on Rails developers to build amazing applications without having to write JavaScript.

As Rails continues its path to being a larger and more complete framework, "batteries included," there have been further shifts in the community. Included with Rails is now a more modern JavaScript packager (though Sprockets is still around if you want to use it). DHH also introduced us to a whole new JavaScript Framework called Stimulus. It allows you to use modern Javascript on your Rails projects without having to go outside the "rails way".

‍‍![two developers collaborating on project in Phoenix LiveView](https://dev-to-uploads.s3.amazonaws.com/i/r0g1wjxlxxqovushi1o2.jpg)

## Phoenix, Elixir, and LiveView
As other communities have popped up to build "rails inspired" frameworks, many of those frameworks emerged as formidable competitors to single-page applications. In the Phoenix and Elixir community, Chris McChord introduced the world to LiveView, a whole new paradigm for serving and updating HTML from your backend.

At its core, LiveView is able to leverage Websockets and update the Document Object Model (DOM) based on responses from the WebSocket connection. These frameworks are fast enough to feel "real-time".

The promise of LiveView is a return to the early days of Ruby on Rails, where we didn't have to worry so much about JavaScript and we could happily focus on our business logic. Everything else to build a robust CRUD application was taken care of using "rails magic."

Some people in the Rails community have jumped ship to the Elixir/Phoenix community. To be fair, I personally think that for most problems, Phoenix is an amazing solution.

## Can Rails still compete?
Rails is nothing to sneeze at and the Rails community has taken steps towards being real-time as well. DHH recently tweeted and to let folks in the community know that much of Hey was built with some sort of frontend updating system that builds on Stimulus, Turbolinks, and the "rails way". I'm excited to hear more.

There have been other efforts to push Rails in the "real-time" direction as well. Recently I came upon StimulusReflex which builds on the Stimulus framework and a gem called CableReady that enables you to build "reflexes" that work much like LiveView does in Phoenix: sending DOM updates over a WebSocket to update the UI.

StimulusReflex could be a possible future for Rails development. I'd like to share my experience building an application with StimulusReflex.

![rails chat app example on MacBook pro screen](https://dev-to-uploads.s3.amazonaws.com/i/8ikpykrz8xw3t8ot1p9g.png)

## Creating a chat app with StimulusReflex
I recently wrote a chat application that is fast, responsive, and was simple to build while leveraging StimulusReflex. I also wrote a total of 24 lines of JavaScript - you can't beat that.

### Setup
It's easy to get started with StimulusReflex. It's a gem and can be installed in your Rails app by running:

`bundle add stimulus_reflex`

To get it up and running, simply run the install scripts:

`bundle exec rails stimulus_reflex:install`

This generates a couple of new starter files and directories. Including the `app/reflexes` directory. You'll see an `example_reflex.rb` and an `application_reflex.rb`.

You also get some JavaScript out of the box, and you'll have a newly created `app/javascript/controllers/application_controller.rb` and an `app/javascript/controllers/example_controller.rb`.

The reflexes are where the magic happens. According to the documentation, a "reflex" is a "full, round-trip life-cycle of a StimulusReflex operation - from client to server and back again. The JavaScript controllers are Stimulus Controllers and StimulusReflex is designed to hook right into Stimulus and other Rails paradigms.

### Up and Running
Now that we have StimulusReflex setup, let's go ahead and start setting up our chat application. I'm going to make some assumptions.

You have a Rails application that is using Devise.

You have a Message model that includes a message and user_id attribute.

Also, I won't go into the minutiae of writing the views or the styling for the application. If you're curious, I'll link out to the entire code base for you to see what I wrote.

The first thing I did was build out a link to a view that will render our reflex enabled HTML. There is no special controller needed here, I called mine the ChatController and linked to it from my routes.rb as `get /chat to: "chat#index"`.

Here I expose my messages to the view:

```ruby
class ChatController < ApplicationController
  skip_authorization_check

  def index
    render locals: { messages: Message.all.order(created_at: :desc) }
  end
end
```

Then consume those messages in the chat index view:

```slim
div.flex.flex-col.mx-4
  div.container.flex.flex-col-reverse.overflow-scroll.bg-white.mt-4.rounded
    - messages.map do |message|
      div
        = message.message
```

You should now have a nice list of messages in your view - easy peasy.

The next step is to add in some Stimulus to create messages. We're just going to get the button click working with Stimulus right now. I added a button with a Stimulus data attribute to `create_message` in the Chat controller.

```slim
div.flex.flex-col.mx-4 data-controller='chat'
  div.container.flex.flex-col-reverse.overflow-scroll.bg-white.mt-4.rounded
    - messages.map do |message|
      div
        = message.message
  div.mb-4
    = form_with model: @message do |form|
      div.mb-4.rounded-md
        = form.label :message
        = form.text_field :message
        = form.submit "Message", data: { action: "click->chat#create_message" }
```

In `app/javascript/controllers/chat_controller.js` you should create a create_message handler that handles the click event from your Message button.

```javascript
import { Controller } from 'stimulus';
import StimulusReflex from 'stimulus_reflex';

export default class extends Controller {
  create_message(event) {
    event.preventDefault()
    console.log('Clicked')
  }
}
```

Now when you click the Message button you should see 'Clicked' in the JavaScript console. I love Stimulus that is easy. You don't actually have to use Stimulus here - you can add data attributes to your HTML that will trigger reflex actions straight from your HTML.

[See the note here](https://docs.stimulusreflex.com/reflexes#declaring-a-reflex-in-html-with-data-attributes) in the StimulusReflex docs.

To connect this to our Reflex we need to import the StimulusReflex module and register it in our Controller.

```javascript
import { Controller } from 'stimulus';
import StimulusReflex from 'stimulus_reflex';

export default class extends Controller {
  connect() {
    StimulusReflex.register(this)
  }

  create_message(event) {
    event.preventDefault()
    this.stimulate('Chat#create_message', event.target)
  }
}
```

Now when we click the button you should see a big fat error saying that the ChatReflex module does not exist. Now let's create the ChatReflex and handle our `create_message` action.

```ruby
class ChatReflex < StimulusReflex::Reflex
  delegate :current_user, to: :connection

  def create_message
    # Create the message
    Message.create(message: params["message"], user_id: current_user.id)
  end
end
```

See the connection delegation at the top of the class? We need to be sure to expose that to our connection.

```ruby
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if current_user = env["warden"].user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
```

We identify our Chat reflex connection by the `current_user` which uses the `current_user.id` to separate connections. We also add a layer of Authentication here that allows us to pick up our user from Devise when we connect to our WebSocket.

After this is created, when you click your Message button, you should now see the message returned back to view and update in real-time. It's really fast, and also super easy.

Summary of where we are right now:
![summary flow chart](https://dev-to-uploads.s3.amazonaws.com/i/msmpif68v55rvad1e6zw.png)

### Bringing it all together
So now you can connect two users to the chat room and attempt to chat with one another. But there is a small problem - you only get the other users' messages when you send a message or refresh the page. That's not very real-time at all.

Currently, the view is only re-rendered and broadcast to the `current_user` on that connection. But, we can leverage ActionCable to tell StimulusReflex that there are new messages that need to be rendered. Easy enough.

To achieve this, we spin up a new ActionCable channel. I called it the ChatChannel.

```ruby
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_#{params[:room]}"
  end
end
```
‍
This allows us to connect to a Chat room outside of the StimulusReflex paradigm and we can broadcast back up this channel that the users that are attached need to get the new messages.

To accomplish this, we add some JavaScript to initiate that connection:

In your Stimulus chat controller the initializer connects to the `ChatChannel` and then binds a function to the received callback of the ActionCable subscription.
‍

It looks like:

```javascript
import { Controller } from 'stimulus';
import StimulusReflex from 'stimulus_reflex';
import consumer from '../channels/consumer';

export default class extends Controller {
  connect() {
    StimulusReflex.register(this)
  }

  initialize() {
    // We need to know when there are new messages that have been created by other users
    consumer.subscriptions.create({ channel: "ChatChannel", room: "public_room" }, {
      received: this._cableReceived.bind(this),
    });
  }

  create_message(event) {
    event.preventDefault()
    this.stimulate('Chat#create_message', event.target)
  }

  _cableReceived() {
    this.stimulate('Chat#update_messages')
  }
}
```

Now when we receive a message (any message currently) we trigger the `_cableReceived` callback which will stimulate the `Chat#update_messages` actions.

Add an `update_messages` action to the Chat reflex and then broadcast a message when you create the Message in the `create_message` action. So, now when any user creates a new Message, we will rebroadcast that message out to anyone that is connected to the `chat_public_room` connection.

```ruby
class ChatReflex < StimulusReflex::Reflex
  delegate :current_user, to: :connection

  def create_message
    # Create the message
    Message.create(message: params["message"], user_id: current_user.id)

    # Broadcast that everyone on this channel should get messages
    ActionCable.server.broadcast(
      "chat_public_room",
      body: 'get_messages'
    )
  end

  def update_messages; end
end
```

Since the view gets re-rendered from the `ChatController`, we don't actually have to do anything with the messages in the Reflex action. We get to treat the view just like we would have in a Controller Action.

Now you should be able to attach two users and chat back and forth with one another.

### The flow through the app looks like:
![flow through the app](https://dev-to-uploads.s3.amazonaws.com/i/5a16tlo0qmsfwjtckdyy.png)

‍
So we short circuit the button to force stimulus to trigger the Reflex to get everyone's chat's to refresh when a new Message is created.

It's simple and very powerful. The tools that build this application are quite well known and StimulusReflex is built on well-known Rails gems and tools including, Stimulus, CableReady, and ActionCable.

I'm excited to continue exploring how to leverage StimulusReflex in apps we build at Headway, as well as other solutions in this vein including LiveView on Phoenix and Elixir. I believe it will reduce the time we all spend writing single-page applications without losing the real-time feel.

### The results so far
Below is the final version of my quick and dirty StimlusReflex chat application.

![final chat app results](https://dev-to-uploads.s3.amazonaws.com/i/g54z2216ah3q2uxc9sz2.png)

## Where to go from here
I remember building a chat application when ActionCable first launched and thinking the set up was lengthy and took some work. WebSockets were mysterious to me too. StimulusReflex does a great job in hiding that complexity and makes your Rails application feel even more like an SPA. There is such little JavaScript in this version it's not even funny.

### More opportunities for this chat application are:
- Add better Authentication and do some work to add presence notifications

- Make it so you can create and join any chat room - not just the public room

## Get access to the code
What would you add to my chat application next? You can see the final version of the code for this walkthrough here on GitHub. I added some styling and made it look a bit like a messenger application.