<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Lazarus Lazaridis</title>
    <description>The latest articles on Forem by Lazarus Lazaridis (@iridakos).</description>
    <link>https://forem.com/iridakos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F45296%2Fb48020d7-1080-42cc-ae8f-9f8a653f7a70.jpeg</url>
      <title>Forem: Lazarus Lazaridis</title>
      <link>https://forem.com/iridakos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/iridakos"/>
    <language>en</language>
    <item>
      <title>stup - a shell tool for daily notes</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Thu, 30 Apr 2020 20:11:21 +0000</pubDate>
      <link>https://forem.com/iridakos/stup-a-cli-tool-for-daily-notes-503f</link>
      <guid>https://forem.com/iridakos/stup-a-cli-tool-for-daily-notes-503f</guid>
      <description>&lt;p&gt;Over the past few years I've been participating in Stand-up meetings and it took me some time to find a convenient and effective way for &lt;strong&gt;keeping notes about what I was doing every day&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I needed to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep &lt;strong&gt;categorized notes&lt;/strong&gt; on the issues I worked on, the meetings I participated in and stuff that blocked my work etc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;easily access&lt;/strong&gt; these notes based on their date&lt;/li&gt;
&lt;li&gt;have an &lt;strong&gt;overview&lt;/strong&gt; of what I did for example the last week&lt;/li&gt;
&lt;li&gt;all of the above:

&lt;ul&gt;
&lt;li&gt;from inside a &lt;strong&gt;terminal&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;with the &lt;strong&gt;notes strictly bound&lt;/strong&gt; to the date they are taken&lt;/li&gt;
&lt;li&gt;without having to manually structure and update a single document&lt;/li&gt;
&lt;li&gt;without having to manually create a document for each day&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/iridakos/stup"&gt;stup&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I made this tool which actually is a bash script and I named it &lt;code&gt;stup&lt;/code&gt; from the term &lt;strong&gt;st&lt;/strong&gt;and-&lt;strong&gt;up&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JY7hv0s8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/iridakos/stup/blob/master/assets/stup-0-1-9.gif%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JY7hv0s8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/iridakos/stup/blob/master/assets/stup-0-1-9.gif%3Fraw%3Dtrue" alt="stup demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the project on &lt;a href="https://github.com/iridakos/stup"&gt;GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage examples
&lt;/h3&gt;

&lt;p&gt;Below are some examples showing the most important features of &lt;code&gt;stup&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding notes
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Adding a note to the default category at current date&lt;/span&gt;
stup add &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"Worked on issue #ABC123"&lt;/span&gt;

&lt;span class="c"&gt;# Adding a note to the default category setting the current date explicitly&lt;/span&gt;
stup add today &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"Worked on issue #ABC123"&lt;/span&gt;

&lt;span class="c"&gt;# Adding a note to the meetings category&lt;/span&gt;
stup add today &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"meetings"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"2 hours with @phoebe for the project kick off"&lt;/span&gt;

&lt;span class="c"&gt;# Adding a note to the blocking category for April 10th, 2020&lt;/span&gt;
stup add @ 2020-04-10 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"blocking"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"connectivity issues"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Showing notes
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Showing yesterday's notes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup

&lt;span class="c"&gt;# Showing yesterday's notes explicitly setting the date&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup yesterday

&lt;span class="c"&gt;# Showing today's notes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup today

&lt;span class="c"&gt;# Showing notes on a specific date&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup show @ 2020-04-18

&lt;span class="c"&gt;# Showing notes on a specific date for the meetings category&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup show @ 2020-04-18 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"meetings"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Retrieving all notes for a period of time
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List current week's notes&lt;/span&gt;
stup log week

&lt;span class="c"&gt;# List current week's notes skipping command's literal&lt;/span&gt;
stup week

&lt;span class="c"&gt;# List previous week's notes&lt;/span&gt;
stup log previous-week

&lt;span class="c"&gt;# List notes between January 20th, 2020 and March 2nd, 2020&lt;/span&gt;
stup log &lt;span class="nt"&gt;--from&lt;/span&gt; 2020-01-20 &lt;span class="nt"&gt;--to&lt;/span&gt; 2020-03-02

&lt;span class="c"&gt;# List meeting notes between January 20th, 2020 and March 2nd, 2020&lt;/span&gt;
stup log &lt;span class="nt"&gt;--from&lt;/span&gt; 2020-01-20 &lt;span class="nt"&gt;--to&lt;/span&gt; 2020-03-02 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"meetings"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Behind the scenes
&lt;/h3&gt;

&lt;p&gt;Notes are organized in categories.&lt;/p&gt;

&lt;p&gt;When a new note is added, &lt;code&gt;stup&lt;/code&gt; creates a markdown file and places it under the category's directory in a sub-directory with a path based on the date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;CATEGORIES_ROOT_DIRECTORY/CATEGORY/YEAR/MONTH/YYYY-MM-DD.md

&lt;span class="c"&gt;# For example, the notes of a category named "programming" April 18th, 2020 are saved under&lt;/span&gt;
CATEGORIES_ROOT_DIRECTORY/programming/2020/04/2020-04-18.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This allows users to retrieve any notes added on a specific date or in a specific period for a specific or for all categories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fetch notes for all categories&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup show @ 2020-04-18

&lt;span class="c"&gt;# Fetch notes for a specific category for previous week&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup log previous-week &lt;span class="nt"&gt;-c&lt;/span&gt; programming

&lt;span class="c"&gt;# Fetch notes for all categories for a specific period&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;stup log &lt;span class="nt"&gt;--from&lt;/span&gt; 2020-04-01 &lt;span class="nt"&gt;--to&lt;/span&gt; 2020-04-15
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can find the full documentation with usage examples on the project's &lt;a href="https://github.com/iridakos/stup"&gt;repository&lt;/a&gt; and more information about features to be added in the project's &lt;a href="https://github.com/iridakos/stup/issues"&gt;GitHub issues page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>productivity</category>
      <category>linux</category>
      <category>bash</category>
    </item>
    <item>
      <title>Creating a chat application from scratch using Rails and WebSockets</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Fri, 11 Oct 2019 08:37:04 +0000</pubDate>
      <link>https://forem.com/iridakos/creating-a-chat-application-from-scratch-using-rails-and-websockets-4a6</link>
      <guid>https://forem.com/iridakos/creating-a-chat-application-from-scratch-using-rails-and-websockets-4a6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this tutorial we are going to create a &lt;strong&gt;chat&lt;/strong&gt; web application from scratch using Rails and WebSockets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kq4_2zep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/rails-chat-tutorial.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kq4_2zep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/rails-chat-tutorial.gif" alt="Rails chat tutorial gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the code of this tutorial on &lt;a class="alert-link" href="https://github.com/iridakos/rails-chat-tutorial"&gt;&lt;i class="fa fa-github"&gt;&lt;/i&gt; GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are WebSockets
&lt;/h3&gt;

&lt;p&gt;WebSocket is actually a protocol that enables bidirectional communication between the client and the server of a web application over a single long living TCP connection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The WebSocket protocol enables interaction between a web browser (or other client application) and a web server &lt;strong&gt;with lower overheads, facilitating real-time data transfer from and to the server&lt;/strong&gt;.&lt;br&gt;
&lt;br&gt;&lt;br&gt;This is made possible by providing a standardized way for the server to send content to the client without being first requested by the client, and allowing messages to be passed back and forth while keeping the connection open. In this way, a two-way ongoing conversation can take place between the client and the server.&lt;br&gt;&lt;br&gt;The communications are done over TCP port number 80 (or 443 in the case of TLS-encrypted connections), which is of benefit for those environments which block non-web Internet connections using a firewall. Similar two-way browser-server communications have been achieved in non-standardized ways using stopgap technologies such as Comet.&lt;br&gt;
&lt;cite&gt;-- &lt;a href="https://en.wikipedia.org/wiki/WebSocket"&gt;WebSocket @ Wikipedia&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why WebSockets
&lt;/h3&gt;

&lt;p&gt;Suppose you have to create a web page that shows the statuses of running processes.&lt;br&gt;
Without WebSockets you would have to either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use AJAX with Javascript intervals to request and render the latest state of the processes or&lt;/li&gt;
&lt;li&gt;Automatically reload the page every x seconds (&lt;code&gt;&amp;lt;meta http-equiv="refresh" content="x"&amp;gt;&lt;/code&gt;) or&lt;/li&gt;
&lt;li&gt;Add a message on the page &lt;em&gt;"The statuses are not updated automatically &lt;span class="text-nowrap"&gt;¯\_(ツ)_/¯&lt;/span&gt; Press &lt;a href="//%7B%7Bpage.url%7D%7D"&gt;here&lt;/a&gt; to reload the page."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these methods would request the process statuses from the server even if nothing has changed.&lt;/p&gt;

&lt;p&gt;WebSockets are here to allow this communication to take place on demand. The cost is having to keep alive TCP connections between the server and all its clients (each for every open browser tab).&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the application
&lt;/h2&gt;

&lt;p&gt;We are going to build the web application using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ruby&lt;/strong&gt;: version &lt;strong&gt;2.6.2&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rails&lt;/strong&gt;: version &lt;strong&gt;5.2.3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setting up the environment
&lt;/h3&gt;

&lt;p&gt;We are going to install the proper ruby and rails versions.&lt;/p&gt;
&lt;h4&gt;
  
  
  Install ruby
&lt;/h4&gt;

&lt;p&gt;I use &lt;a href="https://rvm.io/"&gt;rvm&lt;/a&gt; to manage the &lt;strong&gt;Ruby&lt;/strong&gt; versions installed on my system. To install the desired ruby version use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rvm &lt;span class="nb"&gt;install &lt;/span&gt;ruby 2.6.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Install rails
&lt;/h4&gt;

&lt;p&gt;Create a directory in your system with the name &lt;code&gt;rails-chat-tutorial&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Navigate to that directory&lt;/strong&gt; and create the following two files:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;.ruby-version&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby-2.6.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;.ruby-gemset&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails-chat-tutorial
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With these file we are letting &lt;em&gt;rvm&lt;/em&gt; know that when working on this directory, we want to use the specific ruby version (&lt;code&gt;.ruby-version&lt;/code&gt;) and the gems from the specific gemset (&lt;code&gt;.ruby-gemset&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Now, re-entering in the directory you should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd .

ruby-2.6.2 - #gemset created /home/iridakos/.rvm/gems/ruby-2.6.2@rails-chat-tutorial
ruby-2.6.2 - #generating rails-chat-tutorial wrappers...........
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Install the desired rails version with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem install rails -v 5.2.3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Create the rails application
&lt;/h4&gt;

&lt;p&gt;We are ready to create our new &lt;em&gt;rails&lt;/em&gt; application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We didn't define a name for the application and rails will resolve it using the directory name: &lt;code&gt;rails-chat-tutorial&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rails will create all the application's files and install the required gems.&lt;/p&gt;

&lt;p&gt;Let's start the application to make sure that everything if fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=&amp;gt; Booting Puma
=&amp;gt; Rails 5.2.3 application starting in development
=&amp;gt; Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.2-p47), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open a browser and visit &lt;code&gt;http://localhost:3000&lt;/code&gt;, if you see this we are good to go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZX7Vraga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/01-rails-new.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZX7Vraga--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/01-rails-new.png" alt="Rails new application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Users and devise
&lt;/h3&gt;

&lt;p&gt;We are going to use the awesome &lt;a href="https://github.com/plataformatec/devise"&gt;devise&lt;/a&gt; solution for authentication.&lt;/p&gt;

&lt;p&gt;Append the following gem requirement at the bottom of the &lt;code&gt;Gemfile&lt;/code&gt; file located at the root of the application's directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'devise'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On your terminal, install the new gem by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bundle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finish integration with devise using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate devise:install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will create the model representing our uses using the devise generators.&lt;/p&gt;

&lt;p&gt;On your terminal, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate devise User username:string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: we have added an extra attribute &lt;code&gt;username&lt;/code&gt; to our model (besides the defaults generated by devise) so that we have something more friendly to present when displaying users instead of their email.&lt;/p&gt;

&lt;p&gt;Open the generated migration which you will find under &lt;code&gt;db/migrate/&amp;lt;datetime&amp;gt;_devise_create_users.rb&lt;/code&gt; and append the username's unique index definition with&lt;sup&gt;[4]&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;add_index&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="ss"&gt;unique: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Find the line in the file that defines the &lt;code&gt;username&lt;/code&gt; column and change it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to make the attribute required.&lt;/p&gt;

&lt;p&gt;Then in the User model which is located at &lt;code&gt;app/models/user.rb&lt;/code&gt; add the validation rule for uniqueness and presence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, apply the database migration using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Rooms and messages
&lt;/h3&gt;

&lt;p&gt;Each chat message is going to take place in the context of a room.&lt;/p&gt;

&lt;p&gt;Let's build them all.&lt;/p&gt;

&lt;p&gt;Use the following command to create the &lt;code&gt;Room&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate resource Room name:string:uniq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and the following command to create the &lt;code&gt;RoomMessage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate resource RoomMessage room:references user:references message:text
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are now going to define the appropriate relations&lt;sup&gt;[7]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;app/models/room.rb&lt;/code&gt; and add the relation inside the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:room_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="ss"&gt;inverse_of: :room&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;app/models/room_message.rb&lt;/code&gt; and add the relations inside the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
&lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inverse_of: :room_messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Migrate the database with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can now setup our routes so that the root request is served by the &lt;code&gt;RoomsController#index&lt;/code&gt; action.&lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;config/routes.rb&lt;/code&gt; file and change its contents to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;devise_for&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;

  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ss"&gt;controller: :rooms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action: :index&lt;/span&gt;

  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:room_messages&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:rooms&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Restart the server and try to navigate to the application's root url.&lt;/p&gt;

&lt;p&gt;You should see an error message, no worries:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PkLCcybf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PkLCcybf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/02.png" alt="No action index for the RoomsController"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have to create the &lt;code&gt;index&lt;/code&gt; action in the &lt;code&gt;RoomsController&lt;/code&gt;. Open the controller &lt;code&gt;app/controllers/rooms_controller.rb&lt;/code&gt; and change its contents to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoomsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then create the file &lt;code&gt;app/views/rooms/index.html.erb&lt;/code&gt; and for now just add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Rooms index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Reload and voilà.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SAcKnEtb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SAcKnEtb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/03.png" alt="Rooms index"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding authentication
&lt;/h3&gt;

&lt;p&gt;We want all users to be authenticated before start chatting, so we are going to add the following line in the &lt;code&gt;ApplicationController&lt;/code&gt; located at &lt;em&gt;app/controllers/application_controller.rb&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; now we should be redirected to the sign in page &lt;sup&gt;[10]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ayVG71VR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ayVG71VR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/04.png" alt="Sign in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before continuing with the good stuff, let's gear up the application with some good features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add bootstrap
&lt;/h3&gt;

&lt;p&gt;We are going to use &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; and we will integrate it in the application using the &lt;a href="https://github.com/twbs/bootstrap-rubygem"&gt;bootstrap-rubygem&lt;/a&gt; gem.&lt;/p&gt;

&lt;p&gt;Following the instructions of the gem, append the dependencies in your &lt;code&gt;Gemfile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'bootstrap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 4.3.1'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'jquery-rails'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and execute &lt;code&gt;bundle&lt;/code&gt; to fetch and install it.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;app/assets/stylesheets/application.css&lt;/code&gt; files extension to &lt;code&gt;scss&lt;/code&gt; and &lt;strong&gt;replace its contents&lt;/strong&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add the following lines to the &lt;code&gt;app/assets/javascript/application.js&lt;/code&gt; just before the &lt;code&gt;//= require_tree .&lt;/code&gt; line&lt;sup&gt;[9]&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//= require jquery3&lt;/span&gt;
&lt;span class="c1"&gt;//= require popper&lt;/span&gt;
&lt;span class="c1"&gt;//= require bootstrap-sprockets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Add simple_form
&lt;/h3&gt;

&lt;p&gt;We are going to use this great gem to generate forms easily.&lt;/p&gt;

&lt;p&gt;Append the gem dependency in your &lt;code&gt;Gemfile&lt;/code&gt; and bundle to install it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'simple_form'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then complete the integration using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate simple_form:install &lt;span class="nt"&gt;--bootstrap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We used the &lt;em&gt;--bootstrap&lt;/em&gt; directive since that's the framework we are using.&lt;/p&gt;

&lt;h3&gt;
  
  
  Devise views with bootstrap and simple form
&lt;/h3&gt;

&lt;p&gt;Devise uses its own views for sign in, register etc. But we do have a way to customize these views and now that we have ended up using bootstrap and simple forms, we can generate these views in a way that our choices are respected.&lt;/p&gt;

&lt;p&gt;In your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate devise:views
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The view for signing in is under &lt;code&gt;app/views/devise/sessions/new.html.erb&lt;/code&gt; and for signing up is under &lt;code&gt;app/views/devise/registrations/new.html.erb&lt;/code&gt;. Open these two files and change the submit button's class by replacing the following line&lt;sup&gt;&lt;a href="acknowledgments"&gt;[6]&lt;/a&gt;&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;:submit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Sign up"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;:submit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Sign up"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-success'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to render the buttons &lt;em&gt;bootstrap style&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Before viewing our changes, let's do one last thing in our default layout.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt; and replace its contents with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;RailsChatTutorial&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csrf_meta_tags&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csp_meta_tag&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_link_tag&lt;/span&gt;    &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;media: &lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_include_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This last one was to use &lt;a href="https://getbootstrap.com/docs/4.0/layout/grid/"&gt;Bootstrap's grid&lt;/a&gt; in our views.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; and view what we have created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tj3t0A7r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tj3t0A7r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/05.png" alt="Sign in with Devise and simple form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try to sign up following the &lt;code&gt;Sign up&lt;/code&gt; link of the form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wv6WWvAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wv6WWvAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/06.png" alt="Sign up without username"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there is no field to fill in the username. For that to work we have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the field in the sign up form&lt;/li&gt;
&lt;li&gt;Configure devise to accept the new attribute (&lt;code&gt;username&lt;/code&gt;) or else the &lt;code&gt;ApplicationController&lt;/code&gt; will &lt;a href="https://api.rubyonrails.org/classes/ActionController/Parameters.html"&gt;ignore it&lt;/a&gt; once submitted from the form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add the field in the sign up form, open &lt;code&gt;app/views/devise/registrations/new.html.erb&lt;/code&gt; and add these lines between the email and password fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, open the &lt;code&gt;app/controllers/application_controller.rb&lt;/code&gt; file to configure the new attribute. Change the contents to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;

  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:configure_permitted_parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :devise_controller?&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;configure_permitted_parameters&lt;/span&gt;
    &lt;span class="n"&gt;devise_parameter_sanitizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sign_up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;keys: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:username&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Done, reload and sign up&lt;sup&gt;[5]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zxt066fw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zxt066fw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/07.png" alt="Sign up with username"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleanup unused components
&lt;/h3&gt;

&lt;p&gt;We will not be using &lt;code&gt;coffee script&lt;/code&gt; or &lt;code&gt;turbolinks&lt;/code&gt; so let's remove all the related stuff.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;Gemfile&lt;/code&gt; and remove the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Use CoffeeScript for .coffee assets and views&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'coffee-rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 4.2'&lt;/span&gt;
&lt;span class="c1"&gt;# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'turbolinks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;app/assets/javascripts/application.js&lt;/code&gt; and remove the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//= require turbolinks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt; and change the following lines &lt;sup&gt;[3]&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_link_tag&lt;/span&gt;    &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;media: &lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_include_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'data-turbolinks-track'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'reload'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_link_tag&lt;/span&gt;    &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;media: &lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_include_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Check that your &lt;code&gt;app/assets/javascripts&lt;/code&gt; folder doesn't have any files with extension &lt;code&gt;.coffee&lt;/code&gt; and if you find any, remove them.&lt;sup&gt;2&lt;sup&gt;&lt;/sup&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In the terminal, also execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;rails tmp:cache:clear
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to clear any cached compiled coffee scripts.&lt;/p&gt;

&lt;p&gt;Done. Restart you server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a navigation bar
&lt;/h3&gt;

&lt;p&gt;To improve the usability of the web pages will add a top &lt;a href="https://getbootstrap.com/docs/4.0/components/navbar/"&gt;navigation bar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the directory &lt;code&gt;app/views/shared&lt;/code&gt; and a file inside it named &lt;code&gt;_navigation_bar.html.erb&lt;/code&gt;. This is going to be the partial responsible for rendering the navigation bar and which later on we will add to the application's default layout in order to be rendered on all web pages. Add these contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar navbar-expand-lg navbar-dark bg-dark justify-content-between"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar-brand"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rails chat tutorial&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar-toggler"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;data-toggle=&lt;/span&gt;&lt;span class="s"&gt;"collapse"&lt;/span&gt; &lt;span class="na"&gt;data-target=&lt;/span&gt;&lt;span class="s"&gt;"#nav-bar-collapse"&lt;/span&gt; &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"navbarColor01"&lt;/span&gt; &lt;span class="na"&gt;aria-expanded=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Toggle navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar-toggler-icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dropdown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-link dropdown-toggle"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"navbarDropdown"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;data-toggle=&lt;/span&gt;&lt;span class="s"&gt;"dropdown"&lt;/span&gt; &lt;span class="na"&gt;aria-haspopup=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;aria-expanded=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;gravatar_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;username&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dropdown-menu dropdown-menu-right"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"navbarDropdown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s1"&gt;'Logout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destroy_user_session_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: :delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'dropdown-item'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Mind the &lt;code&gt;gravatar_url(current_user)&lt;/code&gt; line. This is a helper method that we are going to use in order to resolve the gravatar url of the signed in user. This is not a builtin method, we have to defined it but it's pretty straightforward.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;app/helpers/application_helper.rb&lt;/code&gt; and add the following method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gravatar_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;gravatar_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://gravatar.com/avatar/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;gravatar_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As you can see, the user's username, avatar and sign out link will only be rendered if the user is signed in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The avatar image has a css class &lt;code&gt;avatar&lt;/code&gt;. We have to define this class in the application's stylesheets. Create a css file in which we will gather all css class that we will use in the application under the name &lt;code&gt;app/assets/stylesheets/rails-chat-tutorial.scss&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For now add the rule for the avatar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.avatar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and open &lt;code&gt;application.scss&lt;/code&gt; to import the newly created stylesheet. Add the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import "rails-chat-tutorial"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have to add this partial in the application layout. Edit &lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt; and change its contents to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;RailsChatTutorial&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csrf_meta_tags&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csp_meta_tag&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;stylesheet_link_tag&lt;/span&gt;    &lt;span class="s1"&gt;'application'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;media: &lt;/span&gt;&lt;span class="s1"&gt;'all'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_include_tag&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'shared/navigation_bar'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Reload to view the bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mbHdi-hA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mbHdi-hA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/08.png" alt="Sign up with navigation bar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome. Fill in with your desired credentials and submit the form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CypMs73i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CypMs73i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/09.png" alt="Signed in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Room management
&lt;/h3&gt;

&lt;p&gt;We are going to create a simple layout for groups.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One narrow column displaying vertically all the available rooms&lt;/li&gt;
&lt;li&gt;One wide column which is going to host the chat messages and form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rooms index page will have the second column empty since this column will be present only when user is inside a specific room.&lt;/p&gt;

&lt;p&gt;In the index page we will provide the option to create a room.&lt;/p&gt;

&lt;h4&gt;
  
  
  Room index
&lt;/h4&gt;

&lt;p&gt;First we have to load all rooms in the &lt;code&gt;RoomsController&lt;/code&gt;. Open &lt;code&gt;app/controllers/rooms_controller.rb&lt;/code&gt; and change the index action as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="vi"&gt;@rooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;app/views/rooms/index.html.erb&lt;/code&gt; and change its contents to&lt;sup&gt;[8]&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12 col-md-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;new_room_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        Create a room
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav flex-column"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"nav-link room-nav-link"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-muted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        The are no rooms
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h4&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Welcome to the RailsChatTutorial!
      &lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        We need to talk.
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;hr&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        You can create or join a room from the sidebar.
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If there are rooms, the left column of the page will render a vertical navigation with links leading to each room's page. The right column displays a simple welcome message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6eIsODM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6eIsODM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/10.png" alt="Room index"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pressing the &lt;code&gt;Create a room&lt;/code&gt; button we get the expected error for the non-existent action.&lt;/p&gt;

&lt;h4&gt;
  
  
  Room new/edit
&lt;/h4&gt;

&lt;p&gt;We have to define the actions for creating and updating a room.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app/controllers/rooms_controller.rb&lt;/code&gt; and change its contents to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoomsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="c1"&gt;# Loads:&lt;/span&gt;
  &lt;span class="c1"&gt;# @rooms = all rooms&lt;/span&gt;
  &lt;span class="c1"&gt;# @room = current room when applicable&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:load_entities&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@rooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;permitted_parameters&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:success&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Room &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was created successfully"&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;rooms_path&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;edit&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;permitted_parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:success&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Room &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was updated successfully"&lt;/span&gt;
      &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;rooms_path&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_entities&lt;/span&gt;
    &lt;span class="vi"&gt;@rooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="vi"&gt;@room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permitted_parameters&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:room&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: we preload the &lt;code&gt;@rooms&lt;/code&gt; and the &lt;code&gt;@room&lt;/code&gt; variables making them available to all actions with the &lt;code&gt;before_action :load_entities&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;We will create a simple form for the &lt;code&gt;Room&lt;/code&gt; object and we will use it both when creating and editing a room. Create the &lt;code&gt;app/views/rooms/_form.html.erb&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;simple_form_for&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-success'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, create the views for the &lt;code&gt;new&lt;/code&gt;/&lt;code&gt;edit&lt;/code&gt; action accordingly:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app/views/rooms/new.html.erb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
  Creating a room  
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'form'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/views/rooms/edit.html.erb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
  Editing room &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'form'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Time to create the first room. From the rooms' index page, press the &lt;code&gt;Create a room&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9GuW2dIn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9GuW2dIn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/11.png" alt="New room"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save and here it is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zGLeuFOl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zGLeuFOl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/12.png" alt="Room index with rooms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this class in &lt;code&gt;app/assets/stylesheets/rails-chat-tutorial.scss&lt;/code&gt; to improve the display of the rooms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.room-nav-link&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="err"&gt;.room-nav-link&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEWzXBhH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEWzXBhH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/13.png" alt="Room index with improved rooms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: We will add the &lt;code&gt;edit&lt;/code&gt; link in the room's page a.k.a. &lt;code&gt;show&lt;/code&gt; action.&lt;/p&gt;

&lt;p&gt;Before moving on to the Room page, we will refactor the index page so as to be able to use the left column's content inside the room page as well.&lt;/p&gt;

&lt;p&gt;Create the partial &lt;code&gt;app/views/rooms/_rooms.html.erb&lt;/code&gt; with contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;new_room_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-primary'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    Create a room
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav flex-column"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@rooms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'nav-link room-nav-link'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-muted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    The are no rooms
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and change the &lt;code&gt;app/views/rooms/index.html.erb&lt;/code&gt; to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12 col-md-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'rooms'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h4&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Welcome to the RailsChatTutorial!
      &lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        We need to talk.
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;hr&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        You can create or join a room from the sidebar.
      &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Room page
&lt;/h4&gt;

&lt;p&gt;Add the &lt;code&gt;show&lt;/code&gt; action in the &lt;code&gt;app/controllers/rooms_controller.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="vi"&gt;@room_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RoomMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="ss"&gt;room: &lt;/span&gt;&lt;span class="vi"&gt;@room&lt;/span&gt;
  &lt;span class="vi"&gt;@room_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;room_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We construct a new room message which we are going to use in the view to build a form for creating the chat messages.&lt;/li&gt;
&lt;li&gt;When displaying the room message, we access its user's email attribute to resolve the gravatar hash. We used &lt;code&gt;.includes(:user)&lt;/code&gt; in the query for the &lt;code&gt;@room_messages&lt;/code&gt; to fetch them along with their users avoiding &lt;a href="https://medium.com/@bretdoucette/n-1-queries-and-how-to-avoid-them-a12f02345be5"&gt;N+1 queries&lt;/a&gt;&lt;sup&gt;[1]&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create the view &lt;code&gt;app/views/rooms/show.html.erb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12 col-md-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'rooms'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@room_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;room_message&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;simple_form_for&lt;/span&gt; &lt;span class="vi"&gt;@room_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;remote: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-group mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;wrapper: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;input_html: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                                   &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'chat-input'&lt;/span&gt;
                                 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-group-append"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-primary chat-input'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :hidden&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We reused the &lt;code&gt;app/views/rooms/_rooms.html.erb&lt;/code&gt; partial that we created in the previous step&lt;/li&gt;
&lt;li&gt;We added a &lt;code&gt;div&lt;/code&gt; with class &lt;code&gt;.chat&lt;/code&gt; and this is where the room's messages are renderer.&lt;/li&gt;
&lt;li&gt;We added a form for the &lt;code&gt;@room_message&lt;/code&gt; that we instantiated in the controller. We also used the directive &lt;code&gt;remote: true&lt;/code&gt; when we instantiated the form thus the form is going to be submitted by &lt;strong&gt;Ajax&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We added a hidden field for the attribute &lt;code&gt;:room_id&lt;/code&gt; so that the value reaches the &lt;code&gt;RoomMessagesController&lt;/code&gt; once we submit the form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Style the chat components by adding the following lines to the &lt;code&gt;app/assets/stylesheets/rails-chat-tutorial.scss&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.chat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.chat-input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Navigate to a room to see what has been done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DkhOfJpf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DkhOfJpf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/14.png" alt="Room page with chat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pressing the &lt;code&gt;Send&lt;/code&gt; button nothing happens on the page but if you check the server's console you will notice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AbstractController::ActionNotFound (The action 'create' could not be found for RoomMessagesController):
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's fix that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating room messages
&lt;/h4&gt;

&lt;p&gt;This is going to be easy. All we have to do is implement the &lt;code&gt;create&lt;/code&gt; action in the &lt;code&gt;RoomMessagesController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app/controllers/room_messages_controller.rb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoomMessagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:load_entities&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@room_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RoomMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="ss"&gt;room: &lt;/span&gt;&lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:room_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_entities&lt;/span&gt;
    &lt;span class="vi"&gt;@room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:room_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:room_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we preload the room using the &lt;code&gt;room_id&lt;/code&gt; parameter that we added as a hidden field in the form in the previous step&lt;/li&gt;
&lt;li&gt;we create a new message for the room setting its user to currently signed in user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try to submit a message now, again you will see nothing but in the server console you can see from the log that the room message has been created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Started POST "/room_messages" for ::1 at 2019-04-04 19:24:33 +0300
Processing by RoomMessagesController#create as JS
  Parameters: {"utf8"=&amp;gt;"✓", "room_message"=&amp;gt;{"message"=&amp;gt;"My first message", "room_id"=&amp;gt;"8"}, "commit"=&amp;gt;"Send"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ /home/iridakos/.rvm/gems/ruby-2.6.2@rails-chat-tutorial/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
  Room Load (0.2ms)  SELECT  "rooms".* FROM "rooms" WHERE "rooms"."id" = ? LIMIT ?  [["id", 8], ["LIMIT", 1]]
  ↳ app/controllers/room_messages_controller.rb:13
   (0.1ms)  begin transaction
  ↳ app/controllers/room_messages_controller.rb:5
  RoomMessage Create (0.7ms)  INSERT INTO "room_messages" ("room_id", "user_id", "message", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["room_id", 8], ["user_id", 1], ["message", "My first message"], ["created_at", "2019-04-04 16:24:33.456641"], ["updated_at", "2019-04-04 16:24:33.456641"]]
  ↳ app/controllers/room_messages_controller.rb:5
   (4.0ms)  commit transaction
  ↳ app/controllers/room_messages_controller.rb:5
No template found for RoomMessagesController#create, rendering head :no_content
Completed 204 No Content in 88ms (ActiveRecord: 5.1ms)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A user would expect to have the message field cleared after sending a new message. We don't disappoint users.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;app/assets/javascripts/room.js&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#new_room_message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ajax:success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[type="text"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We bind to the &lt;code&gt;ajax:success&lt;/code&gt; event triggered by &lt;a href="https://guides.rubyonrails.org/working_with_javascript_in_rails.html#form-with"&gt;Rails&lt;/a&gt; on successful submission of the form and all we want to do is clear the text field's value.&lt;/p&gt;

&lt;p&gt;Reload the page and try submitting again and check it out. The field value should be emptied after sending the message.&lt;/p&gt;

&lt;h4&gt;
  
  
  Displaying room messages
&lt;/h4&gt;

&lt;p&gt;If you reload the page you will see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_iahexKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_iahexKd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/15.png" alt="Room message to string"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's beautify the messages.&lt;/p&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;app/views/rooms/show.html.erb&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-12 col-md-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s1"&gt;'rooms'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@room_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chat-message-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row no-gutters"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-auto text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;gravatar_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"message-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-right"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;
                    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;created_at&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;simple_form_for&lt;/span&gt; &lt;span class="vi"&gt;@room_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;remote: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-group mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;wrapper: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;label: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="ss"&gt;input_html: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                                   &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'chat-input'&lt;/span&gt;
                                 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-group-append"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Send"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s1"&gt;'btn btn-primary chat-input'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt; &lt;span class="ss"&gt;:room_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :hidden&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and add the following css classes &lt;strong&gt;inside the .chat class&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.chat-message-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;.avatar&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.message-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.chat-message-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Reload the page. Magic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a2AjVTTm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a2AjVTTm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/16.png" alt="Improved display of room messages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing WebSockets - ActionCable
&lt;/h2&gt;

&lt;p&gt;Time to start using WebSockets with ActionCable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Action Cable seamlessly integrates WebSockets with the rest of your Rails application. It allows for real-time features to be written in Ruby in the same style and form as the rest of your Rails application, while still being performant and scalable. It's a full-stack offering that provides both a client-side JavaScript framework and a server-side Ruby framework. You have access to your full domain model written with Active Record or your ORM of choice.&lt;br&gt;
&lt;cite&gt;-- &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html"&gt;Action Cable Overview @ Ruby on Rails Guides (v5.2.3)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Install redis
&lt;/h3&gt;

&lt;p&gt;We are going to use the &lt;code&gt;redis&lt;/code&gt; adapter which is &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html#redis-adapter"&gt;a safe option for production environments&lt;/a&gt; unlike the &lt;code&gt;async&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;You first must install &lt;a href="https://redis.io/"&gt;redis&lt;/a&gt; on your system.&lt;/p&gt;

&lt;p&gt;To install it on Ubuntu you just have to execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;redis-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To check that installation is successful, in your terminal make sure you get a PONG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ redis-cli
127.0.0.1:6379&amp;gt; ping
PONG
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure ActionCable
&lt;/h3&gt;

&lt;p&gt;Since we are working on the development environment, open your &lt;code&gt;config/cable.yml&lt;/code&gt; and replace its contents with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;channel_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rails-chat-tutorial_development&lt;/span&gt;

&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;async&lt;/span&gt;

&lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;channel_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rails-chat-tutorial_production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; we have added an option &lt;code&gt;channel_prefix&lt;/code&gt; because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Additionally, a channel_prefix may be provided to avoid channel name collisions when using the same Redis server for multiple applications&lt;br&gt;
&lt;cite&gt;-- &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html#redis-adapter"&gt;Action Cable Overview # Redis Adapter @ Ruby on Rails Guides (v5.2.3)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, we are going to add the dependency in the &lt;code&gt;Gemfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'redis'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Don't forget to bundle after altering the &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Devise for authenticating websocket connections
&lt;/h3&gt;

&lt;p&gt;When establishing a websocket connection, we don't have access to the user session but we do have access to the cookies. So, in order to be able to authenticate the user we need to do some devise related stuff first (&lt;a href="https://rubytutorial.io/actioncable-devise-authentication/"&gt;credits to Greg Molnar&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Create an initializer for warden hooks under the name &lt;code&gt;config/initializers/warden_hooks.rb&lt;/code&gt; and add the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Warden&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_set_user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Warden&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before_logout&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:scope&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain:&lt;/strong&gt; We add a cookie with the user's id upon successful sign in and we remove it once the user logs out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the websocket connection
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;app/channels/application_cable/connection.rb&lt;/code&gt; and change its contents to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ApplicationCable&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Connection&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;identified_by&lt;/span&gt; &lt;span class="ss"&gt;:current_user&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_verified_user&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_verified_user&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;verified_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user.id'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;verified_user&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;reject_unauthorized_connection&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here identified_by is a connection identifier that can be used to find the specific connection later. Note that anything marked as an identifier will automatically create a delegate by the same name on any channel instances created off the connection.&lt;br&gt;
&lt;cite&gt;-- &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html#connection-setup"&gt;Action Cable Overview # Connection setup @ Ruby on Rails Guides (v5.2.3)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;code&gt;find_verified_user&lt;/code&gt; method we access the cookie that we previously set in the warden hook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the room channel
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A channel encapsulates a logical unit of work, similar to what a controller does in a regular MVC setup.&lt;br&gt;
&lt;cite&gt;-- &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html#channelsp"&gt;Action Cable Overview # Channels @ Ruby on Rails Guides (v5.2.3)&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will create the &lt;code&gt;RoomChannel&lt;/code&gt; in which all Room pages will subscribe to.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;app/channels/room_channel.rb&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoomChannel&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationCable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Channel&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subscribed&lt;/span&gt;
    &lt;span class="n"&gt;room&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:room&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;stream_for&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;

    &lt;span class="c1"&gt;# or&lt;/span&gt;
    &lt;span class="c1"&gt;# stream_from "room_#{params[:room]}"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;subscribed&lt;/code&gt; method gets called once a subscription to the channel is established and it is responsible to setup the stream from which data will be sent back and forth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are going to configure the room page code later on to request subscriptions to this channel passing the &lt;code&gt;room&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;We have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;stream_for&lt;/code&gt;: this way Rails automatically generates a stream name for the given object (&lt;code&gt;room&lt;/code&gt; in our case), for example: "room:asdfwer234". When we want afterwards to broadcast data to the stream, all we have to do is call &lt;code&gt;RoomChannel.broadcast_to(room_object, data)&lt;/code&gt; in which case Rails resolves the stream name from the &lt;code&gt;room_object&lt;/code&gt;. In other words, we don't have to manually resolve the stream name in which the data have to be send (see next item).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This option is available when the channel handles subscriptions bound to models like in our case, a specific room&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;stream_from&lt;/code&gt;: we manually define the name of the stream and later on, when we want to broadcast to the stream, we have to use: &lt;code&gt;ActionCable.server.broadcast("room_#{a_room_id_here}", data)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read more &lt;a href="https://guides.rubyonrails.org/action_cable_overview.html#streams"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Broadcast room messages
&lt;/h3&gt;

&lt;p&gt;Every time a room message is being created, we just need to broadcast to the message's room stream.&lt;br&gt;
To do so, alter the &lt;code&gt;create&lt;/code&gt; action of the &lt;code&gt;app/controllers/room_messages_controller.rb&lt;/code&gt; to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="vi"&gt;@room_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RoomMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                     &lt;span class="ss"&gt;room: &lt;/span&gt;&lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                     &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:room_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;RoomChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast_to&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@room_message&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain&lt;/strong&gt;: we added the line &lt;code&gt;RoomChannel.broadcast_to @room, @room_message&lt;/code&gt; which will broadcast to the room's specific stream (as explained above) the &lt;code&gt;@room_message&lt;/code&gt; transformed to json via the &lt;code&gt;to_json&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;So, on the other side, the client side, we are going to be receiving the json representation of the &lt;code&gt;RoomMessage&lt;/code&gt; model. Let's see what that is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"room_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"My first message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2019-04-04T17:09:00.637Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2019-04-04T17:09:00.637Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We plan to add the new messages in the room's page via Javascript and this information is not adequate. The only thing we are missing is the user avatar. Time to refactor.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;app/models/user.rb&lt;/code&gt; and add the following method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def gravatar_url
  gravatar_id = Digest::MD5::hexdigest(email).downcase
  "https://gravatar.com/avatar/#{gravatar_id}.png"
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We had already implemented this in the &lt;code&gt;app/helpers/application_helper.rb&lt;/code&gt; file and we won't be using it anymore so &lt;strong&gt;remove it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;app/views/shared/_navigation_bar.html.erb&lt;/code&gt; and change the previous gravatar resolution to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gravatar_url&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;app/views/rooms/show.html.erb&lt;/code&gt; and change it there too, with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;room_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gravatar_url&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we will change the &lt;code&gt;JSON&lt;/code&gt; representation of the &lt;code&gt;RoomMessage&lt;/code&gt; to include the user's url:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app/models/room_message.rb&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;as_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_avatar_url: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gravatar_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's confirm that the new JSON transformation is valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"room_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"My first message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2019-04-04T17:09:00.637Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2019-04-04T17:09:00.637Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_avatar_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"https://gravatar.com/avatar/02a28db6886d578f75a820b50f2dd334.png"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, moving on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscribe to the room stream
&lt;/h3&gt;

&lt;p&gt;We are going to add some data in the room page in order to use them via Javascript to subscribe to the appropriate streams each time we visit a room.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;app/views/rooms/show.html.erb&lt;/code&gt; and alter the line in which we define the chat div, to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chat"&lt;/span&gt; &lt;span class="na"&gt;data-channel-subscribe=&lt;/span&gt;&lt;span class="s"&gt;"room"&lt;/span&gt; &lt;span class="na"&gt;data-room-id=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain&lt;/strong&gt;: We added two data attributes, one defining to which channel we want to subscribe and one defining to which room we are.&lt;/p&gt;

&lt;p&gt;At the end of the file, add the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-none"&lt;/span&gt; &lt;span class="na"&gt;data-role=&lt;/span&gt;&lt;span class="s"&gt;"message-template"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chat-message-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row no-gutters"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-auto text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;data-role=&lt;/span&gt;&lt;span class="s"&gt;"user-avatar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"message-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-1"&lt;/span&gt; &lt;span class="na"&gt;data-role=&lt;/span&gt;&lt;span class="s"&gt;"message-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-right"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;small&lt;/span&gt; &lt;span class="na"&gt;data-role=&lt;/span&gt;&lt;span class="s"&gt;"message-date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This snippet is going to be used as a template for each incoming message. Every time a message arrives, we will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clone this html&lt;/li&gt;
&lt;li&gt;alter the appropriate elements' values and&lt;/li&gt;
&lt;li&gt;append the resulting html to the end of the chat div.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we will create the Javascript that will do the work of subscribing and handling incoming channel data.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;app/assets/javascripts/channels/room_channel.js&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-channel-subscribe="room"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;room_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;room-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;messageTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-role="message-template"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollHeight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        

    &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RoomChannel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room_id&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messageTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-role="user-avatar"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_avatar_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-role="message-text"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-role="message-date"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollHeight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explain&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each element that has a data attribute &lt;code&gt;channel-subscribe&lt;/code&gt; with value &lt;code&gt;room&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Create a subscription to the "RoomChannel" passing the element's &lt;code&gt;room-id&lt;/code&gt; data attribute as a parameter with name &lt;code&gt;room&lt;/code&gt; (remember this line of the &lt;code&gt;RoomChannel&lt;/code&gt;: &lt;code&gt;Room.find params[:room]&lt;/code&gt; ?)&lt;/li&gt;
&lt;li&gt;When data is received, clone the template snippet and alter its contents based on the incoming object attributes.&lt;/li&gt;
&lt;li&gt;Append the newly generated content to the chat div and finally&lt;/li&gt;
&lt;li&gt;Do some animation to impress by scrolling smoothly to the bottom of the div.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This tutorial was originally published in &lt;a href="https://iridakos.com/tutorials/2019/04/04/creating-chat-application-rails-websockets.html"&gt;my personal blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you very much for your feedback.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[1] Armando Andini - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/3"&gt;N+1 queries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[2] Rodolfo Ruiz - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/5"&gt;Coffeescript leftovers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[3] Felix Wolfsteller - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/1"&gt;Turbolinks leftovers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[4] Maria Kravtsova - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/2"&gt;Migration typo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[5][7][8] Tony Dehnke - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/6"&gt;Sign up step&lt;/a&gt;, &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/8"&gt;Missing step for adding model relations&lt;/a&gt;, &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/7"&gt;Missing line from html code block&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[6] keytonw - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/9"&gt;Devise view missing button class&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[9] Martin - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/11"&gt;Mention order of requirements in application.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[10] Sumak - &lt;a href="https://github.com/iridakos/rails-chat-tutorial/issues/15"&gt;Remove duplicate authentication related code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all! Long post, tired cat photo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tZElKA9d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/irida.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tZElKA9d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/rails-chat-tutorial/irida.jpg" alt="Tired cat photo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>How to add bash completion to your scripts</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Wed, 09 Oct 2019 10:11:45 +0000</pubDate>
      <link>https://forem.com/iridakos/adding-bash-completion-to-your-scripts-50da</link>
      <guid>https://forem.com/iridakos/adding-bash-completion-to-your-scripts-50da</guid>
      <description>&lt;p&gt;I recently worked on creating a bash completion script for &lt;a class="ga-event-link" href="https://github.com/iridakos/goto"&gt;registering directory aliases and navigating to them with autocomplete in Bash&lt;/a&gt; and I found the feature really interesting. In this post I will familiarize you with the process of adding bash completion to your scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is bash completion?
&lt;/h2&gt;

&lt;p&gt;Bash completion is a functionality through which bash helps users type their commands faster and easier. It accomplishes that by presenting possible options when users press the &lt;code&gt;tab&lt;/code&gt; key while typing a command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git&amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
git                 git-receive-pack    git-upload-archive  
gitk                git-shell           git-upload-pack     
&lt;span class="nv"&gt;$ &lt;/span&gt;git-s&amp;lt;tab&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git-shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;The completion script is code that uses the builtin bash command &lt;code&gt;complete&lt;/code&gt; to define &lt;strong&gt;which completion suggestions&lt;/strong&gt; can be displayed for a given &lt;strong&gt;executable&lt;/strong&gt;. The nature of the completion options vary from simple static to highly sophisticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why bother
&lt;/h3&gt;

&lt;p&gt;It is good to provide such a functionality to users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to save them from typing text when it can be auto-completed&lt;/li&gt;
&lt;li&gt;to help them know what are the available continuations to their commands&lt;/li&gt;
&lt;li&gt;to prevent errors and improve their experience by hiding or showing options based on what they have already typed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hands on
&lt;/h2&gt;

&lt;p&gt;Here's what we are going to do in this tutorial.&lt;/p&gt;

&lt;p&gt;We will first create a dummy executable script called &lt;code&gt;dothis&lt;/code&gt;. All it does is execute the command which resides on the number, that was passed as an argument, in user's history. For example, the following command will simply execute the &lt;code&gt;ls -a&lt;/code&gt; command given that it exists in history with number &lt;code&gt;235&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;dothis 235
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we will create a bash completion script that will display commands along with their number from &lt;strong&gt;user's history&lt;/strong&gt; and we will "bind" it to the &lt;code&gt;dothis&lt;/code&gt; executable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
215 &lt;span class="nb"&gt;ls
&lt;/span&gt;216 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
217 &lt;span class="nb"&gt;cd&lt;/span&gt; ~
218 man &lt;span class="nb"&gt;history
&lt;/span&gt;219 git status
220 &lt;span class="nb"&gt;history&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 8-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DXdAd9uG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/bash-completion/post-image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DXdAd9uG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/bash-completion/post-image.png" alt="Bash completion image"&gt;&lt;/a&gt;&lt;br&gt;
You can see a gif demonstrating the functionality &lt;a href="https://github.com/iridakos/bash-completion-tutorial"&gt;at this tutorial's code repository on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let the show begin.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the executable script
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;dothis&lt;/code&gt; in your working directory and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No command number passed"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-1000&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; ^&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exists&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Command with number &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; was not found in recent history"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We first check if the script was called with an argument&lt;/li&gt;
&lt;li&gt;We then check if the specific number is included in the last 1000 commands

&lt;ul&gt;
&lt;li&gt;if it exists we execute the command using the &lt;code&gt;fc&lt;/code&gt; functionality&lt;/li&gt;
&lt;li&gt;otherwise we display an error message&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make the script executable with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are going to execute this script many times in this tutorial so I suggest you place it in a folder that is included in your &lt;a href="http://www.linfo.org/path_env_var.html"&gt;path&lt;/a&gt; so that we can access it from anywhere with just typing &lt;code&gt;dothis&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I installed it in my &lt;strong&gt;home bin&lt;/strong&gt; folder using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;install&lt;/span&gt; ./dothis ~/bin/dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can do the same given that you have a &lt;code&gt;~/bin&lt;/code&gt; folder and that it is included in your &lt;code&gt;PATH&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Try to see if it's working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should see this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis
No &lt;span class="nb"&gt;command &lt;/span&gt;number passed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the completion script
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;dothis-completion.bash&lt;/code&gt;. From now on we will be referring to this file with the term &lt;strong&gt;completion script&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once we add some code to it, we will &lt;code&gt;source&lt;/code&gt; it to allow the completion to take effect.&lt;br&gt;
We must &lt;code&gt;source&lt;/code&gt; this file &lt;strong&gt;every single time we change something in it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will discuss our options for registering this script whenever a bash shell opens later on.&lt;/p&gt;
&lt;h4&gt;
  
  
  Static completion
&lt;/h4&gt;

&lt;p&gt;Suppose that the &lt;code&gt;dothis&lt;/code&gt; program supported a list of commands, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;now&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tomorrow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;never&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's use the &lt;code&gt;complete&lt;/code&gt; command to register this list for completion. To use the proper terminology, we say we use the &lt;code&gt;complete&lt;/code&gt; command to define a completion specification (&lt;strong&gt;compspec&lt;/strong&gt;) for our program.&lt;/p&gt;

&lt;p&gt;Add this to the completion script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"now tomorrow never"&lt;/span&gt; dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What did we specify with the &lt;code&gt;complete&lt;/code&gt; command above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we used the &lt;code&gt;-W&lt;/code&gt; (&lt;em&gt;wordlist&lt;/em&gt;) option to provide a list of words for completion.&lt;/li&gt;
&lt;li&gt;we defined to which "program" these completion words will be used (the &lt;code&gt;dothis&lt;/code&gt; parameter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./dothis-completion.bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now try pressing tab twice in the command line as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
never     now       tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Try again after typing the &lt;code&gt;n&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis n&amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
never now
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Magic! The completion options are automatically filtered to match only those starting with &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
The options are not displayed in the order that we defined them in the word list, they are automatically sorted.&lt;/p&gt;

&lt;p&gt;There are a lot of other options to be used instead of the &lt;code&gt;-W&lt;/code&gt; that we used in this section. Most of them produce completions in a fixed manner, meaning that we don't intervene dynamically to filter their output.&lt;/p&gt;

&lt;p&gt;For example, if we wanted to have directory names as completion words for the &lt;code&gt;dothis&lt;/code&gt; program, we would change the complete command to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; directory dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pressing tab after the &lt;code&gt;dothis&lt;/code&gt; program, we would get a list of the directories in the current directory from which we execute the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
dir1/ dir2/ dir3/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Find the complete list of the available flags &lt;a href="https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#Programmable-Completion-Builtins"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dynamic completion
&lt;/h4&gt;

&lt;p&gt;We will be producing the completions of the &lt;code&gt;dothis&lt;/code&gt; executable with the following logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If user presses the tab key right after the command, we will show the last 50 executed commands along with their numbers in history&lt;/li&gt;
&lt;li&gt;If user presses the tab key after typing a number that matches more than one commands from history, we will show only those commands along with their numbers in history&lt;/li&gt;
&lt;li&gt;If user presses the tab key after a number that matches exactly one command in history, we auto-complete the number without appending the command's literal (if it's confusing, you will better understand later on, no worries)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start by defining a function that will execute each time user requests completion on a &lt;code&gt;dothis&lt;/code&gt; command. Change the completion script to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  COMPREPLY+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  COMPREPLY+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"tomorrow"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  COMPREPLY+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we used the &lt;code&gt;-F&lt;/code&gt; flag in the complete command defining that the &lt;code&gt;_dothis_completions&lt;/code&gt; is the function that will provide the completions of the &lt;code&gt;dothis&lt;/code&gt; executable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMPREPLY&lt;/code&gt; is an array variable used to store the completions - the completion mechanism uses this variable to display its contents as completions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now source the script and go for completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
never now tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Perfect. We produce the same completions as in the previous section with the word list. Or not? Try this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis nev&amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
never     now       tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, even though we type &lt;strong&gt;nev&lt;/strong&gt; and then we request for completion, the available options are always the same and nothing gets completed automatically. Why is this happening?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The contents of the &lt;code&gt;COMPREPLY&lt;/code&gt; variable are always displayed. The function is responsible to add/remove entries from there now.&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;COMPREPLY&lt;/code&gt; variable had only one element then that word would be automatically completed in the command. Since current implementation always return the same three words, this will not happen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter &lt;code&gt;compgen&lt;/code&gt;: a builtin command that generates completions supporting most of the options of the &lt;code&gt;complete&lt;/code&gt; command (ex. &lt;code&gt;-W&lt;/code&gt; for word list, &lt;code&gt;-d&lt;/code&gt; for directories) and filtering them based on what already has been typed by the user.&lt;/p&gt;

&lt;p&gt;Don't worry if you feel confused, everything will become clear later on.&lt;/p&gt;

&lt;p&gt;Type the following in the console to better understand what compgen does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"now tomorrow never"&lt;/span&gt;
now
tomorrow
never
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"now tomorrow never"&lt;/span&gt; n
now
never
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"now tomorrow never"&lt;/span&gt; t
tomorrow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So now we can use it but we need to find a way to know what has been typed after the &lt;code&gt;dothis&lt;/code&gt; command.&lt;br&gt;
We already have the way. The bash completion facilities provide &lt;a href="https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#Bash-Variables"&gt;bash variables&lt;/a&gt; related to the completion taking place. Here are the more important ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;COMP_WORDS&lt;/code&gt;: an array of all the words typed after the name of the program the &lt;code&gt;compspec&lt;/code&gt; belongs to&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMP_CWORD&lt;/code&gt;: an index of the &lt;code&gt;COMP_WORDS&lt;/code&gt; array pointing to the word the current cursor is at - in other words, the index of the word the cursor was when the tab key was pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMP_LINE&lt;/code&gt;: the current command line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To access the word just after the &lt;code&gt;dothis&lt;/code&gt; word, we can use the value of &lt;code&gt;COMP_WORDS[1]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Change the completion script again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"now tomorrow never"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Source and there you are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;dothis
never     now       tomorrow  
&lt;span class="nv"&gt;$ &lt;/span&gt;dothis n
never  now
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, instead of the &lt;em&gt;now, tomorrow, never&lt;/em&gt; words, we would like to see actual numbers from the command history.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fc -l&lt;/code&gt; command followed by a negative number &lt;code&gt;-n&lt;/code&gt; displays the last &lt;strong&gt;n&lt;/strong&gt; commands.&lt;br&gt;
So, we will use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which lists the last &lt;strong&gt;50&lt;/strong&gt; executed commands along with their numbers. The only manipulation we need to do is replace tabs with spaces in order to be displayed properly from the completion mechanism. &lt;code&gt;sed&lt;/code&gt; to the rescue.&lt;/p&gt;

&lt;p&gt;Change the completion script as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\t//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Source and test in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
632 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   649 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   666 &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.bash_profile
633 clear                           650 clear                           667 &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.bashrc
634 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   651 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   668 clear
635 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   652 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   669 &lt;span class="nb"&gt;install&lt;/span&gt; ./dothis ~/bin/dothis
636 clear                           653 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   670 dothis
637 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   654 clear                           671 dothis 6546545646
638 clear                           655 dothis 654                      672 clear
639 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   656 dothis 631                      673 dothis
640 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   657 dothis 150                      674 dothis 651
641 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   658 dothis                          675 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash
642 clear                           659 clear                           676 dothis 651
643 dothis 623  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;              660 dothis                          677 dothis 659
644 clear                           661 &lt;span class="nb"&gt;install&lt;/span&gt; ./dothis ~/bin/dothis   678 clear
645 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   662 dothis                          679 dothis 665
646 clear                           663 &lt;span class="nb"&gt;install&lt;/span&gt; ./dothis ~/bin/dothis   680 clear
647 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash   664 dothis                          681 clear
648 clear                           665 &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Not bad.&lt;/p&gt;

&lt;p&gt;We do have a problem though. Try typing a number as you see it in your completion list and the press the  key again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis 623&amp;lt;tab&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;dothis 623  &lt;span class="nb"&gt;ls &lt;/span&gt;623  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
...
&lt;span class="nv"&gt;$ &lt;/span&gt;dothis 623  &lt;span class="nb"&gt;ls &lt;/span&gt;623  &lt;span class="nb"&gt;ls &lt;/span&gt;623  &lt;span class="nb"&gt;ls &lt;/span&gt;623  &lt;span class="nb"&gt;ls &lt;/span&gt;623  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is happening because in our completion script, we used the &lt;code&gt;${COMP_WORDS[1]}&lt;/code&gt; to always check the first typed word after the &lt;code&gt;dothis&lt;/code&gt; command (the number &lt;code&gt;623&lt;/code&gt; in the above snippet). Hence the completion continues suggesting the same completion again and again while pressing the tab key.&lt;/p&gt;

&lt;p&gt;To fix this, we will not allow any kind of completion to take place if at least one argument has already been typed. We will add a condition in our function checking the size of the aforementioned &lt;code&gt;COMP_WORDS&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    return
  fi

  &lt;/span&gt;&lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\t//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Source and retry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dothis 623&amp;lt;tab&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;dothis 623 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;&amp;lt;tab&amp;gt; &lt;span class="c"&gt;# SUCCESS: nothing happens here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is another thing we don't like though. We do want to display the numbers along with the corresponding commands to help users decide which one is the desired but when there is only one completion suggestion and gets automatically picked by the completion mechanism, &lt;strong&gt;we shouldn't append the command literal too&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In other words, our &lt;code&gt;dothis&lt;/code&gt; executable accepts only a number and we haven't added any functionality checking or expecting other arguments. When our completion function gives only one result, we should trim the command literal and respond only with the command number.&lt;/p&gt;

&lt;p&gt;To accomplish this, we will keep the response of the &lt;code&gt;compgen&lt;/code&gt; command in an array variable and if its size is &lt;strong&gt;1&lt;/strong&gt; we will trim the one and only element to keep just the number. Otherwise, we'll let the array as is.&lt;/p&gt;

&lt;p&gt;Change the completion script to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    return
  fi&lt;/span&gt;

  &lt;span class="c"&gt;# keep the suggestions in a local variable&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\t/ /'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# if there's only one match, we remove the command literal&lt;/span&gt;
    &lt;span class="c"&gt;# to proceed with the automatic completion of the number&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[0]/%\ */&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# more than one suggestions resolved,&lt;/span&gt;
    &lt;span class="c"&gt;# respond with the suggestions intact&lt;/span&gt;
    &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Registering the completion script
&lt;/h3&gt;

&lt;p&gt;If you want to enable the completion just for you on your machine, all you have to do is add a line in your &lt;code&gt;.bashrc&lt;/code&gt; file sourcing the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &amp;lt;path-to-your-script&amp;gt;/dothis-completion.bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you want to enable the completion for all users, you can just copy the script under &lt;code&gt;/etc/bash_completion.d/&lt;/code&gt; and it will automatically be loaded by Bash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine tuning the completion script
&lt;/h3&gt;

&lt;p&gt;Some extra steps for better results :)&lt;/p&gt;

&lt;h4&gt;
  
  
  Displaying each entry in a new line
&lt;/h4&gt;

&lt;p&gt;Well, in the bash completion script I was working on, I too had to present suggestions consisting of two parts. I wanted to display the first part with the default color and the second part with a gray color to point out that it was just help text. In this tutorial's example, it would be nice to present the numbers in the default color but the command literal in another one, less fancy.&lt;/p&gt;

&lt;p&gt;Unfortunately, this is not possible at least for the time being because the completions are displayed as plain text and color directives are not processed (for example: &lt;code&gt;\e[34mBlue&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;What we can do though to improve user experience (or not :D) is to display each entry in a new line. This solution is not that obvious since again we can't just append a new line character in each &lt;code&gt;COMPREPLY&lt;/code&gt; entry. We will follow a rather &lt;a href="https://unix.stackexchange.com/questions/166908/is-there-anyway-to-get-compreply-to-be-output-as-a-vertical-list-of-words-instea"&gt;hackish&lt;/a&gt; way and pad suggestion literals to such a width that fills the terminal's width.&lt;/p&gt;

&lt;p&gt;Enter &lt;code&gt;printf&lt;/code&gt;. If you want to display each suggestion on each own line, change the completion script to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    return
  fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\t//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[0]/%\ */&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else
    for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!suggestions[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;suggestions[&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%*s'&lt;/span&gt; &lt;span class="s2"&gt;"-&lt;/span&gt;&lt;span class="nv"&gt;$COLUMNS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done

    &lt;/span&gt;&lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Source and test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
...
499 &lt;span class="nb"&gt;source &lt;/span&gt;dothis-completion.bash                   
500 clear
...       
503 dothis 500
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Customizable behavior
&lt;/h4&gt;

&lt;p&gt;In our case, we hard-coded to display the last 50 commands for completion. This is not a good practice. We should first respect what each user might prefer and if he/she hasn't made any preference then we should default to 50.&lt;/p&gt;

&lt;p&gt;To accomplish that we will check if an environment variable &lt;code&gt;DOTHIS_COMPLETION_COMMANDS_NUMBER&lt;/code&gt; has been set.&lt;/p&gt;

&lt;p&gt;Change the completion script one last time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#/usr/bin/env bash&lt;/span&gt;
_dothis_completions&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    return
  fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;commands_number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOTHIS_COMPLETION_COMMANDS_NUMBER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;50&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;compgen&lt;/span&gt; &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;fc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; -&lt;span class="nv"&gt;$commands_number&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\t//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMP_WORDS&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[0]/%\ */&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else
    for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!suggestions[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      &lt;/span&gt;suggestions[&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%*s'&lt;/span&gt; &lt;span class="s2"&gt;"-&lt;/span&gt;&lt;span class="nv"&gt;$COLUMNS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done

    &lt;/span&gt;&lt;span class="nv"&gt;COMPREPLY&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; _dothis_completions dothis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Source and test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOTHIS_COMPLETION_COMMANDS_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;span class="nv"&gt;$ &lt;/span&gt;dothis &amp;lt;tab&amp;gt;&amp;lt;tab&amp;gt;
505 clear
506 &lt;span class="nb"&gt;source&lt;/span&gt; ./dothis-completion.bash
507 dothis clear
508 clear
509 &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOTHIS_COMPLETION_COMMANDS_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/git/git/blob/master/contrib/completion/git-completion.bash"&gt;Git's completion script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/iridakos/goto/blob/master/goto.sh"&gt;Bash completion for &lt;code&gt;goto&lt;/code&gt;, one of my shell scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion"&gt;Bash Reference Manual: Programmable Completion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#Programmable-Completion-Builtins"&gt;Bash Reference Manual: Programmable Completion Builtins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/A-Programmable-Completion-Example.html#A-Programmable-Completion-Example"&gt;Bash Reference Manual: A Programmable Completion Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#Bash-Variables"&gt;Bash Reference Manual: Bash Variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;You can find the &lt;strong&gt;code of this tutorial&lt;/strong&gt; on &lt;a href="https://github.com/iridakos/bash-completion-tutorial"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post was originally published on &lt;a href="https://iridakos.com/tutorials/2018/03/01/bash-programmable-completion-tutorial.html"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long post, cat photo
&lt;/h3&gt;

&lt;p&gt;Let me introduce you to my debugger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2k9cbdXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/bash-completion/cat.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2k9cbdXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/bash-completion/cat.jpg" alt="My debugger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all folks!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tutorial</category>
      <category>bash</category>
    </item>
    <item>
      <title>Composing better emails (with examples from the software development world)</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Tue, 08 Oct 2019 07:26:26 +0000</pubDate>
      <link>https://forem.com/iridakos/composing-better-emails-490m</link>
      <guid>https://forem.com/iridakos/composing-better-emails-490m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Email communication&lt;/strong&gt; is not my favorite but since I can't avoid it, I am trying to compose messages in a way that I think it makes it easier for both me and the recipient:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to &lt;strong&gt;quickly address what is being communicated&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;avoid misunderstandings&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;save time&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some tips. They don't apply to all type of messages, I provide &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; examples to better describe each case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emphasize text with bold/underlined font
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Emphasizing&lt;/strong&gt; the appropriate parts of a message, especially when it's a long one, you help readers &lt;strong&gt;quickly get an idea&lt;/strong&gt; of what the email is about and easily &lt;strong&gt;locate the important stuff&lt;/strong&gt; after going back to it at some point in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;

&lt;pre&gt;Hello all,

I noticed that there are many logs for blabla the last few days and I
 don't think that it is normal. I believe the problem is the updated 
version of gem blabla.

I have opened an issue describing the case in Redmine (#455) in the current version.
Feel free to change its priority in case blabla.

Thanks,
Lazarus
&lt;/pre&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;

&lt;pre&gt;Hello all,

I noticed that there are &lt;b&gt;many logs for blabla the last few days&lt;/b&gt; and
 I don't think that it is normal. I believe the problem is the updated 
version of gem blabla.

I have opened &lt;b&gt;a Redmine issue (#455)&lt;/b&gt; describing the case in the 
current version.
Feel free to change its priority in case blabla.

Thanks,
Lazarus
&lt;/pre&gt;

&lt;h2&gt;
  
  
  Use specific dates instead of &lt;code&gt;yesterday&lt;/code&gt;, &lt;code&gt;tomorrow&lt;/code&gt; etc
&lt;/h2&gt;

&lt;p&gt;The moment you send an email is not the moment that it will be read by its&lt;br&gt;
 recipients. &lt;strong&gt;Avoid using only temporal adverbs/nouns&lt;/strong&gt; like &lt;code&gt;yesterday&lt;/code&gt;, &lt;br&gt;
&lt;code&gt;today&lt;/code&gt;, &lt;code&gt;tomorrow&lt;/code&gt;, &lt;code&gt;two hours ago&lt;/code&gt; etc but &lt;strong&gt;include also the specific &lt;br&gt;
dates/times&lt;/strong&gt; otherwise they might be misunderstood or require from &lt;br&gt;
recipients to check the email's sent date/time to calculate the actual &lt;br&gt;
time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;

&lt;pre&gt;Dear QA,

Yesterday we released a fix for the bug 455 on staging and we plan to 
release it next Monday if you give us the green light by tomorrow end of 
day.

Thanks,
Lazarus&lt;/pre&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;


&lt;pre&gt;Dear QA,

&lt;b&gt;Yesterday&lt;/b&gt;, June 25th, 2019 we released a fix for the &lt;b&gt;bug 
#455 on staging&lt;/b&gt; and we plan to release it on &lt;b&gt;production next 
Monday (July 1st, 2019)&lt;/b&gt; if you give us the &lt;b&gt;green light by tomorrow 
(June 27th, 2019)&lt;/b&gt; end of day.

Thanks,
Lazarus&lt;/pre&gt;

&lt;h2&gt;
  
  
  Use links for references
&lt;/h2&gt;

&lt;p&gt;Use bookmarkable links when you refer to something that would eventually &lt;br&gt;
require from the recipient to search for in another platform.&lt;/p&gt;

&lt;p&gt;This has two benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save time&lt;/li&gt;
&lt;li&gt;Eliminate ambiguous references&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;


&lt;pre&gt;Dear Phoebe,

I didn't understand the process described on the issue about the logging 
bug. Irida's comment was not very clear either. Can you please help me?

Thanks,
Lazarus&lt;/pre&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;


&lt;pre&gt;Dear Phoebe,

I didn't understand the process described on the issue about the logging 
bug (Redmine #453). This comment from Irida was not very clear either. Can you 
please help me?

Thanks,
Lazarus&lt;/pre&gt;

&lt;h2&gt;
  
  
  Structure long messages
&lt;/h2&gt;

&lt;p&gt;Long messages are in general not very effective and parts of them are &lt;br&gt;
prone to be ignored.&lt;/p&gt;

&lt;p&gt;When I have to compose such a message I try to &lt;strong&gt;categorize the text in &lt;br&gt;
contextual sections&lt;/strong&gt; and then structure them &lt;strong&gt;using headers and &lt;br&gt;
paragraphs&lt;/strong&gt; allowing readers to navigate to them at a glance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;


  &lt;pre&gt;Dear team,

Last week we had a problem with the logs in the production environment. I 
am talking about the Redmine issue #453. We noticed a huge increase in 
the log messages blabla leading to delayed responses because blah blah 
blah. At some point the server run out of disk and everything fell apart 
blah blah blah. Administrators backed up the logs and removed them from 
the server to blah blah blah. We started investigating what is going on 
immediately and after two days we managed to reproduce the error in the 
staging environment as well. The problem was the usage of an external 
library that had a bug which blah blah blah. We removed the library and 
the bug was no longer reproducable in the staging environment. The buggy 
library was blah blah blah. We released the fix yesterday and everything 
seems to be back to normal. We also added some scripts that will notify us 
immediately if such conditions start to emerge.

Thank you for your time,
Lazarus&lt;/pre&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;


  &lt;pre&gt;Dear team,

&lt;strong&gt;&lt;u&gt;Background&lt;/u&gt;&lt;/strong&gt;
Last week we had a problem with the logs in the production environment. We
 noticed a huge increase in the log messages blabla.

&lt;strong&gt;&lt;u&gt;Consequences&lt;/u&gt;&lt;/strong&gt;
The increase led to delayed responses because blah blah blah. At some 
point the server run out of disk and everything fell apart blah blah blah.

&lt;strong&gt;&lt;u&gt;Cause of the problem&lt;/u&gt;&lt;/strong&gt;
We started investigating what is going on immediately and after two days 
we managed to reproduce the error in the staging environment as well.
The problem was the usage of an external library that had a bug which blah 
blah blah. The buggy library was blah blah blah.

&lt;strong&gt;&lt;u&gt;Actions taken&lt;/u&gt;&lt;/strong&gt;

1. Administrators &lt;strong&gt;backed up the logs&lt;/strong&gt; and removed them 
from the server to blah blah blah.
2. We &lt;strong&gt;removed the library&lt;/strong&gt; and the bug was no longer 
reproducible in the staging environment.
3. We &lt;strong&gt;released the fix&lt;/strong&gt; yesterday, June 26th, 2019.
4. We also &lt;strong&gt;added some scripts that will notify us on time&lt;/strong&gt; 
if such conditions start to emerge.

&lt;strong&gt;&lt;u&gt;Current status&lt;/u&gt;&lt;/strong&gt;
&lt;strong&gt;The server is up and running, everything seems to be back to 
normal.&lt;/strong&gt;

&lt;strong&gt;&lt;u&gt;More info&lt;/u&gt;&lt;/strong&gt;
Related Redmine issue #453
External library - Official bug report

Thank you for your time,
Lazarus&lt;/pre&gt;

&lt;h2&gt;
  
  
  Be specific on what you request from whom
&lt;/h2&gt;

&lt;p&gt;I have seen many many many emails for which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the sender wanted &lt;strong&gt;something&lt;/strong&gt; by &lt;strong&gt;someone&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;the recipients

&lt;ul&gt;
&lt;li&gt;didn't think they were the ones to provide it&lt;/li&gt;
&lt;li&gt;understood something else than what the sender asked for&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I try to be very specific and provide details (when possible) on what I &lt;br&gt;
need and when there's more than one recipients I refer to each one &lt;br&gt;
explicitly using &lt;code&gt;@&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Before
&lt;/h4&gt;


&lt;pre&gt;Dear all,

I have pushed a commit that possibly fixes the bug with the logging in 
staging. I won't be here tomorrow so can you review and release if ok?

Bye.
&lt;/pre&gt;

&lt;h4&gt;
  
  
  After
&lt;/h4&gt;

&lt;pre&gt;Hey,

I have pushed a &lt;strong&gt;commit that 
possibly fixes the bug (Redmine #453)&lt;/strong&gt; 
with the logging in staging.

Since &lt;strong&gt;I won't be here tomorrow, June 27th&lt;/strong&gt;, can you 
please:

&lt;strong&gt;&lt;u&gt;@captain:&lt;/u&gt;&lt;/strong&gt; &lt;strong&gt;review&lt;/strong&gt; the commit
&lt;strong&gt;&lt;u&gt;@qa:&lt;/u&gt;&lt;/strong&gt; &lt;strong&gt;run the suite&lt;/strong&gt; to validate 
the fix
&lt;strong&gt;&lt;u&gt;@devs:&lt;/u&gt;&lt;/strong&gt; proceed with the &lt;strong&gt;production release 
on June 28th&lt;/strong&gt; given the green light by the QA team?

Sorry for the inconvenience,
Lazarus
&lt;/pre&gt;

&lt;p&gt;Thank you,&lt;/p&gt;

&lt;p&gt;Lazarus&lt;/p&gt;

&lt;h2&gt;
  
  
  Post Scriptum
&lt;/h2&gt;

&lt;p&gt;This post was originally published on &lt;a href="https://iridakos.com/how-to/2019/06/26/composing-better-emails.html"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Find attached a photo of my cats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JE2icfZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/better-emails/irida-phoebe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JE2icfZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://iridakos.com/assets/images/posts/better-emails/irida-phoebe.png" alt="Meow have your attention please?"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>communication</category>
      <category>email</category>
    </item>
    <item>
      <title>goto - navigate to aliased directories</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Wed, 07 Mar 2018 11:18:17 +0000</pubDate>
      <link>https://forem.com/iridakos/goto---navigate-to-aliased-directories--2fda</link>
      <guid>https://forem.com/iridakos/goto---navigate-to-aliased-directories--2fda</guid>
      <description>&lt;p&gt;I created a bash utility named &lt;a href="https://github.com/iridakos/goto" rel="noopener noreferrer"&gt;goto&lt;/a&gt; that allows users to navigate to aliased directories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l8kil47luetkfday2zc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l8kil47luetkfday2zc.gif" alt="goto" width="400" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It comes with a nice auto-completion script so that whenever you press the &lt;code&gt;tab&lt;/code&gt; key after the &lt;code&gt;goto&lt;/code&gt; command, bash prompts with suggestions of the available aliases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;goto &amp;lt;tab&amp;gt;
bc /etc/bash_completion.d                     
dev /home/iridakos/development
rubies /home/iridakos/.rvm/rubies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;The following command will register an alias for a long path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;goto --register workdir /mnt/development/projects/go/workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, user can navigate to that folder with just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;goto workdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the code and the installation and usage guides on &lt;a href="https://github.com/iridakos/goto" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update 2018-03-25
&lt;/h2&gt;

&lt;p&gt;The tool can be used also in zsh and there is a brew formula available for macOS users (&lt;code&gt;brew install goto&lt;/code&gt;)&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>bash</category>
      <category>linux</category>
      <category>script</category>
    </item>
    <item>
      <title>(╯°□°）╯︵ ┻━┻</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Mon, 05 Feb 2018 16:42:51 +0000</pubDate>
      <link>https://forem.com/iridakos/--168k</link>
      <guid>https://forem.com/iridakos/--168k</guid>
      <description>&lt;p&gt;I created a useless gem that prepends the table flipper on Exception messages :D&lt;/p&gt;

&lt;p&gt;Check it out here: &lt;a href="https://github.com/iridakos/table_flipper"&gt;https://github.com/iridakos/table_flipper&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ruby</category>
      <category>fun</category>
      <category>developer</category>
    </item>
    <item>
      <title>Just published: jquery.docout</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Tue, 19 Dec 2017 23:02:33 +0000</pubDate>
      <link>https://forem.com/iridakos/just-published-jquerydocout-1n8p</link>
      <guid>https://forem.com/iridakos/just-published-jquerydocout-1n8p</guid>
      <description>&lt;p&gt;I use GitHub pages to host my &lt;a href="http://iridakos.com"&gt;personal blog&lt;/a&gt; and since some of my posts are long tutorials, I created a customizable jQuery plugin that renders a document outline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iridakos/jquery.docout"&gt;jquery.docout GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AbBMW8dA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/iridakos/jquery.docout/master/doc/resources/images/document-outline.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AbBMW8dA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/iridakos/jquery.docout/master/doc/resources/images/document-outline.gif" alt="jquery.docout demo image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any feedback is welcome!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>jquery</category>
    </item>
    <item>
      <title>Saying "Hello world" with Ruby on Rails</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Tue, 21 Nov 2017 19:49:21 +0000</pubDate>
      <link>https://forem.com/iridakos/saying-hello-world-with-ruby-on-rails-dek</link>
      <guid>https://forem.com/iridakos/saying-hello-world-with-ruby-on-rails-dek</guid>
      <description>&lt;p&gt;If you are new to Ruby I suggest you first read &lt;a href="http://iridakos.com/ruby/2013/11/08/dont-give-up-on-ruby.html"&gt;this&lt;/a&gt; which in short explains why you should not give up on learning Ruby.&lt;/p&gt;

&lt;p&gt;In this post we will do the first steps in order to get familiar with both the language (Ruby) and the framework (Ruby on Rails). The post is based on my own experience back then, so I hope that anyone new here can understand them easily.&lt;/p&gt;

&lt;p&gt;First things first, install ruby and rails on your machine. There are plenty of installation tutorials out there from which you can pick the one matching your machine's operating system. I followed &lt;a href="http://railsapps.github.io/installrubyonrails-mac.html"&gt;this one&lt;/a&gt; in order to install Ruby 2.0 &amp;amp; Rails 4 on OS X Mavericks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development environment
&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Text editor&lt;/strong&gt;: I prefer using GitHub's &lt;a href="https://atom.io/"&gt;Atom&lt;/a&gt; which is an excellent IDE many plugins. There are a lot of other options but this is something you don't have to decide now. Give them a try and you'll find the one you like the most.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Console:&lt;/strong&gt; I use &lt;a href="http://www.iterm2.com/"&gt;iTerm2&lt;/a&gt; which is a powerful replacement for OS X's Terminal application. Find a terminal application for your OS and spend some time customizing to match your preferences, you'll be working a lot in it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browser:&lt;/strong&gt; Your favorite one. I use Chrome or Firefox for development. Well, I used to prefer Chrome but after the release of Firefox Quantum, I can't decide who's the best so I use them both.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's begin
&lt;/h2&gt;

&lt;p&gt;Create a folder in which you are going to put the tutorial's code and navigate to it from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;tutorials
&lt;span class="nb"&gt;cd &lt;/span&gt;tutorials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create our new application with the name &lt;code&gt;hello_world&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new hello_world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above will create a folder with the name &lt;code&gt;hello_world&lt;/code&gt;.&lt;br&gt;
Inside this folder you will find various files and subfolders which actually constitute a Rails application.&lt;/p&gt;

&lt;p&gt;In this post I will only explain the files/folders to which we are going to make changes on (a thorough explanation will be provided in an upcoming one).&lt;/p&gt;

&lt;p&gt;You already have a web server installed in your system that comes bundled with Ruby and its name is WEBRick. Let's start it to see what we have created with the last command.&lt;/p&gt;

&lt;p&gt;From the command line, navigate to the created folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello_world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start WEBrick with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;code&gt;s&lt;/code&gt; instead of &lt;code&gt;server&lt;/code&gt; in the previous command which is just an alias and will do the exact same thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FGcg1HVN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://2.bp.blogspot.com/-NwLVOAjtjag/UpEp1H01XKI/AAAAAAAAAcQ/vgwNU0ZVSgI/s1600/webrick.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FGcg1HVN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://2.bp.blogspot.com/-NwLVOAjtjag/UpEp1H01XKI/AAAAAAAAAcQ/vgwNU0ZVSgI/s1600/webrick.png" alt="Rails server starting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, the server listens to port 3000 but you may change to the one you desire by using the &lt;code&gt;-p&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails server &lt;span class="nt"&gt;-p&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the web server started and our application is successfully deployed. Open your browser and visit the following address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tadaaa... You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v-TIjDim--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://4.bp.blogspot.com/-jXXwccMm_OI/UpEuGykbR8I/AAAAAAAAAcc/iKkofkL8UmY/s640/first-hit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-TIjDim--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://4.bp.blogspot.com/-jXXwccMm_OI/UpEuGykbR8I/AAAAAAAAAcc/iKkofkL8UmY/s640/first-hit.png" alt="Rails welcome aboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we continue, I will try to explain in a simple way how a rails application works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every request to the application is served by methods (called &lt;code&gt;actions&lt;/code&gt;) which are defined in special files called &lt;code&gt;controllers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;These actions execute what has to be executed and define what the response will be (for example an html page, a JSON object etc). The responses (e.x. a page) are defined in special files called views but there are cases (such as when responding with a JSON object) that an action's view is not required to be "defined" (well, in such cases we say we have JSON views etc).&lt;/li&gt;
&lt;li&gt;The rules (&lt;code&gt;routes&lt;/code&gt;) that define which action of which controller will serve each request are placed in a special file called &lt;code&gt;routes.rb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are enough for the time being. Let's create our first controller named &lt;code&gt;pages&lt;/code&gt;.&lt;br&gt;
From the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate controller pages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a bunch of lines coming out from this command but focus on the first one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wMZEHtw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://3.bp.blogspot.com/-ucr0i4t3rfk/UpE4sI-CjpI/AAAAAAAAAcs/BFFYthZjJng/s1600/generate-controller.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wMZEHtw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://3.bp.blogspot.com/-ucr0i4t3rfk/UpE4sI-CjpI/AAAAAAAAAcs/BFFYthZjJng/s1600/generate-controller.png" alt="Rails generating a controller"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, &lt;code&gt;pages_controller.rb&lt;/code&gt; is our controller.&lt;br&gt;
In this file we are going to create our action that will serve the "root" (home) request to our application.&lt;/p&gt;

&lt;p&gt;So, open this file with your text editor. Its location is:&lt;br&gt;
&lt;code&gt;app/controllers/pages_controller.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the controller is empty. We will add an action to it, let's call it &lt;code&gt;home&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, our action does nothing since all we want to do is show a static page with text &lt;em&gt;Hello world&lt;/em&gt;. Let's add something though just to see it being executed from the server's log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hello console!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you speak Java, &lt;code&gt;puts&lt;/code&gt; is similar to Java's &lt;code&gt;System.out.println();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If not told otherwise (which is our case), the home action will try to respond with a view named &lt;code&gt;home&lt;/code&gt; too.&lt;br&gt;
So let's create this view in the appropriate folder which is:&lt;br&gt;
&lt;code&gt;app/views/pages/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create a file in this folder with the name &lt;code&gt;home.html.erb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;erb&lt;/code&gt; extension implies that this file will be processed by Rails in order to embed any dynamic content.&lt;/p&gt;

&lt;p&gt;Edit this file and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's left? Yes, we must route the root requests to our controller's action.&lt;/p&gt;

&lt;p&gt;Open with your text editor the &lt;code&gt;routes.rb&lt;/code&gt; file which is located here:&lt;br&gt;
&lt;code&gt;config/routes.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will find a bunch of help comments (text starting with &lt;code&gt;#&lt;/code&gt;) there which you can delete.&lt;/p&gt;

&lt;p&gt;Now, add the following line which tells that the root path &lt;code&gt;/&lt;/code&gt; will be served by our controller's home action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'pages#home'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you refresh your browser, you will see the amazing and super complex page we just created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L-etfimK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://4.bp.blogspot.com/-fN7Mf2u71nE/UpFE0NERslI/AAAAAAAAAdE/vVDxnUFlrFM/s640/static.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L-etfimK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://4.bp.blogspot.com/-fN7Mf2u71nE/UpFE0NERslI/AAAAAAAAAdE/vVDxnUFlrFM/s640/static.png" alt="Hello world static page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We could stop here but it would be nice if we had the &lt;em&gt;Hello world!&lt;/em&gt; text being passed to the view from the controller's action, yes? Yes.&lt;/p&gt;

&lt;p&gt;Open the pages controller again and change the home action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;
  &lt;span class="vi"&gt;@greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Home action says: Hello world!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we declared an instance variable to our controller with name &lt;code&gt;greeting&lt;/code&gt; and value &lt;code&gt;Home action says: Hello world!.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This variable is available for use from our corresponding view so open the &lt;code&gt;home.html.erb&lt;/code&gt; view and change its contents to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= @greeting %&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the page in your browser and there it is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rE2OsSWQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://1.bp.blogspot.com/-z2SM6eyCAVc/UpFG9SrwaUI/AAAAAAAAAdQ/RHVlNsTm4o0/s640/home-hello.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rE2OsSWQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://1.bp.blogspot.com/-z2SM6eyCAVc/UpFG9SrwaUI/AAAAAAAAAdQ/RHVlNsTm4o0/s640/home-hello.png" alt="Hello world dynamic page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There, &lt;strong&gt;you just created your first Ruby on Rails application!&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;This post has been edited and re-posted here from &lt;a href="http://iridakos.com/tutorials/2013/11/24/saying-hello-world-with-ruby-on-rails.html"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>ruby</category>
      <category>beginners</category>
    </item>
    <item>
      <title>DuckRails - I created an opensource tool for developers</title>
      <dc:creator>Lazarus Lazaridis</dc:creator>
      <pubDate>Sat, 18 Nov 2017 18:54:03 +0000</pubDate>
      <link>https://forem.com/iridakos/duckrails---opensource-tool-for-developers-bei</link>
      <guid>https://forem.com/iridakos/duckrails---opensource-tool-for-developers-bei</guid>
      <description>&lt;p&gt;I have created an opensource development tool called &lt;a href="https://github.com/iridakos/duckrails"&gt;DuckRails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Its main purpose is to allow developers to quickly mock API endpoints that for many possible reasons they can't reach at a specific time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--deOR-Wqz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/iridakos/duckrails/blob/master/documentation/readme/resources/duckrails-home-page.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--deOR-Wqz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/iridakos/duckrails/blob/master/documentation/readme/resources/duckrails-home-page.png%3Fraw%3Dtrue" alt="DuckRails home page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find installation instructions &lt;a href="https://github.com/iridakos/duckrails/wiki/Setting-up-the-application"&gt;here&lt;/a&gt; (including setup via docker image).&lt;br&gt;
You can browse the project's &lt;a href="https://github.com/iridakos/duckrails/wiki"&gt;GitHub wiki pages&lt;/a&gt; for usage guides.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>productivity</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
