<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="feeds/pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Andrei Maxim</title>
  <subtitle>Andrei Maxim&#39;s digital garden with writings about Ruby, Rails, HTML, CSS and JavaScript.</subtitle>
  <link href="https://andreimaxim.com/feed.xml" rel="self" />
  <link href="https://andreimaxim.com/" />
  
  <updated>2026-02-08T00:00:00Z</updated>
  <id>https://andreimaxim.com/</id>
  <author>
    <name>Andrei Maxim</name>
  </author>
  
  <entry>
    <title>Time to Grow</title>
    <link href="https://andreimaxim.com/posts/time-to-grow/" />
    <updated>2026-02-08T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/time-to-grow/</id>
    <content type="html">&lt;p&gt;In the early &#39;90s, my grandmother, who was an accountant back then, would wake up at 4 AM to work on account reconciliation.
It was a long process where she&#39;d write down with a pencil on a big piece of paper all the recorded transactions and then
compare them with the receipts and invoices., then she&#39;d use a desk calculator to sum everything up and the numbers would match.
She&#39;d sometimes make a mistake which meant going over the whole sheet and then do partial calculations to narrow down the line
item that caused the issue.&lt;/p&gt;
&lt;p&gt;A couple of years later, we bought a family PC. Instead of spending a day or more on the reconciliation, she&#39;d spend an hour or
two inputting data into an Excel spreadsheet and the computer would automatically sum everything. I remember offering to input
the data she&#39;d dictate as I was typing much faster and we&#39;d be done in half an hour. If there was an issue with a line item,
we&#39;d find it in minutes instead of hours. Of course, sometimes we&#39;d run into the idiosyncrasies built into Excel and that would
drive us nuts, but those were rare and happened less and less often once we had more experience with the program.&lt;/p&gt;
&lt;p&gt;I distinctly remember how magical it felt the first time I saw the autosum feature from Excel and it&#39;s surprisingly close to
what I feel today when I generate code via LLMs!&lt;/p&gt;
&lt;p&gt;In the last couple of months, I&#39;ve spent probably too many hours trying to figure out what will happen with me, as a programmer,
and the IT industry as a whole, in the years to come, worrying that I&#39;ll lose my job and then won&#39;t be able to provide for my
family and I think I understood two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We are going through a global recession and it&#39;s likely to hit as just as hard as the subprime mortgage crisis. There will be a
market contraction, just like there is one during every crisis, and that shall cause many people to lose their jobs.&lt;/li&gt;
&lt;li&gt;There are always more feature requests than time allows, which is the main reason why we have so many methodologies. LLMs represent
yet another technological leap that will make us be more efficient, just like the email, Excel, AutoCAD, Google and many others.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since there are trillion-dollar companies doing their best to replace humans with a subcription to an LLM-powered plan, there is a
good chance I might be wrong. On the other hand, I don&#39;t remember my grandmother ever complaining that she had to learn Excel
or that she&#39;d rather go to the old way of doing balances and reconciliations.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Adding SimpleCov to Rails</title>
    <link href="https://andreimaxim.com/posts/adding-simplecov-to-rails/" />
    <updated>2026-01-27T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/adding-simplecov-to-rails/</id>
    <content type="html">&lt;p&gt;I&#39;ve stopped using code coverage tools a number of years ago as the effort
to maintain a test suite with 100% code coverage was quite significant, but
nowadays the coverage report can be a useful tool when working with LLMs.&lt;/p&gt;
&lt;p&gt;Unfortunately, the recommended SimpleCov setup for Ruby on Rails does not work
because Rails now uses a parallel test runners and SimpleCov needs to be setup
accordingly, otherwise you&#39;d get much lower coverage numbers than you&#39;d expect.&lt;/p&gt;
&lt;p&gt;I&#39;ve landed on this approach based on the template from &lt;a href=&#34;https://railstemplates.org/simplecov-rails/&#34;&gt;Rails Templates&lt;/a&gt;,
with a sightly different setup and ignoring the &lt;code&gt;CI&lt;/code&gt; environment variable,
because it &lt;a href=&#34;https://reinteractive.com/articles/tutorial-series-for-experienced-rails-developers/CI-simplecov-and-coverage-discrepancies&#34;&gt;can cause discrepancies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, let&#39;s look at how the default Rails &lt;code&gt;test/test_helper.rb&lt;/code&gt; file looks like:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;ENV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;RAILS_ENV&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;test&#34;&lt;/span&gt;&lt;/span&gt;
require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;../config/environment&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;rails/test_help&#34;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActiveSupport&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TestCase&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# Run tests in parallel with specified workers&lt;/span&gt;
    parallelize&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;workers&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:number_of_processors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.&lt;/span&gt;
    fixtures &lt;span class=&#34;token symbol&#34;&gt;:all&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;# Add more helper methods to be used by all tests here...&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, let&#39;s create a &lt;code&gt;test/coverage_test.rb&lt;/code&gt; file with the SimpleCov setup as
per the gem&#39;s instructions.&lt;/p&gt;
&lt;p&gt;Note that the file has an early return so the rest of the code is never executed
unless the correct environment variable is present (Ruby is a scripting language
as well!).&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;ENV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;COVERAGE&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;simplecov&#34;&lt;/span&gt;&lt;/span&gt;

SimpleCov&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;start &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;rails&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# Add custom groups&lt;/span&gt;
  &lt;span class=&#34;token comment&#34;&gt;# e.g. add_group &#34;Services&#34;, &#34;app/services&#34;&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# Exclude files from coverage&lt;/span&gt;
  add_filter &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/test/&#34;&lt;/span&gt;&lt;/span&gt;
  add_filter &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/config/&#34;&lt;/span&gt;&lt;/span&gt;
  add_filter &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/vendor/&#34;&lt;/span&gt;&lt;/span&gt;
  add_filter &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;/lib/generators/&#34;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# Enable branch coverage&lt;/span&gt;
  enable_coverage &lt;span class=&#34;token symbol&#34;&gt;:branch&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to create a concern that we&#39;ll mix into the &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;
class to ensure that SimpleCov combines the coverage results across all
parallel test runners, which I&#39;ve created as &lt;code&gt;test/test_helpers/coverage_test_helper.rb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CoverageTestHelper&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;extend&lt;/span&gt; ActiveSupport&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Concern

  included &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    parallelize_setup &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;worker&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      SimpleCov&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;command_name &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;SimpleCov&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;command_name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;worker&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

    parallelize_teardown &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;worker&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
      SimpleCov&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;result
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let&#39;s require the &lt;code&gt;test/coverage_helper.rb&lt;/code&gt; file as close to the top
as possible, add a line that automatically requires the files in the &lt;code&gt;test/test_helpers&lt;/code&gt;
folder and then add the mixin if the &lt;code&gt;COVERAGE&lt;/code&gt; environment variable is set:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token constant&#34;&gt;ENV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;RAILS_ENV&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;test&#34;&lt;/span&gt;&lt;/span&gt;

require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;coverage_helper&#34;&lt;/span&gt;&lt;/span&gt;
require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;../config/environment&#34;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;rails/test_help&#34;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;# Automatically load all test helpers&lt;/span&gt;
&lt;span class=&#34;token builtin&#34;&gt;Dir&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;Rails&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;root&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;test&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;test_helpers&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;**&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;*.rb&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;file&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;require&lt;/span&gt; file &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActiveSupport&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TestCase&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# Run tests in parallel with specified workers&lt;/span&gt;
    parallelize&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;workers&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:number_of_processors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; CoverageTestHelper &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;ENV&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;COVERAGE&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.&lt;/span&gt;
    fixtures &lt;span class=&#34;token symbol&#34;&gt;:all&lt;/span&gt;

    &lt;span class=&#34;token comment&#34;&gt;# Add more helper methods to be used by all tests here...&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now see you application&#39;s test coverage by running:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;COVERAGE&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;true bin/rails &lt;span class=&#34;token builtin class-name&#34;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>Alternatives to Ruby classes</title>
    <link href="https://andreimaxim.com/posts/alternatives-to-ruby-classes/" />
    <updated>2026-01-02T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/alternatives-to-ruby-classes/</id>
    <content type="html">&lt;p&gt;I&#39;ve noticed that many Ruby developers tend to suffer from &amp;quot;classitis,&amp;quot;
as coined by John Ousterhout in his &amp;quot;A Philosophy of Software Design,&amp;quot;
where there is an explosion of shallow modules (or, in our case, classes)
instead of having fewer deep modules.&lt;/p&gt;
&lt;p&gt;I understand the general criticism that &lt;code&gt;ActionController&lt;/code&gt; instances
aren&#39;t really objects, but sure this can&#39;t be the best &lt;a href=&#34;https://guides.hanamirb.org/v2.3/actions/overview/&#34;&gt;alternative&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Bookshelf&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Actions&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Home&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Show&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; Bookshelf&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Action
        &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;handle&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;request&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; response&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
          name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; request&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;params&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

          response&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;body &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Welcome to Bookshelf &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;!&#34;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It gets even worse when you consider the proliferation of the &lt;a href=&#34;https://www.honeybadger.io/blog/refactor-ruby-rails-service-object/&#34;&gt;ServiceObject&lt;/a&gt;
pattern in the Ruby and Rails codebases, which often looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BookCreator&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;call&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;args&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;args&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;call
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;author_id&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;genre_id&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@title&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; title
    &lt;span class=&#34;token variable&#34;&gt;@description&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; description
    &lt;span class=&#34;token variable&#34;&gt;@author_id&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; author_id
    &lt;span class=&#34;token variable&#34;&gt;@genre_id&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; genre_id
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;call&lt;/span&gt;&lt;/span&gt;
    create_book
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;create_book&lt;/span&gt;&lt;/span&gt;
      Book&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;create&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;
        &lt;span class=&#34;token symbol&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@title&lt;/span&gt;
        &lt;span class=&#34;token symbol&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@description&lt;/span&gt;
        &lt;span class=&#34;token symbol&#34;&gt;author_id&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@author_id&lt;/span&gt;
        &lt;span class=&#34;token symbol&#34;&gt;genre_id&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@genre_id&lt;/span&gt;
      &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; ActiveRecord&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;RecordNotUnique &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; e
      &lt;span class=&#34;token comment&#34;&gt;# handle duplicate entry&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This whole class (which is basically a Factory) could have been a method:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Book&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;create_unique&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;params&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      Book&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;create&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;params&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;rescue&lt;/span&gt; ActiveRecord&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;RecordNotUnique &lt;span class=&#34;token operator&#34;&gt;=&gt;&lt;/span&gt; e
      &lt;span class=&#34;token comment&#34;&gt;# handle duplicate entry&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token comment&#34;&gt;# rest of the code&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I was pleasantly surprised when I saw &lt;a href=&#34;https://www.youtube.com/watch?v=sjuCiIdMe_4&#34;&gt;Dave Thomas&#39;s talk at SFRuby&lt;/a&gt;
on the topic of using &lt;em&gt;less&lt;/em&gt; classes.&lt;/p&gt;
&lt;p&gt;Below are my notes about some the arguments he is making.&lt;/p&gt;
&lt;h2&gt;When to use &lt;code&gt;module&lt;/code&gt; instead of &lt;code&gt;class&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In Ruby, there are two main differences between modules and classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modules are not instantiable (there isn&#39;t a &lt;code&gt;Module.new&lt;/code&gt; method)&lt;/li&gt;
&lt;li&gt;Modules can be mixed into other modules or classes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or, if we frame it in a different way, you should use a &lt;code&gt;module&lt;/code&gt; instead
of a &lt;code&gt;class&lt;/code&gt; when &lt;strong&gt;you need a namespace for related functions&lt;/strong&gt;
or when &lt;strong&gt;you need to share code between different object types&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s consider this class:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Utils&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;date_to_string&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;date&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# code&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since it doesn&#39;t make sense to instantiate the above class at all,
let&#39;s replace it with a module:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Utils&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;extend&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;date_to_string&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;date&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# code&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;extend self&lt;/code&gt; part makes it obvious that the &lt;code&gt;Module&lt;/code&gt; is used
as a namespace, which is a distinct advantage over classes.&lt;/p&gt;
&lt;p&gt;A second use for &lt;code&gt;Module&lt;/code&gt; is its ability to &lt;strong&gt;mix in behavior&lt;/strong&gt;, thus
allowing you to share code between objects.&lt;/p&gt;
&lt;p&gt;Here&#39;s a common pattern I&#39;ve seen in several places:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StrategyBase&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run&lt;/span&gt;&lt;/span&gt;
    run_setup
    perform
    run_callbacks
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run_setup&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token keyword&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Not implemented&#34;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UserStrategy&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; StrategyBase
  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run_setup&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token comment&#34;&gt;# some user-specific setup code&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AdminStrategy&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; StrategyBase
  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run_setup&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token comment&#34;&gt;# some admin-specific setup code&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;StrategyBase&lt;/code&gt; class actually represents a &lt;em&gt;behavior&lt;/em&gt; which
we want to &lt;em&gt;mix into&lt;/em&gt; our &lt;code&gt;UserStrategy&lt;/code&gt; and &lt;code&gt;AdminStrategy&lt;/code&gt; objects,
so why not be explicit about that and convert it to a &lt;code&gt;Module&lt;/code&gt;?&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Runnable&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run&lt;/span&gt;&lt;/span&gt;
    run_setup
    perform
    run_callbacks
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UserStrategy&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Runnable

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run_setup&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token comment&#34;&gt;# some user-specific setup code&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AdminStrategy&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Runnable

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;run_setup&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&#34;token comment&#34;&gt;# some admin-specific setup code&lt;/span&gt;
    &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main advantage is that adding behaviors to classes via mixins scales
much better than using inheritance:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AdminStrategy&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Runnable&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Retriable&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Loggable
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UserStrategy&lt;/span&gt;
 &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Runnable&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Retriable&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Loggable
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GuestStrategy&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Runnable&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Expirable
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These different approaches can be seen in &lt;code&gt;ActiveJob&lt;/code&gt; and &lt;code&gt;Sidekiq&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SomeJob&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; ActiveJob&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Base
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;perform&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;args&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;# vs&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SomeJob&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;include&lt;/span&gt; Sidekiq&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Job

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;perform&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt;args&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The inheritance hierarchy is not a big issue in the case of &lt;code&gt;ActiveJob&lt;/code&gt; mainly
because the convention is that all files in &lt;code&gt;app/jobs&lt;/code&gt; are expected to always be
a type of job, but this might not be as clear in other parts of the codebase
(e.g. &lt;code&gt;app/models&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;When to use a &lt;code&gt;Data&lt;/code&gt; instead of &lt;code&gt;class&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Another common anti-pattern when it comes to using classes in Ruby is when
the class is actually a &lt;strong&gt;data bag&lt;/strong&gt;. In that case, there are two better
constructs, but I&#39;d like to single out the &lt;code&gt;Data&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;So instead of:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  attr_reader &lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:age&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;age&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
    &lt;span class=&#34;token variable&#34;&gt;@age&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; age
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it would be better (and simpler!) to use:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;Person &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Data&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;define&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:age&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In both cases, the usage is identical:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;john &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; first_name&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;John&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Doe&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;age&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;42&lt;/span&gt;

john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_name &lt;span class=&#34;token comment&#34;&gt;#=&gt; &#34;John&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Data&lt;/code&gt; class can also include helper methods, but keep in
mind that the object is frozen so you can&#39;t manipulate instance
variables after it was created:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;Person &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Data&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;define&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:age&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;full_name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt; &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Naming classes after design patterns&lt;/h2&gt;
&lt;p&gt;Dave Thomas says that if your class is named after a design pattern,
then it&#39;s a smell. While I agree that in Ruby we don&#39;t need to have
a dedicated &lt;code&gt;UserFactory&lt;/code&gt; class and just use a class method (e.g.
&lt;code&gt;User#build_admin&lt;/code&gt;), I think his advice is less applicable down when
we go beyond the design patterns presented in the GoF book.&lt;/p&gt;
&lt;p&gt;For example, I don&#39;t think it&#39;s a smell that you have a &lt;code&gt;RemindersJob&lt;/code&gt;,
or a &lt;code&gt;UsersController&lt;/code&gt; class even though both contain the name of the
pattern (&amp;quot;job&amp;quot; and &amp;quot;controller&amp;quot;).&lt;/p&gt;
&lt;h2&gt;Abstract classes&lt;/h2&gt;
&lt;p&gt;I wrote my fair share of abstract classes and the logic behind it was
that they provided core functionality for the classes that inherited
from it (e.g. populating the model with data pulled from a specific URL).&lt;/p&gt;
&lt;p&gt;However, looking back, I think I always got to a place where I&#39;d had
methods that would just raise some sort of &lt;code&gt;NotImplementedError&lt;/code&gt;, or
I would have to override the &lt;code&gt;initialize&lt;/code&gt; method and I&#39;m fairly sure
that using a mix-in would have been much cleaner.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;I think PragDave was a bit too dramatic when he titled his talk
&amp;quot;Stop using classes,&amp;quot; but otherwise I highly agree with the points
that he&#39;s making.&lt;/p&gt;
&lt;p&gt;Whenever you reach for a class, you are also inviting a lot of complexity,
which could be avoided by using other tools, like &lt;code&gt;Module&lt;/code&gt; (which
is stateless) or &lt;code&gt;Data&lt;/code&gt; (which is immutable), that could be easier
to reason about since they are generally simpler. Also, refactoring
from a &lt;code&gt;Module&lt;/code&gt; or &lt;code&gt;Data&lt;/code&gt; to a &lt;code&gt;Class&lt;/code&gt; is generally extremely
trivial.&lt;/p&gt;
&lt;p&gt;That being said, I also think there&#39;s a lot of &lt;em&gt;essential complexity&lt;/em&gt;
that you need to manage and, often, objects and classes can be
extremely helpful.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Benchmarking with Ruby on Rails</title>
    <link href="https://andreimaxim.com/posts/benchmarking-with-ruby-on-rails/" />
    <updated>2025-10-15T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/benchmarking-with-ruby-on-rails/</id>
    <content type="html">&lt;p&gt;One of the things I really enjoy about Ruby on Rails is that it has a lot of tiny conveniences
that can speed up your workflow a lot.&lt;/p&gt;
&lt;p&gt;For example, I recently needed to quickly convert Markdown to HTML, but I wasn&#39;t sure which
gem to use: a quick search revealed the venerable &lt;a href=&#34;https://github.com/vmg/redcarpet&#34;&gt;Redcarpet&lt;/a&gt;,
which is mostly written in C, but also &lt;a href=&#34;https://github.com/gjtorikian/commonmarker&#34;&gt;Commonmarker&lt;/a&gt;,
which wraps a Rust library and provides some additional features.&lt;/p&gt;
&lt;p&gt;But which one to choose?&lt;/p&gt;
&lt;p&gt;Luckily, Rails makes it really simple to create a benchmark via a generator:&lt;/p&gt;
&lt;pre class=&#34;language-shell&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;bin/rails g benchmark markdown_parsing&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It should generate a &lt;code&gt;markdown_parsing.rb&lt;/code&gt; file in the &lt;code&gt;script/benchmarks&lt;/code&gt; folder with the
following contents:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# frozen_string_literal: true&lt;/span&gt;

require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;../../config/environment&#34;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&#34;token comment&#34;&gt;# Any benchmarking setup goes here...&lt;/span&gt;

Benchmark&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;ips &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;x&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;before&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;after&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compare&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the Redcarpet, Commonmarker and Kramdown (as a baseline) gems to your Gemfile
(plus &lt;code&gt;benchmark-ips&lt;/code&gt; if it&#39;s not already in the &lt;code&gt;development&lt;/code&gt; group), use something like
&lt;a href=&#34;https://jaspervdj.be/lorem-markdownum/&#34;&gt;Lorem Markdown&lt;/a&gt; to generate a sample Markdown file,
and then add the benchmark code, which would look something like this:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# frozen_string_literal: true&lt;/span&gt;

require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;../../config/environment&#34;&lt;/span&gt;&lt;/span&gt;

md &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;read&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Rails&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;root&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;script&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;benchmarks&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;sample.md&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

Benchmark&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;ips &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;x&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Commonmarker.to_html&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; Commonmarker&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_html&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;md&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Kramdown.to_html&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;     &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; Kramdown&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;md&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_html &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Redcarpet.render&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;     &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; Redcarpet&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Markdown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Redcarpet&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Render&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;HTML&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;render&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;md&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compare&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, run the benchmark using the Rails runner:&lt;/p&gt;
&lt;pre class=&#34;language-shell&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;bin/rails runner script/benchmarks/markdown_parsing.rb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On my development machine, this was the output:&lt;/p&gt;
&lt;pre class=&#34;language-plain&#34;&gt;&lt;code class=&#34;language-plain&#34;&gt;ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [x86_64-linux]
Warming up --------------------------------------
Commonmarker.to_html 65.000 i/100ms
Kramdown.to_html 155.000 i/100ms
Redcarpet.render 7.151k i/100ms
Calculating -------------------------------------
Commonmarker.to_html 648.281 (± 4.0%) i/s (1.54 ms/i) - 3.250k in 5.021352s
Kramdown.to_html 1.525k (± 3.5%) i/s (655.91 μs/i) - 7.750k in 5.089232s
Redcarpet.render 102.672k (±17.1%) i/s (9.74 μs/i) - 486.268k in 5.011136s

Comparison:
Redcarpet.render: 102672.4 i/s
Kramdown.to_html: 1524.6 i/s - 67.34x slower
Commonmarker.to_html: 648.3 i/s - 158.38x slower&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The results are a bit surprising, especially since Kramdown is written in pure Ruby and &lt;em&gt;seems&lt;/em&gt;
to be much faster than Commonmarker, which is basically just a thin Ruby wrapper over Rust code.
So I&#39;m suspecting that Commonmarker is actually doing a lot more.&lt;/p&gt;
&lt;p&gt;Let&#39;s run the code from the benchmark in the REPL and see what&#39;s the output for each renderer.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; heading from the original Markdown file looks like this:&lt;/p&gt;
&lt;pre class=&#34;language-markdown&#34;&gt;&lt;code class=&#34;language-markdown&#34;&gt;&lt;span class=&#34;token title important&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;#&lt;/span&gt; Monitis velle&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s a heading generated by Commonmarker is quite complex:&lt;/p&gt;
&lt;pre class=&#34;language-html&#34;&gt;&lt;code class=&#34;language-html&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;href&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;#monitis-velle&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;aria-hidden&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;true&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;anchor&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;monitis-velle&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Monitis velle
  &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kramdown only adds an ID to the heading:&lt;/p&gt;
&lt;pre class=&#34;language-html&#34;&gt;&lt;code class=&#34;language-html&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;monitis-velle&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Monitis velle&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output from Redcarpet is very basic, just the HTML and nothing more:&lt;/p&gt;
&lt;pre class=&#34;language-html&#34;&gt;&lt;code class=&#34;language-html&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Monitis velle&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the output is different, this means that the comparison wasn&#39;t exactly fair, so let&#39;s
improve the benchmark by making sure all renderers behave the same (as much as possible) by
disabling any extra features since we&#39;re only interested in converting from Markdown to
plain HTML:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token comment&#34;&gt;# frozen_string_literal: true&lt;/span&gt;

require_relative &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;../../config/environment&#34;&lt;/span&gt;&lt;/span&gt;

md &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token builtin&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;read&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Rails&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;root&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;script&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;benchmarks&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;sample.md&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;token constant&#34;&gt;PLAIN_CM_OPTS&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  extension&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    strikethrough&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;autolink&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;tasklist&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    footnotes&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;description_lists&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;shortcodes&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    header_ids&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
  render&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
    github_pre_lang&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;escaped_char_spans&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;sourcepos&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
    full_info_string&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;
  &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
  parse&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;smart&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;

Benchmark&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;ips &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;x&lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt;
  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Commonmarker.to_html&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    Commonmarker&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_html&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;
      md&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
      options&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;PLAIN_CM_OPTS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;
      plugins&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;syntax_highlighter&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Kramdown.to_html&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    Kramdown&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Document&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;md&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;auto_ids&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;syntax_highlighter&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;math_engine&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
      &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;to_html
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;report&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Redcarpet.render&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;do&lt;/span&gt;
    Redcarpet&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Markdown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Redcarpet&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;Render&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;HTML&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;render&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;md&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  x&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compare&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s run the benchmark again:&lt;/p&gt;
&lt;pre class=&#34;language-shell&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;bin/rails runner script/benchmarks/markdown_parsing.rb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;... and here&#39;s the output:&lt;/p&gt;
&lt;pre class=&#34;language-plain&#34;&gt;&lt;code class=&#34;language-plain&#34;&gt;ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [x86_64-linux]
Warming up --------------------------------------
Commonmarker.to_html 2.677k i/100ms
Kramdown.to_html 139.000 i/100ms
Redcarpet.render 7.175k i/100ms
Calculating -------------------------------------
Commonmarker.to_html 27.100k (± 2.3%) i/s (36.90 μs/i) - 136.527k in 5.040564s
Kramdown.to_html 1.561k (± 3.0%) i/s (640.50 μs/i) - 7.923k in 5.079224s
Redcarpet.render 102.521k (±19.2%) i/s (9.75 μs/i) - 487.900k in 5.102568s

Comparison:
Redcarpet.render: 102520.6 i/s
Commonmarker.to_html: 27100.1 i/s - 3.78x slower
Kramdown.to_html: 1561.3 i/s - 65.66x slower&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Redcarpet is still much faster than Commonmarker, but the difference is two orders of magnitude
smaller, while Kramdown is unsurprisingly the slowest.&lt;/p&gt;
&lt;p&gt;While this is a tiny example, the main advantage of the benchmark scripts is that they load your
entire application, meaning that you can easily A/B test different chunks of code from your
application to check for performance regressions or improvements!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Postgres for Rails Development</title>
    <link href="https://andreimaxim.com/posts/postgres-for-rails-development/" />
    <updated>2024-07-02T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/postgres-for-rails-development/</id>
    <content type="html">&lt;p&gt;I&#39;ve found that the best way to set up Posgres for local development is via Docker, since it has some
clear benefits over using system packages on Windows (via WSL2) or Linux.&lt;/p&gt;
&lt;p&gt;There are three steps to configure Postgres for your Rails application:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the Postgres server&lt;/li&gt;
&lt;li&gt;Install Postgres libraries for development&lt;/li&gt;
&lt;li&gt;Configure your application to connect to the Postgres container&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Installing the Postgres container&lt;/h2&gt;
&lt;p&gt;After some testing, I&#39;ve settled on the following command:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;docker&lt;/span&gt; run &lt;span class=&#34;token parameter variable&#34;&gt;-d&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;--restart&lt;/span&gt; unless-stopped &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;--name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;postgres18 &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-e&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;POSTGRES_HOST_AUTH_METHOD&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;trust &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-e&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;whoami&lt;/span&gt;&lt;span class=&#34;token variable&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  &lt;span class=&#34;token parameter variable&#34;&gt;-p&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;127.0.0.1:5432:5432&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  --shm-size&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;1g &lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;
  postgres:18 &lt;span class=&#34;token parameter variable&#34;&gt;-c&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;max_locks_per_transaction&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1024&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s a short explanation of each flag:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo docker run -d&lt;/code&gt; runs the Docker container in the background (-d means daemon)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--restart unless-stopped&lt;/code&gt; will automatically start the Postgres service when the Docker daemon starts,
unless manually stopped (which is very useful if you have a Postgre 17 instance, but you want to switch
to Postgres 16 without changing the application configuration)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e POSTGRES_HOST_AUTH_METHOD=trust&lt;/code&gt; basically password authentication, which is fine for local development
as long as the container is only exposed to the localhost (see below)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e POSTGRES_USER=$(whoami)&lt;/code&gt; sets the default user to your local OS system account (in my case it&#39;s &lt;code&gt;andrei&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p &amp;quot;127.0.0.1:5432:5432&amp;quot;&lt;/code&gt; binds the internal port 5432 on the Docker container, which is used by Postgres,
to the same port on localhost. Since the service binds to &lt;code&gt;127.0.0.1&lt;/code&gt; this means that the port will not be
accessible from the outside world (e.g. if you are using a shared wifi in a cafe)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--shm-size=1g&lt;/code&gt; increases the default Docker container shared memory from 64MB to 1GB, otherwise Postgres
might raise a &amp;quot;could not resize shared memory&amp;quot; error&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c &amp;quot;max_locks_per_transaction=128&amp;quot;&lt;/code&gt; increases the number of locks per transaction, which can be reached
by Rails when running tests (see &lt;a href=&#34;https://gitlab.com/gitlab-org/gitlab/-/issues/412760#note_1426797584&#34;&gt;this Gitlab issue&lt;/a&gt;).
This needs to be last since it&#39;s passed as a command to &lt;code&gt;postgres&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Install Postgres libraries for development&lt;/h2&gt;
&lt;p&gt;In order to connect to Postgres, we also need the development libraries so we can install the &lt;code&gt;pg&lt;/code&gt; gem.
This is different on every system and Linux distribution, but on Ubuntu there is an official Apt repository
which offers access to the latest Postgres releases:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apt&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; postgresql-common
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install the latest client application (&lt;code&gt;createdb&lt;/code&gt;, &lt;code&gt;dropdb&lt;/code&gt;, &lt;code&gt;pg_dump&lt;/code&gt;, &lt;code&gt;psql&lt;/code&gt;, etc) and development
libraries:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apt&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; postgresql-client-17 libpq-dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should be able to log into the database server now:&lt;/p&gt;
&lt;pre class=&#34;language-shell&#34;&gt;&lt;code class=&#34;language-shell&#34;&gt;$ psql &lt;span class=&#34;token parameter variable&#34;&gt;-h&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;127.0&lt;/span&gt;.0.1
psql &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;18.1&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Ubuntu &lt;span class=&#34;token number&#34;&gt;18.1&lt;/span&gt;-1.pgdg24.04+2&lt;span class=&#34;token punctuation&#34;&gt;))&lt;/span&gt;
Type &lt;span class=&#34;token string&#34;&gt;&#34;help&#34;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; help.

&lt;span class=&#34;token assign-left variable&#34;&gt;andrei&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;#&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configure your application&lt;/h3&gt;
&lt;p&gt;We can simplify the &lt;code&gt;config/database.yml&lt;/code&gt; file quite a lot when using Postgres. here&#39;s an example for
a blog application:&lt;/p&gt;
&lt;pre class=&#34;language-yaml&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;&lt;span class=&#34;token key atrule&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token important&#34;&gt;&amp;amp;default&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; postgresql
&lt;span class=&#34;token key atrule&#34;&gt;encoding&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; unicode
&lt;span class=&#34;token comment&#34;&gt;# For details on connection pooling, see Rails configuration guide&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# https://guides.rubyonrails.org/configuring.html#database-pooling&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;pool&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &amp;lt;%= ENV.fetch(&#34;RAILS_MAX_THREADS&#34;) &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; %&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;

&lt;span class=&#34;token key atrule&#34;&gt;development&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;
  &lt;span class=&#34;token key atrule&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token important&#34;&gt;*default&lt;/span&gt;
  &lt;span class=&#34;token key atrule&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; blog_development

&lt;span class=&#34;token comment&#34;&gt;# Warning: The database defined as &#34;test&#34; will be erased and&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# re-generated from your development database when you run &#34;rake&#34;.&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# Do not set this db to the same as development or production.&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;
   &lt;span class=&#34;token key atrule&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token important&#34;&gt;*default&lt;/span&gt;
  &lt;span class=&#34;token key atrule&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; blog_test

&lt;span class=&#34;token comment&#34;&gt;# ActiveRecord will automatically use the `DATABASE_URL` environment&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# variable for the primary database and `&amp;lt;NAME&gt;_DATABASE_URL` for any&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# secondary database (e.g. `REPLICA_DATABASE_URL`).&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;#&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# If both database.yml and the environment variable contain the same values&lt;/span&gt;
&lt;span class=&#34;token comment&#34;&gt;# (e.g. host or database name), the environment variable wins.&lt;/span&gt;
&lt;span class=&#34;token key atrule&#34;&gt;production&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt;
  &lt;span class=&#34;token key atrule&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token important&#34;&gt;*default&lt;/span&gt;
  &lt;span class=&#34;token key atrule&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; blog_production&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the host, username or password are all missing, which means that ActiveRecord will
connect by default to a socket on &lt;code&gt;localhost&lt;/code&gt; and authenticate using the current system user.&lt;/p&gt;
&lt;p&gt;In order to override that, we need to provide the following environment variables in your
local &lt;code&gt;.env&lt;/code&gt; file or as part of &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;PGHOST&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;127.0&lt;/span&gt;.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In case you might be using a database schema different than &lt;code&gt;public&lt;/code&gt; for your Rails application,
you can set that up using an environment variable as well:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;PGOPTIONS&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;-c search_path=myapp,public&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should have Postgres ready for your Ruby on Rails application!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>RESTful Webhooks in Rails</title>
    <link href="https://andreimaxim.com/posts/restful-webhooks-in-rails/" />
    <updated>2024-05-02T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/restful-webhooks-in-rails/</id>
    <content type="html">&lt;p&gt;I&#39;ve recently seen a code sample about working with Stripe webhooks in Rails
applications and I&#39;ve noticed the following code in the controller:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StripeController&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; ApplicationController
  skip_before_action &lt;span class=&#34;token symbol&#34;&gt;:verify_authenticity_token&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;only&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:webhook&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;webhook&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# Sample code for handling Stripe webhook events&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First of all, I want to point out that creating good code examples is very hard
and you always need to find a balance between good abstractions and code that is
easy to understand by the reader. In this specific case, I&#39;d argue coding best
practices are secondary to clearing showing that the code we&#39;re interested is
in this specific controller action.&lt;/p&gt;
&lt;p&gt;However, I&#39;ve seen cases where developers working with webhooks create controllers
like in the above example, which often become the first &lt;a href=&#34;https://en.wikipedia.org/wiki/Broken_windows_theory&#34;&gt;broken windows&lt;/a&gt;
in an otherwise RESTful Rails application.&lt;/p&gt;
&lt;p&gt;Fortunately, it&#39;s quite easy to avoid it.&lt;/p&gt;
&lt;p&gt;The main issue here is the language used. On the Stripe (or any other third-party service)
configuration page, we enter the URL of our &lt;a href=&#34;https://en.wikipedia.org/wiki/Webhook&#34;&gt;webhook&lt;/a&gt;,
so naturally that specific endpoint becomes &amp;quot;the webhook endpoint&amp;quot;. But the webhook is the
resource we created on the third-party application, not the resource received by our endpoint,
which is the event (or, in our case, the payment event) that triggers the webhook.&lt;/p&gt;
&lt;p&gt;Now that we have identified the resource we&#39;re working with, it&#39;s much easier to name
the controller and use the REST pattern:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Payments&lt;/span&gt;&lt;span class=&#34;token double-colon punctuation&#34;&gt;::&lt;/span&gt;EventsController &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; ApplicationController
  skip_before_action &lt;span class=&#34;token symbol&#34;&gt;:verify_authenticity_token&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;only&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:create&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token comment&#34;&gt;# Sample code for handling Stripe webhook events&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice, I&#39;ve often found that spending a bit of time identifying the resources
(which don&#39;t have to map to models!) in order to use almost exclusively RESTful
endpoints greatly simplifies the controller, which is something that &lt;a href=&#34;https://fullstackradio.com/32&#34;&gt;DHH has spoken
about in the past&lt;/a&gt; and
&lt;a href=&#34;https://www.youtube.com/watch?v=HctYHe-YjnE&#34;&gt;Derek Prior had presented at RailsConf 2017&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Instance Variable Access in Ruby</title>
    <link href="https://andreimaxim.com/posts/instance-variable-access-in-ruby/" />
    <updated>2024-04-17T00:00:00Z</updated>
    <id>https://andreimaxim.com/posts/instance-variable-access-in-ruby/</id>
    <content type="html">&lt;p&gt;Instance variables (or instance attributes) in Ruby are prefixed with an &lt;code&gt;@&lt;/code&gt; sign:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;salutation&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; salutation
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt;  &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

john &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; first_name&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;John&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Doe&#34;&lt;/span&gt;&lt;/span&gt;
john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;John Doe&#34;&lt;/span&gt;

sam &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; salutation&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Mr.&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Sam&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Johnson&#34;&lt;/span&gt;&lt;/span&gt;
sam&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;Mr. Sam Johnson&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example variables are accessed using the &lt;em&gt;direct variable access&lt;/em&gt; pattern, as
described by &lt;a href=&#34;https://www.amazon.com/Smalltalk-Best-Practice-Patterns-Kent/dp/013476904X&#34;&gt;Kent Beck in Smalltalk Best Practice Patterns&lt;/a&gt;
and it has been my preferred style, mainly because they easily stand out when you read the
code and allows you to differentiate between object methods and attributes.&lt;/p&gt;
&lt;p&gt;The other approach is to use the &lt;em&gt;indirect variable access&lt;/em&gt; pattern, which can be done by
defining accessors:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  attr_reader &lt;span class=&#34;token symbol&#34;&gt;:salutation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;salutation&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; salutation
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt;  &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; salutation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; first_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; last_name &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

john &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; first_name&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;John&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Doe&#34;&lt;/span&gt;&lt;/span&gt;
john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;John Doe&#34;&lt;/span&gt;

sam &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; salutation&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Mr.&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Sam&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Johnson&#34;&lt;/span&gt;&lt;/span&gt;
sam&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;Mr. Sam Johnson&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The side effect of defining accessors is that they are now accessible by other objects,
which might not be something you want:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;#=&gt; &#34;John&#34;&lt;/span&gt;
sam&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;salutation &lt;span class=&#34;token comment&#34;&gt;#=&gt; &#34;Mr.&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The solution is to define the accessors in a &lt;em&gt;private&lt;/em&gt; section:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;salutation&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; salutation
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt;  &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; salutation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; first_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; last_name &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;

  attr_reader &lt;span class=&#34;token symbol&#34;&gt;:salutation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

john &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; first_name&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;John&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Doe&#34;&lt;/span&gt;&lt;/span&gt;
john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;John Doe&#34;&lt;/span&gt;
john&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;first_name &lt;span class=&#34;token comment&#34;&gt;# =&gt; NoMethodError: private method `first_name&#39; called for&lt;/span&gt;
                &lt;span class=&#34;token comment&#34;&gt;#    an instance of Person&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it may seem that the main difference between &lt;em&gt;indirect variable access&lt;/em&gt; and
&lt;em&gt;direct variable access&lt;/em&gt; patterns is mainly stylistic, it does have an impact
when using inheritance because you can override methods, but you can&#39;t override instance variables.&lt;/p&gt;
&lt;p&gt;For example, the salutation of the members of a royal house need to start with &amp;quot;HRH&amp;quot;
from &amp;quot;His Royal Highness&amp;quot; or &amp;quot;Her Royal Highness&amp;quot; and has to include the title
(the &lt;a href=&#34;https://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(royalty_and_nobility)&#34;&gt;naming conventions for royalty and nobility are actually quite complex&lt;/a&gt;).
If we used direct variable access, we would need to reimplement the &lt;code&gt;name&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;salutation&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; salutation
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt;  &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Royalty&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; Person
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@title&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; title
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;salutation&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;HRH &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;&lt;span class=&#34;token variable&#34;&gt;@title&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; salutation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

charles &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Royalty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; title&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;King&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Charles&#34;&lt;/span&gt;&lt;/span&gt;
charles&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;HRH King Charles&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also means that if we plan to support name suffixes later on we&#39;ll need to change
the &lt;code&gt;name&lt;/code&gt; method in both the &lt;code&gt;Person&lt;/code&gt; and &lt;code&gt;Royalty&lt;/code&gt; classes.&lt;/p&gt;
&lt;p&gt;On the other hand, if we use indirect variable access, we can override the &lt;code&gt;salutation&lt;/code&gt;
attribute to always start with &amp;quot;HRH&amp;quot;, while adding a suffix will require a change to
the &lt;code&gt;name&lt;/code&gt; method only on the parent &lt;code&gt;Person&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class=&#34;language-ruby&#34;&gt;&lt;code class=&#34;language-ruby&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Person&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;salutation&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@salutation&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; salutation
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt;  &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
    &lt;span class=&#34;token variable&#34;&gt;@suffix&lt;/span&gt;     &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; suffix
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt; salutation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; first_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; last_name&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; suffix &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;compact&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;join&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34; &#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;

  attr_reader &lt;span class=&#34;token symbol&#34;&gt;:salutation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:first_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:last_name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;:suffix&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Royalty&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; Person
  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token symbol&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;last_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;token variable&#34;&gt;@title&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; title
    &lt;span class=&#34;token variable&#34;&gt;@first_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; first_name
    &lt;span class=&#34;token variable&#34;&gt;@last_name&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; last_name
    &lt;span class=&#34;token variable&#34;&gt;@suffix&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; suffix
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;token method-definition&#34;&gt;&lt;span class=&#34;token function&#34;&gt;salutation&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;HRH &lt;/span&gt;&lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;token content&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;token delimiter punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt;

  attr_reader &lt;span class=&#34;token symbol&#34;&gt;:title&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;end&lt;/span&gt;

charles &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Royalty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; title&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;King&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;first_name&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Charles&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token symbol&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string-literal&#34;&gt;&lt;span class=&#34;token string&#34;&gt;&#34;III&#34;&lt;/span&gt;&lt;/span&gt;
charles&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;name &lt;span class=&#34;token comment&#34;&gt;# =&gt; &#34;HRH King Charles III&#34;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, as you can see in this rather contrived example, it is not often that we
need to override an attribute in the parent class with a method in the child class, so,
at the end of the day, it&#39;s mostly a stylistic decision.&lt;/p&gt;
&lt;p&gt;Also, as with every stylistic decision, it&#39;s much more important to be consistent,
at least at the class level.&lt;/p&gt;
</content>
  </entry>
  
</feed>
