A new look at Fullstack development with the Ruby on Rails framework

Is it possible to seriously discuss fullstack development? If you look towards large frameworks for the frontend and for the backend, then talking about fullstack looks doubtful. I suggest looking at the term fullstackfrom the point of view of Ruby on Rails and the earlier simpler principles of implementing interactivity on classic web pages. I present to you an overview of the frontend features provided in the Ruby on Rails framework or related to it.

image


Ruby on Rails is an MVC framework that focuses on rapid development, and pays a lot of attention to maintaining consistency within a project (so that it doesn't turn out “anyhow” “quickly”). It provides many tools for both backend and frontend development. In the classic fullstack direction, there was a momentary omission due to ignorance of its development and misconception about the lag of used funds. The objective of this article is to highlight how the fullstack approach develops and how many different interesting possibilities have appeared in it.

Webpacker


Webpacker is a gem that ships with Ruby on Rails.

Webpacker provides a wrapper over Webpack: commands for connecting and starting configurations for work. Webpacker de facto sets the standard for working with frontend in Ruby on Rails, promotes the use of the latest JavaScript features and modern principles of working with code (structure, modularity, assembly, and much more).

Webpacker defines the general configurations needed to get started and the structure of the application, which increases certainty and simplifies the understanding of the project by different developers. For the JavaScript code, the folder app/javascript/with the primary file is allocated app/javascript/packs/application.js.

Files and folders added by Webpacker
config/webpacker.yml
config/webpack/
config/webpack/development.js
config/webpack/environment.js
config/webpack/production.js
config/webpack/test.js
package.json
postcss.config.js
babel.config.js
.browserslistrc
node_modules/
bin/webpack
bin/webpack-dev-server
app/javascript/
app/javascript/packs/
app/javascript/packs/application.js


Webpacker starts by default during the process of creating a new application and performs its settings. You can create the application right away with additional configurations for Stimulus, Vue, Typescript or another one from the list provided :

rails new myapp --webpack=stimulus

Or install additional configurations after creating the application:

bundle exec rails webpacker:install:stimulus

Develop Frontend with the Ruby on Rails framework = use the most current JavaScript development approaches. All the convenience of using modern JavaScript standards are well integrated with Ruby on Rails. The necessary configurations are provided for working with Webpack, which allows you to be less distracted by the correct organization of the project and focus on solving popular tasks using the familiar environment.

Turbolinks


Turbolinks is the JavaScript library that ships with Ruby on Rails.

The priority task of Turbolinks is to ease the load on the server and reduce “seams” when navigating to application URLs. This feature is often compared with SPA, as it gives the impression of a re-rendering of content in the browser instead of plain plain transitions between pages.

Turbolinks working principle: to navigate between pages not by standard switching to a new address, but by fulfilling a request to this address “in the background” via ajax, loading the response in JavaScript, and replacing the page contents with a new one. This process is accompanied by special events that allow you to add functionality to the transition between pages and to return to previous pages. For instance,
  • to start the transition to a different address: turbolinks:click, turbolinks:before-visit, turbolinks:visit;
  • or the processing of a request for a new page: turbolinks:request-start, turbolinks:request-end;
  • or on the process of displaying a new page turbolinks:before-render, turbolinks:render, turbolinks:load.

In addition, Turbolinks has a progress bar, caches the history of loaded pages and allows you to specify non-updated page elements.

Actioncable


ActionCable is part of the Ruby on Rails framework. ActionCable makes working with web sockets. To list channels on the server, a folder app/channels/with primary files channel.rband is provided connection.rb. To implement the connection to these channels - a folder app/javascript/channels/with files index.jsand consumer.js.

It’s better to get acquainted with the possibilities of ActionCable right away with an example. The simplest connection to web sockets with its help can be implemented in just a couple of steps.

  1. Create Channel File

    app / channels / hello_channel.rb
    # app/channels/hello_channel.rb
    class HelloChannel < ApplicationCable::Channel
      def subscribed
        stream_from "hello_1"
      end
    end
    

  2. Create a subscription to this channel
    app / javascript / channels / hello_channel.js
    // app/javascript/channels/hello_channel.js
    import consumer from "./consumer"
    
    consumer.subscriptions.create({ channel: "HelloChannel" }, {
      received(data) {
        console.log("Data received", data);
        document.body.innerText += `\nHello, ${data.name}!`
      }
    })
    


And the connection to the web sockets is ready.

Now, to check it, we need a simple page and an action for sending out. To do this, create a controller and add its addresses to the routes

app / controllers / hello_controller.rb
# app/controllers/hello_controller.rb
class HelloController < ApplicationController
  def index
    render :html => "Hello page...", :layout => true
  end

  def new
    ActionCable.server.broadcast("hello_1", name: params[:name])
    head 200
  end
end


config / routes.rb
# config/routes.rb
get "/hello" => "hello#index"
get "/hello/new" => "hello#new"


We start the application, go to the address 127.0.0.1haps000 / hello and open the browser console, in it you can see the logging of messages that come from the server via web sockets.

Next, we send a request for action mailing:

curl http://127.0.0.1:3000/hello/new?name=World

And look at the / hello page and the output in its console.

Form Helpers and Rails-ujs


Noteworthy are some not new, but well-established features of the Ruby on Rails framework. Among them are helpers for representations and forms. The initial convenience of helpers is that they facilitate the integration of markup with models, configurations, and other backend components. The advantage of form helpers over conventional markup is the ability to quickly list form fields without going into the details of their binding to model attributes - using helpers, the relationship between them will be formed automatically. A fragment showing an example of binding form fields with controller parameters and model attributes:

app / views / articles / new.html.erb
<%# app/views/articles/new.html.erb %>
<%#      %>
<%= form_with(model: Article.new) do |f| %>
  <div>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>
  <div>
    <%= f.label :text %>
    <%= f.text_area :text %>
  </div>
  <%= f.submit %>
<% end %>


config / locales / ru.yml
# config/locales/ru.yml
#       
ru:
  activerecord:
    attributes:
      article:
        title:  
        text:  


config / application.rb
# config/application.rb
#        config/application.rb
config.i18n.default_locale = :ru


app / controllers / articles_controller.rb
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController

  def new
    #  new   
    # rails -     
  end

  def create
    #        
    @article = Article.create(article_params)

    #    -    ,
    #    url  
    #   ,   
    redirect_to @article
  end

  private

  def article_params
    #      
    params.require(:article).permit(:title, :text)
  end
end


You can learn more about this example here and here .

Rails-ujs


Rails-ujs is the core part of the Ruby on Rails framework for unobtrusive JavaScript.
Rails-ujs provides several additional options for page elements that change or expand the way they work.

Remote option - designed for elements that access the server (links, forms) to make requests asynchronous. Link example:

<%= link_to " ", new_article_path, remote: true %>

To display the results of this query requires additional manipulation, for example, adding handler to remote-events: ajax:success, ajax:error, ajax:complete.

The confirm option - allows you to request confirmation of the action before it is executed.

<%= link_to "", article_path(article), method: :delete,
  data: { confirm: ' ,     ?' } %>


Disable_with option - allows you to deactivate an item after an action
<%= form.submit data: { disable_with: "..." } %>

Additionally, Rails-ujs has several handy features. Here are some of them:

Rails.fire(obj, name, data)- event call function
Rails.ajax(options)- wrapper over XMLHttpRequest
Rails.isCrossDomain(url)- verification of url belonging to another domain
Rails.$(selector)- wrapper over document.querySelectorAll You can

connect them to your code with the command

import Rails from "@rails/ujs"

Stimulus


Stimulus - JavaScript framework from Ruby on Rails developers.

Stimulus is one of the rare frameworks and unique in its own way, since it implements frontend development using new JavaScript approaches, while it does not seek to control all your actions and does not impose a separation of the frontend from the backend.

The basic task of Stimulus is to bind handlers to events. According to Stimulus, the source code must be placed on controller classes, and their methods should be used as handlers. By default, the folder app/javascript/controllers/with the primary file is allocated for stimulus controllers in the project index.js. Here we can add our controllers, for this we need to create a file with a suffix _controller.js, for example,articles_controller.js. Next, the Stimulus loader imports such files and connects the controllers to the corresponding blocks on our pages.

The controllers in Stimulus have additional equipment: initialization of the controller object ( initialize), helpers for accessing elements inside the block ( targets, targets), attaching the controller object to the block ( connect) and disconnecting from it ( disconnect), accessing the data attributes of the block ( this.data.get). The following is an example of an active / inactive stateful block written in Stimulus.

app / views / home / show.html.erb
<%# app/views/home/show.html.erb %>

<%#   home   %>
<%#      (  ) %>
<div data-controller="home"
    data-home-active-text="Activated" data-home-deactive-text="Deactivated">

  <%#    text  home  %>
  <p data-target="home.text"></p>

  <%#   action click  home %>
  <button data-action="home#click"></button>

</div>



app / javascript / controllers / home_controller.js
// app/javascript/controllers/home_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  //  
  static targets = [ "text" ]

  initialize() {
    //      
    this.activeText = this.data.get("active-text");
    this.deactiveText = this.data.get("deactive-text");
  }

  connect() {
    //   /
    this.active = this.data.get("active") == "true";
    //  
    this.refresh();
  }

  disconnect() {
    //     
    this.element.setAttribute("data-home-active", !!this.active);
  }

  click() {
    //  
    this.active = !this.active;
    //  
    this.refresh();
  }

  //    
  refresh(){
    //     
    this.element.style.background =   this.active ? "none" : "#EEE";
    //  
    this.textTarget.innerHTML =   this.active ? this.activeText : this.deactiveText;
    //   
    this.textTarget.style.color = this.active ? "black" : "#777";
  }
}


Despite the preservation of the old principles for implementing interactive functionality on classic pages, the development approach with Stimulus is significantly improved: the structure of the source code is structured in a new way, the binding of handlers to events is changed, and additional equipment is provided. Thanks to these amenities and its simplicity, the Stimulus framework allows you to quickly and competently structure even a large frontend.

Additionally, it is worth emphasizing that Stimulus combines well with other Ruby on Rails features - useful emergence appears in almost every bundle.

Stimulus and Webpacker


Webpacker provides commands for creating an application with a connected Stimulus:

rails new myapp --webpack=stimulus

Or to connect it to an already created project:

bundle exec rails webpacker:install:stimulus

Stimulus and JavaScript


Stimulus promotes the use of modern JavaScript development principles to implement interactivity on your pages. With Stimulus, the frontend solution is built in a modular way, OOP is used for event handlers, and the code is thoughtfully structured. Using the targeting tool, using stimulus controllers, it’s convenient to control the connection to the block elements of complex graphic components taken from third-party libraries or written independently (calendars, auto-compilers, lists, trees and others). Thanks to this, Stimulus is one of the simplest ways to switch from outdated frontend-tools and get the necessary productivity from the use of modern pure JavaScript.

Stimulus and Ruby on Rails


With the code structure recommended by Stimulus, you continue to write in JavaScript in the same way you would in Ruby on Rails. You also declare controllers, also bind actions to methods. With Stimulus, frontend development becomes similar to backend, which makes it easier to work both there and there.

Stimulus and ActionCable


Using the initialize and connect methods in stimulus controllers, it’s convenient to bind web sockets not to the whole page, but to its individual blocks and to precisely work with incoming messages. It is becoming easier to organize several parallel streams on one page with independent channel switching.

Stimulus and Turbolinks


Stimulus is activated as soon as Turbolinks loads the page; no additional manipulations are needed to connect Stimulus to the blocks of the page.

Turbolinks not only facilitates the loading of pages, but also caches their contents upon transition. When you return to the cached page from the history, Stimulus is activated automatically, as when loading a new page. If you want to save some values ​​before disconnecting the controller from the unit, you can use the method disconnect- and then, when returning and connecting the controller, it will be able to restore its last state. In the code of the first example of working with Stimulus, you can see how, when disconnecting ( disconnect), the value is fixed in the data-attribute of the controller block this.active, and when connecting ( connect), it is extracted back.

Return through pages can be a useful way to work with the application. For example, when working with a form, it may be necessary to fill in / edit the value in the directory on another page, then return and select new data.

Another example: let's say we need to edit or see the values ​​on the web chat settings page. It will be useful for us, when disconnecting the controller from the unit with the conversation, to remember the identifier of the last message, so when returning, first request the missing new messages, and then open the connection via web sockets.

Thus, a return on page history can be used as a convenient way to work with your web application.

Stimulus and Helpers Forms


Stimulus works closely with markup, and with the help of helpers, data is easily embedded in html blocks, thanks to this, part of the data can be loaded into the data attribute of the block and delivered to the controller.

app / views / articles / show.html.erb
<%#       app/views/articles/show.html.erb %>

<%#      %>
<% article_data = article.attributes
      .slice("id", "created_at", "updated_at", "author", "title").to_json %>

<%#   div    content_tag %>
<%= content_tag :div,
    :data => { controller: :articles },
    "data-articles-attributes" => article_data do %>
  <%# ... %>
<% end %>


app / javascript / controllers / articles_controller.js (snippet)
//   
//   initialize  app/javascript/controllers/articles_controller.js
initialize() {
  this.attributes = JSON.parse(this.data.get("attributes"));
  //   
  // ...
}


Stimulus and Rails-ujs


Using Stimulus and remote options, you can directly connect controllers to ajax events and process query results. Declare the link using Rails-ujs and attach a stimulus handler to it.

link with remote option and stimulus handler
<%= link_to " ",
  article_path(article, format: :html),
  data: { remote: true, action: "ajax:success->articles#showArticle" } %>


When you click on this link, an asynchronous Ajax request to the rails controller articles_controller.rbfor the show action will occur . Upon receipt of a positive response, an event will fire ajax:successand the method showArticlefrom the controller will be calledapp/javascript/controllers/articles_controller.js

controller showArticle method app / javascript / controllers / articles_controller.js
showArticle(e) {

  //      
  const xhr = e.detail[2];

  //    
  this.showFormTarget.innerHTML = xhr.responseText;

  //  
  this.showFormTarget.style.display = "block";
}


What's next?


These tools, together with the Ruby on Rails framework, open new horizons for fullstack development. Moreover, the described tools are relatively simple and do not require long dives - everything necessary for a successful project is on the surface.

Build web applications with modern and fast fullstack development tools with the Ruby on Rails framework and enjoy it!

All Articles