<?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: Prabin Poudel</title>
    <description>The latest articles on DEV Community by Prabin Poudel (@coolprobn).</description>
    <link>https://dev.to/coolprobn</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%2F377399%2F02063055-ab3f-49a0-8e2f-679700d50f1c.png</url>
      <title>DEV Community: Prabin Poudel</title>
      <link>https://dev.to/coolprobn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coolprobn"/>
    <language>en</language>
    <item>
      <title>Setup RSpec Tests in Rails with Gitlab CI</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Tue, 02 Apr 2024 04:30:00 +0000</pubDate>
      <link>https://dev.to/coolprobn/setup-rspec-tests-in-rails-with-gitlab-ci-4omd</link>
      <guid>https://dev.to/coolprobn/setup-rspec-tests-in-rails-with-gitlab-ci-4omd</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.truemark.dev" rel="noopener"&gt;Truemark&lt;/a&gt;, we are constantly looking to improve the code quality in our projects. And one way to do that is through the integration of CI into our workflow. CI can help in automating code reviews for linting and standard practices as well as for running tests to check if code change breaks any existing functionalities.&lt;/p&gt;

&lt;p&gt;Today we will look at adding configurations to Gitlab CI for running RSpec tests in our Rails application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumption
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You have basic Gitlab CI configurations ready i.e. &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; exists in your project.&lt;/p&gt;

&lt;p&gt;If it doesn't, you can refer to my other article &lt;a href="https://prabinpoudel.com.np/articles/integrate-pronto-with-gitlab-ci-for-rails-app/"&gt;Integrate Pronto with Gitlab for Rails App&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You are using PostgreSQL in your app though with minimal changes it should also work for any other databases like MySQL and SQLite (let me know in comments if it doesn't and I will help you!)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You are using RSpec as a test library but again with minimal changes it should work for MiniTest as well (let me know if it doesn't and I will help you!)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tested and working in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ruby 3.3.0&lt;/li&gt;
&lt;li&gt;Rails 7.1.3&lt;/li&gt;
&lt;li&gt;rspec-rails 6.1.1&lt;/li&gt;
&lt;li&gt;selenium-webdriver 4.18.1&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configure Gitlab CI Variables
&lt;/h2&gt;

&lt;p&gt;Firs of all, we need to add some configurations and files required by the CI to run tests. This should be done over at &lt;a href="https://gitlab.com" rel="noopener"&gt;Gitlab&lt;/a&gt;. Let's looks at them one by one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a variable for storing database.yml file content
&lt;/h3&gt;

&lt;p&gt;It's not considered a good practice to use &lt;code&gt;config/database.yml&lt;/code&gt; file for the CI so we will have to add a Gitlab CI variable and store the content required to setup PostgreSQL database inside it.&lt;/p&gt;

&lt;p&gt;You can visit the &lt;a href="https://docs.gitlab.com/ee/ci/variables/#project-cicd-variables" rel="noopener"&gt;official documentation&lt;/a&gt; to know a way to setup variables. You have to go to your project's setting in Gitlab and configure these in CI/CD variables.&lt;/p&gt;

&lt;p&gt;Create a new variable for this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type: File&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flags&lt;/p&gt;

&lt;p&gt;Uncheck all checklists here i.e. Protect variable, Mask variable and Expand variable reference&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Description&lt;/p&gt;

&lt;p&gt;You can write "Database YML" but it's optional and you can skip this as "Key" (just below this) is already clear enough on what this variable is storing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Key: &lt;code&gt;database_yml&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lastly in "Value" add the following:&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;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
  &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unicode&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_ci_db&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For host make sure to use "postgres" instead of "localhost". For other services like MySQL and SQLite you will most probably have to use "mysql" or "sqlite" respectively as said in the &lt;a href="https://docs.gitlab.com/ee/ci/runners/configure_runners.html#control-jobs-that-a-runner-can-run" rel="noopener"&gt;official documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The service container for MySQL is accessible under the hostname mysql. To access your database service, connect to the host named mysql instead of a socket or localhost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Add a variable for storing environment variables
&lt;/h3&gt;

&lt;p&gt;I normally use Figaro for storing environment variables which uses &lt;code&gt;config/application.yml&lt;/code&gt; but just the plain &lt;code&gt;.env&lt;/code&gt; file is also very popular. Anyway, just copy the content from whatever you are using and paste it inside the Value for this new variable.&lt;/p&gt;

&lt;p&gt;Type, Flags and Description will be the same as described above for database.yml&lt;/p&gt;

&lt;p&gt;You can add &lt;code&gt;env&lt;/code&gt; for Key.&lt;/p&gt;

&lt;p&gt;In "Value", add the copied content from your env file.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: Make sure to only copy what is under "test" block or ".env.test", you won't want to add production variables here as that will lead to security issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Require selenium-webdriver
&lt;/h2&gt;

&lt;p&gt;We need to enable selenium-webdriver that is required to run tests in the browser, especially system tests.&lt;/p&gt;

&lt;p&gt;Add the following to &lt;code&gt;spec/rails_helper.rb&lt;/code&gt; if it's not already there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "selenium-webdriver"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Previously "webdrivers" gem was required to automate the installation and update browser specific drivers. But Selenium 4 ships with webdrivers now leading to the webdrivers being &lt;a href="https://github.com/titusfortner/webdrivers" rel="noopener"&gt;deprecated&lt;/a&gt;. Quoting the webdrivers Github:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you can update to the latest version of Selenium (4.11+), please do so and stop requiring this gem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configure Capybara with Selenium
&lt;/h2&gt;

&lt;p&gt;We will configure Selenium with Chrome to be used both in CI and Local with Headless mode (by default) while also allowing to run in the browser if needed for debugging.&lt;/p&gt;

&lt;p&gt;Create a new file "spec/helpers/capybara.rb" and add the following code:&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;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_driver&lt;/span&gt; &lt;span class="ss"&gt;:selenium_chrome_custom&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;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Selenium&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebDriver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Chrome&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Options&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--headless=new"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SELENIUM_HEADFUL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--window-size=1400,1400"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--no-sandbox"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--disable-dev-shm-usage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;remote_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELENIUM_REMOTE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&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;remote_url&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Selenium&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&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;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;browser: :remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="n"&gt;remote_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;options:
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Selenium&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&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;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;browser: :chrome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&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;configure&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;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;js: &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;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Make the test app listen to outside requests, required for the remote Selenium instance&lt;/span&gt;
    &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELENIUM_REMOTE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# Use the application container's IP instead of localhost so Capybara knows where to direct Selenium&lt;/span&gt;
      &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;IPSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getaddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gethostname&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="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium_chrome_custom&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;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;Let's look at what each of the code block above is doing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Selenium Chrome driver
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Capybara.register_driver :selenium_chrome_custom&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Since existing Selenium Drivers don't provide the custom options we want, we are creating a new driver &lt;code&gt;selenium_chrome_custom&lt;/code&gt; which will handle Remote/Local connection as well as Headless/Headful mode.&lt;/p&gt;

&lt;h4&gt;
  
  
  Options
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--window-size=1400,1400&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Set the window size to 1400x1400 pixels. This is a reasonable size without being too large, but you can set it to whatever you like. This mostly impacts the size of debugging screenshots, but some tests may fail if you ask Capybara to click on an element which is not currently visible on the page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--no-sandbox&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Disables Chrome’s sandbox functionality because it has an issue with Docker version 1.10.0 and later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--disable-dev-shm-usage&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The "/dev/shm" shared memory partition is too small on many VM environments which will cause Chrome to fail or crash so we are disabling it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--headless=new&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enable Chrome’s headless mode which will run Chrome without a UI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELENIUM_HEADFUL&lt;/code&gt; will control this option. In development, you may want to run Chrome and see what's happening in the browser; you can do so by running tests with &lt;code&gt;SELENIUM_HEADFUL=true bundle exec rspec spec/system&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will see list of other commands to run system tests at the end of this explanation section in a bit.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some guides may suggest using the --disable-gpu flag, but &lt;a href="https://issues.chromium.org/issues/40527919" rel="noopener"&gt;this is no longer necessary on any operating system&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This explanation was shamelessly copied from &lt;a href="https://thurlow.io/ruby/2020/11/06/remote-selenium-webdriver-servers-with-rails-capybara-and-rspec.html" rel="noopener"&gt;Remote Selenium WebDriver servers with Rails, Capybara, RSpec, and Chrome&lt;/a&gt; 🙈.&lt;/p&gt;

&lt;h4&gt;
  
  
  Selenium remote URL
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;remote_url = ENV.fetch("SELENIUM_REMOTE_URL", nil)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Remote option is required mostly for CI but you can also test it out in local by running the Selenium Docker image e.g. with &lt;code&gt;SELENIUM_REMOTE_URL=http://localhost:4444/wd/hub bundle exec rspec spec/system&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Remote option is controlled by &lt;code&gt;SELENIUM_REMOTE_URL&lt;/code&gt; which needs to be passed when running tests as seen above.&lt;/p&gt;

&lt;p&gt;Another configuration related to the remote is the use of &lt;code&gt;browser: :remote&lt;/code&gt; inside &lt;code&gt;Capybara::Selenium::Driver.new&lt;/code&gt; which tells Capybara to run tests in remote Chrome browser instead of local one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Capybara server and app host
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELENIUM_REMOTE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Use the application container's IP instead of localhost so Capybara knows where to direct Selenium&lt;/span&gt;
  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;app_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;IPSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getaddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gethostname&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;server_host&lt;/code&gt; and &lt;code&gt;app_host&lt;/code&gt; are required for Capybara to know how it can call driver in the Remote Server.&lt;/p&gt;

&lt;p&gt;This piece of code was extracted from the &lt;a href="https://guides.rubyonrails.org/testing.html#changing-the-default-settings" rel="noopener"&gt;official Rails Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Commands to run tests
&lt;/h4&gt;

&lt;p&gt;Lastly, let's see various commands we can use to run system tests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run in headless mode (default): &lt;code&gt;bundle exec rspec spec/system&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run in headful mode: &lt;code&gt;SELENIUM_HEADFUL=true bundle exec rspec spec/system&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run in headless mode inside external docker image in local: &lt;code&gt;SELENIUM_REMOTE_URL=http://localhost:4444/wd/hub bundle exec rspec spec/system&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For CI, default command &lt;code&gt;bundle exec rspec spec/system&lt;/code&gt; will work. But &lt;code&gt;SELENIUM_REMOTE_URL&lt;/code&gt; will be &lt;code&gt;http://selenium:4444/wd/hub&lt;/code&gt; and it will be passed an Environment Variable instead. We will look at how to do that next. &lt;/p&gt;

&lt;h2&gt;
  
  
  Update &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; to run all tests
&lt;/h2&gt;

&lt;p&gt;We will be adding code to enable all the following tests and you can choose to pickup or ignore as per your requirement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit and Integration tests (Model, Requests, Authorization, Services etc.) which don't require us to start browser&lt;/li&gt;
&lt;li&gt;System Tests where we will start the Chrome browser and run tests inside it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; with the configurations given below. Most of the configurations are accompanied by explanation, you can find clean configuration without comment at the end of the blog in the section "Final .gitlab-ci.yml"&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="c1"&gt;# change to the ruby version your application uses&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby:3.3.0&lt;/span&gt;

&lt;span class="c1"&gt;# explanation in next section&lt;/span&gt;
&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vendor/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn.lock&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="c1"&gt;# base configuration required for running tests&lt;/span&gt;
&lt;span class="na"&gt;.base_db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# add-on docker images required for running tests&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# set Rails environment so we don't have to prefix each command with RAILS_ENV=test&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="c1"&gt;# Postgres runs in a separate docker image and requires authentication to connect. Disabling that here by using "trust" so it doesn't ask for authentication&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trust&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# use same bundler version that was used in bundling the Gemfile&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" --no-document&lt;/span&gt;
    &lt;span class="c1"&gt;# install all gems to "vendor" folder which helps in caching of gem installation in between the execution of CI jobs&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle config set --local path 'vendor'&lt;/span&gt;
    &lt;span class="c1"&gt;# install "nodejs" required for yarn and "cmake" required for pronto&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get update -qq &amp;amp;&amp;amp; apt-get install -y -qq nodejs cmake&lt;/span&gt;
    &lt;span class="c1"&gt;# install gems in parallel, nproc returns the number of available processors&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install --jobs $(nproc)&lt;/span&gt;
    &lt;span class="c1"&gt;# install yarn&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -o- -L https://yarnpkg.com/install.sh | bash&lt;/span&gt;
    &lt;span class="c1"&gt;# Make yarn available in the current terminal&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn install&lt;/span&gt;
    &lt;span class="c1"&gt;# copy all database configurations stored as the Gitlab CI variable to the file "config/database.yml"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $database_yml &amp;gt; config/database.yml&lt;/span&gt;
    &lt;span class="c1"&gt;# 👋 config/application.yml can be different for you. For e.g. if you are using ".env" then this content will be `cat $envfile &amp;gt; .env`&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $env &amp;gt; config/application.yml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails db:create&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails db:schema:load&lt;/span&gt;
    &lt;span class="c1"&gt;# Required for integration and system tests&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails assets:precompile&lt;/span&gt;

&lt;span class="na"&gt;unit_and_integration_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# reuse all configurations defined in .base_db above&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.base_db&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="c1"&gt;# run this job only when merge requests are created, updated or merged&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;merge_requests&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# run all tests except system tests&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rspec --exclude-pattern "spec/system/**/*.rb"&lt;/span&gt;

&lt;span class="na"&gt;system_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.base_db&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# need to declare postgres again because "services" key will override the one defined in .base_db&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="c1"&gt;# Docker image for Selenium with Chrome so test can run inside the browser&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome:122.0&lt;/span&gt;
      &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trust&lt;/span&gt;
    &lt;span class="c1"&gt;# Location of the selenium docker image. "selenium" is an alias, you can also use http://selenium-standalone-chrome:4444/wd/hub or selenium__standalone-chrome (commonly seen in other guides)&lt;/span&gt;
    &lt;span class="na"&gt;SELENIUM_REMOTE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://selenium:4444/wd/hub&lt;/span&gt;
  &lt;span class="c1"&gt;# store necessary files and folders in case of test failure for debugging the error&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_failure&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;log/test.log&lt;/span&gt;
    &lt;span class="na"&gt;expire_in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1 week&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;merge_requests&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rspec spec/system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;Let's look at some configurations where explanation was missing and would be lengthy to add there.&lt;/p&gt;

&lt;h4&gt;
  
  
  cache
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vendor/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn.lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Gitlab CI to cache vendor folder where we are storing all our gems, node_modules where all JS packages are stored, yarn.lock which stores the information about installed packages with their versions.&lt;/p&gt;

&lt;p&gt;Storing all these folders and files speed up the CI in subsequent runs. &lt;code&gt;bundle install&lt;/code&gt; and &lt;code&gt;yarn install&lt;/code&gt; will only install new packages that are not already inside the cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  stages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stages define when to run the jobs. For example, stages that run tests after stages that runs linting on new changes.&lt;/p&gt;

&lt;p&gt;If you also have linting and continuous deployment configured then stages could look like this:&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;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lint&lt;/span&gt;
    &lt;span class="s"&gt;- test&lt;/span&gt;
    &lt;span class="s"&gt;- staging_deploy&lt;/span&gt;
    &lt;span class="s"&gt;- production_deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jobs are run in the same order as configured here i.e. linting will run first then test and lastly deployments.&lt;/p&gt;

&lt;h4&gt;
  
  
  .base_db
&lt;/h4&gt;

&lt;p&gt;This configuration is used by all jobs that require database access. All common configurations for such jobs are extracted here.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;services&lt;/code&gt; are add-on docker images and provide capabilities like database, redis, selenium drivers, etc.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;variables&lt;/code&gt; are environment variables used by Rails.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;before_script&lt;/code&gt; runs before the &lt;code&gt;script&lt;/code&gt; so anything that needs to be pre-configured can be added here.&lt;/p&gt;

&lt;h4&gt;
  
  
  unit_and_integration_tests
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;extends&lt;/code&gt; will extend the configurations defined in the &lt;code&gt;.base_db&lt;/code&gt; and use those configurations for this job.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stage&lt;/code&gt; tells this job at what stage to run. Depending on &lt;strong&gt;&lt;code&gt;stages&lt;/code&gt;&lt;/strong&gt; defined just above this job configuration.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;script&lt;/code&gt; are the series of command to execute for this job. We are running all tests except system tests by using the rspec command helper &lt;code&gt;--exclude-pattern "spec/system/**/*.rb&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  system_tests
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;selenium/standalone-chrome:122.0&lt;/code&gt; configures the docker image for Selenium with Chrome with the version 122.0, normally people use &lt;code&gt;selenium/standalone-chrome:latest&lt;/code&gt; instead of this. But at the time of writing this blog, the latest version "123.0" had some issues and Chrome browser was not starting; I had to spend 6+ hours in debugging just for finding that out 🫠&lt;/p&gt;

&lt;p&gt;&lt;code&gt;artifacts&lt;/code&gt; is used to store necessary files and folders in case of test failure. This helps us in debugging failing tests when needed. We are storing test log files for this purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you also have Pronto or any other linter configured in CI then your final file could look like this:&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby:3.3.0&lt;/span&gt;

&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vendor/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn.lock&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lint&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;pronto&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" --no-document&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get update -qq &amp;amp;&amp;amp; apt-get install -y -qq cmake&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle config set --local path 'vendor'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install --jobs $(nproc)&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lint&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;merge_requests&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PRONTO_GITLAB_API_PRIVATE_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$PRONTO_ACCESS_TOKEN&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec pronto run -f gitlab_mr -c origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME&lt;/span&gt;

&lt;span class="na"&gt;.base_db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trust&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" --no-document&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle config set --local path 'vendor'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get update -qq &amp;amp;&amp;amp; apt-get install -y -qq nodejs cmake&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle install --jobs $(nproc)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -o- -L https://yarnpkg.com/install.sh | bash&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $database_yml &amp;gt; config/database.yml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $env &amp;gt; config/application.yml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails db:create&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails db:schema:load&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails assets:precompile&lt;/span&gt;

&lt;span class="na"&gt;unit_and_integration_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.base_db&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;merge_requests&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rspec --exclude-pattern "spec/system/**/*.rb"&lt;/span&gt;

&lt;span class="na"&gt;system_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.base_db&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome:122.0&lt;/span&gt;
      &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trust&lt;/span&gt;
    &lt;span class="na"&gt;SELENIUM_REMOTE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://selenium:4444/wd/hub&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_failure&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;log/test.log&lt;/span&gt;
    &lt;span class="na"&gt;expire_in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1 week&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;merge_requests&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle exec rspec spec/system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Phew, that was a lot of configurations and explanation.&lt;/p&gt;

&lt;p&gt;The reason why I wrote this blog was because I faced various problems when trying out other blogs in the internet today and couldn't fully understand what was happening inside the configuration file because there were no explanations. I hope I have explained everything the code is doing and you don't have to waste time in researching about these things again.&lt;/p&gt;

&lt;p&gt;With this, your Rails app now has all type of tests running in the Gitlab CI so you can now merge changes without any worry for them breaking the production application.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/ci/services/index.html#how-services-are-linked-to-the-job" rel="noopener"&gt;How services are linked to the Job (Gitlab)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/julianrubisch/7a96e4778302c1cb9911b6f9db2cb75f" rel="noopener"&gt;Gitlab CI Config for System Tests with Minitest (Github Gist)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thurlow.io/ruby/2020/11/06/remote-selenium-webdriver-servers-with-rails-capybara-and-rspec.html" rel="noopener"&gt;Remote Selenium WebDriver servers with Rails, Capybara, RSpec, and Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/testing.html#system-testing" rel="noopener"&gt;System Testing (Official Rails Documentation)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Image Credits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@jenstakesphotos?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Jens Freudenau&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-group-of-pipes-that-are-connected-to-each-other-Xlg2KbYFUoM?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>rspec</category>
      <category>testing</category>
    </item>
    <item>
      <title>Devise raise validations error when new and old passwords are same</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Wed, 21 Feb 2024 18:15:00 +0000</pubDate>
      <link>https://dev.to/truemark/devise-raise-validations-error-when-new-and-old-passwords-are-same-5gj</link>
      <guid>https://dev.to/truemark/devise-raise-validations-error-when-new-and-old-passwords-are-same-5gj</guid>
      <description>&lt;p&gt;Authentication is a deal breaking feature in any applications nowadays. In Rails, Devise makes authentication a breeze; install a gem, run few commands and you have authentication working in your app.&lt;/p&gt;

&lt;p&gt;Today, with the help of Devise we will look into a solution for a very common feature request in the app; throw validation error if user tries to change their password but add the same old password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills required to follow the tutorial
&lt;/h2&gt;

&lt;p&gt;Intermediate:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  You should have
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Existing Rails app with authentication already handled using Devise&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validation Implementation
&lt;/h2&gt;

&lt;p&gt;Let's dive into the code now.&lt;/p&gt;

&lt;p&gt;Add the following validation error to the model that is used for authentication:&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;# ============ Custom Validation ============&lt;/span&gt;
  &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="ss"&gt;:new_and_old_password_must_be_different&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;new_and_old_password_must_be_different&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exclude?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'encrypted_password'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;password_is_same&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Encryptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&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="n"&gt;encrypted_password_was&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="no"&gt;I18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'validations.not_allowed.old_password'&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;password_is_same&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;Please note that I am using "User" model for storing all users and authenticate them but table could be anything else like "Admin" as well.&lt;/p&gt;

&lt;p&gt;We will understand what each line of code means in next section.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;changed.exclude?('encrypted_password')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ActiveModel stores changes that were made in the current transaction inside the variable "changed" and with this line of code we are returning early from the validation if user was updated but password wasn't updated.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Devise::Encryptor.compare(User, encrypted_password_was, password)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are already using Devise for authentication so we are reaching out to the helper module "Encryptor" from Devise to compare new password with the old one. Here, current password will be in plain format and "Encryptor" will hash the password with relevant algorithm before comparing so we know if the password is same or different.&lt;/p&gt;

&lt;p&gt;This line will return true if previous password is same as the new password or false if they are different.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;errors.add(:password, I18n.t('validations.not_allowed.old_password'))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we are adding validation errors to the User model if the password is same. And controller action will return the validation error to show it in frontend.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And with that we have successfully added a way for our app to throw validation errors when old password is used with the help of Devise. I hope you got to learn something new today.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/67110367/rails-devise-how-do-i-get-an-error-message-if-password-is-not-changed" rel="noopener"&gt;[Stack Overflow] Rails + Devise: How do I get an error message if password is not changed?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@alexeh99?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;alexander ehrenhöfer&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/assorted-color-padlocks-in-rope-yI4pFmN9ges?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>devise</category>
    </item>
    <item>
      <title>Run Cron Job Manually</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Wed, 17 Jan 2024 14:08:51 +0000</pubDate>
      <link>https://dev.to/truemark/run-cron-job-manually-2l8h</link>
      <guid>https://dev.to/truemark/run-cron-job-manually-2l8h</guid>
      <description>&lt;p&gt;Cron Job has made the scheduling of various tasks super easy but debugging those scripts from Cron Job is equally harder. This is because Cron Job runs command you provide to it always at one specific time; which means there are only two ways for you to run the Cron Job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wait for the Cron Job to run at the time you have given, could be seconds, minutes or even hours&lt;/li&gt;
&lt;li&gt;Run the Cron Job every minute and see if it throws any error (most people do this)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if I tell you there is also a third way; run the Cron Job manually.&lt;/p&gt;

&lt;p&gt;This approach will help us in running any of our script immediately without having to wait for Crontab to execute our job at specific time. And it will also run the job by replicating the same environment Cron uses while running it's jobs. Super helpful for debugging any Cron related issue if you ask me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;Let's dig into the approach of running the Cron Job manually then.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Get PATHs your normal Cron Job uses
&lt;/h3&gt;

&lt;p&gt;The biggest difference in running the script directly from the command line versus from the Cron Job is difference in it's PATH values.&lt;/p&gt;

&lt;p&gt;A lot of times, Cron Job fails to execute because PATH doesn't include executables for e.g. for Ruby, PERL, etc. and due to these executables not being found script that was running properly in development or even from command line in the production server will no more work when running the same script from the Cron Job.&lt;/p&gt;

&lt;p&gt;Run the command &lt;code&gt;crontab -e&lt;/code&gt; to edit your crontab and add the following entry to the top (or anywhere in the file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* * * * * /usr/bin/env &amp;gt; /home/deploy/cron-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that &lt;code&gt;/home/deploy&lt;/code&gt; is the root path of my logged in user in the Production server, it could be different for you.&lt;/p&gt;

&lt;p&gt;With the above entry in the crontab, we are telling the Cron Job to run every minute and add PATHs it gets from the command "/usr/bin/env" to a file named "cron-env"&lt;/p&gt;

&lt;p&gt;Wait for a minute and check the content inside the file "cron-env" with the command &lt;code&gt;cat cron-env&lt;/code&gt;, it should have the content similar to this:&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;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/deploy
&lt;span class="nv"&gt;LOGNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;deploy
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin:/bin
&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;span class="nv"&gt;SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/sh
&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that "deploy" is the name of the logged in user.&lt;/p&gt;

&lt;p&gt;You should remove the Cron Job we added previously from the Crontab since it has served it's purpose of providing us with PATHs we will require to run commands as a Cron Job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create a bash script to run any command similar to Cron Job
&lt;/h3&gt;

&lt;p&gt;Create a new file with the command &lt;code&gt;nano run-as-cron&lt;/code&gt; and add the following inside:&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;#!/bin/bash&lt;/span&gt;

/usr/bin/env &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/deploy/cron-env&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what the above command is doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/usr/bin/env&lt;/code&gt; is the path to the env command, which is used to find and execute a specified command with a modified environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-i&lt;/code&gt; option tells env to start with an empty environment, ignoring the inherited environment variables.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(cat /home/deploy/cron-env)&lt;/code&gt; uses the "cat" command to read the contents of the file located at /home/deploy/cron-env and then uses command substitution &lt;code&gt;$(...)&lt;/code&gt; to include the contents of the file as arguments to the env command. The contents of the file are expected to be environment variable assignments for any commands that is run using this bash script.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"$@"&lt;/code&gt; is a special variable which represents all the arguments passed to the script or command. In this context, it allows the script or command executed by env to receive the same arguments that were passed to the original script or command containing this line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For e.g. if you run the command &lt;code&gt;./run-as-cron /path/to/script -with arguments&lt;/code&gt; then everything after "./run-as-cron" is passed as it is here and executed by this script.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Run any command you want by replicating the Cron behavior
&lt;/h3&gt;

&lt;p&gt;Now we are ready to run any command we want using the same environment variables Cron Job uses during it's run.&lt;/p&gt;

&lt;p&gt;You can run the command you want now with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./run-as-cron /path/to/script --with arguments --and parameters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example if you have a script let's say to read a CSV file and parse them as JSON then you can run that command now with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./run-as-cron /home/deploy/production/scripts/parse-csv-as-json '/home/deploy/production/csv-files/employees.csv'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes you take the file path as an argument inside the script.&lt;/p&gt;

&lt;p&gt;And that's it, you should now be able to run the Cron Job manually and debug the result from it immediately! &lt;/p&gt;

&lt;p&gt;If you are a Ruby on Rails developer follow along for a bit more as I have a bonus tip for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: Run Ruby on Rails tasks replicating the Cron behavior
&lt;/h3&gt;

&lt;p&gt;Unrelated to this tutorial and being a Rails developer myself, I thought of attaching some bonus example on running the rake task by executing as the Cron Job manually.&lt;/p&gt;

&lt;p&gt;I am also assuming that you also already have whenever gem configured and added some Rake tasks to "config/schedule.rb"&lt;/p&gt;

&lt;p&gt;Now for example if you have a rake task that sends an email to you once a day with the list of users who registered yesterday to your app then the command could look like something below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./run-as-cron /bin/bash -l -c 'cd /home/deploy/production/your-app/current &amp;amp;&amp;amp; RAILS_ENV=production /home/deploy/.rbenv/shims/bundle exec rake users:registered_yesterday --silent &amp;gt;&amp;gt; log/whenever.log 2&amp;gt;&amp;amp;1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have just copied the command whenever executes when we deploy the Rails app with "Capistrano", here is the short explanation of bash command and options used above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/bin/bash&lt;/code&gt; specifies the Bash shell to be used for running the command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-l&lt;/code&gt; option makes Bash act as if it had been invoked as a login shell. This means it will read certain login-specific configuration files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c '...'&lt;/code&gt; option allows you to pass a command as a string for Bash to execute. The command inside the single quotes is the actual command being executed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cd /home/deploy/production/your-app/current &amp;amp;&amp;amp; RAILS_ENV=production /home/deploy/.rbenv/shims/bundle exec rake users:registered_yesterday&lt;/code&gt; tells the script to change the working directory to the folder ".... /current" and execute the Rake task.&lt;/li&gt;
&lt;li&gt;`&lt;code&gt;&amp;gt;&amp;gt; log/whenever.log 2&amp;gt;&amp;amp;1&lt;/code&gt; appends both standard output and standard error to the file "log/whenever.log". This is a common technique to capture both normal output and errors in a log file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this short and to the point tutorial we have learnt how we can run the Cron Job manually and immediately be able to debug issues instead of having to wait for some minutes or hours to debug the issue.&lt;/p&gt;

&lt;p&gt;I hope you have also learnt about some new bash command options and techniques as I did while writing this blog. Lastly, I hope it helps you save a lot of your time into the future now when debugging any Cron Job.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Happy Tinkering!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://serverfault.com/questions/85893/running-a-cron-job-manually-and-immediately" rel="noopener"&gt;Running a cron job manually and immediately [Server Fault]&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@icons8?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Icons8 Team&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/silver-bell-alarm-clock-dhZtNlvNE8M?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cron</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Solved] Ubuntu 22 Temporary failure in name resolution</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Fri, 05 Jan 2024 09:04:06 +0000</pubDate>
      <link>https://dev.to/truemark/solved-ubuntu-22-temporary-failure-in-name-resolution-5aj1</link>
      <guid>https://dev.to/truemark/solved-ubuntu-22-temporary-failure-in-name-resolution-5aj1</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In one of the client project, we had to upgrade our existing Ubuntu server from the version 20 to 22. Upgrade was smooth but after the upgrade was done, we started noticing the issue of "Temporary failure in name resolution".&lt;/p&gt;

&lt;h2&gt;
  
  
  When does this error occur?
&lt;/h2&gt;

&lt;p&gt;The "Temporary failure in name resolution" error occurs when the system cannot translate a website name into an IP address. Somehow it got messed up during the upgrade&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix
&lt;/h2&gt;

&lt;p&gt;To fix the issue, you can hit the following commands; this fix was tested on an upgrade Ubuntu at version 22.04.2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ apt install netplan.io
$ systemctl unmask systemd-networkd.service
$ systemctl unmask systemd-resolved.service
$ ENABLE_TEST_COMMANDS=1 netplan migrate
$ netplan try

# WARNING: This will immediately log you out of the server and restart it, if you are working with Production server; run with care.
$ reboot

$ apt purge ifupdown resolvconf
$ ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada, and that should fix the issue 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Are there any other solutions you tried and it worked? Let us know in the comments below.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Until next time! Happy tinkering.&lt;/p&gt;

&lt;h2&gt;
  
  
  References:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://askubuntu.com/a/1444331"&gt;"Temporary failure in name resolution" after upgrading 22.04 to 22.10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/mss/7a8e048dd51e5ef928039f1450ba8f31"&gt;Migrate from ifupdown to netplan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@albertstoynov?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener"&gt;Albert Stoynov&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Setup Active Job with Sidekiq in Rails</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Mon, 02 Jan 2023 23:58:30 +0000</pubDate>
      <link>https://dev.to/truemark/setup-active-job-with-sidekiq-in-rails-560b</link>
      <guid>https://dev.to/truemark/setup-active-job-with-sidekiq-in-rails-560b</guid>
      <description>&lt;p&gt;Active Job is a framework for declaring jobs and making them run on a variety of queuing backends. These jobs can be everything from regularly scheduled clean-ups, to billing charges, to mailings. Anything that can be chopped up into small units of work and run in parallel.&lt;/p&gt;

&lt;p&gt;Active Job comes pre installed in Rails.&lt;/p&gt;

&lt;p&gt;Now on to Sidekiq; it is one of the most widely used background job frameworks that you can implement in a Rails application. It is multi-threaded and utilizes Redis for its queuing storage.&lt;/p&gt;

&lt;p&gt;Today we will be working on setting up Active Job with Sidekiq while also following the official Rails documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills required to follow the tutorial
&lt;/h2&gt;

&lt;p&gt;Intermediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails&lt;/li&gt;
&lt;li&gt;Linux skills to work with commands in server where your app has been deployed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You should have
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Existing Rails app&lt;/li&gt;
&lt;li&gt;Linux server already setup to run Rails app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install Capistrano Gems
&lt;/h2&gt;

&lt;p&gt;Add the following to your Gemfile under development group:&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;'sidekiq'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following in command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should install the latest version of Sidekiq gem, now let's lock the version by looking at the "Gemfile.lock", this version was what I had in my lock file, yours could be different.&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;'sidekiq'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 7.0.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Enable Sidekiq
&lt;/h2&gt;

&lt;p&gt;Add the following to "config/application.rb"&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;# Use sidekiq for active jobs&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_job&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;:sidekiq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Install Redis
&lt;/h2&gt;

&lt;p&gt;Redis is an open source, in-memory data store used by millions of developers as a database, cache, streaming engine, and message broker. It is an in-memory key-value store known for its flexibility and performance.&lt;/p&gt;

&lt;p&gt;Sidekiq is backed by Redis as a job management store to process thousands of jobs per second.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/getting-started/installation/" rel="noopener noreferrer"&gt;Official Redis Documentation&lt;/a&gt; has got you covered for the installation of Redis in any OS.&lt;/p&gt;

&lt;h3&gt;
  
  
  On MacOS via Homebrew
&lt;/h3&gt;

&lt;p&gt;If you are on MacOS, you can follow instructions at &lt;a href="https://redis.io/docs/getting-started/installation/install-redis-on-mac-os/" rel="noopener noreferrer"&gt;Install Redis on MacOS (Official Documentation)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;

&lt;p&gt;You can either choose to install from &lt;a href="https://redis.io/docs/getting-started/installation/install-redis-from-source/" rel="noopener noreferrer"&gt;source&lt;/a&gt; or from &lt;a href="https://redis.io/docs/getting-started/installation/install-redis-on-linux/" rel="noopener noreferrer"&gt;APT repository&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I had installed Redis from the source and these were the instructions that I needed to perform from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://download.redis.io/redis-stable.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;xvzf redis-stable.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;redis-stable
&lt;span class="nv"&gt;$ &lt;/span&gt;make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the executables to you local usr bin folder so you can run the redis-server and cli commands from your home/user directory&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;sudo cp &lt;/span&gt;src/redis-server /usr/local/bin/
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;src/redis-cli /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Automatically start on machine restart
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;NOTE:&lt;/em&gt; You can also find these instructions in the official Redis documentation at &lt;a href="https://redis.io/docs/getting-started/" rel="noopener noreferrer"&gt;Getting Started &amp;gt; Installing Redis more properly&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a directory in which to store your Redis config files and your data:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /etc/redis
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the init script that you'll find in the Redis distribution under the utils directory into /etc/init.d. We suggest calling it with the name of the port where you are running this instance of Redis. For example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;redis-stable/utils/redis_init_script /etc/init.d/redis_6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit the init script&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/init.d/redis_6379
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Make sure to modify REDISPORT according to the port you are using. Both the pid file path and the configuration file name depend on the port number. I had used the port "6379"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the template configuration file you'll find in the root directory of the Redis distribution into /etc/redis/ using the port number as name, for instance:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;redis-stable/redis.conf /etc/redis/6379.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a directory inside /var/redis that will work as data and working directory for this Redis instance:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/redis/6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit the configuration file with &lt;code&gt;sudo nano /etc/redis/6379.conf&lt;/code&gt;, making sure to perform the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the port accordingly. In our example it is not needed as the default port is already 6379. It is under the section ## NETWORK ##&lt;/li&gt;
&lt;li&gt;Set daemonize to yes (by default it is set to no). It is under the section ## GENERAL ##.&lt;/li&gt;
&lt;li&gt;Set the pidfile to /var/run/redis_6379.pid (modify the port if needed). It is under the section ## GENERAL ##&lt;/li&gt;
&lt;li&gt;Set your preferred loglevel (notice by default). It is under the section ## GENERAL ##&lt;/li&gt;
&lt;li&gt;Set the logfile to /var/log/redis_6379.log ("" by default). It is under the section ## GENERAL ##&lt;/li&gt;
&lt;li&gt;Set the dir to /var/redis/6379 (very important step!). It is under the section ## SNAPSHOTTING ##&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally add the new Redis init script to all the default runlevels using the following command&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;update-rc.d redis_6379 defaults
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You are done! Now you can try running your instance with:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; /etc/init.d/redis_6379 start
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test if it working correctly&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try pinging your instance with redis-cli using the command &lt;code&gt;redis-cli ping&lt;/code&gt;, you should see PONG&lt;/li&gt;
&lt;li&gt;Do a test save with &lt;code&gt;redis-cli save&lt;/code&gt; and check that the dump file is correctly stored into /var/redis/6379/ (you should find a file called dump.rdb).&lt;/li&gt;
&lt;li&gt;Check that your Redis instance is correctly logging in the log file with &lt;code&gt;tail -f /var/log/redis_6379.log&lt;/code&gt;, you can exit with &lt;code&gt;CTRL + c&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If you are on a new machine/server where you can try things without taking anything else down in the machine/server make sure that after a reboot everything is still working (redis should start automatically on machine restart)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;If you are on Windows, you can follow instructions at &lt;a href="https://redis.io/docs/getting-started/installation/install-redis-on-windows/" rel="noopener noreferrer"&gt;Install Redis on Windows (Official Documentation)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Enable Web UI to Monitor Jobs
&lt;/h2&gt;

&lt;p&gt;Add the following to your "config/routes.rb":&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;# config/routes.rb&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sidekiq/web'&lt;/span&gt;

&lt;span class="no"&gt;Myapp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&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;# mount Sidekiq::Web in your Rails app&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Web&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/sidekiq"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Accessing the Sidekiq Web UI
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run rails server: &lt;code&gt;rails s&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to "&lt;a href="http://localhost:3000/sidekiq" rel="noopener noreferrer"&gt;http://localhost:3000/sidekiq&lt;/a&gt;"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now see the UI similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1njk3u1ipvgvajcwmaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1njk3u1ipvgvajcwmaq.png" alt="Home Page of Sidekiq UI in Rails App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Add Basic Authentication to Web UI for Preventing Unauthorized Access
&lt;/h2&gt;

&lt;p&gt;By default there is no authentication and anyone that knows your API URL will be able to access the sidekiq UI.&lt;/p&gt;

&lt;p&gt;You can allow any authenticated &lt;code&gt;User&lt;/code&gt; to access the Sidekiq UI by adding following configurations to your "config/routes.rb":&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;# config/routes.rb&lt;/span&gt;

&lt;span class="n"&gt;authenticate&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Web&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/sidekiq'&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;NOTE:&lt;/em&gt; This configuration is for the &lt;a href="https://github.com/heartcombo/devise" rel="noopener noreferrer"&gt;Devise gem&lt;/a&gt;, if you are using any other authentication gem then this configuration might be different.&lt;/p&gt;

&lt;p&gt;You can view more options for authenticating users to restrict Sidekiq UI at &lt;a href="https://github.com/mperham/sidekiq/wiki/Monitoring#authentication" rel="noopener noreferrer"&gt;Authentication (Sidekiq Gem Github)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Job
&lt;/h2&gt;

&lt;p&gt;Now let's look at how a job can look like in the Rails App after setting up Sidekiq.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Job
&lt;/h3&gt;

&lt;p&gt;Your "app/jobs/application.job" should look similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="n"&gt;queue_as&lt;/span&gt; &lt;span class="ss"&gt;:default&lt;/span&gt;
  &lt;span class="c1"&gt;# Automatically retry jobs that encountered a deadlock&lt;/span&gt;
  &lt;span class="c1"&gt;# retry_on ActiveRecord::Deadlocked&lt;/span&gt;

  &lt;span class="c1"&gt;# Most jobs are safe to ignore if the underlying records are no longer available&lt;/span&gt;
  &lt;span class="c1"&gt;# discard_on ActiveJob::DeserializationError&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only change from the default ApplicationJob that Rails generates by default is that we have added &lt;code&gt;queue_as :default&lt;/code&gt; so we don't have to add it in all other jobs we add in our app.&lt;/p&gt;

&lt;p&gt;If you want your job to be queued with the highest priority then in the new job you generate, you should update &lt;code&gt;queue_as&lt;/code&gt; to high e.g. &lt;code&gt;queue_as :high&lt;/code&gt; to override the &lt;code&gt;queue_as :default&lt;/code&gt; inside the Application Job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expire User Note Job
&lt;/h3&gt;

&lt;p&gt;Let's say you have a job that automatically removes the status of the user after certain time e.g. in the communication app called &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; we can add status and set expiration date and time. Your job could like the following for such feature:&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;ExpireUserStatusJob&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationJob&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_status_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UserStatus&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;user_status_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expired?&lt;/span&gt;

    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expiry_date: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expiry_time: &lt;/span&gt;&lt;span class="kp"&gt;nil&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;And you could queue the job from UserStatus model by triggering the method "schedule_expiration" from the controller whenever the user status is updated:&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;UserStatus&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;:expiry_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expiry_time&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:expiry_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expiry_date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;expiry_date&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;expiry_time&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;schedule_expiration&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;expiry_date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;expired?&lt;/span&gt;

    &lt;span class="n"&gt;expiry_date_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combine_date_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiry_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiry_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="no"&gt;ExpireUserStatusJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;wait_until: &lt;/span&gt;&lt;span class="n"&gt;expiry_date_time&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;perform_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expired?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;expiry_date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;expiry_date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;today&lt;/span&gt;

    &lt;span class="n"&gt;expiry_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;expiry_time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:time&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;combine_date_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&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="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sec&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;h2&gt;
  
  
  Bonus: Setup Sidekiq in Ubuntu Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a file called sidekiq.service &lt;code&gt;sudo nano /lib/systemd/system/sidekiq.service&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy content from &lt;a href="https://github.com/mperham/sidekiq/blob/main/examples/systemd/sidekiq.service" rel="noopener noreferrer"&gt;Example Sidekiq systemd configurations&lt;/a&gt; to this file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the WorkingDirectory to the folder where your app is deployed e.g. &lt;code&gt;WorkingDirectory=/home/deploy/my-app/current&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update the ExecStart path. This specifies the path and command to start Sidekiq. E.g. my current ExecStart path with rbenv is: &lt;code&gt;ExecStart=/home/deploy/.rbenv/shims/bundle exec sidekiq -e production&lt;/code&gt;. 
&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; If you are using different Ruby version manager then "/home/deploy/.rbenv/shims/bundle" will be different.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;ExecReload=/usr/bin/kill -TSTP $MAINPID&lt;/code&gt; below &lt;code&gt;ExecStart=....&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove comment from &lt;code&gt;# User=deploy&lt;/code&gt; and update deploy to be the user you are using in the server, mine was deploy so I changed the line to &lt;code&gt;User=deploy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uncomment &lt;code&gt;# Group=sudo&lt;/code&gt; to make it &lt;code&gt;Group=sudo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uncomment &lt;code&gt;# UMask=0002&lt;/code&gt; to make it &lt;code&gt;UMask=0002&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;WantedBy=multi-user.target&lt;/code&gt; to &lt;code&gt;WantedBy=default.target&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Save the file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Run Sidekiq
&lt;/h3&gt;

&lt;p&gt;You can enable the Sidekiq service with &lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can start the Sidekiq service with &lt;code&gt;sudo systemctl start sidekiq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here are some useful commands for changing status of Sidekiq:&lt;/p&gt;

&lt;p&gt;"sudo systemctl {start,stop,status,restart} sidekiq" e.g. &lt;code&gt;sudo systemctl status sidekiq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also use "sudo service sidekiq {start,stop,status,restart}" to perform commands e.g. &lt;code&gt;sudo service sidekiq status&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidekiq with Capistrano
&lt;/h3&gt;

&lt;p&gt;If you are using capistrano for deployment, you can refer to the section "Bonus 2: Sidekiq for background jobs" to configure Sidekiq with Capistrano at &lt;a href="https://prabinpoudel.com.np/articles/deploy-api-only-rails-app-with-capistrano/#bonus-2-sidekiq-for-background-jobs" rel="noopener noreferrer"&gt;Deploy API only Rails App with Capistrano&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulation!!! You have successfully setup and deployed sidekiq if you followed the blog all the way to the end.&lt;/p&gt;

&lt;p&gt;If you have any queries or confusions please let me know in the comments below and I will help you to clear those to the best of my ability.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Happy tinkering and happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@sunburned_surveyor?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Scott Blake&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/worker?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/active_job_basics.html#setting-the-backend" rel="noopener noreferrer"&gt;Active Job Basics [Rails Documentation]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-add-sidekiq-and-redis-to-a-ruby-on-rails-application" rel="noopener noreferrer"&gt;How To Add Sidekiq and Redis to a Ruby on Rails Application [Digital Ocean]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Deploy API only Rails App with Capistrano</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Thu, 08 Dec 2022 04:18:00 +0000</pubDate>
      <link>https://dev.to/truemark/deploy-api-only-rails-app-with-capistrano-2d0g</link>
      <guid>https://dev.to/truemark/deploy-api-only-rails-app-with-capistrano-2d0g</guid>
      <description>&lt;p&gt;Capistrano is a deployment automation tool built on Ruby, Rake, and SSH. It allows you to deploy your app to a remote server in a single command after initial configurations are done.&lt;/p&gt;

&lt;p&gt;Deployment with capistrano is as easy as &lt;code&gt;cap production deploy&lt;/code&gt;. But to be able to hit this command, a lot of configurations need to be added first.&lt;/p&gt;

&lt;p&gt;Today we will be looking into setting up capistrano in our API only Rails application for making it easier to deploy to any remote server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills required to follow the tutorial
&lt;/h2&gt;

&lt;p&gt;Intermediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails&lt;/li&gt;
&lt;li&gt;Linux skills to work with commands in server where your app has been deployed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You should have
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Existing Rails app&lt;/li&gt;
&lt;li&gt;Linux server already setup to run Rails app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;&lt;/em&gt;:&lt;br&gt;
I am using rbenv for ruby so all configurations will be based on that, you can replace it as required for your version manager as required e.g. for rvm.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Install Capistrano Gems
&lt;/h2&gt;

&lt;p&gt;Add the following to your Gemfile under development group&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="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"capistrano"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"capistrano-rails"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capistrano-rbenv'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following from command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should install the latest version of capistrano gems, now let's lock those versions by looking at Gemfile.lock, these versions were what I had in my lock file, yours could be different.&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="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"capistrano"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.17"&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="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"capistrano-rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.6"&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="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'capistrano-rbenv'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 2.2'&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;h2&gt;
  
  
  Step 2: Generate default configuration files
&lt;/h2&gt;

&lt;p&gt;Run the generator to create a basic set of configuration files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec cap install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Require correct plugins in Capfile
&lt;/h2&gt;

&lt;p&gt;Uncomment the following plugins in Capfile located at the root of the project&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;# require "capistrano/rvm"&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/rbenv'&lt;/span&gt;
  &lt;span class="c1"&gt;# require "capistrano/chruby"&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/bundler'&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"capistrano/rails/assets"&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capistrano/rails/migrations'&lt;/span&gt;
  &lt;span class="c1"&gt;# require 'capistrano/passenger'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Update Deploy file
&lt;/h2&gt;

&lt;p&gt;Update the deploy file at &lt;code&gt;config/deploy.rb&lt;/code&gt; with values as required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;application: Set to the application name or project name&lt;/li&gt;
&lt;li&gt;repo_url: Set to the URL where your code is being stored, you can normally get this with &lt;code&gt;git remote -v&lt;/code&gt; and copy the URL of origin &lt;/li&gt;
&lt;li&gt;linked_files: Set to a list of files that should be shared and persisted over all releases, for e.g. config/database.yml, config/master.key, config/application.yml, etc. These files will not be deleted/reset in every release (other files will!!)&lt;/li&gt;
&lt;li&gt;linked_dirs: Set to a list of folders that should be shared and persisted over all releases, for e.g. log, tmp/pids, node_modules, etc. These folders will not be deleted/reset in every release (other folders will!!)&lt;/li&gt;
&lt;li&gt;keep_releases: Set to a number of previous releases you want to keep in the server after each release, I normally keep this to 3.&lt;/li&gt;
&lt;li&gt;conditionally_migrate: Skip migration if files in "db/migrate" were not modified. I normally set this to "true" but default is "false"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your deploy file could look something like below:&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;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="c1"&gt;# config valid for current version and patch releases of Capistrano&lt;/span&gt;
&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 3.17.0'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'contract-template-editor-api'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:repo_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'git@github.com:truemark/contract-template-editor/api.git'&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for :linked_files is []&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:linked_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w[config/application.yml config/database.yml config/master.key]&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for linked_dirs is []&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:linked_dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w[log tmp/pids tmp/cache tmp/sockets vendor/bundle .bundle public/system public/uploads node_modules]&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for keep_releases is 5&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:keep_releases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="c1"&gt;# Skip migration if files in db/migrate were not modified&lt;/span&gt;
&lt;span class="c1"&gt;# Defaults to false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:conditionally_migrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Update environment specific deploy files
&lt;/h2&gt;

&lt;p&gt;I have normally worked on projects that has staging and production environment and configuration files for these two environment is provided default by Capistrano; each environment specific file is located under &lt;code&gt;config/deploy/{environment}.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In these files, there should be configurations that can change based on the environment of the Rails application.&lt;/p&gt;

&lt;p&gt;Configurations for each environment will be the same with different value based on the environment e.g. server ip address will be different in production and staging. &lt;/p&gt;

&lt;p&gt;We will only be looking at &lt;code&gt;config/deploy/production.rb&lt;/code&gt; in this tutorial.&lt;/p&gt;

&lt;p&gt;Update the deploy file at &lt;code&gt;config/deploy/production.rb&lt;/code&gt; with values as required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;server: IP address of the server where you will deploy the app. It is a good idea to sore this value inside env file or rails credentials and take it from there.&lt;/li&gt;
&lt;li&gt;user: name of the user in the server, normally it will be "deploy" but could be username e.g. prabin&lt;/li&gt;
&lt;li&gt;roles: list of accessible roles for this user&lt;/li&gt;
&lt;li&gt;deploy_to: path of the folder to deploy your app to, you can get the path by logging in to the server, going to the folder you want your app to be deployed to and enter the command &lt;code&gt;pwd&lt;/code&gt;. If you haven't created the folder yet, you can create one e.g. contract-template-api and set the path of that folder.&lt;/li&gt;
&lt;li&gt;branch: git branch that will be used to deploy the app, normally "main" or "master"&lt;/li&gt;
&lt;li&gt;stage: environment of the app, should be "production"&lt;/li&gt;
&lt;li&gt;rails_env: same as stage, should be "production"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your file could look something like below:&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;server&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'deploy_server_ip'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="s1"&gt;'deploy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;roles: &lt;/span&gt;&lt;span class="sx"&gt;%w[app db web]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary: &lt;/span&gt;&lt;span class="s1"&gt;'true'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:deploy_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'path to the folder where app should be deployed e.g. /home/deploy/contract-template-editor/api'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'main'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:production&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Upload secret keys and files
&lt;/h2&gt;

&lt;p&gt;Secret keys and files should never be committed to git. These files are required for app to function properly. Normally these files are always configured in the developer's machine so they can be uploaded directly from the project to the server using ssh.&lt;/p&gt;

&lt;p&gt;Create a new rake task at &lt;code&gt;lib/capistrano/tasks/config_files.rake&lt;/code&gt; and add the following content:&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;namespace&lt;/span&gt; &lt;span class="ss"&gt;:config_files&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Upload yml files inside config folder'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:upload&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;"mkdir -p &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;

      &lt;span class="n"&gt;upload!&lt;/span&gt; &lt;span class="no"&gt;StringIO&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;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config/database.yml'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/database.yml"&lt;/span&gt;
      &lt;span class="n"&gt;upload!&lt;/span&gt; &lt;span class="no"&gt;StringIO&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;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config/application.yml'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/application.yml"&lt;/span&gt;
      &lt;span class="n"&gt;upload!&lt;/span&gt; &lt;span class="no"&gt;StringIO&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;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config/master.key'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;shared_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config/master.key"&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;We now need to tell capistrano to run this code during deployment, add the following to the end of &lt;code&gt;config/deploy.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="c1"&gt;# ================================================&lt;/span&gt;
&lt;span class="c1"&gt;# ============ From Custom Rake Tasks ============&lt;/span&gt;
&lt;span class="c1"&gt;# ================================================&lt;/span&gt;
&lt;span class="c1"&gt;# ===== See Inside: lib/capistrano/tasks =========&lt;/span&gt;
&lt;span class="c1"&gt;# ================================================&lt;/span&gt;

&lt;span class="c1"&gt;# upload configuration files&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="s1"&gt;'deploy:starting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config_files:upload'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Create a database if deploying for the first time
&lt;/h2&gt;

&lt;p&gt;Create a new rake task at &lt;code&gt;lib/capistrano/tasks/database.rake&lt;/code&gt; and add the following content:&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;namespace&lt;/span&gt; &lt;span class="ss"&gt;:database&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Create the database'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;release_path&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="ss"&gt;rails_env: &lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:rails_env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:rake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'db:create'&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to tell capistrano to run this code during deployment, add the following to the end of the deploy 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;# set this to false after deploying for the first time &lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# run only if app is being deployed for the very first time, should update "set :initial, true" above to run this&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="s1"&gt;'deploy:migrate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'database:create'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Reload the Rails application after successful deploy
&lt;/h2&gt;

&lt;p&gt;Create a new rake task at &lt;code&gt;lib/capistrano/tasks/application.rake&lt;/code&gt; and add the following content:&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;namespace&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Reload application'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:reload&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Reload app after deployment'&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;in: :sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wait: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:touch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;release_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tmp/restart.txt'&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;We now need to tell capistrano to run this code during deployment, add the following to the end of the deploy 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;# reload application after successful deploy&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:publishing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'application:reload'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final file
&lt;/h2&gt;

&lt;p&gt;Your final "config/deploy.rb" file should look similar to 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="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="c1"&gt;# config valid for current version and patch releases of Capistrano&lt;/span&gt;
&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 3.17.0'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{project name}'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:repo_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{remote git repository where project for the code is stored}'&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for :linked_files is []&lt;/span&gt;
&lt;span class="n"&gt;append&lt;/span&gt; &lt;span class="ss"&gt;:linked_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w[config/application.yml config/database.yml config/master.key]&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for linked_dirs is []&lt;/span&gt;
&lt;span class="n"&gt;append&lt;/span&gt; &lt;span class="ss"&gt;:linked_dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sx"&gt;%w[log tmp/pids tmp/cache tmp/sockets vendor/bundle .bundle public/system public/uploads node_modules]&lt;/span&gt;

&lt;span class="c1"&gt;# Default value for keep_releases is 5&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:keep_releases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="c1"&gt;# Skip migration if files in db/migrate were not modified&lt;/span&gt;
&lt;span class="c1"&gt;# Defaults to false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:conditionally_migrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# ================================================&lt;/span&gt;
&lt;span class="c1"&gt;# ============ From Custom Rake Tasks ============&lt;/span&gt;
&lt;span class="c1"&gt;# ================================================&lt;/span&gt;
&lt;span class="c1"&gt;# ===== See Inside: lib/capistrano/tasks =========&lt;/span&gt;
&lt;span class="c1"&gt;# ================================================&lt;/span&gt;

&lt;span class="c1"&gt;# upload configuration files&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="s1"&gt;'deploy:starting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'config_files:upload'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# run only if app is being deployed for the very first time, should update "set :initial, true" above to run this&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="s1"&gt;'deploy:migrate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'database:create'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# reload application after successful deploy&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:publishing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'application:reload'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy the app
&lt;/h2&gt;

&lt;p&gt;From the command line you can now deploy the app to production using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cap production deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app should be deployed in around 5-10 minutes, if you encounter any error while deploying and need any help, please post a comment below and I will do my best to help you resolve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus 1: Whenever for cron jobs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/javan/whenever" rel="noopener"&gt;whenever gem&lt;/a&gt; is used in Rails applications to schedule cron jobs e.g. send email notification about monthly expenditure on the 1st of each month.&lt;/p&gt;

&lt;p&gt;Add the following to the deploy file just below "set :conditionally_migrate, true":&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;# Skip migration if files in db/migrate were not modified&lt;/span&gt;
&lt;span class="c1"&gt;# Defaults to false&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:conditionally_migrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Set unique identifier for background jobs&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:whenever_identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:application&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="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:stage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;whenever_identifier should be set to unique identifier for cron jobs, this is required if you have deployed multiple applications to same server (normally for staging servers) that require whenever gem so that those applications don't clash with one another for cron jobs. This is optional if you only have one application in the  server.&lt;/p&gt;

&lt;p&gt;Create a new rake task at &lt;code&gt;lib/capistrano/tasks/whenever.rake&lt;/code&gt; and add the following content which will be responsible for updating cron tasks configured inside the "config/schedule.rb":&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;namespace&lt;/span&gt; &lt;span class="ss"&gt;:whenever&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Update cron job'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:update_crontab&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;current_path&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:bundle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:exec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"whenever --update-crontab &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="ss"&gt;:whenever_identifier&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --set 'environment=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:stage&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="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;We now need to tell capistrano to run this code during deployment, add the following to the deploy file just below "before 'deploy:migrate', 'database:create' if fetch(:initial)":&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;# run only if app is being deployed for the very first time, should update "set :initial, true" above to run this&lt;/span&gt;
&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="s1"&gt;'deploy:migrate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'database:create'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# update cron job from whenever schedule file at "config/schedule.rb"&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:finishing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'whenever:update_crontab'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus 2: Sidekiq for background jobs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mperham/sidekiq" rel="noopener"&gt;Sidekiq gem&lt;/a&gt; is used in Rails applications to schedule background jobs so as to perform them at a later point without having to stop the execution of other codes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capistrano Configurations
&lt;/h3&gt;

&lt;p&gt;Create a new rake task at &lt;code&gt;lib/capistrano/tasks/sidekiq.rake&lt;/code&gt; and add the following content which will be responsible for updating cron tasks configured inside the "config/schedule.rb":&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;namespace&lt;/span&gt; &lt;span class="ss"&gt;:sidekiq&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'Quieten sidekiq'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:quiet&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pgrep -f 'sidekiq' | xargs kill -TSTP"&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;desc&lt;/span&gt; &lt;span class="s1"&gt;'Restart Sidekiq'&lt;/span&gt;
  &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:sudo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:systemctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sidekiq&lt;/span&gt;
      &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:sudo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:systemctl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'daemon-reload'&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;We now need to tell capistrano to run this code during deployment, add the following to the deploy file just below "after 'deploy:publishing', 'application:reload'":&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;# reload application after successful deploy&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:publishing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'application:reload'&lt;/span&gt;

&lt;span class="c1"&gt;# sidekiq related commands&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:starting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sidekiq:quiet'&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:reverted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sidekiq:restart'&lt;/span&gt;
&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="s1"&gt;'deploy:published'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sidekiq:restart'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try to deploy the app to production and server will ask for password when running sidekiq commands. To fix that we need to add some more configurations in the remote server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server Configurations
&lt;/h3&gt;

&lt;p&gt;We are assuming that sidekiq is already configured in your remote server. If you have not configured it yet, you can refer to the section "Bonus: Setup Sidekiq in Ubuntu Server" at &lt;a href="https://prabinpoudel.com.np/articles/setup-active-job-with-sidekiq-in-rails/#bonus-setup-sidekiq-in-ubuntu-server"&gt;Setup Active Job with Sidekiq in Rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For capistrano to perform sudo actions without asking for the password, the user used by capistrano, normally "deploy" user should be in the sudo group and you should add commands that need to be executed in the server with sudo access but without using password to "/etc/sudoers" file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add deploy user to the sudo group&lt;/p&gt;

&lt;p&gt;You can add your deploy user to the sudo group with the following command&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# add "deploy" user to sudo group
$ sudo usermod -aG sudo deploy

# verify if the user has been added to the sudo group
# result should include "sudo" for the deploy user
$ groups deploy
deploy : deploy sudo
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add commands required for the sidekiq restart and daemon-reload to be performed without password&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the sudoers file for the edit with &lt;code&gt;sudo EDITOR=nano visudo&lt;/code&gt;, this will ensure that the content inside the file is validated before saving so you don't end up with invalid file. If you do &lt;code&gt;sudo nano /etc/sudoers&lt;/code&gt; then it doesn't validate the content so you should never do that.&lt;/li&gt;
&lt;li&gt;Add the following below the line &lt;code&gt;root    ALL=(ALL:ALL) ALL&lt;/code&gt; under "# User privilege specification"
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  deploy ALL=NOPASSWD: /bin/systemctl restart sidekiq
  deploy ALL=NOPASSWD: /bin/systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can try running above two commands now in the command line of the server and it should run normally without asking for password:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart sidekiq
# doesn't ask for the password and executes the command
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now if you try to deploy again, capistrano won't stop to ask password when running sidekiq commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have come to the finish line, now you should be able to deploy your API only Rails application to the server with one command.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Happy tinkering and happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@nasa?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;NASA&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/rocket?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>[Solved] Error while Installing mysql2 Gem in M1 Mac</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Mon, 19 Sep 2022 15:21:53 +0000</pubDate>
      <link>https://dev.to/truemark/solved-error-while-installing-mysql2-gem-in-m1-mac-1mab</link>
      <guid>https://dev.to/truemark/solved-error-while-installing-mysql2-gem-in-m1-mac-1mab</guid>
      <description>&lt;p&gt;In Ruby on Rails applications with mysql2 gem, mysql2 gem always threw error when trying in the new M1 Mac. The error always said "ld: library not found for -lzstd" and "make failed".&lt;/p&gt;

&lt;p&gt;Let's resolve this issue today!&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You are using rbenv for ruby&lt;/li&gt;
&lt;li&gt;You are using homebrew&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Error Message
&lt;/h2&gt;

&lt;p&gt;Whenever I did bundle install inside the project, I got the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;linking shared-object mysql2/mysql2.bundle
ld: library not found for -lzstd
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;To resolve the issue, you will have to provide the location for mysql installation in homebrew and install the mysql2 gem separately.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Find out the version of mysql installed in your machine&lt;/p&gt;

&lt;p&gt;Run the following command in the command line:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mysql --version
mysql  Ver 8.0.27 for macos12.5 on arm64 (Homebrew)
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Note the mysql version; here 8.0.27 and move to next step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install mysql2 gem separately&lt;/p&gt;

&lt;p&gt;To resolve the make error we will have to install mysql2 gem separately. To do that run the following command by changing the mysql version you got in previous step.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rbenv exec gem install mysql2 -- \
--with-mysql-lib=/opt/homebrew/Cellar/mysql/8.0.27/lib \
--with-mysql-dir=/opt/homebrew/Cellar/mysql/8.0.27 \
--with-mysql-config=/opt/homebrew/Cellar/mysql/8.0.27/bin/mysql_config \
--with-mysql-include=/opt/homebrew/Cellar/mysql/8.0.27/include 
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run "bundle install" from your project root&lt;/p&gt;

&lt;p&gt;Move to the project root and run &lt;code&gt;bundle install&lt;/code&gt; in the command line.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Still getting the same error?
&lt;/h2&gt;

&lt;p&gt;If you are still getting error, you need to also hit the command given below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # check zstd version
  $ ls /opt/homebrew/Cellar/zstd
  1.5.0

  $ bundle config --local build.mysql2 \
    "--with-ldflags=-L/opt/homebrew/Cellar/zstd/1.5.0/lib"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;bundle install&lt;/code&gt; again from inside the project root and the error should now go away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downside
&lt;/h2&gt;

&lt;p&gt;The downside of this solution is we have to run this command every time the mysql version is updated using homebrew.&lt;/p&gt;

&lt;p&gt;So for example if your mysql version changes from 8.0.27 to 8.0.30 then you will again get this error. And in that case you will have to install the mysql2 gem separately again by using the steps above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you have any other solutions for this problem, do let us know in the comment section below.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[Stack Overflow] &lt;a href="https://stackoverflow.com/a/70053102/9359123" rel="noopener"&gt; An error occurred while installing mysql2 (0.4.8), and Bundler cannot continue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[Github] &lt;a href="https://github.com/brianmario/mysql2/issues/1175#issuecomment-846496862" rel="noopener"&gt;bundle install fails with Gem::Ext::BuildError&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@rubaitulazad?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Rubaitul Azad&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/mysql?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Setup Action Mailbox with SendGrid</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Sun, 22 May 2022 15:55:32 +0000</pubDate>
      <link>https://dev.to/truemark/setup-action-mailbox-with-sendgrid-490k</link>
      <guid>https://dev.to/truemark/setup-action-mailbox-with-sendgrid-490k</guid>
      <description>&lt;p&gt;Rails 6 released with many awesome features and action mailbox was one of them that has come to make the life easier. &lt;a href="https://guides.rubyonrails.org/action_mailbox_basics.html#introduction" rel="noopener noreferrer"&gt;From Official Action Mailbox Guide:&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Action Mailbox routes incoming emails to controller-like mailboxes for processing in Rails. It ships with ingresses for Mailgun, Mandrill, Postmark, and SendGrid. You can also handle inbound mails directly via the built-in Exim, Postfix, and Qmail ingresses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, Action Mailbox can be used to forward all incoming emails to your Rails app and process it further as required like storing attachments, creating records from the email body in you database and many more.&lt;/p&gt;

&lt;p&gt;And today, we will be implementing Action Mailbox with &lt;a href="https://sendgrid.com/" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Setup Action Mailbox with SendGrid using the official Rails documentation&lt;/li&gt;
&lt;li&gt;Update DNS records to forward emails received in the mailbox towards our Rails app&lt;/li&gt;
&lt;li&gt;Test integration in development with built in UI provided by Rails&lt;/li&gt;
&lt;li&gt;Test integration in development with NGROK to ensure seamless production release&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tested and working in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ruby 3.0.0&lt;/li&gt;
&lt;li&gt;Rails 7.0.2.4&lt;/li&gt;
&lt;li&gt;Action Mailbox 7.0.2.4&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You should have
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Existing app built with Rails 7 or higher&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start integrating Action Mailbox with SendGrid in our Rails app now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setup action mailbox
&lt;/h2&gt;

&lt;p&gt;We will be following instructions from the &lt;a href="https://guides.rubyonrails.org/action_mailbox_basics.html#sendgrid" rel="noopener noreferrer"&gt;Official Rails Guide for Action Mailbox&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install migrations needed for InboundEmail and ensure Active Storage is set up:
&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="nv"&gt;$ &lt;/span&gt;rails action_mailbox:install
&lt;span class="nv"&gt;$ &lt;/span&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add Ingress Configurations
&lt;/h2&gt;

&lt;p&gt;Tell Action Mailbox to accept emails from SendGrid by adding the following to both "development.rb" and "production.rb"&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;# config/environments/development.rb &amp;amp; config/environments/production.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ingress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:sendgrid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Generate Password for authenticating requests
&lt;/h2&gt;

&lt;p&gt;First of all, we should generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress.&lt;/p&gt;

&lt;p&gt;You can add any strong password or let Rails generate it for you. You can log into Rails console and generate a password for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; rails c
irb &amp;gt; SecureRandom.alphanumeric
# =&amp;gt; "Kk9YGvzdPN69bfiu"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that you can use &lt;code&gt;rails credentials:edit&lt;/code&gt; in the command line to add the password to your application's encrypted credentials under &lt;code&gt;action_mailbox.ingress_password&lt;/code&gt;, where Action Mailbox will automatically find it:&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;action_mailbox:
  ingress_password: &lt;/span&gt;&lt;span class="no"&gt;YOUR_STRONG_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using &lt;strong&gt;nano&lt;/strong&gt; editor you can edit credentials with 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;  &lt;span class="nv"&gt;$ EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"nano"&lt;/span&gt; rails credentials:edit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can also provide the password in the &lt;code&gt;RAILS_INBOUND_EMAIL_PASSWORD&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;If you are using &lt;code&gt;figaro&lt;/code&gt; gem you can add the following to your "config/application.yml":&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="c1"&gt;# config/application.yml&lt;/span&gt;

&lt;span class="na"&gt;RAILS_INBOUND_EMAIL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR_STRONG_PASSWORD'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Setup a Mailbox
&lt;/h2&gt;

&lt;p&gt;Now we should setup a mailbox that will process all incoming emails through our Rails app.&lt;/p&gt;

&lt;p&gt;You can generate a new mailbox with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;bin/rails generate mailbox forwards
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create &lt;code&gt;forwards_mailbox&lt;/code&gt; inside &lt;code&gt;app/mailboxes&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;# app/mailboxes/forwards_mailbox.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ForwardsMailbox&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationMailbox&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&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;h2&gt;
  
  
  Step 5: Whitelist email domains
&lt;/h2&gt;

&lt;p&gt;We can configure our &lt;code&gt;application_mailbox&lt;/code&gt; to accept all incoming emails to our Rails app and forward it to our &lt;code&gt;forwards_mailbox&lt;/code&gt; for further processing.&lt;/p&gt;

&lt;p&gt;Action Mailbox also accepts regex to whitelist domains or match certain emails. Let's look at how we can configure all these alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept all incoming emails
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# app/mailboxes/application_mailbox.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationMailbox&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionMailbox&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;routing&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:forwards&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Accept all emails from single domain
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# app/mailboxes/application_mailbox.rb&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationMailbox&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionMailbox&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;routing&lt;/span&gt; &lt;span class="sr"&gt;/.*@email-domain.com/i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:forwards&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Accept email from multiple domains
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/mailboxes/application_mailbox.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationMailbox&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionMailbox&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;routing&lt;/span&gt; &lt;span class="sr"&gt;/.*@primary-email-domain.com|.*@secondary-email-domain.com/i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:forwards&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 regex matching is telling application mailbox to forward all emails coming from &lt;code&gt;@email-domain.com&lt;/code&gt; to our &lt;code&gt;forwards_mailbox&lt;/code&gt;. For e.g. if we configure it to be &lt;code&gt;/.*@gmail.com/i&lt;/code&gt; and our Rails app receives email to &lt;code&gt;john-doe@gmail.com&lt;/code&gt; then it will be forwarded to our &lt;code&gt;forwards_mailbox&lt;/code&gt; where we can further process it since this email matches with the pattern &lt;code&gt;@gmail.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: Your mailbox name should match the name you've given it in the routing params i.e. &lt;code&gt;forwards&lt;/code&gt; will route to &lt;code&gt;forwards_mailbox&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Test in development
&lt;/h2&gt;

&lt;p&gt;Action Mailbox provides it's own set of UIs to test inbound emails in the development environment. To access this, let's fire up the Rails server first:&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://localhost:3000/rails/conductor/action_mailbox/inbound_emails" rel="noopener noreferrer"&gt;Action Mailbox Inbound Emails Localhost URL&lt;/a&gt; and click on &lt;code&gt;New inbound email by form&lt;/code&gt;. Fill in all required details like From, To, Subject and Body. You can leave other fields blank.&lt;/p&gt;

&lt;p&gt;Before clicking on &lt;code&gt;Deliver inbound email&lt;/code&gt;, let's add &lt;code&gt;byebug&lt;/code&gt; (or any other debugging breakpoint e.g. binding.pry) to our &lt;code&gt;process&lt;/code&gt; method so we know action mailbox is actually forwarding our emails to the right place.&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/mailboxes/forwards_mailbox.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ForwardsMailbox&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationMailbox&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;
    &lt;span class="n"&gt;byebug&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;You should make sure that email in &lt;strong&gt;From&lt;/strong&gt; input box matches the email domain configured. Now when you click &lt;code&gt;Deliver inbound email&lt;/code&gt;, the execution of the server process should stop at the &lt;code&gt;process&lt;/code&gt; method since we have a breakpoint there. This means action mailbox is correctly forwarding incoming emails and our configurations are correct. You can perform further process as required in your app now.&lt;/p&gt;

&lt;p&gt;But wait. Dang, there is an error from Rails while testing inbound emails in development!&lt;/p&gt;

&lt;p&gt;Let's dig into what is happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue with Inbound Action Mailbox Testing in Development
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntb5j628xjlfjy5cdkk7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntb5j628xjlfjy5cdkk7.jpeg" alt="Error while testing action mailbox in development due to empty attachment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Error reads: "undefined method 'original_filename' for "":String" and "NoMethodError in Rails::Conductor::ActionMailbox::InboundEmailsController#create"&lt;/p&gt;

&lt;p&gt;Looking at the code in Action Mailbox of core Rails, I found out that this error is occurring because controller is trying to process the empty attachment further. But finding out why Rails was submitting empty attachment when we haven't chosen any attachment was hard. Note that, this also happens even if you choose one or multiple attachments.&lt;/p&gt;

&lt;p&gt;In params, we get this &lt;code&gt;"attachments"=&amp;gt;[""]&lt;/code&gt; and controller is trying to process it further with the following code:&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="kp"&gt;private&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_mail&lt;/span&gt;
  &lt;span class="no"&gt;Mail&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;mail_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;except&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:attachments&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;mail&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:bcc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_in_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;mail_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:attachments&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_a&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;attachment&lt;/span&gt;&lt;span class="o"&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;add_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;filename: &lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;original_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&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;Here, we are getting error in the line &lt;code&gt;mail.add_file(filename: attachment.original_filename, content: attachment.read)&lt;/code&gt; because "attachment" is empty string i.e. "" and not an object which has properties like "original_filename". Hence the error.&lt;/p&gt;

&lt;p&gt;After looking into controller, my next stop for debugging the error was to look into the view because it shouldn't have sent the empty attachment in the first place.&lt;/p&gt;

&lt;p&gt;View was just using a normal file_upload tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;%= form.label :attachments, "Attachments" %&amp;gt;&amp;lt;br&amp;gt;
  &amp;lt;%= form.file_field :attachments, multiple: true %&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There couldn't be any issue here, so I looked into the rendered HTML in the webpage and found out that there was a hidden tag for attachment:&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;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"mail[attachments][]"&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;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;input&lt;/span&gt; &lt;span class="na"&gt;multiple=&lt;/span&gt;&lt;span class="s"&gt;"multiple"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"mail[attachments][]"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"mail_attachments"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hence, the form is submitting empty attachment to the controller.&lt;/p&gt;

&lt;p&gt;This problem could be solved in controller by filtering out attachments that are empty and I was near to submitting a PR to Rails. But then I thought, if I am getting this issue, there are obviously other developers who have been into this since this is an issue in Rails core and not in the code I have written.&lt;/p&gt;

&lt;p&gt;Searching further, I found this issue titled &lt;a href="https://github.com/rails/rails/issues/43928" rel="noopener noreferrer"&gt;Action Mailbox Conductor throws NoMethodError when creating inbound email&lt;/a&gt; submitted to Rails Core Github.&lt;/p&gt;

&lt;p&gt;And if there is an issue, there must also be a PR. YES, there was one already titled &lt;a href="https://github.com/rails/rails/pull/44008" rel="noopener noreferrer"&gt;Cannot deliver new inbound email via form&lt;/a&gt; but it hadn't been merged yet. &lt;/p&gt;

&lt;p&gt;But for this tutorial and until PR is merged, we need this to work in our app. So, I was looking into how I can resolve it the best and searching for the solution that would work for all of us and not just me.&lt;/p&gt;

&lt;p&gt;Scrolling further into the issue, I found a monkey patching very suitable for our use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monkey Patching the issue
&lt;/h3&gt;

&lt;p&gt;Add the following to your &lt;code&gt;config/application.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="c1"&gt;# monkey patching to resolve the issue of action mailbox inbound email sending empty attachment&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_prepare&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Conductor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActionMailbox&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InboundEmailsController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&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;new_mail&lt;/span&gt;
      &lt;span class="no"&gt;Mail&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;mail_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;except&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:attachments&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tap&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;mail&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:bcc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_in_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;mail_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:attachments&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact_blank&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;attachment&lt;/span&gt;&lt;span class="o"&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;add_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;filename: &lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;original_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&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;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to restart the server and reload the page. After that you can submit the form again.&lt;/p&gt;

&lt;p&gt;Voilà!! It works 🥳. &lt;/p&gt;

&lt;p&gt;Now, your server should have stuck in the debugging breakpoint.&lt;/p&gt;

&lt;p&gt;That's it, we have now successfully setup action mailbox and tested in development.&lt;/p&gt;

&lt;p&gt;Now let's test using NGROK so we know that our configuration will work seamlessly (pretty much) in our production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Setup NGROK
&lt;/h2&gt;

&lt;p&gt;Let's setup NGROK in our local machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the application&lt;/p&gt;

&lt;p&gt;You can download the application from this &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;download link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are on MacOS, I highly suggest downloading NGROK using homebrew with the command &lt;code&gt;brew install ngrok/ngrok/ngrok&lt;/code&gt;. It's easier than manual download and also don't normally give off any issue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Serve your app using NGROK URL&lt;/p&gt;

&lt;p&gt;While keeping the rails server running as it is, open a new tab in your command line.&lt;/p&gt;

&lt;p&gt;You can then run the command &lt;code&gt;ngrok http 3000&lt;/code&gt;, which will give you an URL connecting your local Rails app running on port 3000 to the internet. You should look at the URL besides the "Forwarding" option, it will be something similar to &lt;code&gt;Forwarding https://e73a-27-34-12-7.in.ngrok.io -&amp;gt; http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When running the NGROK, you should see a screen similar to the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09zygap7kvicdnligmj9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09zygap7kvicdnligmj9.jpeg" alt="Screenshot of active NGROK session in command line"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Access the Rails app with NGROK URL&lt;/p&gt;

&lt;p&gt;Open the URL you got before from NGROK e.g. &lt;code&gt;https://e73a-27-34-12-7.in.ngrok.io&lt;/code&gt; in your browser and you should be able to see the Rails welcome screen or whatever your default page for the app is.&lt;/p&gt;

&lt;p&gt;But, but, there is an error again 😭&lt;/p&gt;

&lt;p&gt;Hah, don't worry. I have got you covered.&lt;/p&gt;

&lt;p&gt;You should be seeing the Error UI similar to what is in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzahc5yalb7rlwg5xmd9h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzahc5yalb7rlwg5xmd9h.jpeg" alt="Ngrok error page when trying to access rails app due to missing auth token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens because of missing "auth token" which you can get after signing up to NGROK for free.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sign up to NGROK&lt;/p&gt;

&lt;p&gt;You can sign up to NGROK using this &lt;a href="https://dashboard.ngrok.com/signup" rel="noopener noreferrer"&gt;signup link&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add NGROK auth-token to local configuration file&lt;/p&gt;

&lt;p&gt;After signing up, you are presented with a dashboard and you can copy the auth-token from setup-and-installation step number 2 called "Connect you account"&lt;/p&gt;

&lt;p&gt;Or you can follow this link to your &lt;a href="https://dashboard.ngrok.com/get-started/your-authtoken" rel="noopener noreferrer"&gt;auth token page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Copy the token given and run the following in your command line:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ngrok config add-authtoken &amp;lt;your-authtoken&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now restart your NGROK server and go to the new URL provided. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: URL changes each time you restart the NGROK server unless you use pro version and pay for the static URL.&lt;/p&gt;

&lt;p&gt;What, Error? Again!!! 🤕&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resolving blocked host in Rails app&lt;/p&gt;

&lt;p&gt;After accessing the NGROK URL, you should see an error page similar to the one below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1m39gu9f1xuz9t7l4awh.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1m39gu9f1xuz9t7l4awh.jpeg" alt="Blocked host error in Rails"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because Rails blocks https access in development and unauthorized URLs overall.&lt;/p&gt;

&lt;p&gt;Let's add the NGROK URL to "config/environments/development.rb"&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/environments/development.rb&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"add-your-ngrok-url"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Restart the rails server and reload the page in the browser. Now, you should be able to see the default page of your Rails app. This is what I see in mine since it's a new application just for this blog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw23wefyup96y3v1u336o.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw23wefyup96y3v1u336o.jpeg" alt="Welcome page in Rails"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Authorize all NGROK URLs in development&lt;/p&gt;

&lt;p&gt;Above, we only allowed current URL provided by NGROK and as I have already said this URL changes each time we restart the server.&lt;/p&gt;

&lt;p&gt;Changing it every time we restart the server is a hassle so we will add regex which will allow matching NGROK URLs to connect to our Rails app in development.&lt;/p&gt;

&lt;p&gt;Let's replace previous configuration with the following:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sr"&gt;/.+\.ngrok\.io/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now, if you restart the rails server you should still be able to access the default page in your app. Also, try with restarting the NGROK server and accessing the page again which you should still be able to without getting blocked host error.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 8: Authenticate domain in SendGrid
&lt;/h2&gt;

&lt;p&gt;You can follow the &lt;a href="https://www.twilio.com/blog/how-to-set-up-domain-authentication-for-twilio-sendgrid" rel="noopener noreferrer"&gt; official SendGrid tutorial&lt;/a&gt; to authenticate your domain in SendGrid.&lt;/p&gt;

&lt;p&gt;Part of the tutorial also goes through setting up MX records which we will go into detail here.&lt;/p&gt;

&lt;p&gt;You can authenticate your domain by following this link to the &lt;a href="https://app.sendgrid.com/settings/sender_auth/domain/create" rel="noopener noreferrer"&gt;Domain Authentication page&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You will receive list of CNAME and Values similar to what is listed below in the process where &lt;code&gt;prabinpoudel.com.np&lt;/code&gt; and &lt;code&gt;em1181&lt;/code&gt; will be different:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;em1181.prabinpoudel.com.np&lt;/li&gt;
&lt;li&gt;s1._domainkey.prabinpoudel.com.np&lt;/li&gt;
&lt;li&gt;s2._domainkey.prabinpoudel.com.np&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will come back to this page after next step again so don't close the page yet.&lt;/p&gt;

&lt;p&gt;Let's go to our DNS provider's dashboard and configure these records first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup DNS Records
&lt;/h3&gt;

&lt;p&gt;We need to add DNS records from SendGrid to our DNS provider so our email is actually being processed by SendGrid and routed to our Rails app with Inbound Parse Hook.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;CloudFlare&lt;/a&gt;, so I will be showing you process to setup MX record using the settings from CloudFlare as an example.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to DNS tab from the left menu&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4blxudrrgry3elrbg34.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4blxudrrgry3elrbg34.jpeg" alt="Side Menu with menu item "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on "Add Record" and choose MX from the dropdown then add the following values to each field&lt;/p&gt;

&lt;p&gt;a. Name: "@"&lt;br&gt;
b. Mail Server: "mx.sendgrid.net"&lt;br&gt;
c. TTL: "auto"&lt;br&gt;
d. Priority: "10"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cgd78jc9cbm4nctvdx5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cgd78jc9cbm4nctvdx5.jpeg" alt="Adding MX Record from SendGrid to Cloudflare"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also find the instruction for adding MX record in the tutorial to &lt;a href="https://docs.sendgrid.com/for-developers/parsing-email/setting-up-the-inbound-parse-webhook#set-up-an-mx-record" rel="noopener noreferrer"&gt;setup Inbound Parse Hook&lt;/a&gt; from SendGrid.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on "Add Record" again and add all three CNAME records we got previously while authenticating the domain one by one&lt;/p&gt;

&lt;p&gt;Copy values from authenticating the domain page add them to CloudFlare:&lt;/p&gt;

&lt;p&gt;a. Type: CNAME&lt;br&gt;
b. Name: value from CNAME&lt;br&gt;
c. Target: value from VALUE&lt;br&gt;
d. Proxy Status: Turn the toggle button off (it will be on by default)&lt;br&gt;
e. TTL: Auto&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6juj7jrkroj18a04fqo.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6juj7jrkroj18a04fqo.jpeg" alt="Adding CNAME Records from SendGrid to Cloudflare"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go back to domain authentication page and click on "I've added these records" and click on "Verify" button&lt;/p&gt;

&lt;p&gt;If everything was copied over correctly, you will see a page with the information "It worked! Your authenticated domain for prabinpoudel.com.np was verified."&lt;/p&gt;

&lt;p&gt;Else you will get errors and you will have to fix those before moving forward.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 9: Configure Inbound Parse in SendGrid
&lt;/h2&gt;

&lt;p&gt;We will be following the &lt;a href="https://docs.sendgrid.com/for-developers/parsing-email/setting-up-the-inbound-parse-webhook" rel="noopener noreferrer"&gt;official SendGrid doc&lt;/a&gt; for configuring inbound parse hook to forward inbound emails to &lt;code&gt;/rails/action_mailbox/sendgrid/inbound_emails&lt;/code&gt; with the username "actionmailbox" and the password we generated just before this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From your SendGrid Dashboard click Settings, and then click Inbound Parse. You are now on the Inbound Parse page. Or you can click on this &lt;a href="https://app.sendgrid.com/settings/parse" rel="noopener noreferrer"&gt;Inbound Parse Link&lt;/a&gt; to go there directly.&lt;/li&gt;
&lt;li&gt;Click "Add Host &amp;amp; URL"&lt;/li&gt;
&lt;li&gt;You can add/leave the subdomain part as required. I have left it blank because I don't have any subdomain just for receiving emails&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under "Domain", choose your domain name that you just verified&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the URL we will have to construct one and add it&lt;/p&gt;

&lt;p&gt;The format for the URL should be: &lt;code&gt;https://actionmailbox:&amp;lt;your_action_mailbox_ingress_password&amp;gt;@&amp;lt;rails_app_nginx_url&amp;gt;/rails/action_mailbox/sendgrid/inbound_emails&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For e.g. it will be &lt;code&gt;https://actionmailbox:my_strong_password@5829-2400-1a00-b050-3fb6-b0ce-5946-b9be.in.ngrok.io/rails/action_mailbox/sendgrid/inbound_emails&lt;/code&gt; for my Rails app.&lt;/p&gt;

&lt;p&gt;In production it can be a different URL so you should replace &lt;code&gt;rails_app_nginx_url&lt;/code&gt; with the URL from where your Rails application is accessible to the internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcl428ri2bonbwttjf5y.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcl428ri2bonbwttjf5y.jpeg" alt="Configure Inbound Parse Hook Page in SendGrid"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check "POST the raw, full MIME message" and click on Add&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we are ready to test our integration with live email using SendGrid and Ngrok.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Test if MX records are recognized by the internet
&lt;/h2&gt;

&lt;p&gt;Before we test our integration with live email, we need to make sure that MX records are recognized by the Internet.&lt;/p&gt;

&lt;p&gt;It may take some time for DNS records to be recognized throughout the world so email forwarding may yet not work for you. The maximum time period until this happens is 24 hours.&lt;/p&gt;

&lt;p&gt;You can test if DNS records for your domain is working correctly and recognized from the website &lt;a href="https://mxtoolbox.com/SuperTool.aspx" rel="noopener noreferrer"&gt;MX Toolbox&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add your domain name e.g. prabinpoudel.com.np&lt;/li&gt;
&lt;li&gt;Click on "MX Lookup"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see "DNS Record Published" status in the test result table&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxf5imz7idckmqrffg01.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxf5imz7idckmqrffg01.jpeg" alt="Test result for MX records in the website of MX Toolbox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 11: Test incoming email with SendGrid and NGROK
&lt;/h2&gt;

&lt;p&gt;Finally, we are now at the last step. We will now send email to our mail server and we should receive it in our local Rails app and server should stop in our debugging breakpoint.&lt;/p&gt;

&lt;p&gt;From your favorite email provider e.g. Gmail, send a test email to your domain e.g. for me I will test it via &lt;a href="mailto:sendgrid-test@prabinpoudel.com.np"&gt;sendgrid-test@prabinpoudel.com.np&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;It takes some time to process the email by SendGrid and receive in our Rails app, maximum ~1 minute.&lt;/p&gt;

&lt;p&gt;You can check if the email is being received by SendGrid or not from &lt;a href="https://app.sendgrid.com/statistics/parse_webhook" rel="noopener noreferrer"&gt;Parse Webhook Statistics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgsz3sugwigimiyx7ks8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgsz3sugwigimiyx7ks8.jpeg" alt="Statistics of incoming emails received by SendGrid from the internet and forwarded to Rails App URL as per the configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tada!! 🎉&lt;/p&gt;

&lt;p&gt;You should have received the email and rails server must have stopped in the debugging breakpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations!!! You have come a long way and gone through a lot of process to integrate Action Mailbox with SendGrid.&lt;/p&gt;

&lt;p&gt;You can find a working app for this blog at &lt;a href="https://github.com/coolprobn/action-mailbox-with-sendgrid-test" rel="noopener noreferrer"&gt;Action Mailbox with SendGrid&lt;/a&gt;. You can view all changes I made for configuring SendGrid with Action Mailbox in the PR: &lt;a href="https://github.com/coolprobn/action-mailbox-with-sendgrid-test/pull/1" rel="noopener noreferrer"&gt;Setting up Action Mailbox with SendGrid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you can deploy the app to staging or production and add new Inbound Parse URL in SendGrid to point to the URL of those applications.&lt;/p&gt;

&lt;p&gt;If you have any confusions, suggestions or issues while implementing any steps in this email, please let me know in comment section below and I will do my best to help you.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Happy coding and tinkering!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/action_mailbox_basics.html" rel="noopener noreferrer"&gt;Action Mailbox (Official Documentation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rob__race/using-action-mailbox-in-rails-6-to-receive-mail-2nje"&gt;Using Action Mailbox in Rails 6 to Receive Mail&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/issues/43928" rel="noopener noreferrer"&gt;Action Mailbox Conductor throws NoMethodError when creating inbound email&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/rails/pull/44008" rel="noopener noreferrer"&gt;Cannot deliver new inbound email via form #44008&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sendgrid.com/for-developers/parsing-email/setting-up-the-inbound-parse-webhook" rel="noopener noreferrer"&gt;Setting Up The Inbound Parse Webhook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/how-to-set-up-domain-authentication-for-twilio-sendgrid" rel="noopener noreferrer"&gt;How to set up domain authentication for Twilio SendGrid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Image Credits:&lt;/strong&gt; Cover Image by &lt;a href="https://unsplash.com/@ecees?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;erica steeves&lt;/a&gt; from &lt;a href="https://unsplash.com/s/photos/mail?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Run ESLint on git commit with Husky and Lint-Staged in ReactJS</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Wed, 20 Apr 2022 01:50:41 +0000</pubDate>
      <link>https://dev.to/truemark/run-eslint-on-git-commit-with-husky-and-lint-staged-in-reactjs-4oeb</link>
      <guid>https://dev.to/truemark/run-eslint-on-git-commit-with-husky-and-lint-staged-in-reactjs-4oeb</guid>
      <description>&lt;p&gt;How do you make sure that ESLint rules configured in your project are followed by all your team members and code with issues are not pushed to remote Git repository?&lt;/p&gt;

&lt;p&gt;Answer to the question is; using &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;Husky package&lt;/a&gt; with git hooks. &lt;a href="https://www.atlassian.com/git/tutorials/git-hooks" rel="noopener noreferrer"&gt;Git hooks&lt;/a&gt; are one of the most popular way to trigger and enforce different side effects like ESLint rules. Husky depends on git hooks to trigger ESLint rules and make sure that all issues are resolved before you or anyone on your team can commit and push new changes to Git.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You have basic knowledge of ReactJS &lt;/li&gt;
&lt;li&gt;You have worked with ESLint previously and have required configuration file for ESLint in your project&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Git Hooks?
&lt;/h2&gt;

&lt;p&gt;Git hooks are a set of scripts that Git executes before or after events such as: commit, push, and receive. Git hooks are a built-in feature - you don't need to download anything for them to work.&lt;/p&gt;

&lt;p&gt;When you initialize git in your project with &lt;code&gt;git init&lt;/code&gt;, git hooks are also automatically added. You can find sample files for each event under the folder &lt;code&gt;your_project_path/.git/hooks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To view list of sample files for various types of hooks, you can hit the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ ls your_project_path/.git/hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;a href="https://truemark.com.np" rel="noopener noreferrer"&gt;Truemark&lt;/a&gt;, we normally use it to enforce coding standards and code quality by running ESLint before "git commit".&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Husky?
&lt;/h2&gt;

&lt;p&gt;One important thing to note for Git Hooks is, it is not version controlled, meaning whatever you add to hooks folder is only in your machine and not configured in the GIT. &lt;/p&gt;

&lt;p&gt;So, what happens when new member in your team clones the repository? &lt;br&gt;
Nothing, they will get sample files like I mentioned above.&lt;/p&gt;

&lt;p&gt;What?&lt;/p&gt;

&lt;p&gt;Then "How do we as a team make sure that hooks are executed for everyone?"&lt;/p&gt;

&lt;p&gt;The answer to that is &lt;strong&gt;husky&lt;/strong&gt; package.&lt;/p&gt;

&lt;p&gt;Husky package helps you and your team to manage and configure Git hooks in your projects. &lt;/p&gt;

&lt;p&gt;With "husky" installed in your project; after you clone the repo, you just have to hit the command &lt;code&gt;npm run prepare&lt;/code&gt; and all hooks are configured by husky in your machine.&lt;/p&gt;

&lt;p&gt;Husky makes git hooks much more manageable because you don't have to write scripts for hooks manually. You can just add the command you want to run e.g. run ESLint before commit inside the configuration file provided by Husky and everything else will be taken care by the package.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Husky
&lt;/h2&gt;

&lt;p&gt;Execute the following in the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  npm install husky -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the husky package to your package.json under "devDependencies":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"husky"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.0.4"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable Git Hooks in your Project with Husky
&lt;/h2&gt;

&lt;p&gt;You can enable git hooks in your project by running the command provided by husky package. In your project root path run following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  npm set-script prepare "husky install"
  npm run prepare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running above commands, you should see the following inside package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"husky install"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will also add required hooks in your project inside the folder &lt;code&gt;.git/hooks/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It will also add configuration files for Husky under the folder &lt;code&gt;.husky&lt;/code&gt; inside your project root. This file is used to control all git hooks configured in your project, and this is also where you will be adding configurations for running ESLint before commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable ESLint as Pre-Commit Hook with Husky
&lt;/h2&gt;

&lt;p&gt;Update scripts under package.json and add the script to run ESLint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a pre-commit hook to run eslint with husky by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  npx husky add .husky/pre-commit "npm run lint"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following code inside &lt;code&gt;.husky/pre-commit&lt;/code&gt; file now:&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;#!/bin/sh&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/husky.sh"&lt;/span&gt;

  npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run ESLint on git commit
&lt;/h2&gt;

&lt;p&gt;After you are done making changes to your code, try committing your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  git add .
  git commit -m "your commit message"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git hooks will run ESLint before commit and throw errors if any. If it didn't throw any error, add new code with issues manually and see the hook in action 🙈&lt;/p&gt;

&lt;p&gt;This is something similar to what you will see in case there are issues in your code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nv4pqv8isqoubilg4rt.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nv4pqv8isqoubilg4rt.jpeg" alt="Overcommit posts RuboCop errors for code with issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there are no errors then your code will be committed to git and you can push to the remote repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is lint-staged?
&lt;/h2&gt;

&lt;p&gt;With Husky, ESLint is run on each and every file inside the project and if you ask me if that is a good idea; I will tell you that it's a very bad idea.&lt;/p&gt;

&lt;p&gt;Why? Because running ESLint on code that was not changed as part of the feature can lead to various unforeseen bugs.&lt;/p&gt;

&lt;p&gt;For big projects it can take a lot of time to run eslint on each and every file inside the project. Also in old projects, it doesn't make sense to sit and fix all best practice issues instead of shipping new features.&lt;/p&gt;

&lt;p&gt;So, how do we run ESLint only on the code that we changed?&lt;/p&gt;

&lt;p&gt;The answer is &lt;a href="https://github.com/okonet/lint-staged" rel="noopener noreferrer"&gt;lint-staged&lt;/a&gt;. It is a package that helps in running pre-commit hooks only on files that have been changed in current commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install lint-staged
&lt;/h2&gt;

&lt;p&gt;Run the following command to install lint-staged in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  npm install lint-staged --save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following in your package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint-staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^12.3.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run ESLint on "git commit" with Husky and lint-staged
&lt;/h2&gt;

&lt;p&gt;You can configure lint-staged in separate file or inside package.json itself, since there is only one command I felt that it was not worth it to have a separate file for the configuration.&lt;/p&gt;

&lt;p&gt;You can view all supported options &lt;a href="https://github.com/okonet/lint-staged#configuration" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add following to package.json just below scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts eject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint-staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"*.{js,jsx}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint --fix"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added {js,jsx} so that staged files with only these extension are run through lint. You can update this to support other extensions like ts, tsx for typescript.&lt;/p&gt;

&lt;p&gt;Update pre-commit file to run lint-staged and remove other commands, your file should look like this:&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;#!/bin/sh&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/husky.sh"&lt;/span&gt;

  npx lint-staged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run lint-staged script which will show ESLint issues only on staged files.&lt;/p&gt;

&lt;p&gt;To test, you can now manually add new code with issues and see issues thrown only on changed files instead of in all files inside the project as what had happened previously before configuring lint-staged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With Husky package configured in your project, you will never have to worry about having to comment on issues in merge requests which could already have been detected by ESLint in local machine of developers. This way, you and your team can focus on having meaningful discussion in merge requests which leads to overall growth of the project and members in your team.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@wolfart32?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;WOLF Λ R T&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/husky?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://githooks.com/" rel="noopener noreferrer"&gt;Git Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;Husky - Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/okonet/lint-staged" rel="noopener noreferrer"&gt;Lint Staged - Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>[Solved] .rbenv/shims/ruby: Argument list too long</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Sun, 24 Oct 2021 04:09:47 +0000</pubDate>
      <link>https://dev.to/truemark/solved-rbenvshimsruby-argument-list-too-long-1i15</link>
      <guid>https://dev.to/truemark/solved-rbenvshimsruby-argument-list-too-long-1i15</guid>
      <description>&lt;p&gt;I couldn't access any ruby command in the linux server where we have hosted the Rails app for one of my client. I had to access rails console to update some database records manually but I couldn't and got stuck in this issue for over 2 days.&lt;/p&gt;

&lt;p&gt;I encountered two issues along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ruby version not found&lt;/li&gt;
&lt;li&gt;.rbenv/shims/ruby: Argument list too long&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solving the second issue solved the first issue too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Inside the root folder of the project , &lt;code&gt;ruby -v&lt;/code&gt; always returned that ruby 2.7.0 was not found and need to be installed. When I tried installing ruby 2.7.0 via rbenv, it said version already exists.&lt;/p&gt;

&lt;p&gt;I tried to execute &lt;code&gt;ruby -v&lt;/code&gt; outside of the project just to be sure that it was not a problem with ruby version. It took a really long time to process the command which returned with following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/home/deploy/.rbenv/libexec/rbenv-exec: line 47: /home/deploy/.rbenv/shims/ruby: Argument list too long
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google searches didn't yield any relevant results.&lt;/p&gt;

&lt;p&gt;In the process (after searching more for around two days), I stumbled upon the rbenv issue &lt;a href="https://github.com/rbenv/rbenv/issues/759" rel="noopener"&gt;rbenv: cannot rehash&lt;/a&gt;. I had never imagined these issues to be related at all. I executed the command to rehash the rbenv to see if it executes successfully instead of throwing the error mentioned in the Github issue.&lt;/p&gt;

&lt;p&gt;Boom! same issue; rbenv could not be rehashed.&lt;/p&gt;

&lt;p&gt;Solution as mentioned in the replies was to delete the file ".rbenv-shim", and rehash rbenv again. It worked!&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Execute the command &lt;code&gt;rbenv rehash&lt;/code&gt;. It will return the location of the file you have to delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rbenv: cannot rehash: /home/deploy/.rbenv/shims/.rbenv-shim exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, "/home/deploy/.rbenv/shims/.rbenv-shim" is the location of the file in my machine and it could be different in your local machine.&lt;/p&gt;

&lt;p&gt;Remove the file to resolve the issue:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rm /home/deploy/.rbenv/shims/.rbenv-shim&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now when you run the command &lt;code&gt;ruby -v&lt;/code&gt; it will return the version of ruby currently installed in your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason behind the issue
&lt;/h2&gt;

&lt;p&gt;It happens when previous rehash of the rbenv was killed prematurely.&lt;/p&gt;

&lt;p&gt;Quoting from &lt;a href="https://github.com/rbenv/rbenv/issues/759#issuecomment-124748535" rel="noopener"&gt;reply to the Github Issue&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;During the rehash process, rbenv writes out the temporary file .rbenv-shim to indicate that the rehash is in progress. Then, if a parallel rbenv rehash process tries to run at the same time, it will fail because the file already exists. This guards against race conditions in parallel rehashes.&lt;/p&gt;

&lt;p&gt;It seems like .rbenv-shim file was never cleaned up after a rehash that ran earlier. That earlier process might have been killed prematurely and never cleaned up after itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am not sure if solution to the issue &lt;code&gt;.rbenv/shims/ruby: Argument list too long&lt;/code&gt; is always to delete the ".rbenv-shim" file but hey, it's worth a try.&lt;/p&gt;

&lt;p&gt;If the solution to your problem was different than this, please let us know below in the replies and I will be sure to include it in this blog so that it helps others.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rbenv/rbenv/issues/759" rel="noopener"&gt;rbenv: cannot rehash [Github]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@art_maltsev?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Artem Maltsev&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/frustration?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Run RuboCop on git commit with Overcommit Gem</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Thu, 26 Aug 2021 12:09:08 +0000</pubDate>
      <link>https://dev.to/truemark/run-rubocop-on-git-commit-with-overcommit-gem-1mf3</link>
      <guid>https://dev.to/truemark/run-rubocop-on-git-commit-with-overcommit-gem-1mf3</guid>
      <description>&lt;p&gt;How do you make sure that RuboCop rules configured in your project are followed by all your team members and code with issues are not pushed to Git repository?&lt;/p&gt;

&lt;p&gt;Answer is using &lt;a href="https://github.com/sds/overcommit" rel="noopener noreferrer"&gt;Overcommit gem&lt;/a&gt; with git hooks. Git hooks are one of the most popular way to trigger and enforce different side effects like RuboCop rules. Overcommit gem depends on git hooks to trigger RuboCop rules and make sure that all issues are resolved before you or anyone on your team can commit and push new changes to Git.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You have basic knowledge of Rails &lt;/li&gt;
&lt;li&gt;You have worked with RuboCop previously and have required configuration files for RuboCop in your project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Shameless Plug&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;If you are new to RuboCop, you can read about it with setup instructions in my another article - &lt;a href="https://prabinpoudel.com.np/articles/beginners-guide-to-rubocop-in-rails/" rel="noopener noreferrer"&gt;Beginner's Guide to RuboCop in Rails&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In this blog
&lt;/h2&gt;

&lt;p&gt;You will be learning the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are git hooks?&lt;/li&gt;
&lt;li&gt;Why use git hooks?&lt;/li&gt;
&lt;li&gt;What is overcommit gem?&lt;/li&gt;
&lt;li&gt;Install overcommit gem&lt;/li&gt;
&lt;li&gt;Enable git hooks in your project with overcommit gem&lt;/li&gt;
&lt;li&gt;Enable RuboCop as pre-commit hook with overcommit gem&lt;/li&gt;
&lt;li&gt;Update git hooks with overcommit gem&lt;/li&gt;
&lt;li&gt;Run RuboCop on "git commit"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Git Hooks?
&lt;/h2&gt;

&lt;p&gt;Git hooks are a set of scripts that Git executes before or after events such as: commit, push, and receive. Git hooks are a built-in feature - you don't need to download anything for them to work.&lt;/p&gt;

&lt;p&gt;When you initialize git in your project with &lt;code&gt;git init&lt;/code&gt;, git hooks are also automatically added. You can find sample files for each event under the folder &lt;code&gt;your_project_path/.git/hooks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To view list of sample files for various types of hooks, you can hit the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ ls your_project_path/.git/hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why use Git Hooks?
&lt;/h2&gt;

&lt;p&gt;There are various use cases for git hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the commit message for spelling errors&lt;/li&gt;
&lt;li&gt;Enforce pattern for commit messages&lt;/li&gt;
&lt;li&gt;Enforce project coding standards like RuboCop&lt;/li&gt;
&lt;li&gt;Email/SMS team members of a new commit&lt;/li&gt;
&lt;li&gt;Push the code to production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At &lt;a href="https://truemark.com.np" rel="noopener noreferrer"&gt;Truemark&lt;/a&gt;, we normally use it to enforce coding standards and code quality by running RuboCop before "git commit".&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Overcommit Gem?
&lt;/h2&gt;

&lt;p&gt;One important thing to note for Git Hooks is it is not version controlled, meaning whatever you add to hooks folder is only in your machine. &lt;/p&gt;

&lt;p&gt;So, what happens when new member in your team clones the repository? &lt;br&gt;
Nothing, they will get sample files like I mentioned above.&lt;/p&gt;

&lt;p&gt;What?&lt;/p&gt;

&lt;p&gt;Then "How do we as a team make sure that hooks are executed for everyone?"&lt;/p&gt;

&lt;p&gt;The answer to that is &lt;strong&gt;overcommit&lt;/strong&gt; gem.&lt;/p&gt;

&lt;p&gt;Overcommit gem helps you and your team to manage and configure Git hooks in your projects. &lt;/p&gt;

&lt;p&gt;With overcommit gem installed in your project; after you clone the repo, you just have to hit the command &lt;code&gt;overcommit --install&lt;/code&gt; and all hooks are configured by overcommit in your machine.&lt;/p&gt;

&lt;p&gt;Overcommit gem makes git hooks much more manageable because you don't have to write scripts for hooks manually. You can just add the command you want to run e.g. run RuboCop before commit, inside the configuration file provided by overcommit gem and everything else will be taken care by the gem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Overcommit Gem
&lt;/h2&gt;

&lt;p&gt;Add the following to your 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="c1"&gt;# run rubocop before commit with overcommit and much more&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'overcommit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 0.58.0'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable Git Hooks in your Project with Overcommit Gem
&lt;/h2&gt;

&lt;p&gt;You can enable git hooks in your project by running the command provided by overcommit gem. From your project root path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  overcommit --install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add required hooks in your project inside the folder &lt;code&gt;.git/hooks/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will also add configuration file &lt;code&gt;.overcommit.yml&lt;/code&gt; inside your project root. This file is used to control all git hooks configured in your project, and this is also where you will be adding configurations for running rubocop before commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable RuboCop as Pre-Commit Hook with Overcommit
&lt;/h2&gt;

&lt;p&gt;You can remove everything inside the configuration file &lt;code&gt;.overcommit.yml&lt;/code&gt; and add the following inside:&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="c1"&gt;# Use this file to configure the Overcommit hooks you wish to use. This will&lt;/span&gt;
&lt;span class="c1"&gt;# extend the default configuration defined in:&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/sds/overcommit/blob/master/config/default.yml&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# At the topmost level of this YAML file is a key representing type of hook&lt;/span&gt;
&lt;span class="c1"&gt;# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can&lt;/span&gt;
&lt;span class="c1"&gt;# customize each hook, such as whether to only run it on certain files (via&lt;/span&gt;
&lt;span class="c1"&gt;# `include`), whether to only display output if it fails (via `quiet`), etc.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# For a complete list of hooks, see:&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# For a complete list of options that you can use to customize hooks, see:&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/sds/overcommit#configuration&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Uncomment the following lines to make the configuration take effect.&lt;/span&gt;

&lt;span class="na"&gt;PreCommit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;RuboCop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
   &lt;span class="na"&gt;on_warn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fail&lt;/span&gt; &lt;span class="c1"&gt;# Treat all warnings as failures&lt;/span&gt;
   &lt;span class="na"&gt;problem_on_unmodified_line&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ignore&lt;/span&gt; &lt;span class="c1"&gt;# run RuboCop only on modified code&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's with all these configuration options?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;"PreCommit"&lt;/p&gt;

&lt;p&gt;Git hooks will run "rubocop" to check issues in code when you try to commit your changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;on_warn: fail&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;All warnings will be treated as failures and you will have to resolve those warnings first before your code is committed to the remote git repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;problem_on_unmodified_line: ignore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Tell overcommit to run RuboCop only on code that were changed in this commit.&lt;/p&gt;

&lt;p&gt;This is specially useful when you are adding overcommit in old projects and you don't want to sit fixing all the issues inside the project. This lets you fix issues that you introduced or issues you want to refactor/fix while you can fix old issues in your own time (ohh, will that time ever come?).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Update Git Hooks with Overcommit
&lt;/h2&gt;

&lt;p&gt;Now that you have added configurations for running RuboCop on pre-commit, you will need to tell overcommit gem to persist these changes to "pre-commit" script inside git hooks folder.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add changes inside overcommit.yml to git&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  git add .
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update scripts inside git hooks folder with overcommit gem&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   overcommit --sign
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run RuboCop on git commit
&lt;/h2&gt;

&lt;p&gt;After you are done making changes to your code and done with the part of "git add", try committing your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  git commit -m "your commit message"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git hooks will run RuboCop before commit and throw errors if any. If it didn't throw any error, add new code with issues manually and see the hook in action 🙈&lt;/p&gt;

&lt;p&gt;This is something similar to what you will see in case there are issues in your code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh77y061xgnq0xjn1y71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh77y061xgnq0xjn1y71.png" alt="Overcommit posts RuboCop errors for code with issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there are no errors then your code will be committed to git and you can push to the remote repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn4lk814nzixe5hdzvtr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn4lk814nzixe5hdzvtr.png" alt="All checks passed when running hooks by Overcommit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With overcommit gem configured in your project, you will never have to worry about having to comment on issues in merge requests which could already have been detected by RuboCop in local machine of developers. This way, you and your team can focus on having meaningful discussion in the merge requests which leads to overall growth of the project and members in your team.&lt;/p&gt;

&lt;p&gt;If you use any other gem for managing git hooks, I would like to hear your opinions on what you think about the gem you are using in comparison to overcommit. Personally, I find overcommit gem much easier to use and with a lot of configuration options. (Oh boy! You haven't even tried other gems)&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@kellysikkema?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Kelly Sikkema&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/commitment?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://githooks.com/" rel="noopener noreferrer"&gt;Git Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sds/overcommit" rel="noopener noreferrer"&gt;Overcommit Gem - Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>git</category>
      <category>codereview</category>
    </item>
    <item>
      <title>Beginner's Guide to RuboCop in Rails</title>
      <dc:creator>Prabin Poudel</dc:creator>
      <pubDate>Wed, 18 Aug 2021 12:24:55 +0000</pubDate>
      <link>https://dev.to/truemark/beginner-s-guide-to-rubocop-in-rails-3mj6</link>
      <guid>https://dev.to/truemark/beginner-s-guide-to-rubocop-in-rails-3mj6</guid>
      <description>&lt;p&gt;RuboCop is a static code analyzer which analyzes the code based on the best practices followed by the Ruby developers around the world and defined on the community &lt;a href="https://rubystyle.guide" rel="noopener"&gt;Ruby style guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Apart from analyzing the code, it also provides us the feature of automatically formatting the code and fix warnings inside our code.&lt;/p&gt;

&lt;p&gt;If you are coming from Javascript background, you may have heard about ESLint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;RuboCop is ESLint for Ruby&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apart from Ruby, RuboCop also provides gems for implementing rules on various extensions like Rails, Minitest, RSpec, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why RuboCop?
&lt;/h2&gt;

&lt;p&gt;It begs the question and curiosity among us, so why do we actually need RuboCop? What's the use of having RuboCop in our projects.&lt;/p&gt;

&lt;p&gt;Here are some reasons on why we would want to use RuboCop in our projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clean code&lt;/p&gt;

&lt;p&gt;We all want to write clean code that adheres to best practices followed by developers around the world. Best practices comes from experience, it may take some years to know about the language and know the anti-patterns and good patterns to follow if we only rely on ourselves. &lt;/p&gt;

&lt;p&gt;With RuboCop, we have the advantage of not having the experience because best practices have been bundled as rules and shipped to us inside the "rubocop" gem. RuboCop throws warnings whenever we violate rules configured for best practices and after fixing these issues, our code is most of the time clean and easy to understand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Eases the code review process&lt;/p&gt;

&lt;p&gt;The main purpose of code review is to fix logics in the code, or fix security vulnerabilities or discuss the path we took to develop the feature.&lt;/p&gt;

&lt;p&gt;But hey, imagine a situation where we push a code with a typo and reviewer spots that, then comments on it for fixing because obviously no one wants to ship the code with typo to production!&lt;/p&gt;

&lt;p&gt;What's wrong with that? It takes significant time to review the code, and with typo or discussion about best practices in merge requests, we as a developer are wasting a lot of time which could have easily be solved with the help of RuboCop by configuring rules.&lt;/p&gt;

&lt;p&gt;RuboCop makes sure that code with issues never makes it to the merge/pull requests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Best practice is no one size fits all&lt;/p&gt;

&lt;p&gt;Normally best practices means what we like or dislike about the code or pattern we follow when we write the code and it differs for each one of us. If we focus our energy in discussing these practices in the code for every feature, when will we ship features?&lt;/p&gt;

&lt;p&gt;With RuboCop, we can discuss with the team on what best practices should the team follow and disable or enable rules based on the conclusion, hence making everyone happy (well, you can never make everyone happy!).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setup RuboCop in Rails
&lt;/h2&gt;

&lt;p&gt;In this article, we will be installing main 'rubocop' gem for implementing rules in Ruby code along with the extension 'rubocop-rails' for Rails specific code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Gems to Gemfile
&lt;/h3&gt;

&lt;p&gt;Add the following to Gemfile inside the group &lt;code&gt;:development, :test&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;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="c1"&gt;# enforce rails best practice with rubocop&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="s1"&gt;'~&amp;gt; 1.18.0'&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="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rubocop-performance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 1.11.0'&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="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rubocop-rails'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 2.11.0'&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;&lt;em&gt;NOTE&lt;/em&gt;: Update gem versions based on what is latest at the time you are installing these gems in our project&lt;/p&gt;

&lt;p&gt;We have added the following gems to our Gemfile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rubocop: For Ruby code&lt;/li&gt;
&lt;li&gt;rubocop-performance: For code performance related rules&lt;/li&gt;
&lt;li&gt;rubocop-rails: For Rails specific rules&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Install Gems in the Project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install rubocop globally&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem install rubocop&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will help us in running commands provided by 'rubocop' gem like auto formatting, running rubocop in the project, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install new gems with &lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Add Configuration Files
&lt;/h3&gt;

&lt;p&gt;To control (enable/disable) rules, we need to create configuration files for each extension. If there is no file then RuboCop will enable default extensions. I like to have configuration files because it provides flexibility to team.&lt;/p&gt;

&lt;p&gt;Let's create configuration files for RuboCop and it's extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ cd /path/to/our/project
  $ touch .rubocop.yml
  $ touch .rubocop-performance.yml
  $ touch .rubocop-rails.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Rules to Configuration files
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I also have a blog written specifically for configuration files of RuboCop, you can find it at &lt;a href="https://prabinpoudel.com.np/articles/rubocop-configuration-files-for-rails/" rel="noopener"&gt;RuboCop Configuration Files for Rails&lt;/a&gt; if you want more options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's update configuration files and add rules for Ruby and installed extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruby
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .rubocop.yml&lt;/span&gt;

&lt;span class="c1"&gt;# The behavior of RuboCop can be controlled via the .rubocop.yml&lt;/span&gt;
&lt;span class="c1"&gt;# configuration file. It makes it possible to enable/disable&lt;/span&gt;
&lt;span class="c1"&gt;# certain cops (checks) and to alter their behavior if they accept&lt;/span&gt;
&lt;span class="c1"&gt;# any parameters. The file can be placed either in your home&lt;/span&gt;
&lt;span class="c1"&gt;# directory or in some project directory.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# RuboCop will start looking for the configuration file in the directory&lt;/span&gt;
&lt;span class="c1"&gt;# where the inspected file is and continue its way up to the root directory.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="na"&gt;inherit_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.rubocop-performance.yml'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.rubocop-rails.yml'&lt;/span&gt;

&lt;span class="na"&gt;require&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rubocop-performance&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rubocop-rails&lt;/span&gt;

&lt;span class="na"&gt;AllCops&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;TargetRubyVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.7&lt;/span&gt;
  &lt;span class="na"&gt;TargetRailsVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6.0&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/db/migrate/*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;db/schema.rb'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/Gemfile.lock'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/Rakefile'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/rails'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/vendor/**/*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/spec_helper.rb'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node_modules/**/*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bin/*'&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;###################### RuboCop ############################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="c1"&gt;# You can find all configuration options for rubocop here: https://docs.rubocop.org/rubocop/cops_bundler.html&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;####################### Gemspec ###########################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Gemspec/DateAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.10)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;######################## Layout ###########################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Layout/ClassStructure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ExpectedOrder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;module_inclusion&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;constants&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;association&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public_attribute_macros&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public_delegate&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;macros&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;initializer&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public_class_methods&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public_methods&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;protected_attribute_macros&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;protected_methods&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;private_attribute_macros&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;private_delegate&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;private_methods&lt;/span&gt;

&lt;span class="s"&gt;Layout/EmptyLineAfterMultilineCondition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/EmptyLinesAroundAttributeAccessor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/FirstArrayElementIndentation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consistent&lt;/span&gt;

&lt;span class="s"&gt;Layout/FirstArrayElementLineBreak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/FirstHashElementIndentation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consistent&lt;/span&gt;

&lt;span class="s"&gt;Layout/FirstHashElementLineBreak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/LineEndStringConcatenationIndentation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.18)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&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;150&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/spec/**/*'&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineArrayBraceLayout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;new_line&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineOperationIndentation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;indented&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineHashBraceLayout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;new_line&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineHashKeyLineBreaks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineMethodCallBraceLayout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;new_line&lt;/span&gt;

&lt;span class="s"&gt;Layout/MultilineMethodDefinitionBraceLayout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;new_line&lt;/span&gt;

&lt;span class="s"&gt;Layout/SpaceAroundMethodCallOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/SpaceBeforeBrackets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Layout/SpaceInLambdaLiteral&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;require_space&lt;/span&gt;


&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;######################## Lint #############################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Lint/AmbiguousAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/AmbiguousBlockAssociation&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/spec/**/*'&lt;/span&gt;

&lt;span class="s"&gt;Lint/AssignmentInCondition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AllowSafeAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Lint/BinaryOperatorWithIdenticalOperands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DeprecatedConstants&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DeprecatedOpenSSLConstant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DuplicateBranch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.3)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DuplicateElsifCondition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DuplicateRegexpCharacterClassElement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DuplicateRequire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/DuplicateRescueException&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/EmptyBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/EmptyClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.3)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/EmptyConditionalBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/EmptyFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/EmptyInPattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.16)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/FloatComparison&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/LambdaWithoutLiteralBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/MissingSuper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/MixedRegexpCaptureTypes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/NoReturnInBeginEndBlocks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.2)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/NumberConversion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/NumberedParameterAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/OrAssignmentToConstant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/RaiseException&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/RedundantDirGlobSort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/SelfAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/SymbolConversion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/ToEnumArguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/TrailingCommaInAttributeDeclaration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/TripleQuotes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/UnexpectedBlockArity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.5)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/UnmodifiedReduceAccumulator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Lint/UnusedBlockArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IgnoreEmptyBlocks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Lint/UnusedMethodArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IgnoreEmptyMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Lint/UselessMethodDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;######################## Metric ###########################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Metrics/AbcSize&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;45&lt;/span&gt;

&lt;span class="s"&gt;Metrics/BlockLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CountComments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&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;50&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/spec/**/*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/*.rake'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/factories/**/*'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/config/routes.rb'&lt;/span&gt;

&lt;span class="s"&gt;Metrics/ClassLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CountAsOne&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;array'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hash'&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;150&lt;/span&gt;

&lt;span class="s"&gt;Metrics/CyclomaticComplexity&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;10&lt;/span&gt;

&lt;span class="s"&gt;Metrics/MethodLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CountAsOne&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;array'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hash'&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;30&lt;/span&gt;

&lt;span class="s"&gt;Metrics/ModuleLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CountAsOne&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;array'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hash'&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;250&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/spec/**/*'&lt;/span&gt;

&lt;span class="s"&gt;Metrics/PerceivedComplexity&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;10&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;######################## Naming ###########################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Naming/InclusiveLanguage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.18)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;######################## Style ############################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="s"&gt;Style/AccessorGrouping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/ArgumentsForwarding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/ArrayCoercion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/AutoResourceCleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/BisectedAttrAccessor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/CaseLikeIf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/ClassAndModuleChildren&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Style/CollectionCompact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.2)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/CollectionMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/CombinableLoops&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/CommandLiteral&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EnforcedStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;percent_x&lt;/span&gt;

&lt;span class="s"&gt;Style/ConstantVisibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/Documentation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Style/DocumentDynamicEvalDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/EndlessMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/ExplicitBlockArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/GlobalStdStream&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashConversion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.10)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashEachMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashExcept&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashLikeCase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashTransformKeys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/HashTransformValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/IfWithBooleanLiteralBranches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/ImplicitRuntimeError&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/InlineComment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/InPatternThen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.16)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/IpAddresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/KeywordParametersOrder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/MethodCallWithArgsParentheses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/MissingElse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/MultilineInPatternThen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.16)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/MultilineMethodSignature&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/NegatedIfElseCondition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.2)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/NilLambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.3)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/OptionalBooleanParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/QuotedSymbols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.16)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.4)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantBegin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantFetchBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantFileExtensionInRequire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/RedundantSelfAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/SingleArgumentDig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/StringChars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.12)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/StringConcatenation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Style/SwapValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.1)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rails
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .rubocop-rails.yml&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;#################### RuboCop Rails ########################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="c1"&gt;# You can find all configuration options for rubocop-rails here: https://docs.rubocop.org/rubocop-rails/cops_rails.html&lt;/span&gt;

&lt;span class="s"&gt;Rails/ActiveRecordCallbacksOrder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/AddColumnIndex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/AfterCommitOverride&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/AttributeDefaultBlockValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/DefaultScope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/EagerEvaluationLogMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/ExpandedDateRange&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/FindById&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/I18nLocaleAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/Inquiry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/MailerName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/MatchRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/NegateInclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/OrderById&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/Pluck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/PluckId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/PluckInWhere&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/RenderInline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/RenderPlainText&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/SaveBang&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;AllowImplicitReturn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

&lt;span class="s"&gt;Rails/ShortI18n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/SquishedSQLHeredocs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/TimeZoneAssignment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.10)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/UnusedIgnoredColumns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/WhereEquals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 2.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/WhereExists&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Rails/WhereNot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;.rubocop-performance.yml&lt;/span&gt;

&lt;span class="c1"&gt;###########################################################&lt;/span&gt;
&lt;span class="c1"&gt;#################### RuboCop Performance ##################&lt;/span&gt;
&lt;span class="c1"&gt;###########################################################&lt;/span&gt;

&lt;span class="c1"&gt;# You can find all configuration options for rubocop-performance here: https://docs.rubocop.org/rubocop-performance/&lt;/span&gt;

&lt;span class="s"&gt;Performance/AncestorsInclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/BigDecimalWithNumericArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/BlockGivenWithExplicitBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/CollectionLiteralInLoop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/ConstantRegexp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/MapCompact&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.11)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/MethodObjectAsBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.9)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/RedundantEqualityComparisonBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.10)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/RedundantSortBlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/RedundantSplitRegexpArgument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.10)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/RedundantStringChars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/ReverseFirst&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/SortReverse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/Squeeze&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/StringInclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.7)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="s"&gt;Performance/Sum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# (new in 1.8)&lt;/span&gt;
  &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run RuboCop
&lt;/h2&gt;

&lt;p&gt;We have the option to run RuboCop on &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whole project&lt;/li&gt;
&lt;li&gt;Files inside single folder&lt;/li&gt;
&lt;li&gt;Only on single file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After running commands of RuboCop in the command line, we will be presented with issues found in our code inside the project, which we can then fix manually or also have option to auto correct issues in most cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Whole Project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ cd /path/to/your/project
  $ rubocop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Files inside single folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ rubocop app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Single file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ rubocop app/models/user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto fix warnings
&lt;/h3&gt;

&lt;p&gt;RubCop also provides the feature of auto correcting issues in our code. &lt;/p&gt;

&lt;p&gt;There are a couple of things to keep in mind about auto-correct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For some offenses, it is not possible to implement automatic correction.&lt;/li&gt;
&lt;li&gt;Some automatic corrections that are possible have not been implemented yet.&lt;/li&gt;
&lt;li&gt;Some automatic corrections might change (slightly) the semantics of the code, meaning they’d produce code that’s mostly equivalent to the original code, but not 100% equivalent. We call such auto-correct behavior "unsafe"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can run auto correction with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rubocop -a
# or
$ rubocop --auto-correct
# or
$ rubocop -A
# or
$ rubocop --auto-correct-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other RuboCop Extensions
&lt;/h2&gt;

&lt;p&gt;RuboCop also has options for implementing rules on other extensions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubocop/rubocop-rspec" rel="noopener"&gt;rubocop-rspec&lt;/a&gt; For Rspec; a test framework popular for testing Rails code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubocop/rubocop-rake" rel="noopener"&gt;rubocop-rake&lt;/a&gt;: A RuboCop plugin for Rake&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rubocop/rubocop-minitest" rel="noopener"&gt;rubocop-minitest&lt;/a&gt;: Another popular testing library for testing Ruby and Rails code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Style Guide
&lt;/h2&gt;

&lt;p&gt;RuboCop is based on style guides which helps in maintaining best practices for each extension. If you are curious, you can view and read guidelines from links below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rubystyle.guide/" rel="noopener"&gt;Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rails.rubystyle.guide/" rel="noopener"&gt;Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rspec.rubystyle.guide/" rel="noopener"&gt;RSpec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://minitest.rubystyle.guide/" rel="noopener"&gt;Minitest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;RuboCop is very helpful in maintaining best practices and it's one of the gem that we include in all our project setup here at &lt;a href="https://truemark.com.np" rel="noopener"&gt;Truemark&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One thing to remember with Static Code Analyzers is we have the flexibility to enable and disable rules, hence we should always discuss with the team what to include, why to include and what to disable.&lt;/p&gt;

&lt;p&gt;This is the guide I hope I had when I was starting our as a Rails developer. I hope you find it useful!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Image Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cover Image by &lt;a href="https://unsplash.com/@scottwebb?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Scott Webb&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/security?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.rubocop.org/rubocop/1.18/installation.html" rel="noopener"&gt;Official RuboCop Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://prabinpoudel.com.np/articles/rubocop-configuration-files-for-rails/" rel="noopener"&gt;RuboCop Configuration Files for Rails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>codereview</category>
    </item>
  </channel>
</rss>
