<?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>DEV Community: Thomas Riboulet</title>
    <description>The latest articles on DEV Community by Thomas Riboulet (@riboulet).</description>
    <link>https://dev.to/riboulet</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%2F1072214%2F89a00a35-7926-47d6-ab3d-891d435054fc.png</url>
      <title>DEV Community: Thomas Riboulet</title>
      <link>https://dev.to/riboulet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/riboulet"/>
    <language>en</language>
    <item>
      <title>An Introduction to Auth0 for Ruby on Rails</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 31 Jul 2024 14:00:00 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-auth0-for-ruby-on-rails-5hhg</link>
      <guid>https://dev.to/appsignal/an-introduction-to-auth0-for-ruby-on-rails-5hhg</guid>
      <description>&lt;p&gt;From custom-made to plug-and-play forms of authentication, Ruby developers have plenty to choose from these days. Yet, as you may know, building your own solution can be costly and dangerous. If Devise is the de facto standard for most teams, an alternative might simplify the lives of most.&lt;/p&gt;

&lt;p&gt;This article will cover the setup and use of Auth0 in a Ruby on Rails application, including everything you need to get going properly, from handling roles to relying on multiple providers to authenticate users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Here's what we need to get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://auth0.com" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;A Ruby on Rails application (version 7.x onwards)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auth0 is a third-party authentication service with a free tier that lets you handle up to 7,000 users. That is plenty to get you started, and its pricing is reasonable if you need more advanced features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Our Ruby App in Auth0
&lt;/h2&gt;

&lt;p&gt;Since your application will rely on Auth0 to authenticate users through redirects and calls, we must ensure it stays secure.&lt;/p&gt;

&lt;p&gt;In our Auth0 account, let's create an application within a tenant. You can create multiple tenants in an account to separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain names.&lt;/li&gt;
&lt;li&gt;Different environments (development, production, etc).&lt;/li&gt;
&lt;li&gt;The country or region within which data will be stored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have created your first tenant, you can build an application. That's where we'll start.&lt;/p&gt;

&lt;p&gt;Head to the "Settings" tab in the application panel. You need to copy and paste the following and save it to a safe place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The domain name: &lt;code&gt;app-name.[eu,us,..].auth0.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The client's ID&lt;/li&gt;
&lt;li&gt;The client's Secret&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We must also fill in the following Application URIs. We'll use these values for our local development setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowed Callback URLs: &lt;code&gt;http://localhost:3000/auth/auth0/callback&lt;/code&gt; (the URL Auth0 will redirect to after authentication).&lt;/li&gt;
&lt;li&gt;Allowed Logout URLs: &lt;code&gt;http://localhost:3000&lt;/code&gt; (the URL Auth0 will redirect to after someone logs out).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's go ahead and configure our app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing Our Ruby on Rails Application
&lt;/h2&gt;

&lt;p&gt;You can start with a vanilla Ruby on Rails application using the &lt;code&gt;rails new&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Let's generate one that relies on SQLite3 for the database and Tailwind for the CSS library. Skip the installation of mini-test and name the application "Auth0 article":&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; ~/my_projects
rails new &lt;span class="nt"&gt;-d&lt;/span&gt; sqlite3 &lt;span class="nt"&gt;-c&lt;/span&gt; tailwind &lt;span class="nt"&gt;-T&lt;/span&gt; auth0_article
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a User model with just a few attributes:&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;auth0_article
bin/rails db:create
bin/rails generate model User email name
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds a simple but efficient base for our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Auth0 Gem to the App
&lt;/h2&gt;

&lt;p&gt;You'll need &lt;code&gt;omniauth-auth0&lt;/code&gt; and &lt;code&gt;omniauth-rails_csrf_protection&lt;/code&gt; to use Auth0 in your Ruby on Rails application.&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;gem&lt;/span&gt; &lt;span class="s1"&gt;'omniauth-auth0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 3.0'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'omniauth-rails_csrf_protection'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring Auth0
&lt;/h3&gt;

&lt;p&gt;We need to create a tiny configuration file (&lt;code&gt;config/auth0.yml&lt;/code&gt;) to store credentials in our development environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;auth0_domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR AUTH0 APPLICATION DOMAIN NAME&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;auth0_client_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR AUTH0 APPLICATION CLIENT ID&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;auth0_client_secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR AUTH0 APPLICATION CLIENT SECRET&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also rely on environment variables here by using some erb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;auth0_domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['AUTH0_APPLICATION_DOMAIN'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;auth0_client_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['AUTH0_APPLICATION_CLIENT_ID'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;auth0_client_secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['AUTH0_APPLICATION_CLIENT_SECRET'] %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, relying on Ruby on Rails credentials ensures that things stay more up-to-date.&lt;/p&gt;

&lt;p&gt;This file will be used in the Auth0 initializer (&lt;code&gt;config/initializers/auth0.rb&lt;/code&gt;), which we will now create:&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="no"&gt;AUTH0_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&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;config_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:auth0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="no"&gt;OmniAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;:auth0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_client_id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_client_secret'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_domain'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;callback_path: &lt;/span&gt;&lt;span class="s1"&gt;'/auth/auth0/callback'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;authorize_params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'openid profile'&lt;/span&gt;
    &lt;span class="p"&gt;}&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;blockquote&gt;
&lt;p&gt;As you can see here, we are using &lt;a href="https://api.rubyonrails.org/classes/Rails/Application.html#method-i-config_for" rel="noopener noreferrer"&gt;&lt;code&gt;Rails.application.config_for&lt;/code&gt;&lt;/a&gt; to load up the content of a YAML file as a convenient hash. We could replace this with any secret handling library, including Rails encrypted credentials storage.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;ul&gt;
&lt;li&gt;The provider name (&lt;code&gt;:auth0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The three items from the YAML file, read and used through the &lt;code&gt;AUTH0_CONFIG&lt;/code&gt; hash.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;callback_path&lt;/code&gt; key and value, matching the one we configured in the Auth0 interface.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;authorize_params&lt;/code&gt; and the &lt;code&gt;scope&lt;/code&gt; key inside it; we will come back to that later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to create the interface and routing elements to allow for authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Routes and UI with Tailwind
&lt;/h2&gt;

&lt;p&gt;In this example, we will work with a Ruby on Rails application using version 7.1 of the framework with TailwindCSS.&lt;/p&gt;

&lt;p&gt;Use 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 new &lt;span class="nt"&gt;-d&lt;/span&gt; sqlite3 &lt;span class="nt"&gt;-c&lt;/span&gt; tailwind &lt;span class="nt"&gt;-T&lt;/span&gt; myApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then add two controllers with the index action, preparing to test the authentication process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/rails g controller public index
bin/rails g controller private index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those two commands, the following are created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both &lt;code&gt;public_controller.rb&lt;/code&gt; and &lt;code&gt;private_controller.rb&lt;/code&gt; controllers, with the index action ready to use&lt;/li&gt;
&lt;li&gt;Both related routes&lt;/li&gt;
&lt;li&gt;The related views&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add the Auth0 controller to handle the callbacks and failures. Create the controller file (&lt;code&gt;bin/rails g controller auth0&lt;/code&gt;) and add 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="c1"&gt;# ./app/controllers/auth0_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Auth0Controller&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;# this will happen in case of success&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;
    &lt;span class="n"&gt;auth_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'omniauth.auth'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'raw_info'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/private/index'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# this will happen in case of failure&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failure&lt;/span&gt;
    &lt;span class="vi"&gt;@error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/public_index'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# logout route&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;logout&lt;/span&gt;
    &lt;span class="n"&gt;reset_session&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;logout_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_other_host: &lt;/span&gt;&lt;span class="kp"&gt;true&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;logout_url&lt;/span&gt;
    &lt;span class="n"&gt;request_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;returnTo: &lt;/span&gt;&lt;span class="n"&gt;root_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;client_id: &lt;/span&gt;&lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_client_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTPS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_domain'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'/v2/logout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;query: &lt;/span&gt;&lt;span class="n"&gt;request_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&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 update the routes to use those three actions:&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rb&lt;/span&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="c1"&gt;# ..&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/auth/auth0/callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'auth0#callback'&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/auth/failure'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'auth0#failure'&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/auth/logout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'auth0#logout'&lt;/span&gt;

  &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="s2"&gt;"public#index"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we can add &lt;code&gt;Login&lt;/code&gt; and &lt;code&gt;Logout&lt;/code&gt; buttons in the public and private views, respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/public/index.html.erb
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-4xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Public#index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Find me in app/views/public/index.html.erb&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;button_to&lt;/span&gt; &lt;span class="s1"&gt;'Login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/auth/auth0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: :post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo: &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;class: &lt;/span&gt;&lt;span class="s2"&gt;"rounded bg-sky-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500"&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;

# app/views/private/index.html.erb
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-4xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Private#index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Find me in app/views/private/index.html.erb&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;button_to&lt;/span&gt; &lt;span class="s1"&gt;'Logout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/auth0/logout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: :get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo: &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;class: &lt;/span&gt;&lt;span class="s2"&gt;"rounded bg-sky-500 px-2 py-1 text-sm font-semibold text-white shadow-sm hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now go to '&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;'. Use the &lt;code&gt;Login&lt;/code&gt; button and you'll be redirected to the &lt;em&gt;private&lt;/em&gt; page. There, you will find a &lt;code&gt;Logout&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;A couple of things are missing, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data from a user's profile&lt;/li&gt;
&lt;li&gt;Identifying a user and handling authorization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Working with Scopes and Data
&lt;/h2&gt;

&lt;p&gt;Let's take a few steps back and bring back the initializer (&lt;code&gt;config/initializers/auth0.rb&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="no"&gt;AUTH0_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&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;config_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:auth0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="no"&gt;OmniAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;:auth0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_client_id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_client_secret'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="no"&gt;AUTH0_CONFIG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'auth0_domain'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;callback_path: &lt;/span&gt;&lt;span class="s1"&gt;'/auth/auth0/callback'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;authorize_params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'openid profile'&lt;/span&gt;
    &lt;span class="p"&gt;}&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;The critical piece here is the scope: &lt;code&gt;openid profile&lt;/code&gt;. This tells Auth0 we are interested in a few pieces of information, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The provider name (Auth0)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;uid&lt;/code&gt;: A unique identifier to match a user&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;info&lt;/code&gt; hash: Containing a name, URL to a profile picture, and an empty 'email' key&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;extra_info&lt;/code&gt; hash: Again, with a name and profile picture, but also a pair of given and family names&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Read more on the topic of &lt;a href="https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes" rel="noopener noreferrer"&gt;scopes in Auth0's documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above information is valuable, and you should ensure that, at least upon first login, you copy that data into a table in your application.&lt;/p&gt;

&lt;p&gt;To do so, we need to use the content of the request-response in the &lt;code&gt;auth0_controller&lt;/code&gt; and, specifically, in the &lt;code&gt;callback&lt;/code&gt; 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;callback&lt;/span&gt;
  &lt;span class="c1"&gt;# all the data sent by auth0&lt;/span&gt;
  &lt;span class="n"&gt;auth_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'omniauth.auth'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# the data that really identifies the user&lt;/span&gt;
  &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'raw_info'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/private/index'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a breakpoint before the redirect to dig into the hash and experiment.&lt;/p&gt;

&lt;p&gt;We usually want the email address too. To get it, you can update the scope:&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="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'openid profile email'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After reloading the application server and going through the login steps, you will need to give the application access to additional information.&lt;/p&gt;

&lt;p&gt;We can now do something like this in the &lt;code&gt;callback&lt;/code&gt; 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;callback&lt;/span&gt;
  &lt;span class="c1"&gt;# all the data sent by auth0&lt;/span&gt;
  &lt;span class="n"&gt;auth_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'omniauth.auth'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# the data that really identify the user&lt;/span&gt;
  &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'raw_info'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;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;email: &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&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;new&lt;/span&gt;
  &lt;span class="k"&gt;if&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;new_record?&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'name'&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&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;save&lt;/span&gt;

  &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/private/index'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure we have a local, up-to-date profile for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Word On Sessions
&lt;/h2&gt;

&lt;p&gt;A session is a special hash in a Ruby on Rails application. It has limited storage that is accessible from the controllers and views and unique to each visitor.&lt;/p&gt;

&lt;p&gt;We can "open" and "close" sessions. If we need a specific dataset, we can decide that a session is open. If it has no data, then it's closed.&lt;/p&gt;

&lt;p&gt;Remember the following lines in our Auth0 controller:&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="c1"&gt;# in the callback method&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'raw_info'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# in the logout method&lt;/span&gt;
&lt;span class="n"&gt;reset_session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first one writes the value in the &lt;code&gt;raw_info&lt;/code&gt; key of the hash to the session hash, while the second one just clears up the session hash.&lt;/p&gt;

&lt;p&gt;We can then define the following helper method in the &lt;code&gt;ApplicationHelper&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="c1"&gt;# get the user&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_user&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;email: &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&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;h2&gt;
  
  
  A Security Concern
&lt;/h2&gt;

&lt;p&gt;Now let's use this as a concern for our controllers. The idea is to define a controller that requires users to be logged in to access it, such as our &lt;code&gt;private_controller&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can write the following concern file:&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="c1"&gt;# ./app/controllers/concerns/secured.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Secured&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;

  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:logged_in?&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;logged_in?&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&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;We can then use it in our controllers like so:&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;PrivateController&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Secured&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;If a visitor isn't logged in, but then tries to open up the &lt;code&gt;/private/index&lt;/code&gt; page, they will automatically be redirected to the site's root.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating with Other Providers
&lt;/h2&gt;

&lt;p&gt;You can rely on multiple providers through Auth0. However, Google is defined as the default. For many companies, that's enough.&lt;/p&gt;

&lt;p&gt;If you need more providers, head to the &lt;em&gt;Authentication&lt;/em&gt; menu for the related tenant in Auth0, and then the &lt;em&gt;Social&lt;/em&gt; submenu, where you can set up additional providers.&lt;/p&gt;

&lt;p&gt;It's also worth noting that you can configure multi-factor authentication (MFA) with Auth0 too.&lt;/p&gt;

&lt;p&gt;The process will remain the same except for those configuration steps. Our application will only return visitor data to prove that a visitor has been authenticated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opening Up Towards Authorization
&lt;/h2&gt;

&lt;p&gt;Now we can authenticate users and send their information to our application through a callback. We have already seen how to get a user's email address out of a hash and find the relevant user in our database.&lt;/p&gt;

&lt;p&gt;From there, we can define authorization policies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/varvet/pundit" rel="noopener noreferrer"&gt;Pundit&lt;/a&gt; is a great choice to define and use authorization policies, yet relies on checking a user's role.&lt;/p&gt;

&lt;p&gt;Using a &lt;code&gt;role&lt;/code&gt; attribute is, in fact, the easiest method. You can fill it in when creating a user in the Rails console or your application's back-end interface, before a user's first login attempt.&lt;/p&gt;

&lt;p&gt;Or you can get a list of admins' emails and match a user's email against the list when creating the user.&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="no"&gt;ADMINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'johndoe@example.com'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_or_create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
  &lt;span class="n"&gt;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;email: &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&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;new&lt;/span&gt;
  &lt;span class="k"&gt;if&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;new_record?&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'name'&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:userinfo&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'email'&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;if&lt;/span&gt; &lt;span class="no"&gt;ADMINS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'admin'&lt;/span&gt;
  &lt;span class="k"&gt;else&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;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'normal'&lt;/span&gt;
  &lt;span class="k"&gt;end&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;save&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  What We've Covered
&lt;/h2&gt;

&lt;p&gt;In this article, we set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tenant and application in Auth0&lt;/li&gt;
&lt;li&gt;Auth0-related gems in a Ruby on Rails application&lt;/li&gt;
&lt;li&gt;Routes and views in the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reviewed the concept of a session and saw how to use it&lt;/li&gt;
&lt;li&gt;Created base tooling to check if a user is logged in&lt;/li&gt;
&lt;li&gt;Added a controller concern to secure controllers behind a login requirement&lt;/li&gt;
&lt;li&gt;Saw how to match users to their roles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auth0 and other authentication providers can help you integrate state-of-the-art authentication in your Ruby on Rails application without writing much code. It's often a much better option than relying on your own implementation of the authentication layer or even using gems like Devise.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic" rel="noopener noreferrer"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Debugging in Ruby with Debug</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 17 Jul 2024 10:42:46 +0000</pubDate>
      <link>https://dev.to/appsignal/debugging-in-ruby-with-debug-578g</link>
      <guid>https://dev.to/appsignal/debugging-in-ruby-with-debug-578g</guid>
      <description>&lt;p&gt;Debugging is a valuable skill for any software engineer to have. Unfortunately, most software engineers are not trained in it. And that's not just specific to developers going through boot camps; even in universities, we are not often taught and trained to use a debugger.&lt;/p&gt;

&lt;p&gt;My teachers and mentors were more interested in getting me to write programs rather than debugging them. If we are fortunate, debugging comes at the end of the semester, in a short, last session.&lt;/p&gt;

&lt;p&gt;Luckily, we have tools that can help us with debugging. Since Ruby 3.1, Ruby ships with the debug gem, a powerful debugger.&lt;/p&gt;

&lt;p&gt;In this article, we will go through a quick overview of the gem. We'll see how to use it for simple and more advanced cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Without A Debugger: What's the Issue?
&lt;/h2&gt;

&lt;p&gt;Many of us rely on what you might call &lt;em&gt;"printf debugging"&lt;/em&gt;: we add &lt;code&gt;puts&lt;/code&gt; (or its equivalent in the language we're using) to the standard output (STDOUT). We include the current state of an object, variable, or just a string so we know if our program is going into specific branches of its logic tree.&lt;/p&gt;

&lt;p&gt;While helpful, this isn't the most optimal way to debug a program. It often leads to many back-and-forth trips between your logs and the code, as you forget to add a &lt;code&gt;puts&lt;/code&gt; here and there, or leave in some debugging code.&lt;/p&gt;

&lt;p&gt;That method also relies on your own preconceptions about how the code is running and what is going on that's different from what you might expect.&lt;/p&gt;

&lt;p&gt;Using a debugger is a very different experience. You add one or more breakpoints in the code where you want to know what's happening. You then run the code and wait for it to hit the breakpoint.&lt;/p&gt;

&lt;p&gt;Then, you get a debugging console to check a variable's values at the breakpoint location. You go back and forth in the execution steps.&lt;/p&gt;

&lt;p&gt;As we will see later, we can even add conditional breakpoints directly from the debugging console. This makes it easier to avoid exiting the debugging console, so you can add breakpoints you've forgotten about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Since &lt;a href="https://www.ruby-lang.org/en/news/2021/12/25/ruby-3-1-0-released/" rel="noopener noreferrer"&gt;Ruby 3.1&lt;/a&gt;, a version of the &lt;code&gt;debug&lt;/code&gt; gem ships with Ruby. We recommend adding it to your &lt;code&gt;Gemfile&lt;/code&gt; so you're using the latest version.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;debug&lt;/code&gt; to your Gemfile and then run &lt;code&gt;bundle install&lt;/code&gt;. I recommend adding it to &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; groups for debugging tests too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Debugging Techniques with Debug for Ruby
&lt;/h2&gt;

&lt;p&gt;Now let's run through some simple debugging methods using &lt;code&gt;debug&lt;/code&gt;: using breakpoints, stepping, other commands, moving in the stack, and using a map. We'll then examine the more advanced method of adding breakpoints on the fly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breakpoints
&lt;/h3&gt;

&lt;p&gt;Breakpoints are calls that tell the debugger to stop. You can do this in modern IDEs that are integrated into a debugger with a simple click in the sidebar. The standard way is to add &lt;code&gt;binding.break&lt;/code&gt; at the line we want to stop at.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'debug'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hornet&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:yellow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:black&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;show_up&lt;/span&gt;
    &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;break&lt;/span&gt;   &lt;span class="c1"&gt;# debugger will stop here&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"bzzz"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Hornet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show_up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By running this little program, we will get the following console output:&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="o"&gt;[&lt;/span&gt;debug] ruby test.rb
&lt;span class="o"&gt;[&lt;/span&gt;4, 13] &lt;span class="k"&gt;in &lt;/span&gt;test.rb
     4|   def initialize
     5|     @colors &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;:yellow, :red, :black]
     6|   end
     7|
     8|   def show_up
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   9|     binding.break   &lt;span class="c"&gt;# debugger will stop here&lt;/span&gt;
    10|     puts &lt;span class="s2"&gt;"bzzz"&lt;/span&gt;
    11|   end
    12| end
    13|
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    Hornet#show_up at test.rb:9&lt;/span&gt;
  &lt;span class="c"&gt;#1    &amp;lt;main&amp;gt; at test.rb:14&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;ruby&lt;span class="o"&gt;)&lt;/span&gt; @colors
&lt;span class="o"&gt;[&lt;/span&gt;:yellow, :red, :black]
&lt;span class="o"&gt;(&lt;/span&gt;rdgb&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we can access the instance variable from the breakpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stepping
&lt;/h2&gt;

&lt;p&gt;Let's dig into a more complex example using stepping.&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;Book&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="vi"&gt;@author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="vi"&gt;@price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&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;class&lt;/span&gt; &lt;span class="nc"&gt;BookStore&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;=&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;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;book&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;remove_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;title&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;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&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;span class="c1"&gt;# Sample Usage:&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BookStore&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;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example app manages books in a bookstore. But at the moment, we cannot be sure which book will be returned when we search titles containing 'Hobbit'. It might well be "The Hobbit", but it's not certain.&lt;/p&gt;

&lt;p&gt;To help debug this, we'll jump into the &lt;code&gt;find_by_title&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Let's add a breakpoint to one of the methods:&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;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;break&lt;/span&gt;
  &lt;span class="vi"&gt;@books&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&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;Then launch the program and get to the breakpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@box &lt;span class="o"&gt;[&lt;/span&gt;debug] ruby library.rb                                                                                                                          20:25:07
&lt;span class="o"&gt;[&lt;/span&gt;22, 31] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
    22|   def remove_book&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt;
    23|     @books.delete_if &lt;span class="o"&gt;{&lt;/span&gt; |book| book.title &lt;span class="o"&gt;==&lt;/span&gt; title &lt;span class="o"&gt;}&lt;/span&gt;
    24|   end
    25|
    26|   def find_by_title&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  27|     binding.break
    28|     @books.find &lt;span class="o"&gt;{&lt;/span&gt; |book| book.title.include?&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    29|   end
    30| end
    31|
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    BookStore#find_by_title(title="Hobbit") at library.rb:27&lt;/span&gt;
  &lt;span class="c"&gt;#1    &amp;lt;main&amp;gt; at library.rb:42&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top part of the console tells us which line and file we are at. We can then query the value of the &lt;code&gt;title&lt;/code&gt; variable.&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; title
&lt;span class="s2"&gt;"Hobbit"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run the code right in that context to see what's happening:&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="o"&gt;(&lt;/span&gt;ruby&lt;span class="o"&gt;)&lt;/span&gt; @books.find &lt;span class="o"&gt;{&lt;/span&gt; |book| book.title.include?&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;#&amp;lt;Book:0x00007fd05e4d59f0 @author="J.R.R. Tolkien", @price=15.0, @title="The Hobbit"&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Here might be a good time to reflect on how you want the program you are building and this piece of code to behave. Expressing the code through RSpec tests might be an excellent way to clarify what it should do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's now continue to the next breakpoint by using the &lt;code&gt;continue&lt;/code&gt; command.&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;    &lt;span class="c"&gt;# command&lt;/span&gt;
The Hobbit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, it goes on until the end of the program.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Commands to Assist Debugging
&lt;/h2&gt;

&lt;p&gt;Of course, we can add more breakpoints to our code to stop at another place. But we can also use commands to move within the stack of our program without restarting it.&lt;/p&gt;

&lt;p&gt;Let's add one more breakpoint to the &lt;code&gt;add_book&lt;/code&gt; method, just after instantiating the bookstore.&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;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;break&lt;/span&gt;
  &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [ .. ]&lt;/span&gt;

&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BookStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;break&lt;/span&gt;
&lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we run the program, it will stop before the &lt;code&gt;book1&lt;/code&gt; variable is instantiated. The &lt;code&gt;continue&lt;/code&gt; command will run the program until the next breakpoint or exit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;next&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of &lt;code&gt;continue&lt;/code&gt;, we can use the &lt;code&gt;next&lt;/code&gt; command, which will only run the next code line, so we can debug our app in smaller steps. We will need to run &lt;code&gt;next&lt;/code&gt; twice to run the line where &lt;code&gt;book1&lt;/code&gt; is defined before we can inspect it.&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="o"&gt;[&lt;/span&gt;30, 39] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
    30|   end
    31| end
    32|
    33| &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    34| store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  35| binding.break
    36| book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    37| book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    38| book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
    39|
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    &amp;lt;main&amp;gt; at library.rb:35&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;ruby&lt;span class="o"&gt;)&lt;/span&gt; book1
nil
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; next    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;31, 40] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
    31| end
    32|
    33| &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    34| store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    35| binding.break
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  36| book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    37| book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    38| book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
    39|
    40| store.add_book&lt;span class="o"&gt;(&lt;/span&gt;book1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    &amp;lt;main&amp;gt; at library.rb:36&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;ruby&lt;span class="o"&gt;)&lt;/span&gt; book1
nil
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; next    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;32, 41] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
    32|
    33| &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    34| store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    35| binding.break
    36| book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  37| book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    38| book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
    39|
    40| store.add_book&lt;span class="o"&gt;(&lt;/span&gt;book1&lt;span class="o"&gt;)&lt;/span&gt;
    41| store.add_book&lt;span class="o"&gt;(&lt;/span&gt;book2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    &amp;lt;main&amp;gt; at library.rb:37&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;ruby&lt;span class="o"&gt;)&lt;/span&gt; book1
&lt;span class="c"&gt;#&amp;lt;Book:0x00007f50fb5f9da0 @author="Frank Herbert", @price=20.0, @title="Dune"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;next&lt;/code&gt; call will run the next line. But it will not step into the code called by &lt;code&gt;Book.new&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;step&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In some cases, we may know that an issue lies within a specific call. The &lt;code&gt;step&lt;/code&gt; command is great for debugging this.&lt;/p&gt;

&lt;p&gt;For example, when we are at line 37, we can use &lt;code&gt;step&lt;/code&gt; to follow the execution of the &lt;code&gt;Book&lt;/code&gt; object that fills the &lt;code&gt;book2&lt;/code&gt; variable.&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; step    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;2, 11] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
     2|
     3| class Book
     4|   attr_accessor :title, :author, :price
     5|
     6|   def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7|     @title &lt;span class="o"&gt;=&lt;/span&gt; title
     8|     @author &lt;span class="o"&gt;=&lt;/span&gt; author
     9|     @price &lt;span class="o"&gt;=&lt;/span&gt; price
    10|   end
    11| end
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    Book#initialize(title="The Hobbit", author="J.R.R. Tolkien", price=15.0) at library.rb:7&lt;/span&gt;
  &lt;span class="c"&gt;#1    [C] Class#new at library.rb:37&lt;/span&gt;
  &lt;span class="c"&gt;# and 1 frames (use `bt' command for all frames)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;step&lt;/code&gt; command brings us directly to the first line of the &lt;code&gt;initialize&lt;/code&gt; method in the &lt;code&gt;Book&lt;/code&gt; class. (If you are new to Ruby, the &lt;code&gt;new&lt;/code&gt; class method is called the &lt;code&gt;initialize&lt;/code&gt; method after it does some internal work). Now we can use the next step from within that method and follow the trail.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;next&lt;/code&gt; and &lt;code&gt;step&lt;/code&gt; are crucial to get familiar with, as they allow us to move forward at different levels and speeds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving In the Stack
&lt;/h3&gt;

&lt;p&gt;We can move up and down (or backward and forwards) in the stack by using the &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; commands. Calling &lt;code&gt;up&lt;/code&gt; twice will get us back to line 37:&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="o"&gt;[&lt;/span&gt;2, 11] &lt;span class="k"&gt;in &lt;/span&gt;library.rb
     2|
     3| class Book
     4|   attr_accessor :title, :author, :price
     5|
     6|   def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7|     @title &lt;span class="o"&gt;=&lt;/span&gt; title
     8|     @author &lt;span class="o"&gt;=&lt;/span&gt; author
     9|     @price &lt;span class="o"&gt;=&lt;/span&gt; price
    10|   end
    11| end
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#0    Book#initialize(title="The Hobbit", author="J.R.R. Tolkien", price=15.0) at library.rb:7&lt;/span&gt;
  &lt;span class="c"&gt;#1    [C] Class#new at library.rb:37&lt;/span&gt;
  &lt;span class="c"&gt;# and 1 frames (use `bt' command for all frames)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; up    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="c"&gt;# No sourcefile available for library.rb&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#1    [C] Class#new at library.rb:37&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; up    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  37| book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to call it twice as we skipped over one step, thanks to the &lt;code&gt;next&lt;/code&gt; command: the call to the parent class of the &lt;code&gt;Book&lt;/code&gt; class: &lt;code&gt;Class&lt;/code&gt; itself (and the &lt;code&gt;new&lt;/code&gt; method).&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a Map
&lt;/h3&gt;

&lt;p&gt;When we start to use &lt;code&gt;up&lt;/code&gt; , &lt;code&gt;down&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, and &lt;code&gt;step&lt;/code&gt;, it's handy to know two more commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: to show where we are in the code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bt&lt;/code&gt; (or &lt;code&gt;backtrace&lt;/code&gt;): to show the trace of the steps we have followed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, when we are at line 37, the &lt;code&gt;bt&lt;/code&gt; command displays the following:&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; bt    &lt;span class="c"&gt;# backtrace command&lt;/span&gt;
  &lt;span class="c"&gt;#0    Book#initialize(title="The Hobbit", author="J.R.R. Tolkien", price=15.0) at library.rb:7&lt;/span&gt;
  &lt;span class="c"&gt;#1    [C] Class#new at library.rb:37&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#2    &amp;lt;main&amp;gt; at library.rb:37&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;down&lt;/code&gt; twice brings us to step &lt;code&gt;#0&lt;/code&gt;. We can also pass an additional integer to both &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; to move through as many steps as we want to in one go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowing What's Available
&lt;/h3&gt;

&lt;p&gt;A very practical command to know is &lt;code&gt;ls&lt;/code&gt;. It will list the variables and methods available to you at your current point in the stack.&lt;/p&gt;

&lt;p&gt;For example, on line 37, we see the following:&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt;    &lt;span class="c"&gt;# outline command&lt;/span&gt;
Object.methods: inspect  to_s
locals: book1  book2  book3  store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using &lt;code&gt;finish&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We can go to our next breakpoint using &lt;code&gt;continue&lt;/code&gt;. However, the &lt;code&gt;finish&lt;/code&gt; or &lt;code&gt;fin&lt;/code&gt; command will also bring us to the next breakpoint, or to the end of our program.&lt;/p&gt;

&lt;p&gt;You can exit more quickly with &lt;code&gt;Ctrl-D&lt;/code&gt; or &lt;code&gt;quit&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Breakpoints On the Fly
&lt;/h2&gt;

&lt;p&gt;A more advanced practice is to add breakpoints on the fly while running the debugger.&lt;/p&gt;

&lt;p&gt;We have different ways to do that. Let's start with some more simple ways to add a breakpoint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To a specific line — &lt;code&gt;break &amp;lt;line number&amp;gt;&lt;/code&gt; — in the current file.&lt;/li&gt;
&lt;li&gt;To the start of a specific method in a specific class: &lt;code&gt;break ClassName#method_name&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;38    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="c"&gt;#0  BP - Line  /mnt/data/Code/clients/AppSignal/debug/library.rb:38 (line)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;BookStore#find_by_title    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="c"&gt;#1  BP - Method  BookStore#find_by_title at library.rb:27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Called on its own, the &lt;code&gt;break&lt;/code&gt; command will list the existing breakpoints (the ones added through the debug console):&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="c"&gt;#0  BP - Line  /mnt/data/Code/clients/AppSignal/debug/library.rb:38 (line)&lt;/span&gt;
&lt;span class="c"&gt;#1  BP - Method  BookStore#find_by_title at library.rb:27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also remove breakpoints that are added this way using the &lt;code&gt;del&lt;/code&gt; or &lt;code&gt;delete&lt;/code&gt; command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;del&lt;/code&gt; will remove all breakpoints in one go (confirmation is needed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;del X&lt;/code&gt; deletes breakpoints numbered X in the breakpoints list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adding Conditions
&lt;/h3&gt;

&lt;p&gt;You can also add conditions when setting a breakpoint. Imagine a method that goes wrong when the book title is "Germinal", but that goes ok if it's "Notre Dame". In this case, we can add a breakpoint on the method, but only if the book title matches.&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="o"&gt;(&lt;/span&gt;rdbg&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;BookStore#find_by_title &lt;span class="k"&gt;if&lt;/span&gt;: book1.title &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Germinal"&lt;/span&gt;    &lt;span class="c"&gt;# command&lt;/span&gt;
&lt;span class="c"&gt;#1  BP - Method  BookStore#find_by_title at library.rb:27 if: book1.title == "Germinal"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integration with IDEs
&lt;/h2&gt;

&lt;p&gt;Many of us rely on modern IDEs and text editors that have support for direct debugging. A good choice is &lt;code&gt;rdbg&lt;/code&gt;: it integrates well with many IDEs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ruby/debug#use-rdbg-with-commands-written-in-ruby" rel="noopener noreferrer"&gt;Check the debug README for more details on &lt;code&gt;rdbg&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap and Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we covered the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing &lt;code&gt;debug&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Adding breakpoints from your favorite code editor with &lt;code&gt;binding.break&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Looking at the value of variables and objects from a debugger session&lt;/li&gt;
&lt;li&gt;Navigating within execution frames from the debugger console (with &lt;code&gt;up&lt;/code&gt;, &lt;code&gt;down&lt;/code&gt;, and &lt;code&gt;next&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Listing available variables and methods at any point in the console with &lt;code&gt;ls&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Adding breakpoints and conditional breakpoints on the fly from the debugger console&lt;/li&gt;
&lt;li&gt;Listing and removing breakpoints (with &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;delete &amp;lt;number&amp;gt;&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ending a debugging session with &lt;code&gt;finish&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, or &lt;code&gt;quit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;help&lt;/code&gt; command also provides plenty of details on the commands we have seen here and more. You can run &lt;code&gt;help break&lt;/code&gt; (for example) to learn more about the &lt;code&gt;break&lt;/code&gt; command and its subcommands.&lt;/p&gt;

&lt;p&gt;In conclusion, the &lt;code&gt;debug&lt;/code&gt; tool will greatly help you with debugging over the years.&lt;/p&gt;

&lt;p&gt;Most debuggers use similar commands, so don't hesitate to try others out too (check out our post on &lt;a href="https://blog.appsignal.com/2024/05/08/debugging-in-ruby-with-pry-byebug.html" rel="noopener noreferrer"&gt;pry-byebug&lt;/a&gt;, for example).&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic" rel="noopener noreferrer"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>How to Use Tailwind CSS for Your Ruby On Rails Project</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 19 Jun 2024 14:00:00 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-use-tailwind-css-for-your-ruby-on-rails-project-ak2</link>
      <guid>https://dev.to/appsignal/how-to-use-tailwind-css-for-your-ruby-on-rails-project-ak2</guid>
      <description>&lt;p&gt;It's hard to overstate the importance of Cascading Style Sheets (CSS) for all websites. Since the first CSS standards were published in late 1996, we have come quite far regarding features and ecosystems.&lt;/p&gt;

&lt;p&gt;Several frameworks have appeared and proved popular, one of the most recent being Tailwind CSS.&lt;/p&gt;

&lt;p&gt;In this post, we'll first examine Tailwind's utility-first approach before diving into how to use it in a Ruby on Rails application. You will see how Tailwind helps you to build excellent websites without the need for custom CSS and long debugging sessions.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS: A Utility-First Approach
&lt;/h2&gt;

&lt;p&gt;Most CSS frameworks (Foundation, Bootstrap, or Bulma, for example) provide ready-to-use components such as buttons and form fields, so you can quickly assemble blocks to shape an interface.&lt;/p&gt;

&lt;p&gt;Typically, adding a button with Bootstrap looks like this:&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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Button&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, a simple button is defined and styled by applying the &lt;code&gt;btn&lt;/code&gt; and &lt;code&gt;btn-primary&lt;/code&gt; classes. &lt;code&gt;btn-primary&lt;/code&gt; sets the right color for our use case. Yet, that interface can't fit our needs, so we add a custom CSS stylesheet to customize every component:&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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary admin-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Button&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tailwind is a "utility-first" concept. Instead of providing ready-to-use components such as buttons, it has low-level utility classes that you can compose to build custom designs. As such, it encourages a more functional approach to styling, where you apply pre-defined classes directly in your HTML. It aims to minimize the need for custom CSS and promotes design consistency through the constraints of the utility classes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Utility-first" means that Tailwind provides atomic, single-purpose classes you can combine to construct complex designs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's have a look at some code to compare Tailwind and Bootstrap. First, here is how Tailwind lets us style a simple button:&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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My Button
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a series of button element classes to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Background color &lt;code&gt;bg-blue-500&lt;/code&gt;:&lt;/strong&gt; While 'blue' is a pre-picked color, we can set the color shade with the number. The higher the number, the darker the color.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background color on hover:&lt;/strong&gt; &lt;code&gt;hover:bg-blue-600&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text color &lt;code&gt;text-white&lt;/code&gt;:&lt;/strong&gt; No need for a number here, as it's white; there is always a default shade if you don't specify a number, such as with text-red.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertical padding &lt;code&gt;py-2&lt;/code&gt;:&lt;/strong&gt; 'p' is padding, 'y' is for the vertical axis, '2' is the spacing value, not in pixels but a scale defined in Tailwind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal padding &lt;code&gt;px-4&lt;/code&gt;:&lt;/strong&gt; Same as above, with 'x' for the horizontal axis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rounding corners:&lt;/strong&gt; &lt;code&gt;rounded&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This looks more verbose than the Bootstrap example, but by only adding classes, we can adjust each part of the style. We don't need to create a custom CSS class.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might not be happy with these colors, but the good news is that you can add custom colors. We will cover that later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A Word on Scales
&lt;/h3&gt;

&lt;p&gt;CSS is mighty when it comes to spacing (such as margins and padding), and you can work with pixels and rems (root-em, a size relative to the size of the root element). This tends to be difficult, though. Tailwind comes with its own spacing scale that hides complexity while also helping with proportionality.&lt;/p&gt;

&lt;p&gt;By default, Tailwind offers values between 0 and 96, with each step proportional to the others. For example, the value &lt;code&gt;16&lt;/code&gt; has twice as much spacing as &lt;code&gt;8&lt;/code&gt;. Thanks to this, we don't have to do the math to work with rems or pixels. We can define our preferred values and reuse them throughout our design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale"&gt;Read more about spacing in Tailwind CSS's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Tailwind in a Ruby on Rails Environment
&lt;/h2&gt;

&lt;p&gt;Ruby on Rails 7.x directly supports Tailwind in its application generator.&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;$&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/workspace/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;tailwind-tryout &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;tailwind-tryout
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; rails new &lt;span class="nt"&gt;-d&lt;/span&gt; sqlite3 &lt;span class="nt"&gt;-c&lt;/span&gt; tailwind &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;We'll skip the test configuration (-T) to avoid adding unnecessary complexity to this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tailwind has a neat feature that generates the CSS file your application needs. Other frameworks require you to include a whole CSS file defining a framework (even the pieces you don't use). In contrast, Tailwind will scan your project and generate a CSS file that contains only the classes your project needs.&lt;/p&gt;

&lt;p&gt;You do need to run a little utility to make that happen. In development mode, you can run a watcher daemon that will keep things up to date as you work: &lt;code&gt;bin/rails tailwindcss:watch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also add the watcher daemon to your &lt;code&gt;Procfile&lt;/code&gt;, then use &lt;code&gt;foreman&lt;/code&gt; or &lt;code&gt;overmind&lt;/code&gt; to start the &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: bin/rails server
css: bin/rails tailwindcss:watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now use it within a simple landing page:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;We can then head to &lt;a href="http://localhost:3000/landing/index"&gt;http://localhost:3000/landing/index&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dissecting Our Landing Page
&lt;/h3&gt;

&lt;p&gt;Every landing page needs a title. The generator works since we configured our application to use Tailwind as its CSS framework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/landing/index.html.erb
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-4xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Landing#index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Find me in app/views/landing/index.html.erb&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We find something that looks like standard HTML here. We have only two classes for the h1 tag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;font-bold&lt;/code&gt;: to control the &lt;a href="https://tailwindcss.com/docs/font-weight"&gt;font weight&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text-4xl&lt;/code&gt;: to control the &lt;a href="https://tailwindcss.com/docs/font-size"&gt;font size&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we change &lt;code&gt;text-4xl&lt;/code&gt; to &lt;code&gt;text-xl&lt;/code&gt; and reload the page, the new style will be automatically applied. Looking at the terminal where Foreman is running, you will see that Tailwind has generated a stylesheet in the background again.&lt;br&gt;
That's how simple it is to integrate Tailwind into a Ruby on Rails application (this relies on the &lt;a href="https://github.com/rails/tailwindcss-rails"&gt;tailwindcss-rails gem&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring Tailwind for Ruby on Rails
&lt;/h3&gt;

&lt;p&gt;You can edit the &lt;code&gt;config/tailwind.config.js&lt;/code&gt; file to adjust Tailwind's settings (e.g., to add additional colors, specify a font to use, adjust spacing, etc).&lt;/p&gt;

&lt;p&gt;For example, we could add a "copper" color to our backgrounds and text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.{html,js}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;copper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FAD9C1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#F6C8A4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#F2B786&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#EEA669&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#E9944C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#D17F3E&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#B96A31&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#A15524&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8A4018&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8A4018&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;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Times&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serif&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="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;108rem&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;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;blockquote&gt;
&lt;p&gt;Note that the shades are helpful but can instead be named. If we only need three shades, for example, we can use 'light', 'medium', and 'dark' instead of numbers in our views.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can then use the shades in our title:&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&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-4xl text-copper-200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Landing#index&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-xl text-copper-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Subtitle&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can find details about this in the &lt;a href="https://github.com/rails/tailwindcss-rails?tab=readme-ov-file#configuration"&gt;tailwindcss-rails gem's README&lt;/a&gt; and also the &lt;a href="https://tailwindcss.com/docs/configuration"&gt;Tailwind CSS documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Asset Pipeline
&lt;/h3&gt;

&lt;p&gt;We have seen how &lt;code&gt;bin/rails tailwindcss:watch&lt;/code&gt; keeps our stylesheets updated in local development mode. If we need to build the stylesheets just once, we can use &lt;code&gt;bin/rails tailwindcss:build&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;For production use, you can rely on &lt;code&gt;bin/rails assets:precompile&lt;/code&gt; to directly call &lt;code&gt;bin/rails tailwindcss:build&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/asset_pipeline.html"&gt;Learn more about the asset pipeline for Ruby on Rails applications&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tailwind for Rails in Action
&lt;/h2&gt;

&lt;p&gt;Let's check out a couple of practical uses of Tailwind in some views: a form and a responsive navigation bar.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Simple Form
&lt;/h3&gt;

&lt;p&gt;Using the Ruby on Rails generator, we create a &lt;code&gt;user&lt;/code&gt; resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/rails g resource user email:string password:string
bin/rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then alter the &lt;code&gt;users_controller.rb&lt;/code&gt; file and create a view for the form.&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="c1"&gt;# app/controllers/users_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&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;def&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@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;new&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/users/new.html.erb
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-4xl text-blue-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Users#new&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;form_with&lt;/span&gt; &lt;span class="ss"&gt;model: &lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;local: &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;"mb-6"&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;label&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;class: &lt;/span&gt;&lt;span class="s2"&gt;"block mb-2 text-sm font-medium text-blue-900"&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;text_field&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;class: &lt;/span&gt;&lt;span class="s2"&gt;"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"&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;"mb-6"&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;label&lt;/span&gt; &lt;span class="ss"&gt;:password&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;"block mb-2 text-sm font-medium text-gray-900"&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;password_field&lt;/span&gt; &lt;span class="ss"&gt;:password&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;"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"&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;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We style each piece individually, adjusting the text color, background color, borders, padding, margins, etc. There is nothing beyond standard Tailwind here, yet we customize the form to fit our needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Responsive Navigation Bar
&lt;/h3&gt;

&lt;p&gt;We can add conditional breakpoints based on a browser's minimum width using any utility class in Tailwind. For example, the following title will change color depending on the window size:&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;h2&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-base font-semibold text-gray-900 sm:text-teal-800 lg:text-purple-500"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Additional information
&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the color is a dark shade of gray. When a browser window's width is between 640px and 1024px, it's a shade of teal. If a window's width is above 1024px, it's a shade of purple.&lt;/p&gt;

&lt;p&gt;As Tailwind can also handle &lt;a href="https://tailwindcss.com/docs/grid-template-columns"&gt;columns&lt;/a&gt;, here is an example to showcase how an element's column width can change based on window size:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sm:col-span-2 md:col-span-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block text-sm font-medium leading-6 text-gray-900"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;State&lt;span class="nt"&gt;&amp;lt;/label&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The label "State" spans two or three columns in this case.&lt;/p&gt;

&lt;p&gt;Here, using Tailwind's grid layout utilities, we define a grid that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One column wide by default (&lt;code&gt;grid-cols-1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Six columns wide above 640px width&lt;/li&gt;
&lt;li&gt;Eight columns wide above 768px width
&lt;/li&gt;
&lt;/ul&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:grid-cols-8"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breakpoints and their widths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sm&lt;/code&gt;: 640px&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;md&lt;/code&gt;: 768px&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lg&lt;/code&gt;: 1024px&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xl&lt;/code&gt;: 1280px&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2xl&lt;/code&gt;: 1536px&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we've seen, Tailwind simplifies page design and the styling of components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind vs. Other Frameworks
&lt;/h2&gt;

&lt;p&gt;Now that we understand how Tailwind can be used, let's review its key differences to other frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Utility-based:&lt;/strong&gt; We compose the style of each element using specific CSS classes, each focusing on different parts of the style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get what we need:&lt;/strong&gt; We only get the parts we need to ship our website, making for faster load times; that optimizes build time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible:&lt;/strong&gt; We can extend or customize TailwindCSS' defaults through a simple configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy shading of colors:&lt;/strong&gt; There's no need to figure out how to make lighter or darker shades of a color to handle hover situations, for example.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple spacing:&lt;/strong&gt; The hidden and proportional scales simplify spacing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less custom CSS:&lt;/strong&gt; Since we only assemble classes to style elements, we rely less on custom CSS and can share styles (including complete themes) using HTML files and snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruby on Rails friendly:&lt;/strong&gt; Thanks to the Tailwind gem, everything is integrated into the layouts and the assets pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;As we've seen, Tailwind's utility-first approach is a great fit for Ruby on Rails. We don't need to spend time adjusting Tailwind to fit our needs by adding complex custom configurations or additional custom CSS. As we conceive our views and partials, we can use Tailwind utility classes to shape and style them.&lt;/p&gt;

&lt;p&gt;If you want to learn more, you can access many ready-to-use templates and components thanks to Tailwind's vibrant community, and products such as &lt;a href="https://tailwindui.com"&gt;TailwindUI&lt;/a&gt; (from Tailwind's creators).&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Creating Forms in Ruby on Rails with Simple Form</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 29 May 2024 11:55:19 +0000</pubDate>
      <link>https://dev.to/appsignal/creating-forms-in-ruby-on-rails-with-simple-form-3od1</link>
      <guid>https://dev.to/appsignal/creating-forms-in-ruby-on-rails-with-simple-form-3od1</guid>
      <description>&lt;p&gt;Ruby on Rails has changed how we build web applications. Early on, the framework came with some great features to help you get started and build robust applications.&lt;/p&gt;

&lt;p&gt;However, it can still be tricky to build and handle forms. Simple Form is a great option. Let's examine what Simple Form is, why we might need it, and some real use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms and Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;Ruby on Rails really simplifies building applications. Yet, it also requires your constant attention to keep your codebase streamlined and coherent. One strategy is to avoid writing code by using abstractions, for example. Code that isn't in your actual application is code you don't have to manage. That's why we like abstractions so much, right? Simple Form is just such an abstraction that simplifies building forms for web pages.&lt;/p&gt;

&lt;p&gt;Forms are generally complex to build, even using Ruby on Rails. Each field requires attention to define the right type and attach it to the correct attribute. Simple Form eases that pain, as you don't need to find the exact type required for each field.&lt;/p&gt;

&lt;p&gt;Let's take a form that gets a user's details, with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, and &lt;code&gt;email&lt;/code&gt; fields. Consider that we have the &lt;code&gt;User&lt;/code&gt; model with similar related attributes. With Simple Form, the form that fills in attributes looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;@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;f&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;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;:name&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;false&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;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="cp"&gt;%&amp;gt;&lt;/span&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;:email&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;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="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;Note that we are not specifying the types of each field. Simple Form will pick the correct input type for each field based on the column's type.&lt;/p&gt;

&lt;p&gt;This saves us time and keeps the code readable and easier to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Simple Form
&lt;/h2&gt;

&lt;p&gt;To install Simple Form in your project, add the &lt;code&gt;simple_form&lt;/code&gt; gem to the Gemfile. Once you have run &lt;code&gt;bundle install&lt;/code&gt;, you can use the included generator to set it up correctly for your application.&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 simple_form:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A note on Bootstrap and Zurb Foundation:&lt;/strong&gt; &lt;em&gt;Both Bootstrap and Zurb Foundation 5 are supported within Simple Form. So, if you are using one of them, you can use the &lt;code&gt;--bootstrap&lt;/code&gt; or &lt;code&gt;--foundation&lt;/code&gt; parameter with the generator to include additional configurations during the installation. In the case of Bootstrap, this would look like the following:&lt;/em&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 generate simple_form:install &lt;span class="nt"&gt;--bootstrap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Usage of Simple Form
&lt;/h2&gt;

&lt;p&gt;As we pointed out earlier, Simple Form will generate a complete form mainly based on the data related to your objects. Yet, it's also important to understand that Simple Form will not only generate the appropriate field for each attribute but also provide labels and display error hints above the input fields themselves!&lt;/p&gt;

&lt;p&gt;Let's consider a fresh new Ruby on Rails 7.x application. We'll generate a User model and build a form for it using Simple Form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g model User username:string password:string email:string remember_me:boolean
&lt;span class="c"&gt;# and follow with running the migration of course&lt;/span&gt;
rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can generate a controller with the &lt;code&gt;new&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, and &lt;code&gt;index&lt;/code&gt; actions. The aim is to have something we can play with in the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g controller &lt;span class="nb"&gt;users&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/users_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&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;new&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &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;new&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;create&lt;/span&gt;
    &lt;span class="n"&gt;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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;users_path&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;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;users: &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;all&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;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user_params&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;:user&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;:username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&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="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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&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;resources&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;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&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;These actions require relevant views. Here is a form that handles a user signing up or being added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/users/new.html.erb --&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="n"&gt;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;f&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;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;label: &lt;/span&gt;&lt;span class="s1"&gt;'Your username please'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;error: &lt;/span&gt;&lt;span class="s1"&gt;'Username is mandatory, please specify one'&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;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;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;hint: &lt;/span&gt;&lt;span class="s1"&gt;'No special characters.'&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;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;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;placeholder: &lt;/span&gt;&lt;span class="s1"&gt;'user@domain.com'&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;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;:remember_me&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline_label: &lt;/span&gt;&lt;span class="s1"&gt;'Yes, remember me'&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;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="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;In this case, we specify a custom label and error for the &lt;code&gt;username&lt;/code&gt; input. If no such customization is passed, it will do its best to guess what the label needs to be based on the attribute's name.&lt;/p&gt;

&lt;p&gt;We have set a custom hint for the &lt;code&gt;password&lt;/code&gt; field and a custom &lt;code&gt;placeholder&lt;/code&gt; for the &lt;code&gt;email&lt;/code&gt; field. Notice, also, the ability to specify an "inline label" instead of one located above the field (the usual default in forms).&lt;/p&gt;

&lt;p&gt;It's also possible to disable labels, hints, and errors by passing the &lt;code&gt;false&lt;/code&gt; value for those attributes: &lt;code&gt;&amp;lt;%= f.input :password_confirmation, label: false %&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We need to complement this with one more view to make it usable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app/views/users/index.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;users&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;user&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;li&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&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;username&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;)&lt;span class="nt"&gt;&amp;lt;/li&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;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&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;'New User'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_user_path&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then start the application using &lt;code&gt;rails s&lt;/code&gt;. Head to &lt;code&gt;localhost:3000/users/new&lt;/code&gt; to see and use the form.&lt;br&gt;
Let's focus on how the form differs from a classic Ruby on Rails form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on validations&lt;/strong&gt;: &lt;em&gt;Simple Form can work with mandatory fields out of the box. For example, if we were to add the following presence validation in our User model:&lt;/em&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;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&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="n"&gt;presence&lt;/span&gt;&lt;span class="ss"&gt;:true&lt;/span&gt;

  &lt;span class="c1"&gt;# more code&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;em&gt;This will be used by Simple Form to add a little '*' next to the &lt;code&gt;username&lt;/code&gt; field, specifying that it's mandatory. This doesn't manage the errors automatically, so you still have to handle that in the controller action. Return to the appropriate field that has errors and act on them.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  About Column Types and Form Fields
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, we haven't specified a type for each field. The &lt;a href="https://github.com/heartcombo/simple_form#available-input-types-and-defaults-for-each-column-type"&gt;Simple Form README&lt;/a&gt; has a complete list of all available input types and the defaults for each column type. Here are the most used ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column Type&lt;/th&gt;
&lt;th&gt;Generated HTML element&lt;/th&gt;
&lt;th&gt;Comment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=text]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=checkbox]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(passwords) &lt;code&gt;string&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=password]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any column with a name containing 'password'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=textarea]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;integer&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input[type=number]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;datetime select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;country&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Booleans
&lt;/h3&gt;

&lt;p&gt;As we can see in the above table, boolean attributes will be represented, by default, as checkboxes. In many cases, that's what we'll want. But if not, there is a way to customize this in Simple Form with the &lt;code&gt;as&lt;/code&gt; attribute, which allows you to specify if you will show radio buttons or a dropdown instead.&lt;/p&gt;

&lt;p&gt;Here is how to specify radio buttons instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;:remember_me&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;value: &lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;as: :radio_buttons&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;This will generate the following HTML:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input radio_buttons optional user_remember_me"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"radio_buttons optional"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Remember me&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user[remember_me]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"off"&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;"radio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"user_remember_me_true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"radio_buttons optional"&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user[remember_me]"&lt;/span&gt;
        &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"user_remember_me_true"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;Yes
    &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&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;"radio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"user_remember_me_false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"radio_buttons optional"&lt;/span&gt;
        &lt;span class="na"&gt;readonly=&lt;/span&gt;&lt;span class="s"&gt;"readonly"&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user[remember_me]"&lt;/span&gt;
        &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"user_remember_me_false"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;No
    &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&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;It's impressive that such a small piece of Ruby code gets you such a complete piece of HTML!&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;From the previous example, we can see how Simple Form generates a lot of HTML for each field. That includes the HTML for the input field and a wrapper div around the label and input field. We can customize those by specifying a custom class and id using the &lt;code&gt;input_html&lt;/code&gt; and &lt;code&gt;wrapper_html&lt;/code&gt; attributes. Here is an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;@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;f&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;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;wrapper_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;'username'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;input_html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="p"&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="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;That's also a way to set attributes for the related HTML, such as &lt;code&gt;maxlength&lt;/code&gt; or &lt;code&gt;value&lt;/code&gt;. Let's take the password field from our user form. We can limit the size and length of the field with the &lt;code&gt;maxlength&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;:password&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;maxlength: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom Inputs and Additional Options
&lt;/h2&gt;

&lt;p&gt;Simple Form is a Ruby library that prepares HTML nodes for you. It comes with a whole set of fields, but you can also add your own. To do so, you can create classes inheriting from Simple Form's classes. Let's consider defining a custom social network input field with a little '@' prefix in front of the actual field.&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="c1"&gt;# app/inputs/social_handle_input.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SocialHandleInput&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;SimpleForm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Inputs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapper_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;merged_input_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;merge_wrapper_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_html_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wrapper_options&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="vi"&gt;@builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;merged_input_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html_safe&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;Now we can use it in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;:network_handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;as: :social_handle&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that, if your User model doesn't have a &lt;code&gt;network_handle&lt;/code&gt; attribute, you must add it through a migration or cheat with an &lt;code&gt;attr_accessor&lt;/code&gt; in the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  i18n Support
&lt;/h2&gt;

&lt;p&gt;As we've mentioned, building forms is often a pain point. But things get even more painful when it comes to websites and applications that support multiple languages. Simple Form, thankfully, follows Ruby on Rails standards. You can use a &lt;code&gt;simple_form&lt;/code&gt; key in the local files to define translations for all labels, hints, placeholders, prompts, etc.&lt;/p&gt;

&lt;p&gt;Here is a little example:&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="ss"&gt;en:
  simple_form:
    labels:
      user:
        username: &lt;/span&gt;&lt;span class="s1"&gt;'User name'&lt;/span&gt;
        &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'Password'&lt;/span&gt;
    &lt;span class="ss"&gt;hints:
      user:
        username: &lt;/span&gt;&lt;span class="s1"&gt;'User name to sign in.'&lt;/span&gt;
        &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'No special characters, please.'&lt;/span&gt;
    &lt;span class="ss"&gt;placeholders:
      user:
        username: &lt;/span&gt;&lt;span class="s1"&gt;'Your username'&lt;/span&gt;
        &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="s1"&gt;'****'&lt;/span&gt;
    &lt;span class="ss"&gt;include_blanks:
      user:
        age: &lt;/span&gt;&lt;span class="s1"&gt;'Rather not say'&lt;/span&gt;
    &lt;span class="ss"&gt;prompts:
      user:
        role: &lt;/span&gt;&lt;span class="s1"&gt;'Select your role'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Value Objects
&lt;/h2&gt;

&lt;p&gt;Sometimes, we might use custom, non-ActiveRecord classes to instantiate the main object a form relies upon. This can be done to compose data from multiple models into a synthetic one for either business logic reasons or to gather several attributes into a single object.&lt;/p&gt;

&lt;p&gt;Whatever the reason, you can still rely on Simple Form to build forms for that kind of object. To do so, the object class needs to implement, in the best case, three methods: &lt;code&gt;to_model&lt;/code&gt;, &lt;code&gt;to_key&lt;/code&gt;, and &lt;code&gt;persisted?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;to_model&lt;/code&gt; method will point to the object itself:&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;to_model&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;to_key&lt;/code&gt; allows us to point to the identifier attribute for the object — usually, that means an &lt;code&gt;id&lt;/code&gt; attribute named:&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;to_key&lt;/span&gt;
  &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the &lt;code&gt;persisted?&lt;/code&gt; method is there to tell Simple Form if the object is directly persisted or not.&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;persisted?&lt;/span&gt;
  &lt;span class="kp"&gt;false&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 that method isn't present, the &lt;code&gt;f.submit&lt;/code&gt; helper isn't usable.&lt;/p&gt;

&lt;p&gt;There is a faster way, though — including the &lt;code&gt;ActiveModel::Model&lt;/code&gt; module in the class:&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="c1"&gt;# app/models/account.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Model&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:company_name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's use it through the User model.&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="c1"&gt;# app/models/user.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:network_handle&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;account&lt;/span&gt;
    &lt;span class="vi"&gt;@account&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;company_name: &lt;/span&gt;&lt;span class="s2"&gt;"Acme Inc."&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;company_name&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;company_name&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;We can then add a field for the company name within the user form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;:company_name&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;Of course, this attribute will not get saved in a table. However, the same concept can be applied to compose a value object with attributes pulled from several models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've seen that Simple Form is easy to install and integrate into your Ruby on Rails application. Simple Form not only takes care of the details of each field in your form, but also generates complex and complete HTML so your form can be styled and shaped with ease.&lt;/p&gt;

&lt;p&gt;I encourage you to dive into &lt;a href="https://www.rubydoc.info/gems/simple_form/1.3.1"&gt;Simple Form's documentation&lt;/a&gt; to see how powerful it can be.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Debugging in Ruby with pry-byebug</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 22 May 2024 09:00:22 +0000</pubDate>
      <link>https://dev.to/appsignal/debugging-in-ruby-with-pry-byebug-3764</link>
      <guid>https://dev.to/appsignal/debugging-in-ruby-with-pry-byebug-3764</guid>
      <description>&lt;p&gt;For a software engineer, even the basic use of a debugger can save a lot of pain: adding breakpoints (places in the code the program will stop at and expose the current context) is very easy, and navigating from one breakpoint to another isn't difficult either.&lt;/p&gt;

&lt;p&gt;And with just that, you can say goodbye to a program's many &lt;code&gt;puts&lt;/code&gt; and runs. Just add one or more breakpoints and run your program. Then you're able to access not only the variables and objects you might have thought of, but also anything accessible from that point in the code.&lt;/p&gt;

&lt;p&gt;In this article, we'll focus on &lt;code&gt;pry-byebug&lt;/code&gt;, a gem that adds debugging and stack navigation to &lt;code&gt;pry&lt;/code&gt; using &lt;code&gt;byebug&lt;/code&gt;. We will see how to set up and use &lt;code&gt;pry-byebug&lt;/code&gt;, how it integrates with Ruby programs, and a few advanced techniques.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up &lt;code&gt;pry-byebug&lt;/code&gt; for Ruby
&lt;/h2&gt;

&lt;p&gt;The setup is really simple. Just add the &lt;a href="https://github.com/deivid-rodriguez/pry-byebug"&gt;&lt;code&gt;pry-byebug&lt;/code&gt; gem&lt;/a&gt; to your Gemfile, and then run &lt;code&gt;bundle install&lt;/code&gt;. I'd advise you to add it to the &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; groups to debug tests as well.&lt;/p&gt;

&lt;p&gt;Let's now cover some simple debugging using breakpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Debugging with Breakpoints
&lt;/h3&gt;

&lt;p&gt;The whole principle of debuggers is to get rid of &lt;code&gt;printf debugging&lt;/code&gt; by giving you direct access to a running program's stack. So, we need to add breakpoints to tell the interpreter where to stop.&lt;/p&gt;

&lt;p&gt;Let's take the example of a simple bookstore with software that manages the titles it offers.&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;Book&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="vi"&gt;@author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;
    &lt;span class="vi"&gt;@price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&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;class&lt;/span&gt; &lt;span class="nc"&gt;BookStore&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;=&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;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;book&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;remove_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;title&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;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@books&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&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;span class="c1"&gt;# Sample Usage:&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BookStore&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;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This program is simple enough. Of course, there is no storage, but that's not needed to demonstrate what we want to do.&lt;/p&gt;

&lt;p&gt;The last call will probably list "The Hobbit", but that's not certain and might not be the one we want. To debug this, it's best to "jump" into the &lt;code&gt;find_by_title&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;So, add the following lines to the top of the file:&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'pry'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'pry-byebug'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also add a breakpoint to the method using &lt;code&gt;binding.pry&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;find_by_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pry&lt;/span&gt;
  &lt;span class="vi"&gt;@books&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&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 then launch the program and get to the breakpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;From: test-pry-byebug/app.rb:29 BookStore#find_by_title:

    27: def find_by_title&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt;
    28:   binding.pry
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 29:   @books.find &lt;span class="o"&gt;{&lt;/span&gt; |book| book.title.include?&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    30: end

&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#&amp;lt;BookStore&amp;gt;)&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing you should see is that we know exactly where we are: there's no need to guess which part of the program that line in STDOUT comes from. Of course, we have only one breakpoint here, but you can easily understand how useful this might be if many breakpoints are used.&lt;/p&gt;

&lt;p&gt;We can then check the value of the &lt;code&gt;title&lt;/code&gt; variable. There likely isn't much to be surprised by here. The issue is with the use of &lt;code&gt;find&lt;/code&gt; (instead of &lt;code&gt;select&lt;/code&gt;, or something more complex) to find and return a list of books instead of just one book.&lt;/p&gt;

&lt;p&gt;By being right in the context, you can experiment with both &lt;code&gt;find&lt;/code&gt; and &lt;code&gt;select&lt;/code&gt; and decide on a course of action.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here might be a good point to reflect on the expected behavior of the program you are building and this piece of code. It might be good to clarify what the code should do through RSpec tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you are ok with that step or want to see what's happening until the next breakpoint, type &lt;code&gt;continue&lt;/code&gt;, and the program execution will start again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Small Steps from Breakpoints
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;pry-byebug&lt;/code&gt;, you can take smaller steps from breakpoints to see what happens after each line (particularly helpful for multiple calls using a method). Once you have stepped into the program and are at a breakpoint, use the &lt;code&gt;next&lt;/code&gt; command to execute the next line.&lt;/p&gt;

&lt;p&gt;Let's take the case of adding a breakpoint within the &lt;code&gt;add_book&lt;/code&gt; method just before the first three books are created in the bookstore:&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;add_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pry&lt;/span&gt;
  &lt;span class="vi"&gt;@books&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BookStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pry&lt;/span&gt;
&lt;span class="n"&gt;book1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;book3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;Upon execution, the program will first stop just before the &lt;code&gt;book1&lt;/code&gt; variable is instantiated. We might want to know the result of that call. To do so, we don't have to stop the program and add another breakpoint. We just have to type &lt;code&gt;next&lt;/code&gt; in the pry console and hit &lt;code&gt;Enter&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;From: test-pry-byebug/app.rb:37 :

    32: end
    33:
    34: &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    35: store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    36: binding.pry
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 37: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    38: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    39: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
    40:
&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; next
From: test-pry-byebug/app.rb:38 :

    33:
    34: &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    35: store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    36: binding.pry
    37: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 38: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    39: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then have access to &lt;code&gt;book1&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;&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; book1
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;#&amp;lt;Book:0x00007fad37f964d8 @author="Frank Herbert", @price=20.0, @title="Dune"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This beats a lot of back-and-forth insertion and tweaking &lt;code&gt;puts&lt;/code&gt; calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Even Smaller Steps
&lt;/h3&gt;

&lt;p&gt;We might want to step into the &lt;code&gt;initialize&lt;/code&gt; method (called through &lt;code&gt;new&lt;/code&gt;) to see more details. We can do so by using &lt;code&gt;step&lt;/code&gt; instead of &lt;code&gt;next&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;From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  8:   @title &lt;span class="o"&gt;=&lt;/span&gt; title
     9:   @author &lt;span class="o"&gt;=&lt;/span&gt; author
    10:   @price &lt;span class="o"&gt;=&lt;/span&gt; price
    11: end

&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#&amp;lt;Book&amp;gt;)&amp;gt; title&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to step into a method when it's called rather than skip over it as we do with &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free from &lt;code&gt;puts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By now, you should see how this approach saves time and frees us from using &lt;code&gt;puts&lt;/code&gt; or its equivalent to debug a program. Right from the debugger shell, we can look into the values of objects and variables to figure out why a program behaves like it does. Furthermore, you can save a lot of time from the shell by adding breakpoints on the fly, thus avoiding the "quit, move &lt;code&gt;binding.pry&lt;/code&gt;, restart" loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finishing a Debugging Session
&lt;/h3&gt;

&lt;p&gt;You can call &lt;code&gt;continue&lt;/code&gt; to go to the next breakpoint or the end of a program. &lt;code&gt;finish&lt;/code&gt; executes and finishes the current step, returning just after that step. In the previous example, we end up at the following 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="o"&gt;[&lt;/span&gt;2] pry&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#&amp;lt;Book&amp;gt;)&amp;gt; finish&lt;/span&gt;

From: test-pry-byebug/app.rb:39 :

    34: &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    35: store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    36: binding.pry
    37: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    38: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 39: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, &lt;code&gt;!!!&lt;/code&gt; exits the debugger directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Use Cases of &lt;code&gt;pry-byebug&lt;/code&gt; for Ruby
&lt;/h2&gt;

&lt;p&gt;Now let's dive into a few more advanced use cases, including rewinding and replaying, adding breakpoints on the fly, and conditional breakpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewinding and Replaying
&lt;/h3&gt;

&lt;p&gt;Can we move back and forth between two points? Yes, totally. Given our previous case, we could move up and down from the &lt;code&gt;initialize&lt;/code&gt; method to its call. Just use &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt; commands after &lt;code&gt;step&lt;/code&gt; to get into the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;From: test-pry-byebug/app.rb:37 :

    34: &lt;span class="c"&gt;# Sample Usage:&lt;/span&gt;
    35: store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    36: binding.pry
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 37: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    38: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    39: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; step

From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  8:   @title &lt;span class="o"&gt;=&lt;/span&gt; title
     9:   @author &lt;span class="o"&gt;=&lt;/span&gt; author
    10:   @price &lt;span class="o"&gt;=&lt;/span&gt; price
    11: end

&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#&amp;lt;Book&amp;gt;)&amp;gt; up&lt;/span&gt;

From: test-pry-byebug/app.rb:37 :

    35: store &lt;span class="o"&gt;=&lt;/span&gt; BookStore.new
    36: binding.pry
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 37: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    38: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
    39: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; down

From: test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  8:   @title &lt;span class="o"&gt;=&lt;/span&gt; title
     9:   @author &lt;span class="o"&gt;=&lt;/span&gt; author
    10:   @price &lt;span class="o"&gt;=&lt;/span&gt; price
    11: end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we need details about the stack we are in, we can call the &lt;code&gt;backtrace&lt;/code&gt; method:&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="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#&amp;lt;Book&amp;gt;)&amp;gt; backtrace&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;#0  Book.initialize(title#String, author#String, price#Float) at test-pry-byebug/app.rb:8&lt;/span&gt;
    ͱ-- &lt;span class="c"&gt;#1  Class.new(*args) at test-pry-byebug/app.rb:37&lt;/span&gt;
    &lt;span class="c"&gt;#2  &amp;lt;main&amp;gt; at test-pry-byebug/app.rb:37&lt;/span&gt;

From: /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb:8 Book#initialize:

     7: def initialize&lt;span class="o"&gt;(&lt;/span&gt;title, author, price&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  8:   @title &lt;span class="o"&gt;=&lt;/span&gt; title
     9:   @author &lt;span class="o"&gt;=&lt;/span&gt; author
    10:   @price &lt;span class="o"&gt;=&lt;/span&gt; price
    11: end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list of frames is in the stack at the top, numbered from 0 to 2. A little cursor shows us which frame we are currently in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Breakpoints On the Fly
&lt;/h2&gt;

&lt;p&gt;To avoid additional debugger exits, we can add breakpoints on the fly from the debugger console, using the &lt;code&gt;break&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You can add breakpoints in the current file or another file, to a specific line or the start of a specific method.&lt;/p&gt;

&lt;p&gt;For example, let's add a breakpoint to the start of the &lt;code&gt;find_by_title&lt;/code&gt; method of the &lt;code&gt;BookStore&lt;/code&gt; class:&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="o"&gt;[&lt;/span&gt;2] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;BookStore#find_by_title

  Breakpoint 2: BookStore#find_by_title &lt;span class="o"&gt;(&lt;/span&gt;Enabled&lt;span class="o"&gt;)&lt;/span&gt;

  28: def find_by_title&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt;
29:   @books.find &lt;span class="o"&gt;{&lt;/span&gt; |book| book.title.include?&lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
30: end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add it to line 38 of the current file:&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="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;38

  Breakpoint 1: /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb @ 38 &lt;span class="o"&gt;(&lt;/span&gt;Enabled&lt;span class="o"&gt;)&lt;/span&gt;

      35: binding.pry
    36: book1 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Dune"&lt;/span&gt;, &lt;span class="s2"&gt;"Frank Herbert"&lt;/span&gt;, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
    37: book2 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The Hobbit"&lt;/span&gt;, &lt;span class="s2"&gt;"J.R.R. Tolkien"&lt;/span&gt;, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 38: book3 &lt;span class="o"&gt;=&lt;/span&gt; Book.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hobbit's Journey"&lt;/span&gt;, &lt;span class="s2"&gt;"Unknown"&lt;/span&gt;, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
    39:
    40: store.add_book&lt;span class="o"&gt;(&lt;/span&gt;book1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A call to &lt;code&gt;break&lt;/code&gt; lists all the breakpoints in place:&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="o"&gt;[&lt;/span&gt;4] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;

  &lt;span class="c"&gt;# Enabled At&lt;/span&gt;
  &lt;span class="nt"&gt;-------------&lt;/span&gt;

  1 Yes     /mnt/data/Code/Pier22/courses/raw-courses/rspec/git-repo/test-pry-byebug/app.rb @ 38
  2 Yes     BookStore#find_by_title
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that they are numbered, so you can actually delete them using the &lt;code&gt;--delete&lt;/code&gt; argument to &lt;code&gt;break&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;&lt;span class="o"&gt;[&lt;/span&gt;4] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional Breakpoints
&lt;/h3&gt;

&lt;p&gt;You might want conditional breakpoints, too.&lt;/p&gt;

&lt;p&gt;Imagine that your code only misbehaves if a &lt;code&gt;user&lt;/code&gt; object's &lt;code&gt;role&lt;/code&gt; attribute is set to &lt;code&gt;:admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Well, you might want to add the following breakpoint, and a condition to trigger it so that the debugger only stops if that condition is set:&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="o"&gt;[&lt;/span&gt;1] pry&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;break &lt;/span&gt;BookStore#add_book &lt;span class="k"&gt;if &lt;/span&gt;book.title.match /Dune/

  Breakpoint 1: BookStore#add_book &lt;span class="o"&gt;(&lt;/span&gt;Enabled&lt;span class="o"&gt;)&lt;/span&gt; Condition: book.title.match /Dune/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;continue&lt;/code&gt;, the debugger executes the addition of &lt;code&gt;Dune&lt;/code&gt; without stopping, until the next call to &lt;code&gt;add_book&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a great feature that will save you a lot of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;By now, you should have fewer reasons to rely on &lt;code&gt;printf()&lt;/code&gt; debugging when facing issues with your Ruby code. You now know how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;pry-byebug&lt;/code&gt; and some of its plugins&lt;/li&gt;
&lt;li&gt;Add breakpoints from your favorite code editor with &lt;code&gt;binding.pry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start a debugger session (starting the app)&lt;/li&gt;
&lt;li&gt;Look at the value of variables and objects from the debugger session (simply calling the object or variable from the debugger console)&lt;/li&gt;
&lt;li&gt;Navigate within the execution frames from the debugger console (with &lt;code&gt;up&lt;/code&gt;, &lt;code&gt;down&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, and &lt;code&gt;frame&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add breakpoints on the fly from the debugger console&lt;/li&gt;
&lt;li&gt;Add conditional breakpoints from the debugger console (with &lt;code&gt;break ClassName#method_name if var.nil?&lt;/code&gt;, for example)&lt;/li&gt;
&lt;li&gt;List and remove breakpoints (with &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;break --delete &amp;lt;number&amp;gt;&lt;/code&gt;, and &lt;code&gt;break --disable-all&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Conclude your debugging session with &lt;code&gt;finish&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, or &lt;code&gt;!!!&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, you can also configure &lt;code&gt;pry-byebug&lt;/code&gt; through the &lt;code&gt;~/.pryrc&lt;/code&gt; file to add aliases and a few custom behaviors. See the main &lt;a href="https://github.com/deivid-rodriguez/pry-byebug#matching-byebug-behaviour"&gt;pry-byebug README&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pry-byebug&lt;/code&gt; is a tool that will help you a lot over the years, saving you time and headaches. It's definitely worth practicing its use so that you can confidently open it the next time you're unsure how your code is behaving.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. &lt;a href="https://www.appsignal.com/tour/errors"&gt;Use AppSignal for Ruby for deeper debugging insights&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Good Database Migration Practices for Your Ruby on Rails App using Strong Migrations</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 03 Apr 2024 11:37:49 +0000</pubDate>
      <link>https://dev.to/appsignal/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations-l4i</link>
      <guid>https://dev.to/appsignal/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations-l4i</guid>
      <description>&lt;p&gt;One great feature that comes with modern web frameworks is the ability to manage database schema migrations. However, schema migrations are not 100% safe and remain a recurring cause of issues within projects I have encountered over the last 15 years.&lt;/p&gt;

&lt;p&gt;This article will review the issues surrounding poorly managed schema migrations and then look into Strong Migrations, a gem that can help you avoid most problems. Finally, we will discuss a few good practices around database management.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Issues with Schema Migrations in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;Schema migrations are changes to a table or database schema within an RDBMS: adding, renaming, removing, and updating a table (or a column within another table), index, or view. Some schema migrations are inherently risky (such as removing a column or table). Generally, all carry a risk, especially alongside any related code changes.&lt;/p&gt;

&lt;p&gt;Schema migrations are also made worse with larger tables. Some changes, like updating columns and altering indexes, take longer to apply and might imply database locks, causing performance issues.&lt;/p&gt;

&lt;p&gt;We also have to talk about how migrations are brought to production via related code changes. Migrations that add, update, or remove a schema element (table or column) and are used in the code are the most problematic. This is because many developers don't build good practices into those cases early enough. The typical scenario is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A developer adds a Model and related table.&lt;/li&gt;
&lt;li&gt;They merge the code change.&lt;/li&gt;
&lt;li&gt;The code change gets deployed, but the migration hasn't run yet.&lt;/li&gt;
&lt;li&gt;The application starts and crashes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reason for the crash is simple: as an application starts, Ruby on Rails will load the schema but won't find the new table in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can We Fix Problems with Schema Migrations?
&lt;/h2&gt;

&lt;p&gt;Looking at the above example, the solution is to run a database migration before an application starts. The deployment tooling should take care of that. However, in the case of a column or table removal, a migration must run after an application starts. So the easiest thing is to separate the change into two deployments in the correct order and make sure the tooling or team runs the migration when appropriate. Sometimes, we may have to split the change into more deployments.&lt;/p&gt;

&lt;p&gt;Let's take the cases we listed before: adding, removing, or updating a column.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the first case of adding a column, we need to deploy and run a migration before the code change that will start to use it.&lt;/li&gt;
&lt;li&gt;In the second case (removing a column), it's the opposite: we need to remove code that's using the column before removing the column itself.&lt;/li&gt;
&lt;li&gt;Updating a column is trickier. Such a change will enforce a lock. And it's even worse if you change the column type. This is the suitable strategy:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Create a new column.&lt;/li&gt;
&lt;li&gt;Write to both old and new columns.&lt;/li&gt;
&lt;li&gt;Backfill existing data from the old column into the new one.&lt;/li&gt;
&lt;li&gt;Move the reads to the new column.&lt;/li&gt;
&lt;li&gt;Stop writing to the old column.&lt;/li&gt;
&lt;li&gt;Drop the old column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three strategies should be used for any project, especially once a database fills up and users rely on the product.&lt;/p&gt;

&lt;p&gt;Yet, just like linters remove some cognitive load by taking care of this kind of toil, it's best to rely on a tool to automate this.&lt;/p&gt;

&lt;p&gt;Here's where the &lt;a href="https://github.com/ankane/strong_migrations"&gt;Strong Migrations&lt;/a&gt; library comes into play. It adds a layer of protection around migrations to ensure you can avoid issues upon deployment in any environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Help from Strong Migrations
&lt;/h2&gt;

&lt;p&gt;The folks at Instacart have open-sourced many &lt;a href="https://www.instacart.com/opensource"&gt;libraries&lt;/a&gt;. Among them sits Strong Migrations, aimed at "catching unsafe migrations in development". Once installed, it will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect potentially dangerous operations.&lt;/li&gt;
&lt;li&gt;Prevent them from running by default, and tell you why.&lt;/li&gt;
&lt;li&gt;Provide instructions on safer ways to do things.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A migration will be deemed dangerous if it's likely to cause the issues we discussed earlier: locks and potential errors due to timing. Check out the &lt;a href="https://github.com/ankane/strong_migrations/blob/master/README.md"&gt;Strong Migrations README on GitHub&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;We will now cover a few cases here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;This is straightforward: use &lt;code&gt;bundle add strong_migrations&lt;/code&gt;, followed by &lt;code&gt;bundle install&lt;/code&gt;, and &lt;code&gt;rails generate strong_migrations:install&lt;/code&gt;. The last command will create an initializer to configure a few things when the application starts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a Column or a Table
&lt;/h3&gt;

&lt;p&gt;There is no help from Strong Migrations on this one, but remember it's good practice to separate a table addition from the code using it. Please ensure a schema change happens before any code using it runs and expects to find it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Renaming a Column
&lt;/h3&gt;

&lt;p&gt;Here's where we start to see what Strong Migrations can do for us. Taking the example of a migration that renames a column from "address" to "location", we get the following message when we try to run it locally in our development environment:&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="o"&gt;==&lt;/span&gt; 20231116202001 RenameUserAddress: migrating &lt;span class="o"&gt;================================&lt;/span&gt;
rails aborted!
StandardError: An error has occurred&lt;span class="p"&gt;;&lt;/span&gt; this and all later migrations have been canceled:

&lt;span class="o"&gt;===&lt;/span&gt; Dangerous operation detected &lt;span class="c"&gt;#strong_migrations ===&lt;/span&gt;

Renaming a column that&lt;span class="s1"&gt;'s in use will cause errors
in your application. A safer approach is to:

1. Create a new column
2. Write to both columns
3. Backfill data from the old column to new column
4. Move reads from the old column to the new column
5. Stop writing to the old column
6. Drop the old column

article/db/migrate/20231116202001_rename_user_address.rb:3:in `change'&lt;/span&gt;
Tasks: TOP &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; db:migrate
&lt;span class="o"&gt;(&lt;/span&gt;See full trace by running task with &lt;span class="nt"&gt;--trace&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, yes, the Strong Migrations gem does not replace you. Instead, it reminds you that what you are doing is dangerous and that you should not do it. It also tells you how you should do things instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing a Column
&lt;/h3&gt;

&lt;p&gt;Now, let's say that we have gone through the first five steps of the list that Strong Migrations generated for us, and it's time to remove the old column.&lt;/p&gt;

&lt;p&gt;Is it as simple as creating a migration and using the &lt;code&gt;remove_column&lt;/code&gt; method? No, it's not.&lt;/p&gt;

&lt;p&gt;Again, Strong Migrations will print the following text:&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="o"&gt;==&lt;/span&gt; 20231116202636 RemoveUserAddress: migrating &lt;span class="o"&gt;================================&lt;/span&gt;
rails aborted!
StandardError: An error has occurred&lt;span class="p"&gt;;&lt;/span&gt; this and all later migrations have been canceled:

&lt;span class="o"&gt;===&lt;/span&gt; Dangerous operation detected &lt;span class="c"&gt;#strong_migrations ===&lt;/span&gt;

Active Record caches attributes, which causes problems
when removing columns. Be sure to ignore the column:

class User &amp;lt; ApplicationRecord
  self.ignored_columns &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"address"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
end

Deploy the code, &lt;span class="k"&gt;then &lt;/span&gt;wrap this step &lt;span class="k"&gt;in &lt;/span&gt;a safety_assured &lt;span class="o"&gt;{&lt;/span&gt; ... &lt;span class="o"&gt;}&lt;/span&gt; block.

class RemoveUserAddress &amp;lt; ActiveRecord::Migration[7.0]
  def change
    safety_assured &lt;span class="o"&gt;{&lt;/span&gt; remove_column :users, :address, :string &lt;span class="o"&gt;}&lt;/span&gt;
  end
end

article/db/migrate/20231116202636_remove_user_address.rb:3:in &lt;span class="sb"&gt;`&lt;/span&gt;change&lt;span class="s1"&gt;'
Tasks: TOP =&amp;gt; db:migrate
(See full trace by running task with --trace)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we know what to add to the model and how to change our schema migration. It's tailored to your use case, including the table's name and column. Once that is done, the migration will go through without an issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backfilling Data
&lt;/h3&gt;

&lt;p&gt;The remaining piece on the database strategy side is backfilling data in new columns from old ones. While relying on the &lt;a href="https://github.com/ilyakatz/data-migrate"&gt;Data Migrate gem&lt;/a&gt; might be tempting, we can also backfill data in a regular migration. A safe backfilling migration will consider a table's size, the task's complexity, and avoid transactions.&lt;/p&gt;

&lt;p&gt;By default, Ruby on Rails uses a transaction around each migration. If we include backfilling in the migration doing the schema change, this might take some time and potentially cancel our whole schema migration.&lt;/p&gt;

&lt;p&gt;So, instead, let's use a separate migration, avoid using a transaction, batch the process, and throttle the work.&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;BackfillCountryCodeColumn&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;7.1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;disable_ddl_transaction!&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&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;unscoped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_batches&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;relation&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_all&lt;/span&gt; &lt;span class="ss"&gt;country_code: &lt;/span&gt;&lt;span class="s1"&gt;'fr'&lt;/span&gt;
      &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;unscoped&lt;/code&gt; ensures that we don't rely on the "default scope" defined in the model to work with all rows.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;in_batches&lt;/code&gt; forces the block to run in batches of 1000 entries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sleep&lt;/code&gt; gives some reprieve to the database in between batches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An alternative method is to use a script or a rake task to handle this. Whatever solution you pick, remember that backfilling a column or table with data might be pretty intensive for the database. Estimate how much data will be handled and consider the techniques used in the above example, if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Strong Migrations to an Existing Ruby on Rails Project
&lt;/h2&gt;

&lt;p&gt;Adding Strong Migrations to an existing project is a good idea. However, you might run into issues when setting up a new project instance on a workstation or within a new environment. A failsafe will kick in and prevent any prior migrations from running.&lt;/p&gt;

&lt;p&gt;So, what shall we do? You don't have to go through all the migrations and alter them accordingly. Instead, you should follow the &lt;a href="https://guides.rubyonrails.org/active_record_migrations.html#old-migrations"&gt;Ruby on Rails guide&lt;/a&gt; and clean up old migrations.&lt;/p&gt;

&lt;p&gt;I've not seen many projects follow that advice, but it's the "good thing" to do: &lt;code&gt;db/schema.rb&lt;/code&gt; or &lt;code&gt;db/structure.sql&lt;/code&gt; holds a snapshot of the current database schema and can be used to load it. Tasks such as &lt;code&gt;db:setup&lt;/code&gt; and &lt;code&gt;db:prepare&lt;/code&gt; (and ones based on either of them) use that snapshot to load schema in a database. Only pending migrations are run; old ones are barely a line in one table, a mere memory.&lt;/p&gt;

&lt;p&gt;You can remove old migration files if you need them: git (or Mercurial, or Svn) will keep a trace of them. Once a migration has been applied to all existing environments, it can be deleted as the &lt;code&gt;schema.rb&lt;/code&gt; (or &lt;code&gt;structure.sql&lt;/code&gt;) file will serve for new environments.&lt;/p&gt;

&lt;p&gt;Here are the steps you should follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before introducing Strong Migrations, ensure all environments are up to date in terms of migrations.&lt;/li&gt;
&lt;li&gt;Remove old migration files.&lt;/li&gt;
&lt;li&gt;Add and install the Strong Migrations gem.&lt;/li&gt;
&lt;li&gt;Start to create new migrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing so will prevent you from going through years of migrations to set them in line with Strong Migrations. It will also lighten the burden of setting up a new environment.&lt;/p&gt;

&lt;p&gt;You can then devise a periodic removal of migration files, keeping the last 5, 10, or any from the previous month, for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Additional Advice on Migrations
&lt;/h2&gt;

&lt;p&gt;Let's finish up by sharing some general tips on migrations when it comes to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production environments and databases&lt;/li&gt;
&lt;li&gt;Avoiding downtime&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Production Environments and Databases
&lt;/h3&gt;

&lt;p&gt;Considering your production environment and its database is key when creating and running any migration. A developer environment is rarely bursting at the seams when it comes to data. Consequently, most migrations will run smoothly with barely any waiting time. Production environments have a lot more data that's more diverse. This will tend to complicate things when it comes to writing and applying a migration.&lt;/p&gt;

&lt;p&gt;You should follow strategies like backfilling. Batching and throttling are also good practices when handling large volumes of data. It's never too early to start using those to develop good habits. Once a migration is out there and running, we should monitor performance metrics: response time and slow queries. Compare these metrics with previous trends, and look into unexpected changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoiding Downtime
&lt;/h3&gt;

&lt;p&gt;One reason that developers tend to be scared about making deployments around the end of the day or the week is the fear of the unknown.&lt;/p&gt;

&lt;p&gt;By following the good practices pointed out by Strong Migrations, we can reduce the amount of risks and unknowns, thus giving us more confidence in our ability to deploy at any time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backups
&lt;/h3&gt;

&lt;p&gt;If things go wrong, don't panic; you should have a backup ready. Most managed solutions available for PostgreSQL, MySQL, and similar RDBMS offer backups at regular intervals. Some also provide point-in-time recovery, allowing us to rewind to better times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we first explored some potential issues related to schema migrations in Rails, before seeing how we can fix these issues ourselves. We then introduced Strong Migrations. As we have seen, Strong Migrations ensures that risky changes such as removals and changes to a schema element are done correctly to avoid risk.&lt;/p&gt;

&lt;p&gt;Finally, we touched on a few additional tips regarding migrations.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>A Deep Dive Into RSpec Tests in Ruby on Rails</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 21 Feb 2024 13:13:17 +0000</pubDate>
      <link>https://dev.to/appsignal/a-deep-dive-into-rspec-tests-in-ruby-on-rails-1hfe</link>
      <guid>https://dev.to/appsignal/a-deep-dive-into-rspec-tests-in-ruby-on-rails-1hfe</guid>
      <description>&lt;p&gt;In our last post, we looked at the basics of RSpec and explored how well it works with Behavior Driven Development in Ruby.&lt;/p&gt;

&lt;p&gt;Now, we will look at specific types of RSpec tests for different parts of a Ruby on Rails application.&lt;/p&gt;

&lt;p&gt;Let's dive straight in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit, Controller, and Integration Tests in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;A Ruby on Rails application is composed of several layers. As the framework is built around Models, Views, and Controllers, we might think of those three as the only layers of an application. Yet, often, that's barely enough to describe a Ruby on Rails application.&lt;/p&gt;

&lt;p&gt;Mailers, Jobs, and Helpers are secondary layers we don't want to miss. When it comes to testing, it's important to remember that these components are largely impacted by how our code has been designed. Using &lt;a href="https://en.wikipedia.org/wiki/SOLID"&gt;SOLID principles&lt;/a&gt; — particularly Single Responsibility — helps keep our code straightforward and more testable.&lt;/p&gt;

&lt;p&gt;So, we usually aim to keep things simple in views, controllers, models, jobs, and mailers: we want to assemble and use the bricks of code we have designed and tested separately already. Once we enter the realm of views and controllers, testing becomes much more complex, and slower too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Categories of Ruby Tests
&lt;/h3&gt;

&lt;p&gt;Usually, we split tests into these categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests&lt;/strong&gt; (models and other plain old Ruby objects — POROs): Ensure class methods are working as expected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controller tests:&lt;/strong&gt; Test the expected outcome of controller actions (rendering the right template, redirections, and flash messages).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View tests:&lt;/strong&gt; (Used rarely) to test specific elements or content in views.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helper tests:&lt;/strong&gt; For when you use a complex helper method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature or system tests:&lt;/strong&gt; Simulate user interactions in the browser — this is where Capybara will help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request tests:&lt;/strong&gt; Test an application's response to various HTTP verbs without browser overhead. This is faster than feature tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mailer and jobs tests:&lt;/strong&gt; Specific tests are needed for this secondary layer to ensure emails are sent properly with the right content and jobs trigger the appropriate work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing tests:&lt;/strong&gt; Complex routing is a code smell. Testing routes will help you avoid trouble when you have too many routes or routes that are too complex.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Kinds of Tests Should We Write?
&lt;/h3&gt;

&lt;p&gt;Do we need to write all of these tests? It depends on your culture and team, but these might be good starting points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests:&lt;/strong&gt; Write focused and (as much as possible) database-dependent tests for your models' public methods and other POROs you craft into your application; they should run fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controller tests:&lt;/strong&gt; Write tests that help ensure controller actions are routable and respond as expected for each context they could be in. These should not aim to test the whole response or complex scenarios made of multiple requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request tests:&lt;/strong&gt; Test request scenarios from a machine client (think API controllers), typically: "for a given HTTP request context (HTTP verb, path, and parameters), what HTTP response should we get?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System tests:&lt;/strong&gt; Test scenarios of human user requests. Typically, this is where we want to test complex scenarios with multiple clicks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mailer and job tests:&lt;/strong&gt; These should only test that a mailer and job are doing the specific "mailer" and "job" work properly. In particular, for jobs, most of the "work" should be done through classes and methods tested separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Controller tests are often replaced by a good layer of request and system tests, as they ultimately serve the same purpose. Too many tests will cause fatigue in your team or slow down your whole test suite for little benefit. It's up to you. The point is to test your controller layer: figure out which way is best in your case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's see how unit, controller, and integration (both request and system) tests look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using RSpec and Ruby on Rails for Testing
&lt;/h2&gt;

&lt;p&gt;As pointed out in &lt;a href="https://blog.appsignal.com/2024/01/24/behaviour-driven-development-in-ruby-with-rspec.html"&gt;our previous post&lt;/a&gt;, we can use the &lt;code&gt;rspec-rails&lt;/code&gt; gem to integrate RSpec into a Ruby on Rails application's code base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Tests
&lt;/h2&gt;

&lt;p&gt;Unit tests are at the base of the &lt;a href="https://www.headspin.io/blog/the-testing-pyramid-simplified-for-one-and-all"&gt;testing pyramid&lt;/a&gt;, so there will be numerous unit tests in a codebase. They need to be fast.&lt;/p&gt;

&lt;p&gt;Remember: tests (with RSpec) are focused on testing code behavior, not implementation. That should help to write fast tests. In the case of Ruby on Rails projects, models are a big source of unit tests. As models are related to database access (either read or write access), those tests can have a performance impact. So be careful about reading and writing to the database for those tests. In some cases, you won't be able to avoid calling &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, or &lt;code&gt;find&lt;/code&gt;, but you can in most. You should be able to rely on &lt;code&gt;new&lt;/code&gt; instead of &lt;code&gt;create&lt;/code&gt;, for example.&lt;/p&gt;

&lt;p&gt;Here is what an RSpec unit test for a Ruby on Rails model looks like:&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="c1"&gt;# spec/models/user.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt; &lt;span class="c1"&gt;# a file generated with rspec-rails containing configuration for rspec in a rails context&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :model&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#valid_email?'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email is valid'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&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="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Internet&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="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email is not valid'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&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="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'bob@example'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns false'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&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;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;While those two examples are written a bit differently, they basically do the same thing. One has no description and reads pretty nicely still; the other one has a description but carries a bit of a duplication, don't you think?&lt;/p&gt;



&lt;p&gt;Note that we use &lt;code&gt;User.new&lt;/code&gt; to instantiate a user. This call doesn't require a read or write to the database. Since our &lt;code&gt;valid_email?&lt;/code&gt; method only works with the instance's attributes, it won't slow the test down either.&lt;/p&gt;

&lt;p&gt;Those two are good unit tests: simple, focused, and fast.&lt;/p&gt;

&lt;p&gt;Unit tests are not just for Ruby on Rails models. They are also good for any classes you build around models and the rest of an application's architecture. We have specified the test &lt;code&gt;type&lt;/code&gt; in the first line (&lt;code&gt;RSpec.describe User, type: :model&lt;/code&gt;), but we can totally avoid that if we write tests for a plain Ruby class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controller and Request Specs
&lt;/h2&gt;

&lt;p&gt;Up until recently, controller tests were the main way to test controllers. Nowadays, we tend to rely on request specs instead. Similarly to controller tests, they are designed to test an application from a &lt;em&gt;machine client&lt;/em&gt;. They allow us to test one, or multiple, controller action/s. They are tests, so we need to define a context, then our expectation. As they are functional tests, we have to define a context composed of a given HTTP verb (get, post, put, ...), a path (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;, ...), parameters, and (potentially) a body. The expectation is then focused on the response (HTTP status code, response/s header/s, and body).&lt;/p&gt;

&lt;p&gt;With request specs, it's not a matter of UI or Javascript.&lt;/p&gt;

&lt;p&gt;A simple request spec will look like this.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails_helper"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"User management"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :request&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"does not render the incorrect template"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/users/new"&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_not&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:show&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;It's simple, but you can see the context in the part doing the request: &lt;code&gt;get "/users/new"&lt;/code&gt;. In turn, the expectation itself is centered on the response. We see a new matcher here, allowing us to test if a specific template is rendered. Another one (&lt;code&gt;redirect_to&lt;/code&gt;) allows us to test that we are properly redirected.&lt;/p&gt;

&lt;p&gt;Request specs can also be used to test more complex scenarios.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails_helper"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"User management"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :request&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a user and redirects to the user's page"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/users/new"&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &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="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"john@example.org"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&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="n"&gt;follow_redirect!&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User John Doe (john@example.org) created."&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;However, request specs are better adapted to test behavior from a machine client's perspective. So, request specs are best used to test an API backend's controller parts.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails_helper"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"User management"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :request&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"creates a user and returns the user's details"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"CONTENT_TYPE"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"application/json"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s2"&gt;"/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params: &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="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"john@example.org"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"application/json; charset=utf-8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_http_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# TODO&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;While we haven't included any in this example, we can also express different contexts to test behaviors. The RSpec vocabulary and grammar still applies fully in that kind of spec. We merely use a bit more to handle the parts specific to making an HTTP request and its response.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Specs
&lt;/h2&gt;

&lt;p&gt;System specs are more complete integration tests than request ones. They are used to test an application in a real or headless browser, and require a driver for the browser (the default is Selenium; this can be changed).&lt;/p&gt;

&lt;p&gt;System specs are similar to request specs in concept, but with improved grammar to focus on a user's actions in the browser rather than the requests being made. Here is an example.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails_helper"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"Account management"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"enables me to create an account"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="s2"&gt;"/account/new"&lt;/span&gt;

    &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="s2"&gt;"Acm'e Ltd."&lt;/span&gt;
    &lt;span class="n"&gt;fill_in&lt;/span&gt; &lt;span class="s2"&gt;"Address"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: &lt;/span&gt;&lt;span class="s2"&gt;"6 Av. Elysian fields"&lt;/span&gt;
    &lt;span class="n"&gt;click_button&lt;/span&gt; &lt;span class="s2"&gt;"Create Account"&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Account successfully created."&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;Notice that here, the context (verb, path, parameters) is defined underneath but still there: we &lt;em&gt;visit&lt;/em&gt; a page, fill out a form (which sets parameters), and then click a button (trigger a POST request). Finally, the expectation is defined as the content of the page rendered. Indirectly, it's still about testing the response's content, although the browser interprets that.&lt;/p&gt;

&lt;p&gt;Because system specs use and drive the complete stack, they are a lot slower than other specs. As such, they should be limited in number and potentially run at particular times within the CI pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complimentary Test Types in Ruby
&lt;/h2&gt;

&lt;p&gt;As mentioned, Ruby on Rails applications are composed of a few more components with specific roles: mailers, jobs, serializers, and decorators. Those require tests as well — ones closer to unit than integration tests. It can be useful to test out specifics regarding mailers and jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mailer Specs
&lt;/h3&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/expectedbehavior/active_mailer"&gt;ActiveMailer&lt;/a&gt;, Ruby on Rails has a simple way to abstract interactions with a third party that sends emails. Thus, instead of testing email sends, you can focus on testing what matters: behavior. In the case of emails, what matters is the subject, from and to addresses, and the body of the response.&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="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails_helper"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :mailer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"notify"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:mail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Announcements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account_renewal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"prepares the email headers properly"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Renewal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"company@example.org"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"bot@example.com"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"renders the body"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"A customer has renewed their account."&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Job Specs
&lt;/h3&gt;

&lt;p&gt;Similarly, job specs allow you to test specific behavior around jobs: if they have been enqueued, performed, etc. This includes testing arguments using &lt;a href="https://github.com/collectiveidea/delayed_job"&gt;Delayed Job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can be tempted to test ActiveJob's behavior. We do know that calling &lt;code&gt;perform&lt;/code&gt; and &lt;code&gt;perform_later&lt;/code&gt; bill queues a job; what we want to know is if, and when, that happens.&lt;/p&gt;

&lt;p&gt;So, you should test if a job is queued in contexts requiring it. Then, test that a job properly calls upon code that has its own unit tests.&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;describe&lt;/span&gt; &lt;span class="no"&gt;Building&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:building&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Building&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Bank'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#setup'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'triggers the preparation job'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;building&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_enqueued_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'setup'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on_queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'low'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="c1"&gt;# alternative&lt;/span&gt;
   &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'triggers the preparation job'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="no"&gt;ActiveJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queue_adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;
     &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Building&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PreparationJob&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_been_enqueued&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'setup'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exactly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:once&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This expresses and tests a job's enqueued behavior with certain parameters when a specific method is called.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Concerns in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;Ruby on Rails has a way to factorize code used in multiple models or controllers through a &lt;a href="https://blog.appsignal.com/2020/09/16/rails-concers-to-concern-or-not-to-concern.html"&gt;disguised Ruby module called a concern&lt;/a&gt;. Concerns are a great way to avoid duplication of code. Make sure you test the content of concerns. Proper unit tests should also cover models. In the case of controllers, those are tested through request and system tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Thoughts on Testing with RSpec for Ruby
&lt;/h2&gt;

&lt;p&gt;It's worth reiterating that your first layer of testing should be unit tests. Tests for models and other POROs should represent the vast majority of your tests in a code base.&lt;/p&gt;

&lt;p&gt;Tests should run fast and avoid database and external services access as much as possible. To test the controller layer, request and system specs are best, respectively, for machine or human-driven activity on controller actions. They are slower than unit tests due to their inherent complexity, so there should be less of them than unit tests.&lt;/p&gt;

&lt;p&gt;Finally, components like mailers and jobs should be used wisely and in their specific context, not to test the behavior of the lower library (like ActiveMailer and ActiveJob).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;As we saw in part one of this series, we can factorize a lot of code and avoid duplication by making use of RSpec's basics: &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; hooks, &lt;code&gt;let&lt;/code&gt;, and a proper structure built with &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this part, we took a deep dive into testing with RSpec for Ruby, focusing on unit, controller, and integration tests.&lt;/p&gt;

&lt;p&gt;We have explored how it's usually easy to keep unit tests small, while minimizing request and system tests is more difficult. So, after you have written (and made green) system and request specs, don't hesitate to spend some time slimming them down. This will keep them readable and easier to maintain.&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>rspec</category>
    </item>
    <item>
      <title>Behaviour Driven Development in Ruby with RSpec</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 07 Feb 2024 11:32:58 +0000</pubDate>
      <link>https://dev.to/appsignal/behaviour-driven-development-in-ruby-with-rspec-5dmf</link>
      <guid>https://dev.to/appsignal/behaviour-driven-development-in-ruby-with-rspec-5dmf</guid>
      <description>&lt;p&gt;RSpec is a library for writing and running tests in Ruby applications. As its landing page states, RSpec is: "Behaviour Driven Development for Ruby. Making TDD productive and fun". We will return to that last part later.&lt;/p&gt;

&lt;p&gt;This post, the first of a two-part series, will focus on introducing RSpec and exploring how RSpec especially helps with Behaviour Driven Development in Ruby.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The History of RSpec
&lt;/h2&gt;

&lt;p&gt;RSpec started in 2005 and, through several iterations, reached version 3.0 in 2014. Version 3.12 has been available for almost a year. Its original author was Steven Baker, but the mantle has passed to several maintainers through the years: David Chelimsky, Myron Marston, Jon Rowe, and Penelope Phippen. Many contributors have also been part of the project.&lt;/p&gt;

&lt;p&gt;When you read RSpec's good practices, you see this phrase early on: "focus on testing the behavior, not the implementation". That's not a strategy specific to RSpec; it's good advice for anyone writing tests.&lt;br&gt;
In more practical terms, you should focus your tests on how the code you are testing behaves, not how it works.&lt;/p&gt;

&lt;p&gt;For example, to test that a user's email address is valid, you should test that the &lt;code&gt;validate_email&lt;/code&gt; method returns &lt;code&gt;false&lt;/code&gt; when the email address is invalid. You are not testing a specific implementation but rather how that code reacts (i.e., behaves) when handling different strings that should be email addresses.&lt;/p&gt;

&lt;p&gt;Behavior interests us: how the code acts and defines how our application will work. Furthermore, this approach simply lets us know whether things work (or not), and we can then be more relaxed to either fix or refactor the implementation. Our tests will tell us if the code's behavior has changed; we will know if our changes have been successful or not in the most direct way possible.&lt;/p&gt;

&lt;p&gt;The inner workings of the code don't interest us so much. Of course, we don't want a bad implementation, but measuring a good implementation is much harder than knowing if the code does what it's expected to do or not.&lt;/p&gt;

&lt;p&gt;Let's look at how to install RSpec next.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing RSpec for Ruby
&lt;/h3&gt;

&lt;p&gt;RSpec comes as a gem, &lt;code&gt;rspec-core&lt;/code&gt;, so you can add it to a test group in your Gemfile. It's probably best you also add &lt;code&gt;rspec&lt;/code&gt;. It's a meta gem that includes &lt;code&gt;rspec-core&lt;/code&gt;, &lt;code&gt;rspec-expectations&lt;/code&gt;, and &lt;code&gt;rspec-mocks&lt;/code&gt;. You can also add the &lt;code&gt;rspec-rails&lt;/code&gt; one in a Ruby on Rails project.&lt;/p&gt;

&lt;p&gt;Tests usually live in the &lt;code&gt;spec/&lt;/code&gt; folder at the root of your project, and you can launch them by providing a path to a file or a directory: &lt;code&gt;rspec spec/models/*rb&lt;/code&gt;, for example.&lt;/p&gt;

&lt;p&gt;Now let's turn to how the RSpec DSL is set up to help with behavior testing.&lt;/p&gt;
&lt;h2&gt;
  
  
  RSpec DSL: How it Helps with Testing Behavior
&lt;/h2&gt;

&lt;p&gt;RSpec's whole Domain Specific Language (DSL) is completely worked around behavior testing, giving you a direct way to describe the behavior you expect from your code within different contexts. A few parts could be smoother, but overall, tests in RSpec read directly as English, much like a good piece of Ruby code.&lt;/p&gt;

&lt;p&gt;Tests in RSpec are not written as classes, with methods taking center stage as tests. Instead, tests are written as Ruby blocks (ever used &lt;code&gt;do .. end&lt;/code&gt;?), which, thanks to the method name we pass to the block as an argument, makes things very easy to read.&lt;/p&gt;

&lt;p&gt;The primary method used in RSpec tests is &lt;code&gt;describe&lt;/code&gt;. &lt;code&gt;describe&lt;/code&gt; will contain one or more tests and can even contain more &lt;code&gt;describe&lt;/code&gt; calls. The second method is &lt;code&gt;it&lt;/code&gt;. The &lt;code&gt;it&lt;/code&gt; blocks are called examples and contain the actual assumptions; they are where the testing happens. Finally, RSpec relies on "expectations" within the &lt;code&gt;it&lt;/code&gt; blocks. Using the &lt;code&gt;expect&lt;/code&gt; method, we define how the subject of our test is expected to behave.&lt;/p&gt;

&lt;p&gt;Now we can start writing some simple tests.&lt;/p&gt;
&lt;h2&gt;
  
  
  Simplest Tests in RSpec for Ruby: &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;it&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's imagine a &lt;code&gt;User&lt;/code&gt; class. We want a &lt;code&gt;name&lt;/code&gt; method that will output first and last names together if both are present (or just one, if one is missing). Those are the different behaviors we want to test.&lt;/p&gt;

&lt;p&gt;Here is how the simplest of those contexts look expressed as an RSpec test.&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="c1"&gt;# first call to describe, as topmost one, its description or title is used to tell what we are testing, here the User class.&lt;/span&gt;
&lt;span class="c1"&gt;# this title can be a string or a class name&lt;/span&gt;
&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="c1"&gt;# we are adding a second describe to regroup the tests focused on the `name` method&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="c1"&gt;# our first example ! Note the description focusing on the behavior&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the complete name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

      &lt;span class="c1"&gt;# we define a user variable by instantiating a user&lt;/span&gt;
      &lt;span class="n"&gt;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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# and here comes the expectation&lt;/span&gt;
      &lt;span class="n"&gt;expect&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;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the general structure: we start by describing the focus of our test in the &lt;code&gt;User&lt;/code&gt; class, the method we are testing (&lt;code&gt;name&lt;/code&gt;), and then present an expected behavior.&lt;/p&gt;

&lt;p&gt;Note how the expectation is written: we &lt;strong&gt;expect&lt;/strong&gt; the value returned by &lt;code&gt;user.name&lt;/code&gt; &lt;strong&gt;to equal&lt;/strong&gt; &lt;code&gt;'John Doe'&lt;/code&gt;. The &lt;code&gt;eq&lt;/code&gt; method is a matcher. It allows us to match the tested value (on the left) and the expected one (on the right). The &lt;code&gt;expect&lt;/code&gt; part is always followed with &lt;code&gt;to&lt;/code&gt; or &lt;code&gt;not_to&lt;/code&gt; to dictate how the matcher that follows will be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Multiple Contexts
&lt;/h2&gt;

&lt;p&gt;While this first test shows us how it's done, it needs to catch up to what we want. It only handles one case if both first and last names are present. Let's see how we can test another case if the first name is absent.&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;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the complete name when both first and last name are present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;expect&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;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns only the last name when the first name is missing'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;expect&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;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Doe'&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks more realistic, but we still need to test another case. The description is also relatively verbose and repetitive. We could add another layer of description between the &lt;code&gt;describe '.name'&lt;/code&gt; call and the example for each one. Thankfully, though, RSpec gives us a more obvious synonym for &lt;code&gt;describe&lt;/code&gt; to express what we need to express for different contexts: &lt;code&gt;context&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="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when both first and last name are present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the complete name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expect&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;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&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;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when the first name is missing'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns only the last name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expect&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;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Doe'&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;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;Thanks to this, the whole file reads even more quickly and gives us, without much thinking, an understanding of exactly which behavior we are testing within different contexts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Subject of Tests
&lt;/h2&gt;

&lt;p&gt;We can use the &lt;code&gt;subject&lt;/code&gt; method to make the subject of our tests obvious.&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;describe&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&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;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when both first and last name are present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="s1"&gt;'Doe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the complete name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&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;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when only the first name is present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the complete name'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John'&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;span class="c1"&gt;# ... other contexts&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;This is especially handy to avoid repetition and add clarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Complex Setup (Before, After Hooks) in Ruby on Rails
&lt;/h2&gt;

&lt;p&gt;In many cases, we need a bit more to prepare a context. Let's take, as an example, a class method on a Ruby on Rails model named &lt;code&gt;latest_three&lt;/code&gt;. It's expected to return the last three users created in the database. If we have less than that, we should get whatever users we have. By omitting the topmost &lt;code&gt;describe&lt;/code&gt;, here is how a test might look.&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="c1"&gt;# note that we are using the '::method_name' here to refer to a class method, '#method_name' is reserved to refer to an instance method's name&lt;/span&gt;
&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'::latest_three'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when more than three users are present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns three users'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&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;latest_three&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when no users are present'&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns an empty collection'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&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;latest_three&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you are unfamiliar with &lt;code&gt;Faker&lt;/code&gt;, it's a Ruby library used to generate fake data such as names and dates through handy methods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These two tests look ok, but the creation of the data doesn't belong to the example. It's important for the specific context, though: we need that data created &lt;strong&gt;before&lt;/strong&gt; the example is run. To do so, we can use a &lt;code&gt;before&lt;/code&gt; block. Those blocks are run before the tests that follow them (in each block's context), thus giving us a perfect opportunity to set up our data.&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;describe&lt;/span&gt; &lt;span class="s1"&gt;'::latest_three'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when more than three users are present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns three users'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&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;latest_three&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when no users are present'&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns an empty collection'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&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;latest_three&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again, I hope this shows how well thought-through the RSpec DSL is. Doesn't it read nicely and give us a good understanding of each context and the behavior we expect?&lt;/p&gt;

&lt;p&gt;If we were tempted to destroy data or execute some other form of cleanup &lt;strong&gt;after&lt;/strong&gt; a context, we could do so through an &lt;code&gt;after&lt;/code&gt; block. This is especially useful if you are writing tests using a database without the comfort of built-in automatic database cleanup between test runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding Repetition with &lt;code&gt;let&lt;/code&gt; in RSpec for Ruby
&lt;/h2&gt;

&lt;p&gt;We still lack a few more concepts to be able to write real-world tests. Let's take the case of email validation again.&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;describe&lt;/span&gt; &lt;span class="s1"&gt;'#valid_email?'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email does not contain an @'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'bob'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email does not have a tld'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'bob@appsignal'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email is valid'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'bob@example.org'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&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;There are several repetitions, to the point of blurring the actual tests. We notice that the only thing that changes, in the setup of each context, is the actual value of the email address. Couldn't we use a variable to do this?&lt;/p&gt;

&lt;p&gt;We can use &lt;code&gt;let&lt;/code&gt; blocks. They allow us to define a memoized helper method. The value is cached across multiple calls in the relative context. &lt;code&gt;let&lt;/code&gt;'s syntax is similar to the one we saw for the &lt;code&gt;subject&lt;/code&gt; block: first, we pass a name for the helper, then a block to be evaluated. That block is lazy-evaluated. If we don't call it, it will never be evaluated.&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;describe&lt;/span&gt; &lt;span class="s1"&gt;'#valid_email?'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;subject&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="p"&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email does not contain an @'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&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="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'bob'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email does not have a tld'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&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="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'bob@appsignal'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when email is valid'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&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="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'bob@example.org'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expect&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;valid_email?&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&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;Note that the &lt;code&gt;subject&lt;/code&gt; is moved up in the structure too: it will be evaluated within each context and thus use each context's &lt;code&gt;email&lt;/code&gt; value. Here we can see the purpose of &lt;code&gt;subject&lt;/code&gt; within a &lt;code&gt;describe&lt;/code&gt; with multiple contexts: we define the subject of the test early to make it obvious. We can then focus on expressing each different context we want to check the subject behavior in.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on &lt;code&gt;let&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;let&lt;/code&gt; is lazily defined. In the above example, &lt;code&gt;email&lt;/code&gt; won't be instantiated and set until it's called. Once it is invoked, though, it's set. In effect, it's just like a memoized helper method.&lt;/p&gt;

&lt;p&gt;Yet, in some cases, you might want to set the value associated with a &lt;code&gt;let&lt;/code&gt; before the examples run. To do so, you can use &lt;code&gt;let!&lt;/code&gt;. With &lt;code&gt;let!&lt;/code&gt;, the defined memoized helper method is called within an implicit &lt;code&gt;before&lt;/code&gt; hook for each example. In other words, the value associated with the &lt;code&gt;let!&lt;/code&gt; is eagerly defined before the example is run.&lt;/p&gt;

&lt;p&gt;Let's create a user in our context before we run our example:&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;describe&lt;/span&gt; &lt;span class="s2"&gt;"#count_users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Acc Ltd'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let!&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="p"&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Jane'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;account: &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"counts the users in the account"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count_users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&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="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;This prevents us from additional setup or even a call to &lt;code&gt;user&lt;/code&gt; within the before hook to get the value memoized.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;let&lt;/code&gt; Vs Instance Variables in RSpec for Ruby
&lt;/h3&gt;

&lt;p&gt;Some developers might be tempted to rely on instance variables through a &lt;code&gt;describe&lt;/code&gt; or &lt;code&gt;context&lt;/code&gt; and their &lt;code&gt;before&lt;/code&gt; hooks:&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;describe&lt;/span&gt; &lt;span class="s2"&gt;"#count_users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="vi"&gt;@account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Acc Ltd'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Jane'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;account: &lt;/span&gt;&lt;span class="vi"&gt;@account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"counts the users in the account"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count_users&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&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="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;This is not very practical. It adds dependencies and state sharing between contexts, weakens isolation, and is more difficult to debug.&lt;/p&gt;

&lt;p&gt;The additional issue is that, if you were to make a call to an instance variable that has not been initialized, you'd get a &lt;code&gt;nil&lt;/code&gt; value in return. That's in contrast to the exception you'd get if you were to call a local variable that doesn't exist (raising a &lt;code&gt;NameError&lt;/code&gt; exception).&lt;/p&gt;

&lt;p&gt;So, when writing tests with RSpec, &lt;code&gt;let&lt;/code&gt; is preferred, and &lt;code&gt;let!&lt;/code&gt; is to be used when you need an eager evaluation. Other methods to handle variables are not recommended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matchers in RSpec for Ruby
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;context&lt;/code&gt;, and &lt;code&gt;it&lt;/code&gt; are very important to the structure of RSpec tests, the key part to making actual tests is matchers.&lt;/p&gt;

&lt;p&gt;We have only seen a few, mainly &lt;code&gt;be()&lt;/code&gt; and &lt;code&gt;eq()&lt;/code&gt;. Those two are the simplest ones and are very handy. Here is a list of the others you should know about as a start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eq&lt;/code&gt;: test the equality of two objects (actually, their equivalence, the same as &lt;code&gt;==&lt;/code&gt;); &lt;code&gt;expect(1).to eq(1.0) # is true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eql&lt;/code&gt;: test the equality of two objects (if they are identical, not just equivalent); &lt;code&gt;expect(1).to eql(1.0) # is false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;be&lt;/code&gt;: test for object identity; &lt;code&gt;be(true)&lt;/code&gt;, &lt;code&gt;be(false)&lt;/code&gt; ...&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;be_nil&lt;/code&gt;: test if an object is &lt;code&gt;nil&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;be &amp;lt;= X&lt;/code&gt;: test if a number is less or equal to a value (X); also works with &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;==&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;be_instance_of&lt;/code&gt;: test if an object is an instance of a specific class; &lt;code&gt;expect(user.name).to be_instance_of(String)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include&lt;/code&gt;: test if an object is part of a collection; &lt;code&gt;expect(['a', 'b']).to include('a')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;be_empty&lt;/code&gt;: test if a collection is empty; &lt;code&gt;expect([]).to be_empty&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start_with&lt;/code&gt;, &lt;code&gt;end_with&lt;/code&gt;: test if a string or array starts (or ends) with the expected elements; &lt;code&gt;expect('Brian is in the kitchen').to start_with('Brian')&lt;/code&gt;, &lt;code&gt;expect([1, 2]).not_to start_with('0')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;match&lt;/code&gt;: test if a string matches a regular expression; &lt;code&gt;expect(user.name).to match(/[a-zA-Z0-9]*/)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;respond_to&lt;/code&gt;: test if an object responds to a particular method; &lt;code&gt;expect(user).to respond_to(:name)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;have_attributes&lt;/code&gt;: test if an object has a specific attribute; &lt;code&gt;expect(user).to have_attributes(age: 42)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;have_key&lt;/code&gt;: test if a key is present within a hash; &lt;code&gt;expect({ a: 1, b: 2 }).to have_key(:a)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;raise_error&lt;/code&gt;: test if a block of code raises an error; &lt;code&gt;expect { user.name }.to raise_error(ArgumentError)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;change&lt;/code&gt;: test that a block of code changes the value of an object or one of its attributes; &lt;code&gt;expect { User.create }.to change(User.count).by(1)&lt;/code&gt;, &lt;code&gt;expect { user.activate! }.to change(user, :is_active).from(false).to(true)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;to_all&lt;/code&gt;: test that all items in a collection match a given matcher; &lt;code&gt;expect([nil, nil, nil]).to all(be_nil)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;match_array&lt;/code&gt;: test that one array has the same items as the expected one (the order isn't of importance); &lt;code&gt;expect([1, 3, 2]).to match_array([2, 1, 3])&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can already write most of the tests you'll ever need with those matchers. To read more about matchers, you can check out &lt;a href="https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/"&gt;RSpec's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Thoughts
&lt;/h2&gt;

&lt;p&gt;As you have seen, we have yet to write a line of actual code; we just wrote tests. That might be the most crucial point of this article: RSpec's DSL and structure allow you to write your test first from the behavior point of view. When you start to work on a new class, you can first express the behavior as an RSpec example within a given context. Then, simply rely on the guard rails to make your implementation a reality.&lt;/p&gt;

&lt;p&gt;That's actually how TDD works. We are not writing tests just for the sake of tests. Instead, we write tests to express the behavior we want to see from the code. In effect, those tests are merely a transcription (through RSpec DSL) of the behavior expected for a feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;To summarize what we have covered in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RSpec is a library that gives us a powerful DSL to express and test the behavior of code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;describe&lt;/code&gt; is the main element to structure tests in each file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;context&lt;/code&gt; is equivalent to &lt;code&gt;describe&lt;/code&gt;, but is used to separate different contexts for testing code behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;it&lt;/code&gt; allows us to define examples: the blocks within which tests happen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;expectations&lt;/strong&gt; define the actual tests with &lt;strong&gt;matchers&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;let!&lt;/code&gt; allow us to define memoized helpers through custom-named blocks to avoid repetitions; &lt;code&gt;let!&lt;/code&gt; is eagerly loaded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt; allows us to clearly define what is being tested and can be named&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post, we will look at specific types of tests for different parts of a Ruby on Rails application.&lt;/p&gt;

&lt;p&gt;Until then, happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rspec</category>
    </item>
    <item>
      <title>Keep your Ruby Code Maintainable with Draper</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 24 Jan 2024 12:05:47 +0000</pubDate>
      <link>https://dev.to/appsignal/keep-your-ruby-code-maintainable-with-draper-2ggn</link>
      <guid>https://dev.to/appsignal/keep-your-ruby-code-maintainable-with-draper-2ggn</guid>
      <description>&lt;p&gt;Design patterns can help to simplify your codebase so you don't need to reinvent the wheel.&lt;/p&gt;

&lt;p&gt;In this post, we'll go into how to use Draper. But first, we will start with an overview of the decorator pattern and how to use it with Ruby's standard library.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decorator Pattern for Ruby
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://refactoring.guru/design-patterns/decorator"&gt;Refactoring.Guru&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Decorator can also be called a wrapper. The idea is that you initialize an instance of a decorator class by passing the object you want to decorate to that decorator class initializer.&lt;/p&gt;

&lt;p&gt;The new returned object can expose the same methods the original object did, plus a few more, or alter the original ones. In most cases (and in Draper's case), we use this pattern to delegate calls to the embedded object.&lt;/p&gt;

&lt;p&gt;Let's dig into the Ruby standard library to look at the &lt;code&gt;SimpleDelegator&lt;/code&gt; class, itself a child of the &lt;code&gt;Delegator&lt;/code&gt; class. It's a great way to see the decorator's side of the pattern.&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;Company&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founded_on&lt;/span&gt;
    &lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1981&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&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;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompanyDecorator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;SimpleDelegator&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founding_year&lt;/span&gt;
    &lt;span class="n"&gt;founded_on&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;year&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;decorated_company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CompanyDecorator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;decorated_company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;founding_year&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; 1981&lt;/span&gt;
&lt;span class="n"&gt;decorated_company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__getobj__&lt;/span&gt;  &lt;span class="c1"&gt;#=&amp;gt; #&amp;lt;Company: ...&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to note: the &lt;code&gt;decorated_company&lt;/code&gt; object has direct access to the public methods of the object it decorates.&lt;/p&gt;

&lt;p&gt;And here lies the principle behind the decorator: the &lt;code&gt;founded_on&lt;/code&gt; method, which can be an attribute pulled from the database, provides a key value for the company.&lt;/p&gt;

&lt;p&gt;To display the year the company was founded, we might be tempted to use &lt;code&gt;company.founded_on.year&lt;/code&gt;. This can work, yet we will slowly riddle the codebase with such calls. That is not practical; if we were to change the math or presentation of that value, we would need to change every single instance of that piece of code.&lt;/p&gt;

&lt;p&gt;This kind of presentation code doesn't belong in the &lt;code&gt;Company&lt;/code&gt; class; in Ruby on Rails, it doesn't belong in models either. If you have seen a few Rails projects, you probably have seen views littered with such presentation code. Decorators, delegators, or presenters are a better place for such presentation logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Decorators in Ruby on Rails Views, Models, and Controllers
&lt;/h2&gt;

&lt;p&gt;Most, if not all, web applications need to display attributes of objects in different ways: names, dates, times, prices, numerical values, and coordinates. These uses are all over the place. Sometimes, we copy this kind of code in every view, use helper methods, or fill the model with methods that are just presenting data. Delegators are handy in moving all this logic into focused places.&lt;/p&gt;

&lt;p&gt;Helper methods are usually spread around in different places. They might be grouped, but only carry a little meaning beyond the simple method's name. While models sound more appropriate, adding presentation methods tends to fatten them for little purpose. Any business logic in models or ActiveRecord-related code (associations, scopes, etc.) will be muddied by the presence of those methods.&lt;/p&gt;

&lt;p&gt;In short, in Ruby on Rails, decorators allow us to keep responsibilities limited in the model, the view, the decorator, and the controller. Here is how it could work (note that we have simplified the code a bit to stay concise):&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="c1"&gt;# app/models/company.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Company&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founded_on&lt;/span&gt;
    &lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1981&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/decorators/company_decorator.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompanyDecorator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;SimpleDelegator&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founding_year&lt;/span&gt;
    &lt;span class="n"&gt;founded_on&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;year&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/companies_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CompaniesController&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;show&lt;/span&gt;
    &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&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="n"&gt;decorated_company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CompanyDecorator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;company: &lt;/span&gt;&lt;span class="n"&gt;decorated_company&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/companies/show.html.erb

&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;company&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;h2&amp;gt;&lt;/span&gt;Founded in &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;founding_year&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Draper in Your Ruby App
&lt;/h2&gt;

&lt;p&gt;Draper is a bit more advanced than &lt;code&gt;SimpleDelegator&lt;/code&gt;. To install Draper, simply add the gem to the application's Gemfile:&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;gem&lt;/span&gt; &lt;span class="s1"&gt;'draper'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Decorators are usually written in the &lt;code&gt;app/decorators&lt;/code&gt; folder; you can also use namespaces within that folder. Namespaces and the decorator's name should mirror the related models for the best results.&lt;/p&gt;

&lt;p&gt;Draper includes a couple of Ruby on Rails generators too. So when generating resources (&lt;code&gt;rails generate resource company&lt;/code&gt;), the related decorator will be created automatically.&lt;/p&gt;

&lt;p&gt;If you prefer to do it by hand, you can also call the &lt;code&gt;decorator&lt;/code&gt; generator with the model's name: &lt;code&gt;rails generate decorator Company&lt;/code&gt;. By following this, you can rely on a bit of magic within your code to call &lt;code&gt;decorate&lt;/code&gt; on the &lt;code&gt;Company&lt;/code&gt; class instance and get the &lt;code&gt;CompanyDecorator&lt;/code&gt; instance for that company.&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;decorated_company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&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="nf"&gt;decorate&lt;/span&gt;
&lt;span class="c1"&gt;# equivalent to&lt;/span&gt;
&lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&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="n"&gt;decorated_company&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CompanyDecorator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Draper also works with collections through the decorator's &lt;code&gt;decorate_collection&lt;/code&gt; class method or the &lt;code&gt;decorate&lt;/code&gt; method on a collection of objects.&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;decorated_companies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CompanyDecorator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filtered_companies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;span class="n"&gt;decorated_companies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decorator is similar to the one we saw with &lt;code&gt;SimpleDelegator&lt;/code&gt;; it relies on &lt;code&gt;Draper::Decorator&lt;/code&gt; instead. Methods of the decorated object are not directly accessible. Instead, we must call them through the &lt;code&gt;object&lt;/code&gt; or &lt;code&gt;model&lt;/code&gt; alias.&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;CompanyDecorator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Draper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Decorator&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founding_year&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;founded_on&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;year&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, if we want to directly call public methods of the decorated object with the decorator instance, we can define a list of such methods by using the &lt;code&gt;delegate&lt;/code&gt; method.&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;CompanyDecorator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Draper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Decorator&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;founding_year&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;founded_on&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;year&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;That way, we can use &lt;code&gt;decorated_company.name&lt;/code&gt;. This approach is also handy to limit the exposure of the object that's being decorated from the view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Draper Use Case: Rails View, Controller, and Model
&lt;/h2&gt;

&lt;p&gt;A classic Draper use case relates to a User, Account, or Post model: they are often part of the core domain. The sheer quantity of business logic and presentation logic in those models can cause senior developers to frown and grab a new cup of coffee.&lt;/p&gt;

&lt;p&gt;Let's take a User model use case: always a good magnet for plenty of excess fat.&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;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="c1"&gt;# table would have the following columns&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# first_name, String&lt;/span&gt;
  &lt;span class="c1"&gt;# last_name, String&lt;/span&gt;
  &lt;span class="c1"&gt;# date_of_birth, DateTime&lt;/span&gt;
  &lt;span class="c1"&gt;# company_id, Uuid&lt;/span&gt;
  &lt;span class="c1"&gt;# street_name, String&lt;/span&gt;
  &lt;span class="c1"&gt;# street_number, String&lt;/span&gt;
  &lt;span class="c1"&gt;# city, String&lt;/span&gt;
  &lt;span class="c1"&gt;# zipcode, String&lt;/span&gt;
  &lt;span class="c1"&gt;# country, String&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;age&lt;/span&gt;
    &lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;years_ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;year&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;year&lt;/span&gt; &lt;span class="c1"&gt;# Note: this is Ruby on Rails specific&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;company_name&lt;/span&gt;
    &lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&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;None of those methods add any value to the model. They are misplaced or purely related to the presentation of data.&lt;/p&gt;

&lt;p&gt;The view might look something like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/models/users/show.html.erb

&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="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;first_name&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&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;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Born &lt;span class="cp"&gt;&amp;lt;%=&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;age&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; years ago&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Address: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;street_number&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;City: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;zipcode&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Company: &lt;span class="cp"&gt;&amp;lt;%=&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;company_name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller is something like this:&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;UsersController&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;show&lt;/span&gt;
    &lt;span class="n"&gt;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&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="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&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;Of course, another view, within the cart display (for example), would make use of an address partial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/carts/_address.html.erb
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;street_number&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&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;zipcode&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="si"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A decorator can make things cleaner.&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;UserDecorator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Draper&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Decorator&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:company_name&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;age&lt;/span&gt;
    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;365&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&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;address_line&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_number&lt;/span&gt;&lt;span class="si"&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;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;street_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;city_line&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zipcode&lt;/span&gt;&lt;span class="si"&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;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="si"&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;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;name&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&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;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;The controller then looks like 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
  &lt;span class="n"&gt;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&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="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;user: &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;decorate&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;And the view is simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/models/users/show.html.erb

&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;user&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;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Born &lt;span class="cp"&gt;&amp;lt;%=&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;age&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; years ago&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Address: &lt;span class="cp"&gt;&amp;lt;%=&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;street_line&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;City: &lt;span class="cp"&gt;&amp;lt;%=&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;city_line&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Company: &lt;span class="cp"&gt;&amp;lt;%=&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;company_name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it's not so different either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decorators: When Not to Use Them in Your Ruby on Rails App
&lt;/h2&gt;

&lt;p&gt;Decorators can be overused and end up poisoning your code base. You might stuff too much in them, causing complex queries and an overhead. Decorators could also end up mixed in with helper methods and undecorated classes. All in all, you may overuse Draper.&lt;/p&gt;

&lt;p&gt;Decorators, like any class, need to be focused and stick to one responsibility: presentation logic. If a method doesn't fit, maybe it belongs somewhere else. Complex queries might appear when the &lt;code&gt;company_name&lt;/code&gt; method or something similar pulls data from a model and its associates.&lt;/p&gt;

&lt;p&gt;A similar issue can emerge when you decorate too many objects at once. After all, you basically double the number of objects if you decorate a whole collection. So, be aware of this and decorate when needed.&lt;/p&gt;

&lt;p&gt;Ruby on Rails developers tend to use a lot of helper methods. Some helper methods make sense but lack the object-oriented angle, in my opinion, and end up causing confusion. Relying on decorators will help limit the number of helper methods, but if you do use some, keep it light.&lt;/p&gt;

&lt;p&gt;Testing Ruby code is almost a standard, it's definitely a good practice, and it also applies to Decorators. This will help, just like for any other class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing a Whole View with Decorator
&lt;/h2&gt;

&lt;p&gt;There is a form of Decorator that is a bit more advanced: the view model, which can be done with Draper, SimpleDelegator, or even a plain old Ruby object (PORO). The concept is as follows: you create classes to prepare the data for a view from one or more objects. If we're talking just one object, we have something similar to what we have seen. If it's more than one, then it gets more crunchy.&lt;/p&gt;

&lt;p&gt;The idea is to gather the whole responsibility of "data presentation" into one class, even for different objects at once. This is a case where we might cause complex queries, so don't hesitate to use &lt;code&gt;includes&lt;/code&gt; and &lt;code&gt;preload&lt;/code&gt; methods with queries to eager load associations as needed.&lt;/p&gt;

&lt;p&gt;That way, instead of passing several objects to the view from the controller, you can prepare just one, and then access the data — ready to be used — from properly named methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we ran through the basics of Decorator and how to use it, before diving into how to use Decorators in Draper for a Ruby on Rails application. We finally touched on when to avoid using decorators and how to prepare a whole view with Decorator.&lt;/p&gt;

&lt;p&gt;Draper is one of those gems you'll wish you had discovered before. It gives wings to Decorators. It's also the kind of nicely integrated library that will not only help you keep your code clean by setting clear limits between responsibilities, but also provide you with custom grammar and magic that fits the Ruby on Rails ecosystem like a glove.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>An Introduction to RuboCop for Ruby on Rails</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 13 Sep 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-rubocop-for-ruby-on-rails-2fna</link>
      <guid>https://dev.to/appsignal/an-introduction-to-rubocop-for-ruby-on-rails-2fna</guid>
      <description>&lt;p&gt;Good code has a lot to do with how readable it is. As developers, we more often read code than write it. As my Perl teacher told us many times: the flexibility of Perl's syntax was its best and worst trait at the same time. Ruby's syntax was influenced partly by Perl and is also quite flexible.&lt;/p&gt;

&lt;p&gt;Whatever language you pick, set some guidelines to avoid overusing a language's flexibility. Style guides for Ruby abound on the web, and it's not difficult to pick a style nowadays. But there is not much point in having copious debates on style and whether a proposed change follows a guide. Style enforcement is best left to a tool.&lt;/p&gt;

&lt;p&gt;Such tools are called linters and static code analyzers. The de facto standard for Ruby in that category is RuboCop. Here follows an introduction to RuboCop: what it is, how it helps developers, how to use it, and some key practical use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is RuboCop for Ruby?
&lt;/h2&gt;

&lt;p&gt;First, RuboCop is a great tool to lint a code base and ensure a specific coding style is followed (such as indentation, spacing, naming conventions, etc.). It's heavily configurable on that level through many rules, called "cops". Each can be activated (or not) and tailored (for example, the number of characters per line).&lt;/p&gt;

&lt;p&gt;Ensuring a specific coding style is followed helps reduce developers' cognitive load in writing and reading code. It's not a matter of code looking "nice"; it's rather a matter of code looking similar to the rest of the code base.&lt;/p&gt;

&lt;p&gt;The second side of RuboCop is its ability to analyze code statically for quick insights into code complexity and potential code issues, such as the misuse of variables.&lt;/p&gt;

&lt;p&gt;As RuboCop is a little utility, it's easily run within a CI pipeline and in any developer's IDE. Thus, RuboCop's feedback can be integrated into the key places where it matters: when code is being written and before it's merged into the code base.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use RuboCop for Ruby
&lt;/h2&gt;

&lt;p&gt;Style is a matter of taste, so it is entirely subjective. Remember the main point behind using a linter: it's just there to help follow a chosen style because it gives us more capacity to do what matters in any project.&lt;/p&gt;

&lt;p&gt;By default, RuboCop will enforce the style defined in the &lt;a href="https://rubystyle.guide"&gt;Ruby Community Style Guide&lt;/a&gt;. We can tailor it to our specific tastes and context, but let's rely on this basic set of rules to learn how to use RuboCop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Use of RuboCop
&lt;/h3&gt;

&lt;p&gt;Adding RuboCop to a project is as simple as adding the &lt;code&gt;rubocop&lt;/code&gt; gem to the development and test group in the Gemfile.&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;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rubocop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;require: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also several complimentary gems you can use, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rubygems.org/gems/rubocop-rails"&gt;rubocop-rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubygems.org/gems/rubocop-rspec"&gt;rubocop-rspec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubygems.org/gems/rubocop-performance"&gt;rubocop-performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those last two will be useful for many of you.&lt;/p&gt;

&lt;p&gt;Once the gem, or gems, is installed, we can run &lt;code&gt;rubocop&lt;/code&gt; from within the root folder of the Ruby application.&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;$&amp;gt;&lt;/span&gt; rubocop &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will print out a report of all "offenses" and how many can be automatically fixed.&lt;/p&gt;

&lt;p&gt;For example:&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;$&amp;gt;&lt;/span&gt; rubocop &lt;span class="k"&gt;*&lt;/span&gt;
Inspecting 807 files
...CCC....................
Offenses:

app/controllers/api/messages_controller.rb:46:25: C: Naming/MethodParameterName: Method parameter must be at least 3 characters long.

80 files inspected, 20 offenses detected, 7 offenses autocorrectable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's dissect the only offense we have kept in this example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.rubocop.org/rubocop/cops_naming.html#namingmethodparametername"&gt;RuboCop's documentation&lt;/a&gt; explains that this issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Checks method parameter names for how descriptive they are. It is highly configurable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is followed by an explanation of the configuration that can be made for this specific rule (we will cover that soon).&lt;/p&gt;

&lt;p&gt;What will be of interest to most are the &lt;a href="https://docs.rubocop.org/rubocop/cops_naming.html#namingmethodparametername"&gt;examples of good and bad styles&lt;/a&gt;, as checked by this rule:&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="c1"&gt;# bad&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;varOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;varTwo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;varOne&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;varTwo&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# With `AllowNamesEndingInNumbers` set to false&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# With `MinNameLength` set to number greater than 1&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# good&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thud&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;thud&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fred&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;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;distance&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;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender_c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gender_c&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;We can apply this advice and rewrite the code to match the required style. RuboCop can often fix the issue it has found (we will look at this a bit later).&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Rules in RuboCop
&lt;/h3&gt;

&lt;p&gt;By default, out of the box, RuboCop comes with a default set of pre-configured rules. The documentation will tell you &lt;a href="https://github.com/rubocop/rubocop/blob/master/config/default.yml"&gt;Rubocop's default rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Configuring RuboCop for a project is done through the &lt;code&gt;.rubocop.yml&lt;/code&gt; file at the project's root. This can be tailored further in sub-folders, but I wouldn't recommend it.&lt;br&gt;
And, of course, it's also possible to have system-wide configuration files within the user's home folder: &lt;code&gt;~/.rubocop.yml&lt;/code&gt; or &lt;code&gt;~/.config/rubocop/config.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a simple example of such a configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Layout/LineLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Max&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;99&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's reuse the previous rule: &lt;code&gt;Naming/MethodParameterName&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Naming/MethodParameterName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MinNameLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;AllowedNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;as&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;at&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;in&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;to&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a lot of things that can be tailored in the RuboCop configuration file. The documentation can help you pick the rules you want to use, but a few other options are available to give you a starting point beyond the default rules.&lt;/p&gt;

&lt;p&gt;The Ruby style guide matches RuboCop's default configuration, for example. But you can also find other guides like the &lt;a href="https://relaxed.ruby.style"&gt;Relaxed Ruby Style&lt;/a&gt;, and some companies - such as &lt;a href="https://ruby-style-guide.shopify.dev"&gt;Shopify&lt;/a&gt; - share theirs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arguments in RuboCop
&lt;/h3&gt;

&lt;p&gt;RuboCop can be run without arguments for a basic check, but we can add on a few arguments for different effects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt; : to auto-correct offenses, but only when it's safe to do so.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-A&lt;/code&gt;: to auto-correct offenses, even when it's not safe to do so.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-E&lt;/code&gt;: to display extra details for each offense listed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-S&lt;/code&gt;: to display style guide URLs in the offense messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the main options we might be interested in. &lt;code&gt;-E&lt;/code&gt; and &lt;code&gt;-S&lt;/code&gt; are great to add whenever you run RuboCop to check the code base. They both give more details for developers to figure out what to do.&lt;br&gt;
&lt;code&gt;-a&lt;/code&gt; and &lt;code&gt;-A&lt;/code&gt; are handy to quickly take care of the "small" issues, but beware of the automatic side. It might do well enough for minor style offenses, such as spaces, but other things might not be that simple.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bending the Law
&lt;/h3&gt;

&lt;p&gt;In some rare cases, you might want a RuboCop rule or rules to be disabled around one or several lines in a file. This is generally frowned upon, as it creates a precedent many developers won't hesitate to replicate. Yet it might still be useful to know how to do this hack for certain cases.&lt;/p&gt;

&lt;p&gt;The way to disable a rule and then re-enable it is to use a pair of comments:&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="c1"&gt;# rubocop:disable Layout/ClassStructure, Style/AndOr&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# rubocop:enable Layout/ClassStructure, Style/AndOr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will disable and enable two rules: &lt;code&gt;Layout/ClassStructure&lt;/code&gt;, and &lt;code&gt;Style/AndOr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the previous example, you can also disable whole sections (or departments) of rules by using the department name: &lt;code&gt;Layout&lt;/code&gt; or &lt;code&gt;Style&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, you can also disable all the rules in one go.&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="c1"&gt;# rubocop:disable all&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# rubocop:enable all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  RuboCop in Practice
&lt;/h2&gt;

&lt;p&gt;Now that we have seen what it is and how to use it, let's review how we might use RuboCop in our day to day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Uses
&lt;/h3&gt;

&lt;p&gt;As discussed before, a linter's purpose is to ensure a standard style is used throughout a code base. Standards are how we help each other work together without putting up complex conversion and compensation schemes.&lt;br&gt;
When building a house, standards are the way every carpenter knows how to work together. The actual details will be a bit different from one house to another, but, using the standard, the work different carpenters do will match.&lt;/p&gt;

&lt;p&gt;RuboCop helps software engineers work together by keeping their code up to a certain standard style.&lt;/p&gt;

&lt;p&gt;The simplest way to use RuboCop is to run it before making a commit or once in a while before pushing commits to a remote repository. As described in the first part of this post, using &lt;code&gt;rubocop *&lt;/code&gt; will run a check of the code base and display any offenses for you to fix.&lt;/p&gt;

&lt;p&gt;You can then use the &lt;code&gt;-a&lt;/code&gt; or &lt;code&gt;-A&lt;/code&gt; option on another run to get some or all offenses fixed by RuboCop.&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;$&amp;gt;&lt;/span&gt; rubocopo &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also fix them yourself, of course.&lt;/p&gt;

&lt;h3&gt;
  
  
  Within an IDE
&lt;/h3&gt;

&lt;p&gt;Some IDEs and text editors have RuboCop plugins, so you can get notices about style offenses while working directly on code.&lt;br&gt;
The only issue with these is the configuration, which can be fiddly.&lt;/p&gt;
&lt;h3&gt;
  
  
  Integration with CI
&lt;/h3&gt;

&lt;p&gt;It's important to run RuboCop within CI pipelines.&lt;/p&gt;

&lt;p&gt;Usually, we run linters such as RuboCop as part of a pipeline's first steps to notify the author of code changes about any offense quickly. We also consider such infractions important, so they will block the Pull or Merge request in its tracks. A merge should not be possible if the style is not correct.&lt;/p&gt;

&lt;p&gt;If your project relies on GitHub's Actions, you can find a RuboCop action. You can build similar steps for any CI/CD pipeline out there. The principle is simple: run RuboCop against the code base as an early block within the pipeline; if there is an infraction, it will return a non-zero code, and the CI will treat that as a failure. The output can be used more easily as an artifact.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-f&lt;/code&gt; option will allow you to tailor the output format. This can be practical when running RuboCop in the CI pipeline: in markdown, HTML, or GitHub. I'd advise starting with markdown or HTML, but you should also consider the fairly readable default format.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adopting RuboCop in a New Project
&lt;/h2&gt;

&lt;p&gt;Many projects start without a coding style or diverge from one for some reason. Getting such code bases back in the fold is often challenging and hard work.&lt;/p&gt;

&lt;p&gt;The first run of RuboCop in such a context will usually output a fairly long list. Trying to solve all offenses at once is foolish. Instead, RuboCop allows us to generate a to-do list.&lt;/p&gt;

&lt;p&gt;Running RuboCop with the &lt;code&gt;--auto-gen-config&lt;/code&gt; option will generate the &lt;code&gt;todo&lt;/code&gt; file. This file will disable any rule that is not respected. It's located within the project folder &lt;code&gt;.rubocop_todo.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Disabling all the rules that are not respected rather than fixing them might seem counterproductive. But we don't stop here. This step ensures we don't face a mountain of work.&lt;br&gt;
We can remove a rule from the ignore list and fix all offenses against it, working our way, one rule at a time, through the to-do list and the code base.&lt;/p&gt;

&lt;p&gt;This approach — of ignoring infractions to one or more rules within multiple files and then fixing the infractions one by one in many files — is doable, but will require a lot of back and forth between files. You might end up changing several files and several lines multiple times over the course of the process.&lt;/p&gt;

&lt;p&gt;Another method is to use a "per file" approach. Using the following syntax within the to-do list file, you will ignore infractions to all cops in the files listed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AllCops&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app/models/user.rb"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app/controllers/users_controller.rb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can work your way through those files one by one: removing the file from the list, running RuboCop against it, and then fixing all offenses.&lt;/p&gt;

&lt;p&gt;Whichever approach you pick, you must remember this is a step towards improvement through continuous learning and effort. You need a strategy and vision that works well with that: include enough Slack time within the team schedule, or set specific times aside to work on this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making RuboCop Easier to Use with Standard Ruby
&lt;/h2&gt;

&lt;p&gt;RuboCop and coding style guides have helped us determine standards for each project and team. We should only debate what code is doing, not how it looks.&lt;/p&gt;

&lt;p&gt;Some Ruby developers have made the point that we can even forego adjusting RuboCop's configuration. The idea is simple: after so many years working with Ruby, we more or less know how Ruby code should look, so a style that works for most will be quite okay.&lt;/p&gt;

&lt;p&gt;This approach is known as &lt;a href="https://github.com/standardrb/standard"&gt;Standard Ruby&lt;/a&gt;. It can also be completed with plugins, including one for Ruby on Rails projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;RuboCop is, alongside Bundler, a good friend to any Ruby developer. As we have seen, it's easy to run it locally to get a list of offenses against a project's style standard.&lt;/p&gt;

&lt;p&gt;We have also seen how RuboCop can fix a lot of those offenses on its own, and how to work our way through a long list of offenses if we start from a large backlog.&lt;/p&gt;

&lt;p&gt;RuboCop is a tool in your toolbox; code style is only meant to help a team work together and shouldn't be a perpetual topic of debate. Yet, we have also seen that if you find it tedious to figure out a style for your project, you should look into projects such as Standard Ruby.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Keep Your Ruby App Secure with Bundler</title>
      <dc:creator>Thomas Riboulet</dc:creator>
      <pubDate>Wed, 05 Jul 2023 13:12:52 +0000</pubDate>
      <link>https://dev.to/appsignal/keep-your-ruby-app-secure-with-bundler-30e0</link>
      <guid>https://dev.to/appsignal/keep-your-ruby-app-secure-with-bundler-30e0</guid>
      <description>&lt;p&gt;This article covers the use of &lt;code&gt;bundler&lt;/code&gt; features to secure Ruby applications. In this day and age, we have to be more and more careful about software supply chain security.&lt;/p&gt;

&lt;p&gt;We'll show you how to start this journey by relying on a Gemfile and &lt;code&gt;bundler&lt;/code&gt; to manage your project's dependencies.&lt;/p&gt;

&lt;p&gt;By the end of the post, you will better understand how &lt;code&gt;bundler audit&lt;/code&gt; and &lt;code&gt;bundler outdated&lt;/code&gt; work. Both can help you monitor the security state of your project's dependency tree.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  An Introduction to Bundler for Ruby
&lt;/h2&gt;

&lt;p&gt;The history of &lt;a href="https://bundler.io/"&gt;Bundler&lt;/a&gt; is linked to &lt;a href="https://rubygems.org/"&gt;RubyGems&lt;/a&gt;. RubyGems, first released in 2004 by Chad Fowler, is a package manager that makes it possible to distribute and manage Ruby libraries, applications, and their dependencies.&lt;/p&gt;

&lt;p&gt;Bundler, on the other hand, is a dependency manager. It was first released in 2009 by Carl Lerche.&lt;/p&gt;

&lt;p&gt;Bundler helps developers manage dependencies between different Ruby libraries and applications. It uses a Gemfile to define the libraries and versions that a project depends on, then installs and loads those libraries in the correct order. Bundler provides a simple way to manage dependencies and ensure that all the required libraries are installed and loaded correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supply Chain Security
&lt;/h2&gt;

&lt;p&gt;RubyGems and Bundler can only solve some security issues. For example, in 2013, 2018, and 2020, several security issues emerged directly related to RubyGems. Each time, gems were compromised, potentially exposing thousands of projects and products to malicious actors.&lt;/p&gt;

&lt;p&gt;Nowadays, the security of the software supply chain has become more high profile, due to major incidents like 2020's SolarWinds hack, EventStream in 2018, and Equifax, Maersk, Merck, and FedEx in 2017.&lt;/p&gt;

&lt;p&gt;Let's see how Bundler can help us avoid some security issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundler's Security-Related Features for Ruby
&lt;/h2&gt;

&lt;p&gt;Bundler comes with several features that allow us to secure a Ruby project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source validation:&lt;/strong&gt; ensures gems are installed from a specific, trusted source.&lt;/li&gt;
&lt;li&gt;With &lt;strong&gt;a dependency graph&lt;/strong&gt;, we can see a whole list of dependencies for each gem, helping us to identify potential security risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;Gemfile.lock&lt;/code&gt; file&lt;/strong&gt; records the specific versions of each gem used in the project. As it's packaged with the project, it ensures that the same versions of gems are used across different environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vulnerability detection:&lt;/strong&gt; Thanks to an integration with the Ruby Advisory Database (RDB), Bundler can detect known security vulnerabilities in gems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An audit command&lt;/strong&gt; lets you list known security vulnerabilities related to gems a project depends on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checksum verification:&lt;/strong&gt; Bundler verifies the checksum of each gem before installing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gem signing and signature verification:&lt;/strong&gt; Gems can be signed cryptographically by a developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, alas, signing gems is complicated and requires a lot of steps. As a consequence, not all gems are signed, thus putting many projects at risk.&lt;/p&gt;

&lt;p&gt;Yet, in the meantime, we can rely on two features in particular to ensure that a project doesn't rely on packages that are too old or that have been identified as carrying a security risk.&lt;/p&gt;

&lt;p&gt;These features are &lt;code&gt;bundler audit&lt;/code&gt; and &lt;code&gt;bundler outdated&lt;/code&gt;. Let's look at &lt;code&gt;bundler audit&lt;/code&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Bundler-audit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is a command-line tool that helps you identify security vulnerabilities in the Ruby libraries that your project depends on. It is a plugin for the Ruby dependency manager Bundler and is included with Bundler version 1.10 and above.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; works by analyzing your Gemfile.lock file, which lists all the dependencies for your project and their versions. It then compares this information with a database of known vulnerabilities in Ruby gems (maintained by Rubysec).&lt;/p&gt;

&lt;p&gt;If a vulnerability is found, &lt;code&gt;bundle audit&lt;/code&gt; will provide you with information about the vulnerability, including its severity level and which gem versions have been affected. It will also suggest actions you can take to address the vulnerability, such as upgrading to a patched version of the gem or removing it entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is a useful tool for ensuring the security of your Ruby projects, especially if you are using third-party libraries or dependencies. By regularly running &lt;code&gt;bundle audit&lt;/code&gt; as part of your development workflow, you can stay up-to-date on any vulnerabilities that may affect your project and take proactive steps to address them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage of Bundler-audit in Ruby
&lt;/h3&gt;

&lt;p&gt;The basic use of Bundler-audit is through the &lt;code&gt;bundle audit&lt;/code&gt; command:&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;$&amp;gt;&lt;/span&gt; bundle-audit

Name: actionpack
Version: 3.2.10
Advisory: OSVDB-91452
Criticality: Medium
URL: http://www.osvdb.org/show/osvdb/91452
Title: XSS vulnerability &lt;span class="k"&gt;in &lt;/span&gt;sanitize_css &lt;span class="k"&gt;in &lt;/span&gt;Action Pack
Solution: upgrade to ~&amp;gt; 2.3.18, ~&amp;gt; 3.1.12, &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 3.2.13

&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it outputs the name and version of a package used by the project we are testing, which contains a medium-level security issue. It also outputs the URL of the security advisory and a possible solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping Bundler-audit's Database Up to Date
&lt;/h3&gt;

&lt;p&gt;As the audit is basically comparing a list of gems used in the project with a list of known security issues in a local copy of the security advisory database, you need to keep that database up to date.&lt;/p&gt;

&lt;p&gt;To do so, you just need to run the &lt;code&gt;bundle audit update&lt;/code&gt; command. This will fetch the latest version of the database. You should only have to run the audit again to check for any new security risks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration In a CI Pipeline
&lt;/h3&gt;

&lt;p&gt;As with other tools aimed at keeping your project safe, it's best to ensure an audit runs automatically on a regular basis (if not after every commit and push to the Git repository).&lt;/p&gt;

&lt;p&gt;You can rely on a git post commit hook to run the audit locally if the &lt;code&gt;Gemfile.lock&lt;/code&gt; changes. Here is an example:&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="c"&gt;# .git/hooks/post-commit&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD~1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; Gemfile.lock&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;"Gemfile.lock has changed, running audit..."&lt;/span&gt;
    bundle audit update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bundle audit
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a bit radical, but it works. You can rely on the same approach within the CI pipeline. As the &lt;code&gt;bundle audit&lt;/code&gt; command will return a non-zero exit status, the CI pipeline will consider it to have failed. Unfortunately, we don't have a proper audit report out of the box. Instead, you have to direct the output of the &lt;code&gt;bundle audit&lt;/code&gt; command towards a file and save it as an artifact of the build for the CI pipeline.&lt;/p&gt;

&lt;p&gt;Here is how to direct the command's output to a file:&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;$&amp;gt;&lt;/span&gt; bundle audit update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bundle audit &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/bundle-audit-report.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As each CI service handles artifacts differently, check the relevant documentation to see how to do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundler-audit in Summary
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;bundle audit&lt;/code&gt; is not a complete solution, yet it still lets your team know if your project relies on unsafe gems (without costing you an arm and a leg). Your team should put tools such as git hooks or CI tasks in place, and relevant integrations to make any issues visible.&lt;/p&gt;

&lt;p&gt;All things considered, I'd say it's not too much trouble. It probably requires a few hours to get started and have the basic information spit out as a comment in your favorite git hosting solution, or as a notice in a Slack channel.&lt;/p&gt;

&lt;p&gt;Yet &lt;code&gt;bundle audit&lt;/code&gt; is only one part of what you can do. It only tells you if there is a security issue related to a gem relied on by a project. It does not tell you if a gem is outdated. To handle that, &lt;code&gt;bundler outdated&lt;/code&gt; is the command to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundler Outdated for Ruby Gems
&lt;/h2&gt;

&lt;p&gt;Auditing for security risks is very important, yet listing outdated gems is also a big issue. The longer you wait to update a dependency, the higher the risk that your code breaks.&lt;/p&gt;

&lt;p&gt;You should aim to work in small iterations, deployed frequently, to limit the amount of change contained in each deployment. Equally, you should aim to update any gem you use as soon as it's updated, or as close to that as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage of Bundler Outdated
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;bundler audit&lt;/code&gt;, &lt;code&gt;bundler outdated&lt;/code&gt; is very simple to use. Just call it at the root of your Ruby project. It will compare the &lt;code&gt;Gemfile.lock&lt;/code&gt; content to the current gem releases listed in it.&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;$&amp;gt;&lt;/span&gt; bundle outdated
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies......

Gem           Current    Latest    Requested   Groups
addressable    2.8.1      2.8.4
capybara       3.38.0     3.39.0    &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 0        &lt;span class="nb"&gt;test
&lt;/span&gt;devise         4.9.0      4.9.2     &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 4.6.0    default

&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, three gems are outdated in this project. Let's see how we should read this table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first column contains the name of the gem.&lt;/li&gt;
&lt;li&gt;The second column shows the currently installed version.&lt;/li&gt;
&lt;li&gt;The third column shows the latest version available.&lt;/li&gt;
&lt;li&gt;The fourth column shows the request version (from the Gemfile).&lt;/li&gt;
&lt;li&gt;The fifth column shows the group the gem is part of (in the Gemfile).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, we can see we don't have much to worry about for &lt;code&gt;addressable&lt;/code&gt; and &lt;code&gt;devise&lt;/code&gt;, as the difference in releases is within the patch versions (third part of the release number). The version of &lt;code&gt;capybara&lt;/code&gt; is only behind one minor release, which implies a few more changes, but this rarely means breaking changes.&lt;/p&gt;

&lt;p&gt;Still, the logical thing to do here is to update all three as soon as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage In a CI Pipeline
&lt;/h3&gt;

&lt;p&gt;As &lt;code&gt;bundle outdated&lt;/code&gt; will return a non-zero exit status for outdated gems, a CI task it's based on is considered failed. So you can have a simple and direct flag in your CI pipeline in case of outdated dependencies.&lt;/p&gt;

&lt;p&gt;Yet you might want to rely on a pair of flags to improve your use of &lt;code&gt;bundle outdated&lt;/code&gt;. The command can filter its output to only list gems that have pending patch-level updates:&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;$&amp;gt;&lt;/span&gt; bundle outdated &lt;span class="nt"&gt;--filter-patch&lt;/span&gt;
...
Gem           Current    Latest    Requested   Groups
addressable    2.8.1      2.8.4
devise         4.9.0      4.9.2     &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 4.6.0    default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same manner, you can list only gems with pending minor version updates:&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;$&amp;gt;&lt;/span&gt; bundle outdated &lt;span class="nt"&gt;--filter-minor&lt;/span&gt;
Gem           Current    Latest    Requested   Groups
capybara       3.38.0     3.39.0    &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 0        &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the same way, you can list only gems with pending major release updates:&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;$&amp;gt;&lt;/span&gt; bundle outdated &lt;span class="nt"&gt;--filter-major&lt;/span&gt;
Gem                Current    Latest    Requested   Groups
sidekiq-scheduler   4.0.3      5.0.1      &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 0       default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those three flags let you easily tailor a CI task to only warn you that a patch-level update is available but not block the build (or the reverse), for example.&lt;/p&gt;

&lt;p&gt;A complimentary option allows you to only list outdated gems within the default group:&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;$&amp;gt;&lt;/span&gt; bundle outdated &lt;span class="nt"&gt;--group&lt;/span&gt; default
...
Gem                Current  Latest  Requested          Groups
devise             4.9.0    4.9.2   &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 4.6.0           default
faker              3.1.1    3.2.0   &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 0               default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As gems within test and development groups don't impact the production release, they could be considered less of a priority to keep up to date, for example. Thus you can use the flag only to raise a warning or block the CI pipeline if gems within the default group are outdated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Maintaining the security of your Ruby application is not just the result of one action. Rather, it's a cumulative effect of several actions put together. As we've covered in this post, Bundler provides us with two commands that can help you to improve your application's security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bundle audit&lt;/code&gt; quickly lets you know if any gem you add or use in your project could pose a security threat and
what to do about it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bundle outdated&lt;/code&gt; allows you to flag gems that should be updated. It complements &lt;code&gt;bundle audit&lt;/code&gt;, and if you keep on top of it, you avoid having an update bundle that's too large.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two commands are easy to use and integrate with local feedback loops and CI pipelines.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, &lt;a href="https://blog.appsignal.com/ruby-magic"&gt;subscribe to our Ruby Magic newsletter and never miss a single post&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>bundler</category>
    </item>
  </channel>
</rss>
