<?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: Martin Streicher</title>
    <description>The latest articles on DEV Community by Martin Streicher (@martinstreicher).</description>
    <link>https://dev.to/martinstreicher</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%2F172694%2F47fd68ab-b36f-4809-a64b-40c1427ad2cb.jpeg</url>
      <title>DEV Community: Martin Streicher</title>
      <link>https://dev.to/martinstreicher</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martinstreicher"/>
    <language>en</language>
    <item>
      <title>Squash Your Ruby and Rails Bugs Faster</title>
      <dc:creator>Martin Streicher</dc:creator>
      <pubDate>Thu, 05 Sep 2024 11:00:00 +0000</pubDate>
      <link>https://dev.to/appsignal/squash-your-ruby-and-rails-bugs-faster-4nkn</link>
      <guid>https://dev.to/appsignal/squash-your-ruby-and-rails-bugs-faster-4nkn</guid>
      <description>&lt;p&gt;A bug in software can be disruptive, elusive, maddening, and invasive. Indeed, a developer often needs the tenacity of Edison to find and fix an issue.&lt;/p&gt;

&lt;p&gt;But grit isn't the only asset a developer requires. One also needs information to debug code: What are the symptoms and effects of the issue? What is its frequency? Pervasiveness? Provenance? The evidence and artifacts of a bug — a core dump, stack trace, log, or test case — are invaluable.&lt;/p&gt;

&lt;p&gt;Let's explore a few techniques and tools readily available to Ruby and Rails developers for gathering and investigating evidence of an issue. Data can't supplant tenacity, but it can help to ease your efforts (and spare you your sleep).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Environment Variables to Tune Your Tools in Rails
&lt;/h2&gt;

&lt;p&gt;Rails provides some excellent tools to peer into a running application, including a configurable &lt;a href="https://guides.rubyonrails.org/debugging_rails_applications.html#the-logger" rel="noopener noreferrer"&gt;Logger&lt;/a&gt; to capture diagnostics of its own and any that you'd like to add. Rails also provides a verbose query log to pinpoint the origin of database queries and a verbose enqueue log to illuminate where background jobs are queued. The latter two logs are enabled by default in the &lt;code&gt;development&lt;/code&gt; environment; you can enable both in other environments with two statements.&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;# In any initializer... enable the verbose query log to see the file&lt;/span&gt;
&lt;span class="c1"&gt;# name and line number of the code initiating each query&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verbose_query_logs&lt;/span&gt; &lt;span class="o"&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;# In config/application.rb or any environment initializer, enable the&lt;/span&gt;
&lt;span class="c1"&gt;# queueing log to find where each job is enqueued&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;verbose_enqueue_logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A lesser-known log option available since Rails 7 annotates each SQL query with comments. If you add the following to &lt;code&gt;config/application.rb&lt;/code&gt; or any environment 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;##&lt;/span&gt;
&lt;span class="c1"&gt;# Add to config/application.rb or any environment initializer file&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_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_log_tags_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your query log is automatically amended with the application name, controller name, action name, or background job name. The following sample output comes directly from &lt;a href="https://guides.rubyonrails.org/debugging_rails_applications.html" rel="noopener noreferrer"&gt;Rails's debugging guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Article Load (0.2ms)  SELECT "articles".* FROM "articles"
/*application='Blog',controller='articles',action='index'*/

Article Update (0.3ms)  UPDATE "articles" SET "title" = ?, "updated_at" = ? WHERE "posts"."id" = ?
/*application='Blog',job='ImproveTitleJob'*/
[["title", "Improved Rails debugging guide"], ["updated_at", "2022-10-16 20:25:40.091371"], ["id", 1]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You likely do not want to enable all these features in production. Log generation can be costly in terms of memory and time (finite resources for an application under load).&lt;/p&gt;

&lt;p&gt;However, there are exceptions. You may want to briefly enable an on-demand feature, especially when debugging in development and test modes. Rather than alter code (and perhaps redeploy) to enable or disable a diagnostic, use environment variables to control state. In some environments, such as Heroku, changing the setting of an environment variable does not mandate a new deployment.&lt;/p&gt;

&lt;p&gt;For example, you can define a set of variables to control logging and verbosity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMENTED_QUERY_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable comments in query log&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOG_ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable all logging features&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LOG_LEVEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Control the threshold for log messages&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;fatal&lt;/code&gt;, &lt;code&gt;unknown&lt;/code&gt; (from most verbose to least verbose order)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERBOSE_ENQUEUE_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Display where jobs are queued&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERBOSE_QUERY_LOG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the origin of each Active Record query&lt;/td&gt;
&lt;td&gt;Non-blank value enables feature&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For convenience, create a small class to query configuration settings.&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;# Put this in a file such as config/initializers/env_configuration.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentConfiguration&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;envvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&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="s1"&gt;'LOG_ALL'&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="nf"&gt;present?&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="n"&gt;envvar&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="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now use the variables and the code to set logging features in &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;##&lt;/span&gt;
&lt;span class="c1"&gt;# Centralize configuration of all logging features in config/application.rb&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyApplication&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;Application&lt;/span&gt;
    &lt;span class="c1"&gt;# . . .&lt;/span&gt;
    &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verbose_query_logs&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EnvironmentConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'VERBOSE_QUERY_LOG'&lt;/span&gt;&lt;span class="p"&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;active_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verbose_enqueue_logs&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EnvironmentConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'VERBOSE_ENQUEUE_LOG'&lt;/span&gt;&lt;span class="p"&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;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_log_tags_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EnvironmentConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COMMENTED_QUERY_LOG'&lt;/span&gt;&lt;span class="p"&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;log_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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="s1"&gt;'LOG_LEVEL'&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="nf"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_sym&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;Use your shell, a dotenv file, your continuous integration, or hosting and deployment platform to set each option. You can also use a feature on demand. Simply start the application with the environment variable defined on 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;LOG_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on &lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tune Puma for Development
&lt;/h3&gt;

&lt;p&gt;Building on the previous section, let's look at how to use environment variables to tailor your Puma configuration for debugging in the development environment.&lt;/p&gt;

&lt;p&gt;Typically, Puma is configured to maximize throughput in production, running multiple workers and many threads per worker. In development, you want the opposite: one worker, one thread, and a very long timeout to allow for interactive debugging. Each of those is a parameter you can tune.&lt;/p&gt;

&lt;p&gt;Alter &lt;code&gt;config/puma.rb&lt;/code&gt; to reflect 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="c1"&gt;##&lt;/span&gt;
&lt;span class="c1"&gt;# Control thread count with environment variables.&lt;/span&gt;
&lt;span class="c1"&gt;# Each environment setting, if set, is always a string. Use `Integer` to&lt;/span&gt;
&lt;span class="c1"&gt;# convert the string to an integer value.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="n"&gt;max_threads_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Integer&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="s1"&gt;'PUMA_MAX_THREADS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;min_threads_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Integer&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="s1"&gt;'PUMA_MIN_THREADS, max_threads_count)
threads min_threads_count, max_threads_count

##
# Specify the duration Puma waits before terminating a worker.
# The default is 30 seconds.
#
worker_timeout Integer ENV.fetch('&lt;/span&gt;&lt;span class="no"&gt;PUMA_WORKER_TIMEOUT&lt;/span&gt;&lt;span class="s1"&gt;', 30)

##
# Set the number of workers
worker_count = Integer ENV.fetch('&lt;/span&gt;&lt;span class="no"&gt;PUMA_WEB_CONCURRENCY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="n"&gt;worker_count&lt;/span&gt;
&lt;span class="n"&gt;preload_app!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;worker_count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;positive?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now configure the three environment variables to control Puma in each environment. In development, set the values to optimize for interactive debugging.&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;export &lt;/span&gt;&lt;span class="nv"&gt;PUMA_MAX_THREADS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PUMA_WORKERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PUMA_WEB_CONCURRENCY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PUMA_WORKER_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to validate your Puma settings, set the environment variable &lt;code&gt;PUMA_LOG_CONFIG=true&lt;/code&gt; and start your application. Puma emits its active configuration on startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Booting Puma
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Rails 6.1.7.7 application starting &lt;span class="k"&gt;in &lt;/span&gt;development
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Run &lt;span class="sb"&gt;`&lt;/span&gt;bin/rails server &lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;more startup options
Configuration:
&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
- environment: development
- max_threads: 5
- min_threads: 5
- worker_timeout: 3600
- workers: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coincidentally, the &lt;a href="https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/app/templates/config/puma.rb.tt" rel="noopener noreferrer"&gt;default Puma configuration&lt;/a&gt; in the most recent Rails release is similar to what's shown here (thanks to Nate Matykiewicz for the tip).&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Background Jobs Inline
&lt;/h3&gt;

&lt;p&gt;A Rails application of any complexity typically leverages background jobs to run compute-intensive and (relatively) long-running tasks. Background jobs run asynchronously, disconnected from the request/response cycle. Ideal candidates for "out of band" processing include generating reports, sending emails, and interacting with third-party APIs. But the asynchronous nature of jobs also complicates debugging.&lt;/p&gt;

&lt;p&gt;To simplify troubleshooting, run jobs &lt;em&gt;immediately&lt;/em&gt; in your local development and test environments. In immediate mode, jobs are not queued, but instead execute instantaneously. Running in the "foreground", you can set breakpoints and inspect state interactively.&lt;/p&gt;

&lt;p&gt;Let's look at an example using &lt;a href="https://github.com/collectiveidea/delayed_job" rel="noopener noreferrer"&gt;Delayed Job&lt;/a&gt;, a popular and easy-to-manage queueing backend for &lt;a href="https://guides.rubyonrails.org/active_job_basics.html#" rel="noopener noreferrer"&gt;Active Job&lt;/a&gt;. Delayed Job provides a setting to enable queueing. By default, the setting is &lt;code&gt;true&lt;/code&gt; and jobs are queued as per usual. However, if set to &lt;code&gt;false&lt;/code&gt;, jobs run immediately.&lt;/p&gt;

&lt;p&gt;Add the following code to your application in &lt;code&gt;config/initializers/delayed_job.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Delayed&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay_jobs&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="s1"&gt;'DELAYED_JOBS_DISABLE_JOB_QUEUES'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/code&gt; is set to any value, queueing is disabled. If the environment variable is blank or not defined, queueing is enabled.&lt;/p&gt;

&lt;p&gt;Next, in your shell, on the command line, or in your dot files, set&lt;br&gt;
&lt;code&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/code&gt; as needed.&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;# Set the variable in the current shell&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set the variable only for the current command&lt;/span&gt;
&lt;span class="nv"&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In .env.development, .env.local, and/or .env.test, add the line...&lt;/span&gt;
&lt;span class="nv"&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the environment variable to nothing or delete the environment variable to restore queueing.&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;# Set the variable to blank&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="c"&gt;# Remove the variable from the environment&lt;/span&gt;
&lt;span class="nb"&gt;unset &lt;/span&gt;DELAYED_JOBS_DISABLE_JOB_QUEUES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are no rules for naming environment variables. Choose names meaningful to you. One helpful convention to categorize variables is to add a package name as a prefix, such as &lt;code&gt;PUMA_&lt;/code&gt; and &lt;code&gt;DELAYED_JOB_&lt;/code&gt;. The former indicates variables affecting Puma; the latter connotes variables used by Delayed Job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peer Into Network Calls
&lt;/h3&gt;

&lt;p&gt;Like background jobs, Rails applications can also consume application&lt;br&gt;
programming interfaces (APIs). APIs provide access to external services, such as GraphQL databases, e-commerce transactions, and public data sources.&lt;/p&gt;

&lt;p&gt;Here's another example to conditionally emit HTTP requests and responses from the &lt;code&gt;Net::HTTP&lt;/code&gt; library. If the environment variable &lt;code&gt;DEBUG_HTTP&lt;/code&gt; is set to any non-blank value, the outgoing request and inbound responses are printed to &lt;code&gt;STDOUT&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;construct_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="ss"&gt;protocol: &lt;/span&gt;&lt;span class="s1"&gt;'https'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;Addressable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;uri&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;target&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;debug?&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="s1"&gt;'DEBUG_HTTP'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;
  &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;construct_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&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;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;port&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;http&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_debug_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vg"&gt;$stdout&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;debug?&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"https"&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;To see the network activity, simply start the application with&lt;br&gt;
the environment variable defined on 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;DEBUG_HTTP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;debug?&lt;/code&gt; method provides some abstraction from the actual flag implementation. It, and the rest of this code, can form a &lt;a href="https://gist.github.com/martinstreicher/334e24943a51495dbddd67901ed29b51" rel="noopener noreferrer"&gt;base class for all your network requests, as shown in this gist taken from production code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Ruby Gems to Your Toolbox
&lt;/h2&gt;

&lt;p&gt;The universe of Ruby gems is vast: it's impossible to cover all the great gems available for debugging. Instead, let's look at a few tools you can add today for an instant boost. You'll likely add each of these to all your projects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/awesome-print/awesome_print" rel="noopener noreferrer"&gt;&lt;code&gt;awesome_print&lt;/code&gt;&lt;/a&gt; and &lt;a href="http://tableprintgem.com" rel="noopener noreferrer"&gt;&lt;code&gt;table_print&lt;/code&gt;&lt;/a&gt; create rich, readable inspections of Ruby data structures, including Active Record models. You can use either gem in code or from the console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example of a model emitted by &lt;code&gt;awesome_print&lt;/code&gt; in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ap&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'george@bigbrother.gov'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;#&amp;lt;User:0x00000001163482f0&amp;gt; {&lt;/span&gt;
         &lt;span class="ss"&gt;:account_balance&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;:confirmed_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="no"&gt;Mar&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;30.000000000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:created_at&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Mon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="no"&gt;Mar&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;30.000000000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="ss"&gt;:email&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"george@bigbrother.gov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:encrypted_password&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"992c45d528f9b445de3d6653e14d3e6165171944"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="ss"&gt;:first_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"George"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="ss"&gt;:last_name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Orwell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="ss"&gt;:sign_in_count&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="ss"&gt;:state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"active"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of &lt;code&gt;p&lt;/code&gt;, use &lt;code&gt;ap&lt;/code&gt; to show the enhanced output. &lt;em&gt;awesome_print&lt;/em&gt; lists attributes in alphabetical order, one attribute per line, among other niceties.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/BetterErrors/better_errors" rel="noopener noreferrer"&gt;&lt;code&gt;better_errors&lt;/code&gt;&lt;/a&gt; replaces the standard Rails error page with an enhanced stack backtrace, parameter list, and an interactive console where you can probe the stack frame and variables at the scene of the exception. You can also tie source code links to your favorite editor. Here is code to tie &lt;code&gt;better_errors&lt;/code&gt; to Visual Studio Code:
&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="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt;
  &lt;span class="no"&gt;BetterErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&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;'BETTER_ERRORS_EDITOR'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;presence&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="s1"&gt;'EDITOR'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/vscode/i&lt;/span&gt;
      &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"vscode://file&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;file&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;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="ss"&gt;:macvim&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;Run &lt;code&gt;export BETTER_ERRORS_EDITOR=vscode&lt;/code&gt; in your shell and restart the Rails server. Now links to files open automatically in Visual Studio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the &lt;code&gt;faker&lt;/code&gt; and &lt;code&gt;factory_bot&lt;/code&gt; gems (or their alternatives) in
both the testing and development groups in &lt;em&gt;Gemfile&lt;/em&gt;. Both are invaluable if you need to generate data quickly in the console.
&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="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;# other gems&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'factory_bot'&lt;/span&gt;
    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'faker'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more idea: If you use AppSignal as your application monitoring tool, it has features to interpret your logs and pinpoint the source of errors. See &lt;a href="https://blog.appsignal.com/2022/09/21/debugging-in-ruby-with-appsignal.html" rel="noopener noreferrer"&gt;Debugging in Ruby with AppSignal&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging is a Skill
&lt;/h2&gt;

&lt;p&gt;Debugging code is something of an art, and like any creative endeavor, the more practice you have, the better you become.&lt;/p&gt;

&lt;p&gt;Debug while pairing with another developer, especially an experienced developer. If your partner is willing, think out loud together and exchange hunches and insights.&lt;/p&gt;

&lt;p&gt;Go forth and hack!&lt;/p&gt;

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

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>An Introduction to Nix for Ruby Developers</title>
      <dc:creator>Martin Streicher</dc:creator>
      <pubDate>Wed, 21 Aug 2024 13:52:08 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-nix-for-ruby-developers-1mfo</link>
      <guid>https://dev.to/appsignal/an-introduction-to-nix-for-ruby-developers-1mfo</guid>
      <description>&lt;p&gt;A predictable, stable environment (in terms of your operating system, system libraries, build tools, and programming libraries) is essential to each development step: from onboarding, to collaboration, continuous integration, quality assurance, and deployment. Deviation can cause one-off, intermittent, and even catastrophic failures.&lt;/p&gt;

&lt;p&gt;However, consistency can be elusive, even with the best intentions, best practices, and tools in place, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer machines are easily polluted with clashing libraries and binaries.&lt;/li&gt;
&lt;li&gt;Programming libraries — Ruby's gems or Python's eggs, for example — are regularly updated on unadvertised and unpredictable schedules.&lt;/li&gt;
&lt;li&gt;Docker images can be removed from public repositories without notice.&lt;/li&gt;
&lt;li&gt;Continuous integration providers change internals without forewarning or ex post facto notice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nix aims to solve some of these issues. Specifically, it is designed to reproduce packages and environments &lt;em&gt;verbatim&lt;/em&gt;. Given a set of inputs, it generates the same output every time.&lt;/p&gt;

&lt;p&gt;Let's explore how Nix can reproduce an environment for Ruby and Rails&lt;br&gt;
development, including a version of Ruby, a collection of gems, a PostgreSQL database, and a Redis cache.&lt;/p&gt;
&lt;h2&gt;
  
  
  An Overview of Nix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nixos.org" rel="noopener noreferrer"&gt;Nix&lt;/a&gt; is an entire universe of software. It runs on Linux and macOS, on both Apple Silicon and Intel processors, and has several components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://nixos.org/manual/nix/stable/language/index.html" rel="noopener noreferrer"&gt;Nix Programming Language&lt;/a&gt; is a declarative, domain-specific, functional language dedicated to system composition. Portions of the language manipulate Nix directly, while others provide typical programming constructs like flow control and variables.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NixOS/nixpkgs" rel="noopener noreferrer"&gt;Nix Packages&lt;/a&gt; is an expansive repository of software, from &lt;code&gt;awk&lt;/code&gt; to &lt;code&gt;zsh&lt;/code&gt;. At the time of writing, the repository contains more than 100,000 entries, each usable with Nix. Each package in the Nix archive explicitly documents all its dependencies down to a specific commit. Further, the nixpkgs archive is itself versioned, providing even greater control over package provenance.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nixos.org/manual/nix/stable/command-ref/nix-shell.html" rel="noopener noreferrer"&gt;&lt;code&gt;nix-shell&lt;/code&gt;&lt;/a&gt; provides a virtual, sacrosanct environment contained in a shell. Given a Nix description of a system — a &lt;em&gt;derivation&lt;/em&gt; in Nix parlance — you can compose and boot a system in a walled garden isolated from all other virtual and physical systems. For example, you can download, install, build, and run an independent instance of Ruby with &lt;code&gt;nix-shell&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://nixos.org/manual/nix/stable/store/types/" rel="noopener noreferrer"&gt;Nix Store&lt;/a&gt; persists immutable data (such as software packages) and all dependencies. On some systems, the Nix Store is the local file system; however, it can also be retained on Amazon S3 and even a remote SSH server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nixos.org/manual/nixos/stable/#sec-installation" rel="noopener noreferrer"&gt;NixOS&lt;/a&gt; is an entire Linux distribution configured entirely by the Nix language and composed from versions in Nixpkgs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nix is expansive and each component warrants deep exploration. This&lt;br&gt;
introduction focuses on the &lt;code&gt;nix-shell&lt;/code&gt; and Nix Packages and presents&lt;br&gt;
just enough of the Nix programming language to boot a Rails application. Tony Finn's &lt;a href="https://tonyfinn.com/blog/nix-from-first-principles-flake-edition/" rel="noopener noreferrer"&gt;Nix from First Principles&lt;/a&gt;&lt;br&gt;
provides another take on Nix.&lt;/p&gt;
&lt;h2&gt;
  
  
  Nix Versus Docker
&lt;/h2&gt;

&lt;p&gt;Docker and Nix are both capable of building environments. However, Nix provides consistency everywhere, including build tools and source packages.&lt;/p&gt;

&lt;p&gt;Docker is not a package manager. While you can connect and coordinate&lt;br&gt;
containers using Docker Compose, you cannot use Docker to combine containers into a new container.&lt;/p&gt;

&lt;p&gt;It's trivial with Nix to combine disparate Ruby and PostgreSQL derivations into a new environment.&lt;/p&gt;

&lt;p&gt;If a Docker image changes without notice, the output of &lt;code&gt;docker build&lt;/code&gt; changes too. Pinning a version obviously helps, but if a&lt;br&gt;
Docker image is deleted or an image's repository goes offline, a&lt;br&gt;
Docker build fails without recourse.&lt;/p&gt;

&lt;p&gt;Each package and version stored by Nix can have a unique ID akin to a git SHA. You can think of a complete Nix derivation as a manifest of tens or hundreds of SHAs. A change in any one SHA is detectable and two derivations will not be the same if a single entity has changed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Get Started with Nix
&lt;/h2&gt;

&lt;p&gt;Let's dive into Nix.&lt;/p&gt;

&lt;p&gt;To install Nix natively on Apple MacOS, open the &lt;em&gt;Terminal&lt;/em&gt; application in &lt;em&gt;Applications/Utilities&lt;/em&gt; and run the following command at the shell prompt:&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;# Shell command to download and install Nix on your system&lt;/span&gt;
curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-L&lt;/span&gt; https://install.determinate.systems/nix &lt;span class="se"&gt;\&lt;/span&gt;
  | sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the password for the &lt;code&gt;root&lt;/code&gt; user of your system when prompted.&lt;br&gt;
(If you are a Mac administrator, you can enter your password too.) You need privileged access as the install creates a new storage volume specifically for Nix and tweaks several system settings, such as excluding the Nix store from Time Machine backups.&lt;/p&gt;

&lt;p&gt;The installer summarizes all the modifications it plans to perform and prompts you to proceed. Enter &lt;code&gt;Yes&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Nix output&lt;/span&gt;
Nix &lt;span class="nb"&gt;install &lt;/span&gt;plan &lt;span class="o"&gt;(&lt;/span&gt;v0.18.0&lt;span class="o"&gt;)&lt;/span&gt;
Planner: macos &lt;span class="o"&gt;(&lt;/span&gt;with default settings&lt;span class="o"&gt;)&lt;/span&gt;

Planned actions:
&lt;span class="k"&gt;*&lt;/span&gt; Create an encrypted APFS volume &lt;span class="sb"&gt;`&lt;/span&gt;Nix Store&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;Nix on &lt;span class="sb"&gt;`&lt;/span&gt;disk1&lt;span class="sb"&gt;`&lt;/span&gt; and add it to &lt;span class="sb"&gt;`&lt;/span&gt;/etc/fstab&lt;span class="sb"&gt;`&lt;/span&gt; mounting on &lt;span class="sb"&gt;`&lt;/span&gt;/nix&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Fetch &lt;span class="sb"&gt;`&lt;/span&gt;https://releases.nixos.org/nix/nix-2.21.2/nix-2.21.2-x86_64-darwin.tar.xz&lt;span class="sb"&gt;`&lt;/span&gt; to &lt;span class="sb"&gt;`&lt;/span&gt;/nix/temp-install-dir&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Create a directory tree &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;/nix&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Move the downloaded Nix into &lt;span class="sb"&gt;`&lt;/span&gt;/nix&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Create build &lt;span class="nb"&gt;users&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;UID 301-332&lt;span class="o"&gt;)&lt;/span&gt; and group &lt;span class="o"&gt;(&lt;/span&gt;GID 30000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Configure Time Machine exclusions
&lt;span class="k"&gt;*&lt;/span&gt; Setup the default Nix profile
&lt;span class="k"&gt;*&lt;/span&gt; Place the Nix configuration &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;/etc/nix/nix.conf&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; Configure the shell profiles
&lt;span class="k"&gt;*&lt;/span&gt; Configuring zsh to support using Nix &lt;span class="k"&gt;in &lt;/span&gt;non-interactive shells
&lt;span class="k"&gt;*&lt;/span&gt; Create a &lt;span class="sb"&gt;`&lt;/span&gt;launchctl&lt;span class="sb"&gt;`&lt;/span&gt; plist to put Nix into your PATH
&lt;span class="k"&gt;*&lt;/span&gt; Configure Nix daemon related settings with launchctl
&lt;span class="k"&gt;*&lt;/span&gt; Remove directory &lt;span class="sb"&gt;`&lt;/span&gt;/nix/temp-install-dir&lt;span class="sb"&gt;`&lt;/span&gt;

Proceed? &lt;span class="o"&gt;([&lt;/span&gt;Y]es/[n]o/[e]xplain&lt;span class="o"&gt;)&lt;/span&gt;: Y

 INFO Step: Create an encrypted APFS volume &lt;span class="sb"&gt;`&lt;/span&gt;Nix Store&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;Nix on &lt;span class="sb"&gt;`&lt;/span&gt;disk1&lt;span class="sb"&gt;`&lt;/span&gt; and add it to &lt;span class="sb"&gt;`&lt;/span&gt;/etc/fstab&lt;span class="sb"&gt;`&lt;/span&gt; mounting on &lt;span class="sb"&gt;`&lt;/span&gt;/nix&lt;span class="sb"&gt;`&lt;/span&gt;
 INFO Step: Provision Nix
 INFO Step: Create build &lt;span class="nb"&gt;users&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;UID 301-332&lt;span class="o"&gt;)&lt;/span&gt; and group &lt;span class="o"&gt;(&lt;/span&gt;GID 30000&lt;span class="o"&gt;)&lt;/span&gt;
 INFO Step: Configure Time Machine exclusions
 INFO Step: Configure Nix
 INFO Step: Configuring zsh to support using Nix &lt;span class="k"&gt;in &lt;/span&gt;non-interactive shells
 INFO Step: Create a &lt;span class="sb"&gt;`&lt;/span&gt;launchctl&lt;span class="sb"&gt;`&lt;/span&gt; plist to put Nix into your PATH
 INFO Step: Configure Nix daemon related settings with launchctl
 INFO Step: Remove directory &lt;span class="sb"&gt;`&lt;/span&gt;/nix/temp-install-dir&lt;span class="sb"&gt;`&lt;/span&gt;

Nix was installed successfully! To get started using Nix, open a new shell or run &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installation takes only a moment or two to finish.&lt;/p&gt;

&lt;p&gt;You can easily test it when it's done by running the following sequence of commands in your current shell.&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;# Load Nix into the current shell environment&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Display the free space found on the new volume at /nix&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; /nix

&lt;span class="c"&gt;# Filesystem   512-blocks      Used Available Capacity iused      ifree %iused  Mounted on&lt;/span&gt;
&lt;span class="c"&gt;# /dev/disk1s7  976490576    799416 228759728     1%   73715 1143798640    0%   /nix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use Nix to install and run the `hello` command&lt;/span&gt;
nix run &lt;span class="s1"&gt;'nixpkgs#hello'&lt;/span&gt;
&lt;span class="c"&gt;# Produces `Hello, World!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;nix run&lt;/code&gt; command is something akin to magic. To run the &lt;a href="https://manpages.org/hello" rel="noopener noreferrer"&gt;&lt;code&gt;hello&lt;/code&gt;&lt;/a&gt; utility, Nix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloads all the packages required to build &lt;code&gt;hello&lt;/code&gt;, including tools such as &lt;code&gt;make&lt;/code&gt;, &lt;code&gt;gcc&lt;/code&gt;, and &lt;code&gt;tar&lt;/code&gt;. (On the machine used to develop this article, an &lt;em&gt;i9&lt;/em&gt; iMac running MacOS Sonoma, Nix downloaded more than 400 prerequisite packages into &lt;em&gt;/nix/store&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;Downloads the most recent version of &lt;code&gt;hello&lt;/code&gt; (as no version number was specified).&lt;/li&gt;
&lt;li&gt;Builds and executes the utility, producing &lt;code&gt;Hello, World!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Terminates and returns to the original shell.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run the same command — &lt;code&gt;nix run 'nixpkgs#hello'&lt;/code&gt; — again, Nix executes &lt;code&gt;hello&lt;/code&gt; immediately, as the assets required for the utility are cached and readily accessible.&lt;/p&gt;

&lt;p&gt;You can use Nix to run almost any utility on-demand. Here is another example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Launch the `fish` shell (see https://fishshell.com)&lt;/span&gt;
nix run &lt;span class="sb"&gt;`&lt;/span&gt;nixpkgs#fish&lt;span class="sb"&gt;`&lt;/span&gt;
martin@iMac ~/p/o/a/martin &lt;span class="o"&gt;(&lt;/span&gt;ruby-on-nix&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The penultimate line of the output above is the default &lt;code&gt;fish&lt;/code&gt; prompt, showing a user name, machine name, an abbreviated current working directory name, and the current git branch, if any. Press Control-D to exit the shell.&lt;/p&gt;

&lt;p&gt;If you're following along, try this: Launch &lt;code&gt;fish&lt;/code&gt; via Nix and enter the command &lt;code&gt;which hello&lt;/code&gt;. The result 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;strike@iMac ~/p/o/a/martin &lt;span class="o"&gt;(&lt;/span&gt;ruby-on-nix&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; which hello
strike@iMac ~/p/o/a/martin &lt;span class="o"&gt;(&lt;/span&gt;ruby-on-nix&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;which hello&lt;/code&gt; fails in the current environment. (&lt;code&gt;[1]&lt;/code&gt; is the return value of the previous command, not the output of the previous command. Shell utilities return non-zero values on failure.) Where is the &lt;code&gt;hello&lt;/code&gt; from the previous install? It's in its own encapsulated derivation, completely isolated from the one created for &lt;code&gt;fish&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A one-off Nix derivation such as the one above has its uses, but a Nix derivation is not limited to a sole utility. A derivation may contain any number of packages. Better yet, a derivation can be declared, shared, and re-instantiated in identical form on any machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nix for Ruby Development
&lt;/h2&gt;

&lt;p&gt;Let's create a Nix derivation for Rails development. The derivation must have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A version of Ruby, such as 3.2.0 (the latest version of Ruby available via Nix at the time of writing). Each Ruby package includes &lt;code&gt;gem&lt;/code&gt; and &lt;code&gt;irb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A database service to persist information. Here, let's use PostgreSQL Version 15.&lt;/li&gt;
&lt;li&gt;A key store for caching data, partials, and views. Redis is quite capable.&lt;/li&gt;
&lt;li&gt;Tools for JavaScript and asset compilation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, a plenary Rails environment also needs the &lt;code&gt;rails&lt;/code&gt; gem and many others. Ideally, a Nix derivation for Rails development also automatically bundles necessary gems, producing a standalone environment ready for development.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Nix Derivation for Ruby Development
&lt;/h3&gt;

&lt;p&gt;The code below is a nascent, but working, Nix derivation for Rails development. A Nix derivation is like a &lt;em&gt;Dockerfile&lt;/em&gt; or a blueprint to build a working environment from scratch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;nixpkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;fetchTarball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;https://github.com/NixOS/nixpkgs/archive/ee4a6e0f566fe5ec79968c57a9c2c3c25f2cf41d.tar.gz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;targetRuby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;ruby_3_2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;myBundler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;bundler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;ruby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;targetRuby&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nv"&gt;gems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;bundlerEnv&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ruby_3_2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nv"&gt;name&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rails-gems"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;bundler&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;myBundler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;gemfile&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./Gemfile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;lockfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./Gemfile.lock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;gemset&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./gemset.nix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kn"&gt;in&lt;/span&gt;
  &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkShell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;buildInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nv"&gt;targetRuby&lt;/span&gt;
      &lt;span class="nv"&gt;gems&lt;/span&gt;
      &lt;span class="nv"&gt;gems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;wrappedRuby&lt;/span&gt;
      &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;bundler&lt;/span&gt;
      &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;bundix&lt;/span&gt;
      &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nodejs&lt;/span&gt;
      &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;yarn&lt;/span&gt;
      &lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;postgresql&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sample derivation has two sections. The &lt;code&gt;let&lt;/code&gt; section defines settings, while the &lt;code&gt;in&lt;/code&gt; portion controls the build and enumerates the packages to construct and install in the derivation.&lt;/p&gt;

&lt;p&gt;Before discussing how to use the derivation, some commentary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The setting &lt;code&gt;nixpkgs&lt;/code&gt; requires a specific version of the Nix archive. Like a &lt;code&gt;git&lt;/code&gt; repository, each iteration of the Nix packages archive is represented by a unique SHA reflecting the state of its contents. If the contents of the archive change, such as by incorporating a new version of Ruby, the SHA changes in tandem. Similar to referencing specific commits in &lt;code&gt;git&lt;/code&gt;, you can lock your derivation to a specific iteration of the archive. The particular archive referred to here, &lt;code&gt;ee4a6e0&lt;/code&gt;, contained a bug fix required to build the example derivation.&lt;/li&gt;
&lt;li&gt;The setting &lt;code&gt;targetRuby&lt;/code&gt; specifies a Ruby version. Here, the derivation uses the latest version of Ruby 3.2 available in the archive.&lt;/li&gt;
&lt;li&gt;The setting &lt;code&gt;myBundler&lt;/code&gt; customizes the Nix build to use the same version of Ruby specified for the shell environment.&lt;/li&gt;
&lt;li&gt;Lastly, the setting named &lt;code&gt;gems&lt;/code&gt; configures the Nix version of &lt;code&gt;bundler&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As with native &lt;code&gt;bundler&lt;/code&gt;, the Nix bundler requires a &lt;em&gt;Gemfile&lt;/em&gt; and a &lt;em&gt;Gemfile.lock&lt;/em&gt;. &lt;em&gt;gemset.nix&lt;/em&gt; is original to Nix and is a translation of &lt;em&gt;Gemfile.lock&lt;/em&gt; to the Nix syntax.&lt;/p&gt;

&lt;p&gt;If you want to know what versions of Ruby are available, point your browser to the &lt;a href="https://search.nixos.org/packages" rel="noopener noreferrer"&gt;Nix Packages Index&lt;/a&gt; and search for &lt;code&gt;ruby&lt;/code&gt;. The search results include all the Ruby iterations available, such as &lt;code&gt;ruby&lt;/code&gt;, &lt;code&gt;ruby_3_2&lt;/code&gt;, and &lt;code&gt;ruby_3_3&lt;/code&gt;, for Ruby 3.1, 3.2, and 3.3, respectively. Each package in the search result specifies the exact version number, such as &lt;code&gt;3.1.4&lt;/code&gt; for the &lt;code&gt;ruby&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;If you want to use a specific version of Ruby that is not named in the archive, try &lt;a href="https://github.com/bobvanderlinden/nixpkgs-ruby" rel="noopener noreferrer"&gt;nixpkgs-ruby&lt;/a&gt;. For instance, to use Ruby 3.2.2, replace the line &lt;code&gt;targetRuby = nixpkgs.ruby_3_2;&lt;/code&gt; in &lt;em&gt;shell.nix&lt;/em&gt; with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;nixpkgs-ruby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;fetchTarball&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/bobvanderlinden/nixpkgs-ruby/archive/c1ba161adf31119cfdbb24489766a7bcd4dbe881.tar.gz"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;targetRuby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nixpkgs-ruby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;x86_64-linux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"ruby-3.2.2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the Nix Shell
&lt;/h3&gt;

&lt;p&gt;Using the Nix derivation for Rails development proceeds much like development in any shell. The big exception: There is no need to install Ruby or attendant services beforehand. If you have Nix installed, you're ready to go.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Nix if necessary.&lt;/li&gt;
&lt;li&gt;Clone a Rails project into a new directory or choose an existing codebase. &lt;code&gt;cd&lt;/code&gt; to the directory for the project.&lt;/li&gt;
&lt;li&gt;Copy and paste the code for the sample derivation to a new file in the project directory named &lt;em&gt;shell.nix&lt;/em&gt;. (&lt;em&gt;shell.nix&lt;/em&gt; is the customary name for a derivation, but the name is otherwise arbitrary.)&lt;/li&gt;
&lt;li&gt;Generate &lt;em&gt;gemset.nix&lt;/em&gt; to catalog the gems used within the project.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nix &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--extra-experimental-features&lt;/span&gt; nix-command &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--extra-experimental-features&lt;/span&gt; flakes &lt;span class="se"&gt;\&lt;/span&gt;
     run &lt;span class="s1"&gt;'nixpkgs/nixos-unstable#bundix'&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command runs &lt;code&gt;bundix&lt;/code&gt;, a Nix program, to convert &lt;em&gt;Gemfile.lock_into _gemset.nix&lt;/em&gt;. Both are gem manifests, albeit the latter expressed natively in the Nix language. For comparison, here is an entry for &lt;code&gt;actioncable&lt;/code&gt; from &lt;em&gt;gemset.nix&lt;/em&gt; and from &lt;em&gt;Gemfile.lock&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;   &lt;span class="c"&gt;# From gemset.nix&lt;/span&gt;
   &lt;span class="c"&gt;#&lt;/span&gt;
   &lt;span class="nv"&gt;actioncable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nv"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"actionpack"&lt;/span&gt; &lt;span class="s2"&gt;"activesupport"&lt;/span&gt; &lt;span class="s2"&gt;"nio4r"&lt;/span&gt; &lt;span class="s2"&gt;"websocket-driver"&lt;/span&gt; &lt;span class="s2"&gt;"zeitwerk"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
     &lt;span class="nv"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
     &lt;span class="nv"&gt;platforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
     &lt;span class="nv"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;remotes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://rubygems.org"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
       &lt;span class="nv"&gt;sha256&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0ifiz4nd6a34z2n8lpdgvlgwziy2g364b0xzghiqd3inji0cwqp1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nv"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gem"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
     &lt;span class="nv"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"7.1.3.2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="c"&gt;## From Gemfile.lock&lt;/span&gt;
   &lt;span class="c"&gt;#&lt;/span&gt;
   &lt;span class="nv"&gt;actioncable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nv"&gt;actionpack&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nv"&gt;activesupport&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nv"&gt;nio4r&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nv"&gt;websocket-driver&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nv"&gt;zeitwerk&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Generate and launch the derivation.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nix-shell &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--verbose&lt;/code&gt; option is not required, but provides good and numerous insights into how &lt;code&gt;nix-shell&lt;/code&gt; assembles the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Downsides: Nix has Some Flaws
&lt;/h2&gt;

&lt;p&gt;Nix is a rich, robust, and burgeoning option for building, reproducing, and sharing environments. Nix is also an emergent tool: its current users are early adopters.&lt;/p&gt;

&lt;p&gt;Nix is already a workable solution for many problems, but Ruby support seems to be a work in progress. For example, at the time of writing, no official archive is available for newer versions of Ruby 3.3, and there is no guarantee the latest Nix archive contains every gem in your Gemfile. Documentation for Ruby on Nix is scattered between various GitHub repos, the official Nix website, and user forums. I am grateful to a number of intrepid Nix developers who helped me craft the sample &lt;em&gt;shell.nix&lt;/em&gt;, including Evan Travers, Bob van der Linden, and Norbert Melzer.&lt;/p&gt;

&lt;p&gt;Nix currently is akin to &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain" rel="noopener noreferrer"&gt;git's "porcelain"&lt;/a&gt;: powerful but esoteric. However, much like &lt;code&gt;git&lt;/code&gt; evolved into exoteric, user-friendly tools such as &lt;a href="https://dev.to/amirfakour/git-flow-easy-to-understand-tutorial-27jb"&gt;git-flow&lt;/a&gt;, &lt;a href="https://desktop.github.com" rel="noopener noreferrer"&gt;GitHub Desktop&lt;/a&gt;, and &lt;a href="https://www.git-tower.com" rel="noopener noreferrer"&gt;Tower&lt;/a&gt; to become user-friendly, many developers are building abstractions, wrappers, and utilities to simplify Nix usage. Let's briefly look at a few of these tools now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools to Help with Nix: &lt;code&gt;devbox&lt;/code&gt;, &lt;code&gt;devenv.sh&lt;/code&gt;, and &lt;code&gt;fleek&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A tool to experiment with is &lt;a href="https://www.jetify.com/devbox" rel="noopener noreferrer"&gt;&lt;code&gt;devbox&lt;/code&gt;&lt;/a&gt;. &lt;code&gt;devbox&lt;/code&gt; turned a 10-year-old Macbook Pro laptop running macOS Big Sur into a development machine for a modern Rails application in a few minutes. The setup did not include automatic bundling of gems, but it did install Ruby (including &lt;code&gt;bundler&lt;/code&gt; and &lt;code&gt;gem&lt;/code&gt;), Redis, and PostgreSQL in an isolated and pristine shell environment. &lt;code&gt;devbox&lt;/code&gt; eschews a bespoke programming language for configuration and uses JSON. The environment on the MacBook Pro booted from this minimal file named &lt;em&gt;devbox.json&lt;/em&gt;:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/jetify-com/devbox/0.10.6/.schema/devbox.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packages"&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="s2"&gt;"postgresql@latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"redis@latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ruby@3.2.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"libsass@latest"&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="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;&lt;a href="https://devenv.sh" rel="noopener noreferrer"&gt;&lt;code&gt;devenv.sh&lt;/code&gt;&lt;/a&gt; merits exploration too. It is something of a hybrid, with a JSON-like programming language, YAML configuration, and Docker-like composition of services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getfleek.dev" rel="noopener noreferrer"&gt;&lt;code&gt;fleek&lt;/code&gt;&lt;/a&gt; is another Nix-based tool to craft and share configurations for development machines. It also avoids the Nix programming language and instead provides Homebrew-like commands to build an environment.&lt;/p&gt;

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

&lt;p&gt;In this post, we've seen how Nix can help reproduce a stable environment for Ruby and Rails applications.&lt;/p&gt;

&lt;p&gt;While it has some flaws, Nix and the myriad offshoots in development are worth watching.&lt;/p&gt;

&lt;p&gt;Go forth and hack!&lt;/p&gt;

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

</description>
      <category>nix</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Five Things to Avoid in Ruby</title>
      <dc:creator>Martin Streicher</dc:creator>
      <pubDate>Wed, 05 Jun 2024 12:25:03 +0000</pubDate>
      <link>https://dev.to/appsignal/five-things-to-avoid-in-ruby-4j5i</link>
      <guid>https://dev.to/appsignal/five-things-to-avoid-in-ruby-4j5i</guid>
      <description>&lt;p&gt;As a contract software developer, I am exposed to oodles of Ruby code. Some code is readable, some obfuscated. Some code eschews whitespace, as if carriage returns were a scarce natural resource, while other code resembles a living room fashioned by Vincent Van Duysen. Code, like the people who author it, varies.&lt;/p&gt;

&lt;p&gt;Yet, it's ideal to minimize variation. Time and effort are best spent on novel problems.&lt;/p&gt;

&lt;p&gt;In this post, we'll explore five faux pas often seen in Ruby code and learn how to turn these idiosyncrasies into idioms.&lt;/p&gt;

&lt;p&gt;But first, how can we minimize variance in Ruby?&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimizing Variance in Ruby with Rubocop and Idioms
&lt;/h2&gt;

&lt;p&gt;Ruby developers can gain efficiency with &lt;a href="https://rubocop.org"&gt;Rubocop&lt;/a&gt;, which unifies style within a single project or across many projects. Rails is a boon to uniformity, too, as it favors convention over configuration. Indeed, disparate Rails codebases are identical in places and, overall, quite similar.&lt;/p&gt;

&lt;p&gt;Beyond tools and convention, variance is also minimized by idioms, or those &lt;a href="https://www.merriam-webster.com/dictionary/idiom"&gt;"structural forms peculiar to a language"&lt;/a&gt;. For example, Ruby syntax is a collection of explicit idioms enforced by the interpreter. Reopening a class is a Ruby idiom, too.&lt;/p&gt;

&lt;p&gt;But not all Ruby idioms are dicta. Most Ruby idioms are best practices, shorthand, and common usages Ruby developers share informally and in practice.&lt;/p&gt;

&lt;p&gt;Learning idioms in Ruby is like learning idioms in any second spoken (or programming) language. It takes time and practice. Yet the more adept you are at recognizing and proffering Ruby's idioms, the better your code will be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Avoid in Ruby
&lt;/h2&gt;

&lt;p&gt;Now, let's move on to looking at five things to avoid in Ruby:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verbosity&lt;/li&gt;
&lt;li&gt;Long expressions to detect &lt;code&gt;nil&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Overuse of &lt;code&gt;self&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Collecting results in temporary variables&lt;/li&gt;
&lt;li&gt;Sorting and filtering in memory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Verbosity
&lt;/h3&gt;

&lt;p&gt;If you've delved into Ruby, you can appreciate how expressive, compact, fun, and flexible it is. Any given problem can be solved in a number of ways. For example, &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;unless&lt;/code&gt;, &lt;code&gt;case&lt;/code&gt;, and the ternary operator &lt;code&gt;?:&lt;/code&gt; all express decisions — which one you should apply depends on the problem.&lt;/p&gt;

&lt;p&gt;However, per Ruby idiom, some &lt;code&gt;if&lt;/code&gt; statements are better than others. For instance, the following blocks of code achieve the same result, but one is idiomatic to Ruby:&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;# Verbose approach&lt;/span&gt;

&lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Groucho"&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Chico"&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Harpo"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zeppo"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Idiomatic approach&lt;/span&gt;

&lt;span class="n"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="s2"&gt;"Groucho"&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="s2"&gt;"Chico"&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="s2"&gt;"Harpo"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="s2"&gt;"Zeppo"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Almost every Ruby statement and expression yields a value, including &lt;code&gt;if&lt;/code&gt;, which returns a final code statement value and a matching condition. The version of the &lt;code&gt;if&lt;/code&gt; statement in the second code block above leverages this behavior. If &lt;code&gt;response&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;actor&lt;/code&gt; is set to &lt;code&gt;Chico&lt;/code&gt;. Assigning the result of an &lt;code&gt;if&lt;/code&gt; statement is idiomatic to Ruby. (The same construct can be applied to &lt;code&gt;case&lt;/code&gt;, &lt;code&gt;unless&lt;/code&gt;, &lt;code&gt;begin/end&lt;/code&gt;, and others.)&lt;/p&gt;

&lt;p&gt;Another Ruby idiom is present: you need not predefine a variable used within an &lt;code&gt;if&lt;/code&gt; statement (or &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, and others). So, the latter code removes the line &lt;code&gt;actor = nil&lt;/code&gt;. In Ruby, unlike other languages, the body of an &lt;code&gt;if&lt;/code&gt; statement is &lt;em&gt;not&lt;/em&gt; considered a separate scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long Expressions to Detect &lt;code&gt;nil&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nil&lt;/code&gt; represents "nothing" in Ruby. It's a legitimate value and is its own class (&lt;code&gt;NilClass&lt;/code&gt;) with methods. Like other classes, if you call a method that's not defined on &lt;code&gt;nil&lt;/code&gt;, Ruby throws an exception akin to &lt;code&gt;undefined method 'xxx' for nil:NilClass&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To avoid this exception, you must first test a variable to determine if it's &lt;code&gt;nil.&lt;/code&gt; For example, code such as this is common in Rails applications:&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;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Standard'&lt;/span&gt;
  &lt;span class="c1"&gt;# ... some code for the Standard plan&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trouble is that such a long chain of assertions is unwieldy. Imagine having to repeat the condition &lt;code&gt;user &amp;amp;&amp;amp; user.plan &amp;amp;&amp;amp; ...&lt;/code&gt; every time you have to reference a user's plan name.&lt;/p&gt;

&lt;p&gt;Instead, use Ruby's safe navigation operator, &lt;code&gt;&amp;amp;.&lt;/code&gt; It is shorthand for the logic "If a method is called on &lt;code&gt;nil&lt;/code&gt;, return &lt;code&gt;nil&lt;/code&gt;; otherwise, call the method per normal." Using &lt;code&gt;&amp;amp;.&lt;/code&gt; reduces the code above to the much more readable:&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;if&lt;/span&gt; &lt;span class="n"&gt;user&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;plan&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Standard'&lt;/span&gt;
  &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;user&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt; and &lt;code&gt;plan&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, the expression &lt;code&gt;user&amp;amp;.plan&amp;amp;.name&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The example above assumes &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;plan&lt;/code&gt; represent a custom structure or Rails model of some kind. You can also use the safe navigation operator if a value represents an &lt;code&gt;Array&lt;/code&gt; or &lt;code&gt;Hash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, assume &lt;code&gt;a_list_of_values&lt;/code&gt; is an &lt;code&gt;Array&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;a_list_of_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the variable &lt;code&gt;a_list_of_values&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, an exception is thrown. If you expectantly try &lt;code&gt;a_list_of_values&amp;amp;.[index]&lt;/code&gt;, a syntax error occurs. Instead, use &lt;code&gt;&amp;amp;.&lt;/code&gt; with the &lt;code&gt;Array#at&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a_list_of_values&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;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if &lt;code&gt;a_list_of_values&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, the result of the expression is &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overuse of &lt;code&gt;self&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Ruby uses &lt;code&gt;self&lt;/code&gt; in three substantive ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To define class methods&lt;/li&gt;
&lt;li&gt;To refer to the current object&lt;/li&gt;
&lt;li&gt;To differentiate between a local variable and a method if both have the same name&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's an example class demonstrating all three usages.&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;Rectangle&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&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;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&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;area&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;width&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;volume&lt;/span&gt;
    &lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:height&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;def self.area&lt;/code&gt; is an example of the first purpose for &lt;code&gt;self&lt;/code&gt;, defining a class method. Given this class, the Ruby code &lt;code&gt;puts Rectangle.area(10, 5)&lt;/code&gt; produces &lt;code&gt;50&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The code &lt;code&gt;self.length = length&lt;/code&gt; demonstrates the second application of &lt;code&gt;self&lt;/code&gt;: the instance variable &lt;code&gt;length&lt;/code&gt; is set to the value of the argument &lt;code&gt;length&lt;/code&gt;. (The &lt;code&gt;attr_reader&lt;/code&gt; statement at the bottom defines the instance variable and provides a getter method.) Here, the statement &lt;code&gt;self.length = length&lt;/code&gt; is functionally the same as &lt;code&gt;@length = length&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The third use of &lt;code&gt;self&lt;/code&gt; is shown in the &lt;code&gt;volume&lt;/code&gt; method. What does &lt;code&gt;puts Rectangle.volume(10, 5, 2)&lt;/code&gt; emit? The answer is &lt;code&gt;100&lt;/code&gt;. The line &lt;code&gt;area = 100&lt;/code&gt; sets a method-scoped, local variable named &lt;code&gt;area&lt;/code&gt;. However, the line &lt;code&gt;self.area&lt;/code&gt; refers to the method &lt;code&gt;area&lt;/code&gt;. Hence, the answer is &lt;code&gt;10 * 5 * 2 = 100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consider one more example. What is the output if the &lt;code&gt;volume&lt;/code&gt; method is written like this?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;volume&lt;/span&gt;
   &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
   &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;area&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer is &lt;code&gt;500&lt;/code&gt; because both uses of &lt;code&gt;height&lt;/code&gt; refer to the local variable, not the instance variable.&lt;/p&gt;

&lt;p&gt;Inexperienced Rails developers make unnecessary use of &lt;code&gt;self&lt;/code&gt;, as in:&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;# Class User&lt;/span&gt;
&lt;span class="c1"&gt;# first_name: String&lt;/span&gt;
&lt;span class="c1"&gt;# last_name: String&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;full_name&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, this code is correct, yet using &lt;code&gt;self&lt;/code&gt; to refer to model attributes is unnecessary. If you combine Rubocop with your development environment, Rubocop flags this issue for you to correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collecting Results in Temporary Variables
&lt;/h3&gt;

&lt;p&gt;A common code chore is processing lists of records. You might eliminate records due to certain criteria, map one set of values to another, or separate one set of records into multiple categories. A typical solution is to iterate over the list and accumulate a result.&lt;/p&gt;

&lt;p&gt;For instance, consider this a solution to find all even numbers from a list of integers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;even_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;even_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="n"&gt;list&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;number&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;even_numbers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&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;even?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;even_numbers&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code creates an empty list to aggregate results and then iterates over the list, ignoring &lt;code&gt;nil&lt;/code&gt; items, and accumulating the even values. Finally, it returns the list to the caller. This code serves its purpose, but it isn't idiomatic.&lt;/p&gt;

&lt;p&gt;A better approach is to use Ruby's Enumerable methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;even_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:even?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# shorthand for `.select { |v| v.even? }`&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;select&lt;/code&gt; is one of many Enumerable methods. Each Enumerable method iterates over a list and collects results based on a condition. Specifically, &lt;code&gt;select&lt;/code&gt; iterates over a list and accumulates all items where a condition yields a &lt;em&gt;truthy&lt;/em&gt; value. In the example above, &lt;code&gt;even?&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; if the item is an even number. &lt;code&gt;select&lt;/code&gt; returns a new list, leaving the original list unchanged. A variant named &lt;code&gt;select!&lt;/code&gt; performs the same purpose, but alters (mutates) the original list.&lt;/p&gt;

&lt;p&gt;The Enumerable methods include &lt;code&gt;reject&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt; (also known as &lt;code&gt;collect&lt;/code&gt;). &lt;code&gt;reject&lt;/code&gt; collects all items from a list where a condition yields a falsey value. &lt;code&gt;map&lt;/code&gt; returns a new list where each item in the original list is transformed by an expression.&lt;/p&gt;

&lt;p&gt;Here's one more example Enumerable method in action. First, some non-idiomatic 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;new_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_pair&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;new_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_hash&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 now a more idiomatic approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform_values&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;value&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;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;transform_values&lt;/code&gt; is another Enumerable method available for &lt;code&gt;Hash&lt;/code&gt;. It returns a new hash with the same keys, but each associated value is transformed. Remember, even integers are objects in Ruby and have methods. &lt;code&gt;value&amp;amp;.*(2)&lt;/code&gt; returns &lt;code&gt;nil&lt;/code&gt; if &lt;code&gt;value&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, else &lt;code&gt;value * 2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sorting and Filtering in Memory
&lt;/h3&gt;

&lt;p&gt;Here's one last faux pas — this one is specific to Rails and ActiveRecord. Let's examine this 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="c1"&gt;# class Student&lt;/span&gt;
&lt;span class="c1"&gt;# name: String&lt;/span&gt;
&lt;span class="c1"&gt;# gpa: Float&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&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="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;top_students&lt;/span&gt;
    &lt;span class="no"&gt;Student&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gpa&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gpa&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;90.0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&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;Calling &lt;code&gt;Student.top_students&lt;/code&gt; returns all students with a GPA greater than or equal to &lt;code&gt;90.0&lt;/code&gt; in ranked order, from highest to lowest. Technically, this code is correct, but it isn't very efficient in space or time because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It must load all records from the &lt;code&gt;students&lt;/code&gt; table into memory.&lt;/li&gt;
&lt;li&gt;It first sorts all records and &lt;em&gt;then&lt;/em&gt; filters based on GPA, performing unnecessary work.&lt;/li&gt;
&lt;li&gt;It reverses the order of the list in memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sorting and filtering are best performed in the database, if possible, using ActiveRecord's tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;top_students&lt;/span&gt;
  &lt;span class="no"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;gpa: &lt;/span&gt;&lt;span class="mf"&gt;90.0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;gpa: :desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shorthand &lt;code&gt;90.0..&lt;/code&gt; in the &lt;code&gt;where&lt;/code&gt; clause is a Ruby &lt;code&gt;Range&lt;/code&gt; expressing a value between &lt;code&gt;90.0&lt;/code&gt; and &lt;code&gt;Float::INFINITY&lt;/code&gt;. If &lt;code&gt;gpa&lt;/code&gt; is indexed in the &lt;code&gt;students&lt;/code&gt; table, this query is likely very fast and loads only the matching records. If the student records are being fetched for display, more efficiency (in memory) can be gained via pagination (albeit at the possible expense of more queries to fetch the batches).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up and Next Steps
&lt;/h2&gt;

&lt;p&gt;In this post, we've covered five key things to avoid in Ruby and the idioms to use instead.&lt;/p&gt;

&lt;p&gt;I highly recommend reading the documentation for Ruby's core classes and modules, including &lt;a href="https://ruby-doc.org/3.3.0/Array.html"&gt;&lt;code&gt;Array&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://ruby-doc.org/3.3.0/Hash.html"&gt;&lt;code&gt;Hash&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://ruby-doc.org/3.3.0/Enumerable.html"&gt;&lt;code&gt;Enumerable&lt;/code&gt;&lt;/a&gt;. The documents are a treasure trove of methods and techniques, and chances are a method exists to solve your problem at hand. Turn knowledge into practice by writing small code samples to learn how each method works.&lt;/p&gt;

&lt;p&gt;Add Rubocop into your workflow, even your text editor. Rubocop keeps your code looking nice but can also flag idiosyncratic code. Using Rubocop is one of the best ways to learn to code the Ruby way.&lt;/p&gt;

&lt;p&gt;Finally, read other developers' code, especially open-source Ruby projects. To peruse the code of any gem, run &lt;code&gt;bundle open &amp;lt;gem&amp;gt;&lt;/code&gt;, where &lt;code&gt;&amp;lt;gem&amp;gt;&lt;/code&gt; is the name of a library. If you've included a debugger in your &lt;em&gt;Gemfile&lt;/em&gt;, you can even set breakpoints in any gem and step through the code.&lt;/p&gt;

&lt;p&gt;Go forth and code!&lt;/p&gt;

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

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

</description>
      <category>ruby</category>
    </item>
  </channel>
</rss>
