In this chapter, you will learn how to use the Action Cable feature in Rails to build a real-time chat application that employs the WebSocket protocol.
What are WebSockets?
The World Wide Web primarily works on the principle of REST, which encapsulates the HTTP protocol. However, one of its drawbacks is that it can be slower for real-time updates since it initiates a fresh connection each time.
WebSockets are highly efficient for real-time data exchange, providing low-latency communication. Unlike HTTP which provides a half-duplex communication, the WebSocket protocol enables full-duplex interaction between a web browser or any other client, and a web server with low overhead.
Most browsers including Chrome, Firefox and Edge, support the WebSocket protocol. The WebSocket protocol introduces ws (WebSocket) and wss (WebSocket Secure) as two new uniform resource identifier (URI) schemes.
The process of initiating the WebSocket communication starts with a client opening a HTTP connection with the server. The connection request has an upgrade header that specifies the WebSocket protocol. The server responds by including the upgrade header, indicating that it is switching to WebSocket protocol. This process is called as handshake.
A Real-time Chat App using WebSocket
Let us create a Rails application and enable Action Cable feature in it, to build a simple chat application that implements the WebSocket protocol.
To start with, create a new application –
rails new chat_app
cd chat_app
If you are using Rails version earlier than 8.0, then you will need to include action_cable gem in your GemFile â
gem "action_cable"
And then,
bundle install
You need not perform the above step, if you have the following in your Gemfile,
gem "rails","~> 8.0.0"
As Rails will automatically include the correct version of Action Cable.
You should mount the Action Cable in the application’s routing configuration by modifying config/routes.rb as:
Rails.application.routes.draw do
root "chatroom#index"
mount ActionCable.server =>"/cable"end
Your chat application provides a channel for the users. Use the generate command to generate Chatroom channel.
rails generate channel Chatroom
The messages entered by the users are characterized by the content attribute of the message model.
rails generate model Message content:text
rails db:migrate
You now have a Message table in the SQLite database, and the files app/channels/chatroom_channel.rb as well as a JavaScript code in app/javascript/channels/chatroom_channel.js
Update chatroom_channel.rb and define subscribed as well as receive methods in the ChatroomChannel class −
The subscribed Method is called automatically when the client sends a WebSocket message. It tells the server: "Start streaming messages from the chatroom broadcast to this client", while stream_from sets up a listener (stream) on the server side for the "chatroom" broadcast.
The receive method is called when the client sends a WebSocket message. It gets that data payload (after parsing JSON). It saves the message to the database and immediately re-broadcasts it back to all clients subscribed to "chatroom".
To create a Controller with an index action –
rails generate controller Chatroom index
Modify the index action to retrieve all the messages from the Message model â
How does this view function? Note that Rails mounts the WebSocket server as it starts. The index view starts a WebSocket client and sends a connection request. When the send Button in the browser is clicked, the text in the input box is received by the server and is stored in the Message model. The text area element of the web page is populated by the messages available so far.
With all the above steps accurately performed, run the Rails server. Open two tabs of your browser and try sending messages as shown in the figure below:
Ajax stands for Asynchronous JavaScript and XML. Ajax is not a single technology; it is a suite of several technologies. Ajax incorporates the following −
XHTML for the markup of web pages
CSS for the styling
Dynamic display and interaction using the DOM
Data manipulation and interchange using XML
Data retrieval using XMLHttpRequest
JavaScript as the glue that meshes all this together
Ajax enables you to retrieve data for a web page without having to refresh the contents of the entire page. In the basic web architecture, the user clicks a link or submits a form. The form is submitted to the server, which then sends back a response. The response is then displayed for the user on a new page.
When you interact with an Ajax-powered web page, it loads an Ajax engine in the background. The engine is written in JavaScript and its responsibility is to both communicate with the web server and display the results to the user. When you submit data using an Ajax-powered form, the server returns an HTML fragment that contains the server’s response and displays only the data that is new or changed as opposed to refreshing the entire page.
For a complete detail on AJAX, you can go through our AJAX Tutorial
How Rails Implements Ajax
Rails has a simple, consistent model for how it implements Ajax operations. Once the browser has rendered and displayed the initial web page, different user actions cause it to display a new web page (like any traditional web application) or trigger an Ajax operation −
Trigger − This trigger could be the user clicking on a button or link, the user making changes to the data on a form or in a field, or just a periodic trigger (based on a timer).
Request − A JavaScript method, XMLHttpRequest, sends data associated with the trigger to an action handler on the server. The data might be the ID of a checkbox, the text in an entry field, or a whole form.
Processing − The server-side action handler (Rails controller action) does something with the data and returns an HTML fragment to the web client.
Response − The client-side JavaScript, which Rails creates automatically, receives the HTML fragment and uses it to update a specified part of the current page’s HTML, often the content of a <div> tag.
These steps are the simplest way to use Ajax in a Rails application, but with a little extra work, you can have the server return any kind of data in response to an Ajax request, and you can create custom JavaScript in the browser to perform more involved interactions.
Example of AJAX
This example works based on scaffold; Destroy concept works based on AJAX.
In this example, we will provide, list, show and create operations on ponies table. If you did not understand the scaffold technology, then we would suggest you to go through the previous chapters first and then continue with AJAX on Rails.
Creating An Application
Let us start with the creation of an application It will be done as follows â
rails new ponies
The above command creates an application, now we need to call the app directory using with cd command.
cd ponies
It will enter into an application directory then we need to call a scaffold command. It will be done as follows −
Above command generates the scaffold with name and profession column.
Model(app/models/pony.rb)
Controller (app/controllers/ponies_controller.rb)
Views (app/views/ponies/)
Routes
We need to migrate the data base with the following command −
rails db:migrate
Scaffolding adds the required routes in config/routes.db
Rails.application.routes.draw do
resources :poniesend
Now Run the Rails application as follows command
rails server
Now open the web browser and call a URL as http://localhost:3000/ponies/new. The output will be as follows −
Click the Create Pony button, it will generate the result as follows â
Creating an Ajax
Now open app/views/ponies/index.html.erb with a suitable text editor. Update your destroy line with :remote => true, :class => ‘delete_pony’. It will look like as follows −
Now open your controller file which is placed at app/controllers/ponies_controller.rb and add the following code in the destroy method as shown below −
classPoniesController<ApplicationController
before_action :set_pony, only:%i[ show edit update destroy ]# GET /ponies or /ponies.jsondefindex@ponies=Pony.all
end# GET /ponies/1 or /ponies/1.jsondefshowend# GET /ponies/newdefnew@pony=Pony.newend# GET /ponies/1/editdefeditend# POST /ponies or /ponies.jsondefcreate@pony=Pony.new(pony_params)
respond_to do|format|[email protected]
respond_to do|format|
format.html { redirect_to ponies_url }
format.json { head :no_content}
format.js { render layout:false}endendprivate# Use callbacks to share common setup or constraints between actions.defset_pony@pony=Pony.find(params.expect(:id))end# Only allow a list of trusted parameters through.defpony_params
params.expect(pony:[:name,:profession])endend
Create a few ponies as shown below â
Till now, we are working on scaffold, now click the Destroy button, it will call a pop-up that works based on Ajax.
If you click the OK button, it will delete the record from pony. Final output will be as follows −
In Rails, the Asset Pipeline library is designed for organizing, caching, and serving static assets, such as JavaScript, CSS, and image files. In this chapter, you will learn about ImportMap that streamlines and optimizes the delivery of JavaScript to enhance the performance and maintainability of the application.
From Version 7, Rails uses ImportMaps as default, enabling building JavaScript applications without JS bundlers like jsbuild. You can still use the JS builders such as esbuild, rollup or webpack by specifying in the rails new command −
Railsnew myapp --javascript=webpack
Install ImportMaps
When the application structure is created without the above syntax, Rails automatically installs the importmaps-rails gem, as it can be found in the projectâs Gemfile −
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"
You can also install it manually in existing applications −
Note: ImportMaps are supported natively in all major, modern browsers.
ImportMap helps you to map and load JavaScript modules directly in browser without installing the npm packages with Node.js.
With ImportMap installed, Rails uses it to serve JavaScript by adding the following statement inside the <head> section of the layout template (app\views\layouts\application.html.erb) −
<%= javascript_importmap_tags %>
A file named as application.js is also stored in app\javascript\controllers\application.js which the modular JS files live. Additionally, the config\importmap.rb file the module pins from where the modules are imported. This file is automatically reloaded in Development upon changes, but note that you must restart the server if you remove pins and need them gone from the rendered ImportMap or list of preloads.
It makes sense to use logical names that match the package names used by npm, such that if you later want to start transpiling or bundling your code, you won’t have to change any module imports.
To add new npm Packages if your application is supported by importmap-rails, run the bin/importmap pin command from your terminal −
importmap pin react react-dom
Then, import the package into application.js as usual −
import React from "react"
import ReactDOM from "react-dom"
Choosing between ImportMaps and other JS bundlers depends on the requirements of the application. Import maps are the default option because it reduces complexity, improves developer experience, and delivers performance gains. You may need other JS bundlers like webpack if your code requires a transpilation step, such as JSX or TypeScript.
Asset Pipeline in Rails is a framework that helps you manage and deliver static assets â like JavaScript, CSS, and image files â in an organized, efficient, and performant way.
Earlier versions of Rails used a gem called Sprockets for the asset pipeline. However, there has been a lot of evolution of Asset Management Techniques within the last few years, leading to significant changes that have influenced how assets are managed in web applications. These include −
Browser Support − Modern browsers have improved support for new features and syntax, reducing the need for transpilation.
HTTP/2 − The introduction of HTTP/2 has made it easier to serve multiple files in parallel, reducing the need for bundling.
ES6+ − Modern JavaScript syntax (ES6+) is supported by most modern browsers, reducing the need for transpilation.
Therefore, the asset pipeline powered by Propshaft, no longer includes transpilation, bundling, or compression by default.
The purpose of the Asset Pipeline is to −
Organize your frontend assets
Preprocess assets (e.g., compile SCSS to CSS, ES6 to JS)
Concatenate multiple files into a single file to reduce HTTP requests
Minify or compress assets for faster loading.
Cache-bust assets using fingerprinting
Propshaft Features
Propshaft assumes that your assets are in a browser-ready formatâlike plain CSS, JavaScript, or preprocessed images (like JPEGs or PNGs). Its job is to organize, version, and serve those assets efficiently. This is done by adopting the following strategies −
Asset Load Order
With Propshaft, the loading order of dependent files can be controlled by specifying each file explicitly and organizing them manually or ensuring they are included in the correct sequence within your HTML or layout files.
Here is an example of how you can specify the exact order for loading CSS and JavaScript files by including each file individually in a specific order −
ES6 modules are helpful if you have dependencies within JavaScript files. Use import statements to explicitly control dependencies within JavaScript code. Just make sure your JavaScript files are set up as modules using <script type=”module”> instead of script type=”text/javascript” in your HTML −
// main.js
import { initUtilities } from "./utilities.js";
import { setupFeature } from "./feature.js";
Combine Files when necessary
If you have several JavaScript or CSS files that must always load together, you can combine them into a single file. For example, you could create a combined.js file that imports or copies code from other scripts.
Fingerprinting
Fingerprinting is a technique that makes the name of a file dependent on its content. In Rails, asset versioning uses fingerprinting to add unique identifiers to asset filenames. Fingerprinting still remains an integral part in spite of the evolution mentioned above.
Propshaft: The Default Asset Pipeline in Rails 8
In Rails 8, Propshaft is the default asset pipeline replacing Sprockets for new apps. With Propshaft, the asset management becomes Simple, modern, minimalistic.
Propshaft uses a direct folder structure to map assets. For example, your assets are placed here −
app/assets/
stylesheets/
javascripts/
images/
When a Rails application is created with Propshaft as its asset management tool,
rails new myapp
The projectâs Gemfile includes “propshaft” gem in addition to the other important gems such as −
# For asset pipeline
gem "propshaft"# For managing JavaScript imports
gem "importmap-rails"# Turbo and Stimulus (Hotwire)
gem "turbo-rails"
gem "stimulus-rails"
The projectâs assets live inside the designated folders as −
As you can see, Assets are directly served based on folder structure. Also, the fingerprinting is automatic. When you run rails assets:precompile, files get digested with a unique hash.
For example −
application-0d23fcd3245.css
logo-3f9d7ab8c3.png
Rails also uses ImportMap as the default for serving JavaScript. Hence, Propshaft uses the ImportMap configuration to pin various JavaScript modules −
The applicationâs CSS files are loaded via Propshaft and JavaScript via ImportMap. This is reflected in the master layout of the application (app/views/layouts/application.html.erb)
In this chapter, you will learn how to integrate JavaScript functionality into your Rails application, and how you can use external JavaScript packages. This chapter also introduces Turbo which is used for creating fast, modern web applications without having to reach for a client-side JavaScript framework.
In modern web development, we often break our JavaScript into many smaller, manageable files (modules). We take these separate JavaScript files (along with assets like CSS and images) and bundle them into a smaller number of optimized files that the browser can then load more efficiently. This process is performed by JS bundlers.
The Role of JS Bundlers
A JS bundler typically does the following −
Module Resolution: Creates a dependency graph based on the relationships between your JavaScript files
Bundling: Take the dependent modules and combine them into one or more bundle files to reduce the number of HTTP requests by the browser. Bundlers also integrate with other tools to transform your code. For example, transpilation which means Converting newer JavaScript syntax (ES6+, JSX, TypeScript) into older, more widely supported JavaScript versions; minification (to reduce its file size) and apply various optimizations.
Asset Handling: Some bundlers can also handle other types of assets, like CSS, images, and fonts, often processing them through loaders and plugins.
When you create a new Rails application, you can specify the JavaScript bundler webpack, esbuild, rollup, etc.
Webpack JS Bundler
Webpack is a static module bundler for modern JavaScript applications. It builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, as the static assets to serve your content from.
Railsnew myapp --javascript=webpack
Esbuild JS Bundler
esbuild is a very fast bundler, good for modern apps. It is a free and open-source module bundler and minifier for JavaScript and CSS written in Go. It supports TypeScript, JSX, tree-shaking and is extensible through plugins.
Railsnew myapp --javascript=esbuild
Rollup JS Bundler
Rollup is another JS module bundler which compiles small pieces of code into something larger libraries by using the new standardized format for code modules included in the ES6 revision of JavaScript.
Railsnew myapp --javascript=rollup
To skip JavaScript support, you can create a Rails application with the following command line
Railsnew myapp --skip-javascript
Since version 7, Rails uses ES modules with no build step. ImportMap is the default.
Railsnew myapp --javascript=importmap
Installing a JavaScript Runtime
If you are using esbuild, Rollup.js, or Webpack to bundle your JavaScript in your Rails application, Node.js and Yarn must be installed.
Install Node.js
For Windows, download the installer (https://nodejs.org/dist/v22.15.0/node-v22.15.0-x64.msi) from the official website and run the wizard. For other OS platforms, obtain the download files from https://nodejs.org/en/download.
Install Yarn
It is recommended to install Yarn through the npm package manager, which comes bundled with Node.js when you install it on your system.
Once you have npm installed, you can run the following both to install and upgrade Yarn −
npm install --global yarn
Turbo
Rails also installs Turbo to speed up your application. It reduces the amount of custom JavaScript that most web applications will need to write and fast, modern, progressively enhanced web applications.
Turbo Drive, Turbo Frame and Turbo Streams are the components of Turbo.
Turbo Drive speeds up the page load process.
Turbo Frame allows predefined parts of a page to be updated on request.
Turbo Streams allow you to broadcast changes made by other users over WebSockets and update pieces of a page after a Form submission without requiring a full page load.
Turbo lets you send HTML from the server which updates parts of the page dynamically, without any custom JavaScript.
In this chapter, you will learn how to handle rich text content in a Rails application with the help of Action Text component.
We will cover the following topics in this chapter:
What Action Text is, and how to install and configure it.
How to create, render, style, and customize rich text content.
What is Action Text?
Installing Action Text enables you to display the rich text content – text that can including formatting features such as bold, italics, colors, fonts, etc. Action Text stores the rich text content in a table, then attach it to any of our models.
Action Text component comes with a WYSIWYG editor called Trix. You can provide a user-friendly interface in your application (similar to what you see in the email composer in Gmail application).
For this chapter, we start by creating a new Rails application:
rails newBlogApp
Then, inside the application, letâs generate a scaffolded Post resource and run migrations.
rails generate scaffold Post title:string
rails db:migrate
Next, add a root route for our Postâs index view to the top of our routes.rb file.
Rails.application.routes.draw do
root to:"posts#index"
resources :postsend
Install Action Text
To add the rich text support, install Action Text component, followed by running migrations again.
rails action_text:install
rails db:migrate
This creates the files app/javascript/application.js and app/assets/stylesheets/actiontext.css. The second migration creates tables for Active Storage blobs and attachments (action_text_rich_texts, active_storage_blobs, active_storage_attachments, active_storage_variant_records)
The application.js file shows that trix and actiontext support has been added.
Presently, the new Post form only has the text input field for title. Let us add rich_text_area input type (provided by ActionText) in the app/views/posts/_form.html.erb template (shown in bold)
<%= form_with(model: post) do |form| %>
<% if post.errors.any? %>
<div style="color: red"><h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.each do |error| %>
<li><%= error.full_message %></li><%end%>
</ul></div><%end%>
That’s it. Save all the changes and run the server. The /posts/new route provides a trix editor for the content field.
Click on the Create Post. The application displays the confirmation of the post creation with the help of show view.
Adding Embedded Images
In this chapter, you learned how to add rich text editor in your blogging app. You can add Embedded Images and Attachments support. For this, you will need to install the image_processing gem.
Add the following in the application’s Gemfile −
gem 'image_processing','~> 1.2'
And then run the following command −
bundle install
The image_processing gem is used by Active Storage to things such as resize, crop, and optimize images, generate thumbnails for embedded images and convert images to different formats (JPEG, PNG, WebP, etc.).
The Active Job framework in Rails is extremely helpful for declaring background jobs and executing them asynchronously.
Applications that involve background tasks such as sending emails, pushing notifications, background processing of CSV files and backing up the databases, integration with external apps through APIs, handling file uploads, etc., can be scheduled with Active Job framework.
Emails with Active Job
As explained in the previous chapter (Ruby on Rails – Sending Emails), there is a welcome_email() method in the UserMailer class.
classUserMailer<ApplicationMailer
default from:" GMAIL_USERNAME"defwelcome_email(user)@user= user
mail(to:@user.email, subject:"Welcome to My Awesome Site!")endend</pre>
Next, you need to create an Active Job with the following command:
rails generate job send_emails
This creates a custom job (SendEmailsJob) to handle the email-sending process asynchronously.
Active Job provides a default perform() method which you need to override to call the welcome_email() method from the UserMailer class.
You now need to install a queuing service to handle the background jobs. We have a variety of background queuing libraries such as delayed jobs, sidekiq, rescue, and so on.
We will be using Sidekiq for the active job. For that, we must add a gem of sidekiq into Gemfile:
gem 'sidekiq'
bundle install
However, you need to let Rails know that this gem is to be used for background processing. So, edit your application.rb to set the active_job.queue_adapter.
moduleMyApplicationclassApplication<Rails::Application# make sure the adapter's gem has been included in your Gemfile.
After configuring queueing backend, you must add your job to the queue. Enqueue a job for execution when the queuing system is available. Along with it, the queue will change whenever the time changes. You may set times for when a task needs to be completed after a month or to be executed as soon as possible.
Considering that SendEmailsJob is the name of our job, the multiple techniques listed below can be used to achieve enqueuing options.
Recent versions of Ruby on Rails do not require you to generate a custom job when you're just using ActionMailer with deliver_later. To enable Active Job in your Rails application for sending emails asynchronously (such as when a User is created), you just need to queue the email delivery job instead of sending it directly.
Edit your UserController and modify the create action as below:
# UserMailer.welcome_email(@user).deliver_now # Send email after user creationUserMailer.welcome_email(@user).deliver_later # Send email later
redirect_to @user, notice:"User was successfully created. A welcome email has been sent."else
render :new, status::unprocessable_entityendend</pre>
You can specify the wait parameter to deliver_later() method such as −
The Active Job framework has certain hooks so that a defined logic can be triggered at various points during a job’s life cycle. These are the available callbacks for active jobs:
Callback
Description
before_enqueue
Defines a callback that will get called right before the job is enqueued.
around_enqueue
Defines a callback that will get called around the enqueuing of the job.
after_enqueue
Defines a callback that will get called right after the job is enqueued.
before_perform
Defines a callback that will get called right before the jobâs perform method is executed.
around_perform
Defines a callback that will get called around the jobâs perform method.
after_perform
Defines a callback that will get called right after the jobâs perform method has finished.
Retrying or Discarding Failed Jobs
It's possible to retry or discard a failed job by using retry_on or discard_on, respectively. If the configuration doesn't specify, a failed job will not be retried. Using retry_on or discard_on, respectively, you can attempt again after a failed job. And default retry_on is to 3 seconds and 5 attempts.
Include this form element in new.html.erb script and visit http://localhost:3000/books/new in the browser to display the form −
If you provide a pure HTML form in a Rails application, it raises the following issues:
You must manually set action and method attributes.
No automatic field population as the values are lost if the form fails validation – especially if the form is used for update action.
You must manually handle CSRF protection.
Rails Form Helpers
Rails provides various form helpers to be used along with other helpers that generate the form elements like input types. These helpers when used instead of raw HTML forms has several advantages, making development faster, cleaner, and more maintainable.
Following form helpers are available:
form_with
form_tag
form_for
The form_with Helper
The form_with helper is the recommended approach to render model-backed forms (linked to a database record).
When called without arguments, it creates an HTML <form> tag with the value of the method attribute set to post and the value of the action attribute set to the current page.
For example, assuming the current page is a home page at /home, the generated HTML will look like this:
<%= form_with do|form|%>
Put Form elements here
<% end %>
The corresponding HTMLscript will look like the following:
<!--BEGIN app/views/books/new.html.erb --><form action="/books" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token"
value="XolEv4UeO6RkT0yLuhmBKvYcPANvmW4Fj1CP07wvb2Bd4-WqCvoPt_R2Zo055uQTzM-FURYNLMibkcSbiGm5pg" autocomplete="off" />PutForm elements here
</form><!--END app/views/books/index.html.erb -->
Note that the form contains a hidden input element, authenticity_token, required for POST and PUT form submissions so as to prevent cross-site request forgery (CSRF) attacks. You can thus see how the form helpers automatically enable the security feature.
The most preferred approach is to create a model-backed form y passing the model object as a parameter to form_with helper:
<%= form_with model:@bookdo|f|%>
Put various form elements here
<% end %>
Instead of the raw HTML form elements, you can use Rails helpers to render elements such as text fields, radio buttons, etc.
The label Helper
The label helper renders a HTML label.
<%= form_with model: @book do |f| %>
<%= f.label :title%>
This is equivalent to the HTML label element:
<label for=" title">Title</label>
The text_field Helper
The text_field helper renders a text input element, equivalent to <input type= “text”>
<%= form_with model: @user do |f| %>
<%= f.text_field :author%>
<% end %>
The corresponding HTML code would be −
<input type="text" name="author" id="author">
The text_area Helper
The text_area helper generates a textarea element. Example:
<%= form_with model: @user do |f| %>
<%= f.textarea :detail%>
<% end %>
The radio_button Helper
It’s the Rails helper for <input type= “radio”>. For example:
<%= f.radio_button :format,"Gender"%>
The check_box Helper
The check_box helper is used to render a check box, as done by <input type=”checkbox”>. For example:
<%= f.check_box :available%>
The submit Helper
The submit helper outputs an <input type= “submit”> button that submits the form. Example:
<%= f.submit "Create Book"%>
Its equivalent HTML would be:
<input type="submit" value="Create Book">
We shall use these helpers to build a form that is used to enter book data.
The browser display in response to http://localhost:3000/books/new URL is:
The form_tag Helper
The form_tag helper also renders the HTML tag, but it is not linked to any ActiveRecord model. While the form_with helper uses the model object such as @book, the form_tag uses a URL path. You also must define the form submission method (POST/PUT) manually. The forms built with form_tag are generally used for setting search criteria or filters.
The helpers for rendering form elements like text field are suffixed with _tag. For example, the text_field_tag helper generates text type input field.
The following code renders a serach form with the use of form_tag helper
The browser display in response to http://localhost:3000/books/new URL is:
The form_tag Helper
The form_tag helper also renders the HTML tag, but it is not linked to any ActiveRecord model. While the form_with helper uses the model object such as @book, the form_tag uses a URL path. You also must define the form submission method (POST/PUT) manually. The forms built with form_tag are generally used for setting search criteria or filters.
The helpers for rendering form elements like text field are suffixed with _tag. For example, the text_field_tag helper generates text type input field.
The following code renders a serach form with the use of form_tag helper
While you’re developing Rails applications, especially those which are mainly providing you with a simple interface to data in a database, it can often be useful to use the scaffold method.
Scaffolding provides more than cheap demo thrills. Here are some benefits −
You can quickly get code in front of your users for feedback.
You are motivated by faster success.
You can learn how Rails works by looking at the generated code.
You can use scaffolding as a foundation to jump start your development.
To understand scaffolding, let’s start by creating a new Rails application named cookbook. It will have database called cookbook and a table called recipes.
Creating an Empty Rails Web Application
Open a command window and navigate to where you want to create this cookbook web application and run the following command to create a complete project structure.
rails new cookbook
Rails uses SQLite as the default database. To specify other database type, use -d option. For example, the following command creates a Rail application with mysql as its backend database.
rails new cookbook -d mysql
Rails supports PostgreSql, Oracle, SQL Server and Maria DB databases also.
By default, Rails includes importmaps for JavaScript, but you can choose a different frontend framework such as Webpack, Tailwind CSS, Bootstrap or ESBuild.
If you are using RubyMine IDE, the new project wizard lets you choose the database and the frontend framework.
Rails generates database.yml configuration file inside the config/ directory. It has the configuration for development as well as production environment.
If you specify -d option with mysql as the database, database.yml file must contain the database credentials.
development:
adapter: mysql
database: cookbook
username: root
password:[password]
host: localhost
test:
adapter: mysql
database: cookbook
username: root
password:[password]
host: localhost
production:
adapter: mysql
database: cookbook
username: root
password:[password]
host: localhost
If you prefer to use MySQL or PostgreSQL, you will need to run the command:
rails db:create
Generated Scaffold Code
With the scaffold action, Rails generates all the code it needs dynamically. By running scaffold as a script, we can get all the code written to disk, where we can investigate it and then start tailoring it to our requirements.
Run the scaffold generator to create a Recipe model with title (string) and instructions (text):
rails g scaffold Recipe tittle:string instructions:text
The command prompt terminal (terminal in Ubuntu) shows the following log as different files are created:
This command creates:
Model: app/models/recipe.rb
Migration: db/migrate/ rb
Controller: app/controllers/recipes_controller.rb
Views: app/views/recipes/
Routes: Updates config/routes.rb
Run the Migration
You can use Rails Migrations to create and maintain tables. Apply the migration to create the recipes table in MySQL:
Let's look at the code behind the controller. This code is generated by the scaffold generator. If you open app/controllers/recipes_controller.rb, then you will find something as follows
classRecipesController<ApplicationController
before_action :set_recipe, only:%i[ show edit update destroy ]# GET /recipes or /recipes.jsondefindex@recipes=Recipe.all
end# GET /recipes/1 or /recipes/1.jsondefshowend# GET /recipes/newdefnew@recipe=Recipe.newend# GET /recipes/1/editdefeditend# POST /recipes or /recipes.jsondefcreate@recipe=Recipe.new(recipe_params)
respond_to do|format|
format.html { redirect_to recipes_path, status::see_other, notice:"Recipe was successfully destroyed."}
format.json { head :no_content}endendprivate# Use callbacks to share common setup or constraints between actions.defset_recipe@recipe=Recipe.find(params.expect(:id))end# Only allow a list of trusted parameters through.defrecipe_params
params.expect(recipe:[:tittle,:instructions])endend</pre>
When the user of a Rails application selects an action, e.g. "Show" - the controller will execute any code in the appropriate section - "def show" - and then by default will render a template of the same name - "show.html.erb". This default behavior can be overwritten.
The controller uses ActiveRecord methods such as find, find_all, new, save, update_attributes, and destroy to move data to and from the database tables. Note that you do not have to write any SQL statements, rails will take care of it automatically.
This single line of code will bring the database table to life. It will provide with a simple interface to your data, and ways of −
Creating new entries
Editing current entries
Viewing current entries
Destroying current entries
When creating or editing an entry, scaffold will do all the hard work like form generation and handling for you, and will even provide clever form generation, supporting the following types of inputs −
Simple text strings
Text areas (or large blocks of text)
Date selectors
Date-time selectors
Now, go to the cookbook directory and run the Web Server using the following command −
rails server
Now, open a browser and navigate to http://127.0.0.1:3000/recipes/new. This will provide you a screen to create new entries in the recipes table. A screenshot is shown below
Once you press the Create button to create a new recipe, your record is added into the recipes table and it shows the following result
You can see the option to edit, show, and destroy the records. So, play around with these options.
Enhancing the Model
Rails gives you a lot of error handling for free. To understand this, add some validation rules to the empty recipe model −
Modify app/models/recipe.rb as follows and then test your application −
When Rails renders a view as a response, it does so by combining the view with the current layout. A layout defines the surroundings of an HTML page. It’s the place to define a common look and feel of your final output.
By default, Rails uses the app/views/layouts/application.html.erb file as the main layout. It is created when you create a new ails application.
If you create a library application with rails new library command, its main layout code would be as follows:
The application.html.erb file acts as a base template used by the individual view templates. For example, the index action in the BooksController renders the index.html.erb view. The tag <%= yield %> inside application.html.erb marks a section where content from the view should be inserted.
This can be verified by looking at the log generated on the command terminal when you start the server and visit the index view (http://localhost:3000/books/)
StartedGET"/books"for::1 at 2025-03-2500:11:13+0530Processing by BooksController#index as HTMLRendering layout layouts/application.html.erb
Rendering books/index.html.erb within layouts/application
BookLoad(4.3ms)SELECT"books".*FROM"books"/*action='index',application='Controllerdemo',controller='books'*/
â³ app/views/books/index.html.erb:1Rendered books/index.html.erb within layouts/application (Duration:14.6ms |GC:0.0ms)Rendered layout layouts/application.html.erb (Duration:519.7ms |GC:0.3ms)Completed200OKin2448ms (Views:518.6ms |ActiveRecord:4.3ms (1 query,0 cached)|GC:0.3ms)
Rails first renders the application layout and then renders the index.html.erb inside it. Thus the yield tag is a place holder for the output of the view to be inserted.
Let us assume that the Rails application has a BooksController with index and show actions, A bookmodel that has been migrated and a few book records already entered.
classBooksController<ApplicationControllerdefindex@books=Book.all
end
The index action renders the index view (index.html.erb):
<% if @books.blank? %>
<p>There are not any books currently in the system.</p>
<% else %>
<p>These are the current books in our system</p><ul id = "books">
<% @books.each do |c| %>
<li><%= link_to c.title, {:action => 'show', :id => c.id} -%></li>
<% end %>
</ul>
<% end %>
<p><%= link_to "Add new Book", {:action => 'new' }%></p>
The following figure shows the output of index view when the default application layout is used.
Changing the Default Layout
Let us now change the default layout. Save the following code to app\views\layouts\standard.html.erb.
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns ="http://www.w3.org/1999/xhtml"><head><meta http-equiv ="Content-Type" content ="text/html; charset = iso-8859-1"/><meta http-equiv ="Content-Language" content ="en-us"/><title>LibraryInfoSystem</title></head><body id ="library"><div id ="container"><div id ="header"><h1>LibraryInfoSystem</h1><h3>Library powered by Ruby on Rails</h3></div><div id ="content"><%= yield -%>
</div>
<div id ="sidebar"></div></div></body></html></pre>
You let the controllers know what template to use by the name of the file, so following a same naming scheme is advised. Now open books_controller.rb and add layout command to specify the layout to be used.
classBooksController<ApplicationController
layout "standard"defindex@books=Book.all
end
It instructs the controller that we want to use a layout available in the standard.html.erb file. Now try browsing books that will produce the following screen. Restart the server and visit the index view. The browser shows the following output.
The command terminal log also confirms that the standard layout is in use.
Rendered books/index.html.erb within layouts/standard (Duration:6.9ms |GC:0.0ms)Rendered layout layouts/standard.html.erb (Duration:9.0ms |GC:0.0ms)
Adding the Style Sheet
Till now, we have not created any style sheet, so Rails is using the default style sheet. Now let's create a new file called style.css and save it in /public/stylesheets. Add the following code to this file.
You can also create a layout with multiple yielding regions:
<html><head>
<%= yield :head %>
</head><body>
<%= yield %>
</body></html>
The main body of the view will always render into the unnamed yield. To render content into a named yield, call the content_for method with the same argument as the named yield.
Using Partials
Rails supports Partial templates - usually just called "partials" are reusable components stored in files prefixed with an underscore (_).
Let us add a navbar to the standard layout. For this purpose, first save the following HTML as _navbar.html.erb in app\views\books folder
To render a partial as part of a view, you use the render method inside the standard layout. Here, the navbar is to be displayed on the top, so add the following stamen in the <head> section.
<%= render "navbar" %>
This will render a file named _navbar.html.erb at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore to distinguish them from regular views, even though they are referred to without the underscore.
You may have to update the style.css to include the navbar styles.
.horizontal-navbar {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
}
.horizontal-navbar li {
margin-right: 20px;
}
.horizontal-navbar li a {
text-decoration: none;
color: black;
}
Save the changes and refresh the browser. The index view now displays the navbar at the top as a result of including the partial template.