MaxLap.devRuby, Rails, Rant & Toolshttps://maxlap.dev/blog2023-12-10T19:00:00-05:00Maxime LapointeThe lesser known Rails race conditionhttps://maxlap.dev/blog/2023/12/11/the-lesser-known-rails-race-condition.html2023-12-10T19:00:00-05:002023-12-11T08:27:15-05:00Maxime Lapointe<div class="markdown-body"><p>Note: This post mentions and gives examples in Rails, but the content is relevant to anyone using databases systems,
regardless of the use cases.</p>
<p>There are many posts around discussing race conditions in Rails and how to handle/avoid them, but I haven't seen
one discussion this particular case.</p>
<p>Simply put, race conditions happen when more than one worker/process/thread interact with the same data and
the order those interactions happen in can give undesirable results.</p>
<h2 id="classical-race-condition">Classical race condition</h2>
<p>The classical example of a race condition is having 2 processes altering the same bank account at the same time.
Depending on the order that operations happen in, some of the alterations might be lost.</p>
<p>Starting with 100$, if one process does a +10 and another a -15 at the same time. You can end up with each of the
following amounts in the account:</p>
<ul>
<li>110 (wrong)</li>
<li>85 (wrong)</li>
<li>95 (good)</li>
</ul>
<p>I won't delve into this since there are already lots of articles about and how to avoid it in Rails.
<a href="https://www.honeybadger.io/blog/avoid-race-condition-in-rails/">Here's one</a>.</p>
<h2 id="the-lesser-known-race-condition">The lesser known race condition</h2>
<p>Let's think about a single writer and a single reader. Reading some of the articles on Rails race conditions, you
could believe everything will be fine.</p>
<p>Consider the database already contains these two records:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="no">Post</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">published: </span><span class="kp">true</span><span class="p">)</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">published: </span><span class="kp">false</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><p>Now if you have a reader doing a report:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="n">report</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">report</span><span class="p">[</span><span class="ss">:total</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">count</span>
<span class="n">report</span><span class="p">[</span><span class="ss">:published</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">published: </span><span class="kp">true</span><span class="p">).</span><span class="nf">count</span>
<span class="n">report</span><span class="p">[</span><span class="ss">:unpublished</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">published: </span><span class="kp">false</span><span class="p">).</span><span class="nf">count</span>
<span class="nb">puts</span> <span class="n">report</span></code></pre></div></div>
<div class="markdown-body"><p>And you have a writer adding some data at the same time:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="no">Post</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">published: </span><span class="kp">false</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><p>Here are the possible reports you might get:</p>
<ul>
<li>{total: 2, published: 1, unpublished: 1}</li>
<li>{total: 2, published: 1, unpublished: 2}</li>
<li>{total: 3, published: 1, unpublished: 2}</li>
</ul>
<p>One of these is incoherent. You can't have 2 posts total but then have numbers that add up to 3 at the same time.</p>
<p>This example may feel contrived. It almost always feel like a technicality; the shown result is clearly wrong, but the
database doesn't end up with wrong data. You can "just" refresh the page the very few times this happens and the
problem will be gone. It's one of those little glitch that no one can reproduce and no one bothers tracking down
since it doesn't happen often anyways.</p>
<p>If something like this is in some kind of nightly report, it's not so easy to "just" refresh the page.</p>
<h2 id="but-wait-theres-more">But wait, there's more!</h2>
<p>You might be tempted to think that this only happens when doing multiple queries on a single table, so you are safe
as lon as you query only once per table. If you define the problem as any incoherency caused by the timing, you would
be wrong.</p>
<p>If the reader does:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="n">post</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">{}</span>
<span class="c1"># Querying the posts table</span>
<span class="n">report</span><span class="p">[</span><span class="ss">:has_admin_comment</span><span class="p">]</span> <span class="o">=</span> <span class="n">post</span><span class="p">.</span><span class="nf">has_admin_comment</span> <span class="c1"># an attribute</span>
<span class="c1"># Querying the comments table</span>
<span class="n">report</span><span class="p">[</span><span class="ss">:comments</span><span class="p">]</span> <span class="o">=</span> <span class="n">post</span><span class="p">.</span><span class="nf">comments</span> <span class="c1"># Imagine they are rendered in a page</span>
<span class="nb">puts</span> <span class="n">report</span></code></pre></div></div>
<div class="markdown-body"><p>And you have a writer adding some data at the same time:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># A callback will see the post's has_admin_comment</span>
<span class="c1"># Imagine a call</span>
<span class="n">post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">by_admin: </span><span class="kp">true</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="n">post</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="ss">has_admin_comment: </span><span class="kp">true</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><p>There is now the possibility that the report contains a comment by an admin while the <code>has_admin_comment</code> attribute
says there are no such comments.</p>
<p>So as soon as you do 2 queries, if any of the data of those two queries is related to the other query, there is a
chance you'll encounter this. Bummer, I frequently do more than one SQL query in my pages.</p>
<h2 id="a-solution">A solution</h2>
<p>The root of the problem is that we are reading data at different points in time, but we want to treat it as if it was
from a single point in time.</p>
<p>The only solution I'm aware of (other than doing only one query), is to tell the database: "Ignore all changes from
elsewhere when working on my queries". Where elsewhere means other processes / transations.</p>
<p>In PostgreSQL, that can be done with a transaction with the <code>repeatable_read</code> isolation level. From <a href="https://www.postgresql.org/docs/current/transaction-iso.html">PostgreSQL's doc</a>:</p>
<blockquote>
<p>The Repeatable Read isolation level only sees data committed before the transaction began; it never sees
either uncommitted data or changes committed during transaction execution by concurrent transactions.</p>
</blockquote>
<p>So going back to our first example, if the reader did this instead:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="no">Post</span><span class="p">.</span><span class="nf">transaction</span><span class="p">(</span><span class="ss">isolation: :repeatable_read</span><span class="p">)</span> <span class="k">do</span>
 <span class="n">report</span> <span class="o">=</span> <span class="p">{}</span>
 <span class="n">report</span><span class="p">[</span><span class="ss">:total</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">count</span>
 <span class="n">report</span><span class="p">[</span><span class="ss">:published</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">published: </span><span class="kp">true</span><span class="p">).</span><span class="nf">count</span>
 <span class="n">report</span><span class="p">[</span><span class="ss">:unpublished</span><span class="p">]</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">published: </span><span class="kp">false</span><span class="p">).</span><span class="nf">count</span>
 <span class="nb">puts</span> <span class="n">report</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p>Then only the 2 valid reports would be possible:</p>
<ul>
<li>{total: 2, published: 1, unpublished: 1}</li>
<li>{total: 3, published: 1, unpublished: 2}</li>
</ul>
<p>Note: In other database systems (such as MySQL or SQLite), it's possible that you need a different isolation level
than <code>repeatable_read</code>.</p>
<h2 id="when-to-solve-it">When to solve it?</h2>
<p>I think the final question is when should you "solve" this problem? When should you run within a repeatable_read
transaction?</p>
<p>I don't know... It depends.</p>
<p>I have never actually tackled this problem in an application-wide manner... yet. So take these suggestions with a
grain of salt.</p>
<p>Doing more restrictive transactions can have an impact on performance, can add latency, could introduce deadlocks,
could increase the number of transactions that need to be retried. But I'm not sure this is actually a problem in
practice.</p>
<p>When starting a new application, I would consider wrapping every request in a transaction and calling it a day.
It's too early to focus on scaling/performance in the beginning. The day you grow enough (if that happens) or
encounter problems caused to this, then you can either disable this for some request or start doing it only for
more critical requests.</p>
<p>For existing applications, wrapping every request could cause new problems to appear:</p>
<ul>
<li>Existing race conditions you didn't know about might now produce an error. (I think this is good)</li>
<li>Nested transactions can behave slightly differently.</li>
<li>Locks will be held longer, possibly resulting in longer latency and deadlocks.</li>
</ul>
<p>So is it worth it to wrap every request for existing application? Your guess is as good as mine. Maybe do it,but progressively?</p>
<p>One thing to consider about this is that while you can "just" refresh a page, it's not always so simple.
If you are generating a pdf or generating new data from what you queries, then the glitch isn't quite so temporary
anymore, is it?</p>
<p>So at the very least, you probably want to use the <code>repeatable_read</code> transaction whenever you are generating
something longer lasting than a web page, no matter the consequences.</p>
<h2 id="code-to-apply-everywhere">Code to apply %everywhere</h2>
<p>In my case, for my new application, I'm trying it. It's barely any code at all.</p>
<p>Here is a callback on my <code>ApplicationController</code>:</p>
<pre><code>around_action :wrap_in_transaction
def wrap_in_transaction
 ActiveRecord::Base.transaction(isolation: :repeatable_read) do
 yield
 end
end
</code></pre>
<p>It's that simple! Except... It's likely your tests may fail, because they also start a transaction, with a different
isolation level.</p>
<p>So add this your your <code>test_helper.rb</code> / <code>spec_helper.rb</code>:</p>
<pre><code># Tests already use a transaction, this tries to nest with a distinct isolation level, which breaks. So skip
ApplicationController.skip_around_action :wrap_in_transaction
</code></pre>
<h2 id="closing-words">Closing words</h2>
<p>Hope you learned something useful!</p>
<p>If you want to play with examples that are similar to the post and demonstrate the effects of the <code>repeatable_read</code>
transaction, here is a <a href="/blog/2023/12/11/the-lesser-known-rails-race-condition.rb">self-contained ruby script</a>.</p>
</div>
Vision and glasseshttps://maxlap.dev/blog/2023/07/15/vision-and-glasses.html2023-07-14T20:00:00-04:002023-10-12T13:13:23-04:00Maxime Lapointe<script src="https://unpkg.com/paper@0.12.17/dist/paper-full.min.js"></script>
<script src="/javascripts/optics.js"></script>
<div class="markdown-body"><p>This is not in my usual style. This is a little visualization tool I wanted to have. It's likely this will be
improved upon over time. I just wanted to release it to be able to show it to friends and family.</p>
<p>Other than "glasses work by bending light and usually make us see clearer", the way they
interact with our vision is not common knowledge. The goal here is to help you understand the
effects of glasses on your vision and maybe help you understand how prescriptions impact you.</p>
<p>I'm not an optometrist, but I learned a lot on the subject. There are of course simplifications in here.</p>
<p>Your eye is a lens that tries to focus (to bend) all of the light coming from a single point in front of it
to a single point on its retina (it's back surface).</p>
<p>What makes this hard is that that the closer an object, the stronger the lens must be to focus it at the
same spot. Yet we can see clearly from many distances, not just from a specific once. The eye
accomplishes this by having a lens than can change strength: the crystalline lens.</p>
<p>The strength of lenses is measured using diopters. Without going into details, what interests us is the
size in meters of a diopter... It varies based on the distance!</p>
<p>Move your mouse in this diagram. The top line is a distance in meter from the left. The second line
is consecutive 0.25 diopters. When you move the mouse, you will see the highlighted the distance when
going closer (left, aqua) or farther (right, orange) by 0.25 diopter.</p>
</div>
<canvas style="height: 200px; width: 100%" id="paperscript_canvas_1"></canvas>
<script type='text/paperscript' data-paper-canvas="paperscript_canvas_1">
project.currentStyle = {
strokeColor: 'black',
fillColor: 'black',
strokeWidth: 1,
};
var diopter_before_cursor = 0.25;
var diopter_after_cursor = 0.25;
view.scrollBy(new Point(-25, -10))
makeMeterRuler(paper, 10, 4)
makeDiopterRuler(paper, 50, 4, 0.25, 10)
var vision_area_layer = new Layer()
project.insertLayer(0, vision_area_layer)
var vision_highlight_left = makeVisionArea(paper, vision_area_layer, 10, 40, 'aqua')
var vision_highlight_right = makeVisionArea(paper, vision_area_layer, 10, 40, 'orange')
var cursor_distance_text = new PointText(new Point(20, 100))
cursor_distance_text.strokeWidth = 0
var min_distance_text = new PointText(new Point(20, 130))
min_distance_text.strokeWidth = 0
var max_distance_text = new PointText(new Point(20, 160))
max_distance_text.strokeWidth = 0
function updateDistances(x) {
if (x < 0) {
x = 0
}
var cursor_position_m = pixelToMeter(paper, x);
var left_xs = updateVisionAreaWithShiftPixelByDiopter(paper, vision_highlight_left, x, 0.25, 0)
var right_xs = updateVisionAreaWithShiftPixelByDiopter(paper, vision_highlight_right, x, 0, -0.25)
var min_distance = pixelToMeter(paper, left_xs[0])
var max_distance = pixelToMeter(paper, right_xs[1])
cursor_distance_text.content = "Cursor position: " + cursor_position_m.toFixed(3) + "m";
min_distance_text.content = "Min distance: " + min_distance.toFixed(3) + "m";
max_distance_text.content = "Max distance: " + max_distance.toFixed(3) + "m";
}
function onMouseMove(event) {
updateDistances(event.point.x);
}
function onMouseDown(event) {
updateDistances(event.point.x);
}
</script>
<div class="markdown-body"><p>Notice how the aqua side is smaller than the orange one? When you get close to half a meter, the
sizes gets tiny! And at the other extreme, they get huge! In fact, at 4m, adding 0.25 diopter takes you do infinity!</p>
<p>The crystalline lens of the eye is great, but is has limits. This means that the eye has both a minimum and
a maximum distance it can see. Try it, bring a piece of text close to your eye. At one point, it will get blurry.</p>
<p>How much the eye can change strength lower as you age. Wikipedia says "The age-related decline in accommodation [the
strength change] occurs almost universally to less than 2 dioptres by the time a person reaches 45 to 50 years"</p>
<p>So by the time you reach 45 to 50, your eye is likely to be able to adjust by less than 2 diopters.</p>
<p>Lets visualize this. Here, the mouse location represent your minimum distance and the orange zone represent 2 diopter
farther: your optimistic but likely maximum distance.</p>
</div>
<canvas style="height: 200px; width: 100%" id="paperscript_canvas_2"></canvas>
<script type='text/paperscript' data-paper-canvas="paperscript_canvas_2">
project.currentStyle = {
strokeColor: 'black',
fillColor: 'black',
strokeWidth: 1,
};
var diopter_before_cursor = 0;
var diopter_after_cursor = -2;
view.scrollBy(new Point(-25, -10))
makeMeterRuler(paper, 10, 4)
makeDiopterRuler(paper, 50, 4, 0.25, 10)
var vision_area_layer = new Layer()
project.insertLayer(0, vision_area_layer)
var vision_highlight_right = makeVisionArea(paper, vision_area_layer, 10, 40, 'orange')
var cursor_distance_text = new PointText(new Point(20, 100))
cursor_distance_text.strokeWidth = 0
var min_distance_text = new PointText(new Point(20, 130))
min_distance_text.strokeWidth = 0
var max_distance_text = new PointText(new Point(20, 160))
max_distance_text.strokeWidth = 0
function updateDistances(x) {
if (x < 0) {
x = 0
}
var cursor_position_m = pixelToMeter(paper, x);
var right_xs = updateVisionAreaWithShiftPixelByDiopter(paper, vision_highlight_right, x, diopter_before_cursor, diopter_after_cursor)
var min_distance = pixelToMeter(paper, right_xs[0])
var max_distance = pixelToMeter(paper, right_xs[1])
cursor_distance_text.content = "Cursor position: " + cursor_position_m.toFixed(3) + "m";
min_distance_text.content = "Min distance: " + min_distance.toFixed(3) + "m";
max_distance_text.content = "Max distance: " + max_distance.toFixed(3) + "m";
}
function onMouseMove(event) {
updateDistances(event.point.x);
}
function onMouseDown(event) {
updateDistances(event.point.x);
}
</script>
<div class="markdown-body"><p>If your mouse is close to the left edge, such as minimum distance of 0.25m / 25cm, then the maximum distance
would still be quite close at 0.5m / 50cm.</p>
<p>If you move your mouse to 0.5m, then finally, you get to see clearly at any distance... As long as
that distance is above 0.5m.</p>
<h3 id="vision-glasses">Vision glasses</h3>
<p>Ok, so how do glasses fit into this? Well they are lenses and are rated in diopters! And when you have multiple lenses,
the effect is similar to a single lens with the diopters added. This means when you wear glasses, your whole
vision range, minimum and maximum, get shifted by that many diopters.</p>
<p>The two main categories of people wearing vision glasses into are:</p>
<ul>
<li>nearsighted (myopia): Without glasses, they see clearly objects that are close, but those that are far are blurry.</li>
<li>farsighted (hyperopia): Without glasses, they see clearly objects that are far, but those that are close get blurry.<br>
As mentioned, an object that is really close to the eye will be blurry for anyone. The idea here is about comfort, if
you can't see clearly an object that is half a meter (18 inches) away from you, this can be quite annoying.</li>
</ul>
<p>For nearsighted people, glasses will normally have the strength required to bring their vision's maximum distance to
close to infinity (from 10 meters, infinity is only 0.1 diopter away). For younger people, their vision's
minimum distance will remain low enough to not even notice that it moved.</p>
<p>For farsighted people, it's all about that minimum distance. It's too far! But unlike nearsighted people, there isn't
a clear limit to how much it can be corrected. With strong enough glasses, your minimum could become 10cm. Instead,
the goal is to move the vision close enough for most close work to be comfortable. This may vary based on what you do.</p>
<h3 id="progressive-lenses-multi-focal">Progressive lenses / multi-focal</h3>
<p>TODO: I lack expertise to continue here...</p>
</div>
Rant: I miss LastPasshttps://maxlap.dev/blog/2023/02/24/rant-i-miss-lastpass.html2023-02-23T19:00:00-05:002023-10-12T13:13:23-04:00Maxime Lapointe<div class="markdown-body"><p>This post is a bit of a rant / review / basic wishlist for password manager.</p>
<p>Like many, I stopped using LastPass when their recent breach happened and noticed how slow they were to provide details. But now after using an alternative (Keeper) for a while, which took me a while to pick, I realize that I miss LastPass.</p>
<p>While the breach made LastPass feel unsafe to many, including me, some of their basic options made me feel safer and less annoyed in day to day usage.</p>
<p>Enough with the preamble, lets talk features!</p>
<h3 id="emergency-access-contacts">Emergency Access contacts</h3>
<p>This is the possibility to setup contacts to be able to get access to my passwords should an emergency occur. The access should be granted after a delay of my choosing, so that I can prevent a wrongful emergency access.</p>
<p>I have lots of things spread in many place, if something happens to me, I want it to be possible for my partner to get access to them.</p>
<p>To me, this feature is a must have. If a password manager doesn't have something that make sense for this, I'm not bothering installing it or learning more about it.</p>
<p>I don't want to share master password because I care about privacy; I don't want her password either.</p>
<p>When I browsed around, I was astounded by how many password managers don't have something that make sense for this. Lets go over the main contenders.</p>
<p><strong>LastPass</strong>: It has the full package. That's an important reason I went with it in the beginning.</p>
<p><strong>Dashlane</strong>: You make a password protected backup of your passwords and make both accessible to the person, ideally in two different ways and ideally without giving immediate access to the file. They describe this all at high level, with recommendations. But it's really just that, a backup that you share, something you can do with any Password Manager that allows you to export data. When you change your passwords, they won't be updated and you need another backup. You also will have no clue if/when the person accesses the backup. And Dashlane dares to put "A way to share your Dashlane data with people you trust during an emergency" in their features, marketing BS...</p>
<p><strong>1Password</strong>: If you thought Dashlane was bad, 1Password just straight up tells you to give you master password to someone you trust. They market it as making an "emergency kit", provide you a cute template to make sure you don't forget the email and secret key. But it's just giving your password to someone.</p>
<p><strong>Zoho vault</strong>: To be honest, Zoho didn't pop in my radar when I was doing my replacement search. It just did now that I'm writing this post. In their system, you setup contacts to receive the full access in case of an emergency. An email is sent to all contacts in the system to alert them when an emergency is declared. This is pretty nice. For an enterprise setting, this sounds quite reasonable. The only thing missing is the possibility of setting a delay so that the emergency can be stopped before access is given.</p>
<p><strong>Bitwarden</strong>: It has the full package. You setup contacts, they can trigger the emergency access, you receive an email, the person gets access after a delay you've setup unless to deny it (you can also approve from the email). Good job!</p>
<p><strong>Keeper</strong>: They have the full emergency access package just like Bitwarden. Good job! Searching about it though, I can't really find documentation about this other than a blog post.</p>
<p>These appeared to be the main contenders in this space. So they're are the only one I've used and can have more feedback on.</p>
<h3 id="bitwarden">Bitwarden</h3>
<p>So, after LastPass, I first tried Bitwarden, but quickly got turned off from it.</p>
<p>To share things with other users, you create an organization and put the users in there. In the organization, you then create collections (folders) which contain the passwords. You can then give access to users based on the collection. It can make sense, but they way it's setup, moving password to the organization is pretty easy, removing them from it (to make them private again) is a pain. You can't. You must recreate it in your private account and delete from the organization.</p>
<p>The organization idea sounds good at the start, but it ends up feeling more like a hassle when interacting with them in the UI. Simple shared folders would do the trick without needing an organization layer where there is now a concept of administrator which makes the concept of privacy a bit weird. This feels focused on the enterprise context.</p>
<p>I tried to import my passwords from LastPass into my account that had the "Families" plan and every passwords got imported into the organization instead of my private account and would therefore have been shared if my partner had already made her account. I contacted the support about it and was told basically that I could try again with a link to the doc... Which I did and somehow the passwords got there again... I didn't see a way to select where they go, but maybe I got confused by the way you need to always pick on which organization you are working on.</p>
<p>I didn't even get to try the Android app before I was tired of this. I couldn't think of myself trying to explain how to do basic things in this to my partner without myself getting frustrated. I wasn't even sure how to get her to import her data without it becoming accessible to me? I quickly moved on.</p>
<h3 id="keeper">Keeper</h3>
<p>Keeper was the next and last one I saw recommended that matched the very high bar of having a meaningful emergency access. Many people spoke nicely about it online. It's the one I still use, so it's a positive sign about it at least.</p>
<p>It's a lot more intuitive than Bitwarden was, that's for sure. But that's kind of easy when you have so few features and so few options.... I'd describe this as the Apple of password managers? It does things one way, if you hope to change it behavior, well, there are about 10 checkbox total as settings, and basically no options on a per-login aspect.</p>
<p>This is what I'm using right now.</p>
<h3 id="feeling-less-safe-than-with-lastpass">Feeling less safe than with LastPass</h3>
<p>There is always this question of when/how often should you have to enter your master password. Too often gets annoying, not often enough can feel unsafe.</p>
<p>LastPass has these two options that I activated:</p>
<ul>
<li>make showing/copying any password require to re-enter the master password</li>
<li>allow configuring specific logins to require to re-enter the master password to use them</li>
</ul>
<p>The first feature doesn't add much safety because it's quite easy to open the login form, tell LastPass to fill it, and then get the password from the password field. It but it adds a little friction to just browsing the vault and taking every passwords.</p>
<p>The second option does make things actually secure. Applying it to financial, email and identity related sites is great.</p>
<p>With those two options, I didn't feel the need to have a timeout on my LastPass session. LastPass allowed me quickly use all those logins that I don't care much about without ever re-entering my master password, but still have the safety for the important passwords.</p>
<p>Now, with Keeper (and probably many other managers), my only choice is how long do I want before I get logged out... I can specify how many minutes, but it all comes down to how many times per day I want to have to write my password... It's not related to what the password is being used for.</p>
<p>Oh, and Keeper makes entering the password just a bit extra painful! It first shows your email so you can edit it (that must almost never be needed), you must click next, wait a tiny bit, and then you get the password prompt. Why the extra click? Maybe because in some case there is a 2FA, and they just have a single UI flow? No idea, but poor user experience.</p>
<p>So yeah, in the end, I settled for having to entered my master password once a day... I'm usually at home, so the hassle of having to do it many times is greater than what I consider the risk to be... But wouldn't it be great if I didn't have to settle and a Password Manager just had those "basic" (in my mind) features.</p>
<p>So for now, I'm using Keeper. At one point I'll probably look around hoping for alternatives to have a better story for emergency access so that trying them isn't a waste of time and I find one that suits me better.</p>
</div>
What makes Ruby's blocks greathttps://maxlap.dev/blog/2022/02/10/what-makes-ruby-blocks-great.html2022-02-09T19:00:00-05:002023-10-12T13:13:23-04:00Maxime Lapointe<div class="markdown-body"><p>In Ruby, we often talk about "blocks". While not unique to Ruby, few mainstream languages
have something I would consider equivalent and, to be frank, I think they're missing out.</p>
<p>Just explaining to someone what's special about them can be tricky. I think many rubyists
use blocks without realizing that they are different from what's possible in other
mainstream languages.</p>
<p>This post is an attempt to explain what's great about blocks without being a verbose
in-depth guide. It's written with non-rubyists in mind. I hope it can pique your interest.</p>
<h2 id="anonymous-function-with-a-nicer-syntax">Anonymous function with a nicer syntax</h2>
<p>That's the first thing one may say to describe them, or the first thing that someone
hearing about them will think of, and... that's a start. The nicer syntax is certainly an
important part of what makes Ruby's blocks feel great to use, but there is more than just that.</p>
<p>Compared to anonymous functions, blocks are more powerful and more consistent with other
standard constructs such as for loops. So much so that they have entirely replaced for loops
in Ruby; you instead call a method and pass it a block.</p>
<p>Blocks managed to do that because they have more control over the flow of code than your regular
anonymous functions, giving them as much power as a for loop.</p>
<p>To explain what I mean, let's work with a simple "for each" loop. Here is what a block looks like
in Ruby.</p>
</div>
<div class="code-book "><div class="code-book__left"><div class="markdown-body"><p>To make it easier for non-rubyists, I'm using a style closer to other languages.</p>
<p>What we call a "block" goes from <code>do</code> to <code>end</code>.</p>
<p>Quick code explanation:</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">each</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="nb">puts</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="k">end</span></code></pre></div></div></div><div class="code-book__left"><div class="markdown-body"><p>An Array literal, it "returns" an Array with those 4 values</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]</span></code></pre></div></div></div><div class="code-book__left"><div class="markdown-body"><p>Calls the method <code>each</code> (on the Array), passing it the block that follows the parentheses</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="p">.</span><span class="nf">each</span><span class="p">()</span> <span class="k">do</span> <span class="o">...</span> <span class="k">end</span></code></pre></div></div></div><div class="code-book__left"><div class="markdown-body"><p>The parameters the block accepts: one parameter called "v".</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="o">|</span><span class="n">v</span><span class="o">|</span></code></pre></div></div></div><div class="code-book__left"><div class="markdown-body"><p>The content of the block, what will run when it is called. This prints the value to the console.</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="nb">puts</span><span class="p">(</span><span class="n">v</span><span class="p">)</span></code></pre></div></div></div></div>
<div class="markdown-body"><p>Other than the clean syntax for passing the block ("anonymous function"), this is nothing special.
In javascript, this would be:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight javascript"><code><span class="c1">// Another syntax exists, but I think this is more universally recognizable</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span> <span class="p">{</span>
 <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">v</span><span class="p">);</span>
<span class="p">})</span></code></pre></div></div>
<div class="markdown-body"><p>From this common ground, let's see how blocks compare to passing anonymous functions for
replacing loops.</p>
<h2 id="break"><code>break</code></h2>
<p>Most languages with loops allows you to exit early from them. Usually with a <code>break</code> keyword.</p>
<p>Think of the previous examples, but stopping (like <code>break</code>) after <code>v == 3</code>.</p>
<p>How do you do this with the <code>forEach</code> method in JavaScript? The simple answer is that you can't
do it simply. You either switch to an actual for loop or use exceptions or some other workaround.</p>
<p><code>break</code> cannot be used to exit an anonymous function. But in Ruby, in a block...</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">each</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="nb">puts</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
 <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="mi">3</span>
 <span class="k">break</span>
 <span class="k">end</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p><code>break</code> just works.</p>
<p><code>break</code> exits from the function to which the block was passed, so the block can stop early
just like in regular for loop.</p>
<p>We'll talk more about <code>break</code> in a bit; it can do more, but I want to go over the simpler
cases first.</p>
<h2 id="return"><code>return</code></h2>
<p>How about returning from a function from within a loop? That's pretty common.</p>
<p>You, again, can't do this using anonymous functions in most languages. The <code>return</code> keyword
would only return from the anonymous function, and its caller would call it again for the
next iterations.</p>
<p>But it's easy in Ruby with its blocks:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="k">def</span> <span class="nf">my_func</span><span class="p">()</span> <span class="c1"># Just defining a function to return out of</span>
 <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">each</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="nb">puts</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
 <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="mi">3</span>
 <span class="k">return</span> <span class="mi">42</span>
 <span class="k">end</span>
 <span class="k">end</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p>When the code reaches the <code>return</code>, it's not the block which returns 42, it's actually
<code>my_func</code>. <code>return</code> leaves the block, the call to <code>each</code>, and returns from <code>my_func</code>
with the specified value. Just like <code>return</code> does in actual loops.</p>
<p>The behavior of <code>return</code> with anonymous functions is still useful. If you think of loops, this
is the equivalent of the <code>continue</code> / <code>next</code> keywords. So let's move over to these.</p>
<h2 id="continue-next"><code>continue</code> / <code>next</code></h2>
<p>In loops, we sometimes want to skip an iteration, just go to the next one. Most languages
will have a <code>continue</code> or <code>next</code> keyword that can be used in loops.</p>
<p>Instead of stopping, let's skip the iteration where <code>v == 3</code>.</p>
<p>As mentioned in the previous section, JavaScript and most other languages can achieve this
in anonymous functions using <code>return</code>, but they cannot use the standard <code>continue</code> or <code>next</code>:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight javascript"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span> <span class="p">{</span>
 <span class="k">if</span> <span class="p">(</span><span class="nx">v</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
 <span class="k">return</span><span class="p">;</span>
 <span class="p">}</span>
 <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">v</span><span class="p">);</span>
<span class="p">})</span></code></pre></div></div>
<div class="markdown-body"><p>Ruby has <code>next</code> and it works in blocks exactly as one would expect in a loop.</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">each</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="mi">3</span>
 <span class="k">next</span>
 <span class="k">end</span>
 <span class="nb">puts</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p>Pretty neat.</p>
<p>This is where the parallels with for loops end.</p>
<h2 id="next-42"><code>next 42</code></h2>
<p>Blocks are used for more than just replacing a simple for loop. Let's switch to using
<code>map</code> for our examples. For those unfamiliar, <code>map</code> is a "for loop" which also saves a
return value for each iteration in an array and returns that array.</p>
<p>As an example, <code>squares</code> gets set to an array of the square (2nd power) of each number.</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="n">squares</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">map</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="c1"># Ruby detail: the `next` is optional for the last expression, it always has a `next`</span>
 <span class="k">next</span> <span class="n">v</span> <span class="o">**</span> <span class="mi">2</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p><code>next</code> can receive a value. It's what it will "return" to the method that called the block
(<code>map</code> in this case). It really is just a <code>return</code> that stays at the block's level.</p>
<p>In JavaScript, you would use <code>return</code> with a value.</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="n">squares</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">map</span><span class="p">(</span><span class="n">function</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="p">{</span>
 <span class="k">return</span> <span class="n">v</span> <span class="o">**</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">})</span></code></pre></div></div>
<div class="markdown-body"><h2 id="break-42"><code>break 42</code></h2>
<p>In Ruby, <code>break</code> can also receive a value. <em>Break</em>-ing news, right? :)</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># We add a secret override code... it's an example ok! suggest a better one in the comments</span>
<span class="n">squares</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">666</span><span class="p">,</span> <span class="mi">4</span><span class="p">].</span><span class="nf">map</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span>
 <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="mi">666</span>
 <span class="k">break</span> <span class="p">[</span><span class="o">-</span><span class="mi">666</span><span class="p">]</span>
 <span class="k">end</span>
 <span class="n">v</span> <span class="o">**</span> <span class="mi">2</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><p>In addition to breaking out of the function that received the block (the <code>map</code> in this example),
the function will return the value given to <code>break</code>. So in the above example, <code>squares</code> will
contain <code>[-666]</code>.</p>
<p>Again, no equivalent here in JavaScript and most other languages.</p>
<p>These were the main things that make blocks in Ruby more than just anonymous functions.
Still with me? Here is a little wrap up.</p>
<h2 id="building-an-intuition">Building an intuition</h2>
<p>With the right perspective, blocks' behaviors are quite intuitive.</p>
<p>When dealing with loops, you have 3 nested constructs interacting: a wrapping function, a loop
statement and the loop's body; and you have 3 keywords to choose where the flow of the code goes.</p>
<ul>
<li><code>return</code> returns from the wrapping function</li>
<li><code>break</code> leaves the loop statement</li>
<li><code>next</code> / <code>continue</code> leaves loop's body</li>
</ul>
<p>When dealing with blocks or anonymous functions, it's instead 3 nested "functions" that are
interacting: a wrapping function, a called function and an anonymous functions (or block).</p>
<p>Ruby's blocks, let you use the same 3 keywords to choose where the flow of the code goes.</p>
<ul>
<li><code>return</code> returns from the wrapping function (ex: <code>my_func</code>)</li>
<li><code>break</code> returns from the called function (ex: <code>each</code>, <code>map</code>)</li>
<li><code>next</code> returns from the block</li>
</ul>
<p>Quite consistent. But since we are talking about functions instead of statements (loop),
return values are also involved. Allowing both <code>break</code> and <code>next</code> to provide a return value fits
well in that model and is quite useful. The 3 keywords are <code>return</code>, but they have different targets.</p>
<h2 id="more-than-just-loops">More than just loops</h2>
<p>I've been focusing on looping structures because those are common and simple to understand,
but blocks are used for lots of things.</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="s2">"my_file.txt"</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
 <span class="n">f</span><span class="p">.</span><span class="nf">puts</span><span class="p">(</span><span class="s1">'x'</span><span class="p">)</span>
 <span class="n">f</span><span class="p">.</span><span class="nf">puts</span><span class="p">(</span><span class="s1">'y'</span><span class="p">)</span>
 <span class="n">f</span><span class="p">.</span><span class="nf">puts</span><span class="p">(</span><span class="s1">'z'</span><span class="p">)</span>
 <span class="c1"># It is automatically closed when leaving the block</span>
 <span class="c1"># Including if you use return/break/next within</span>
<span class="k">end</span>

<span class="n">thr</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span><span class="p">()</span> <span class="k">do</span>
 <span class="c1"># Do stuff in your thread</span>
<span class="k">end</span>

<span class="no">Timeout</span><span class="p">.</span><span class="nf">timeout</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="k">do</span>
 <span class="c1"># Code that will be interrupted if it takes more than 5 seconds</span>
<span class="k">end</span>

<span class="c1"># (on a Hash, often called a Map or a Dictionary in different languages)</span>
<span class="c1"># The block is only called if "my_key" is not in the hash.</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">my_hash</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"my_key"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="o">|</span>
 <span class="c1"># generate default value, maybe set it in the hash</span>
<span class="k">end</span>

<span class="c1"># Ok, another loop because it's cute</span>
<span class="mi">5</span><span class="p">.</span><span class="nf">times</span><span class="p">()</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
 <span class="c1"># This block gets called 5 times, with `i` going from 0 to 4</span>
<span class="k">end</span></code></pre></div></div>
<div class="markdown-body"><h2 id="tool-making">Tool making</h2>
<p>Programming is all about making tools from other tools. To me, blocks are a very neat
one that fit seamlessly in the Ruby language and handle many roles. They really feel
like a generalization of anonymous functions and I wish more mainstream languages would
have them.</p>
<h2 id="try-ruby-out">Try Ruby out</h2>
<p>You can try Ruby from your browser at <a href="https://try.ruby-lang.org/">https://try.ruby-lang.org/</a>. There is a small
Ruby lesson built into the site, but it may feel too simple for more advanced programmers.
Anyhow, it will let you type some Ruby and see the result.</p>
</div>
You should avoid `includes` in Railshttps://maxlap.dev/blog/2021/02/15/you-should-avoid-includes-in-rails.html2021-02-14T19:00:00-05:002023-10-12T13:13:23-04:00Maxime Lapointe<div class="markdown-body"><p><code>includes</code> is the recommended way to load associations of your records eagerly. In fact, the
<a href="https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations">Rails guide for eager loading</a>
only mentions <code>includes</code>. However, there are other ways, and I want to argue that you should
avoid <code>includes</code>.</p>
<p>Here's why:</p>
<ul>
<li><code>includes</code> makes it easy to introduce an odd bug</li>
<li><code>preload</code> takes the same arguments as <code>includes</code>, but can't introduce the bug</li>
<li>When needed, <code>eager_load</code> also takes the same arguments, can introduce the bug, but makes it explicit</li>
</ul>
<p>What bug? Let's show the introduction of an unexpected bug:</p>
</div>
<div class="code-book "><div class="code-book__left"><div class="markdown-body"><p>Say you have these records:</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="nb">p</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">create!</span>
<span class="nb">p</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="ss">content: </span><span class="s1">'hello'</span><span class="p">,</span> <span class="ss">spam: </span><span class="kp">true</span><span class="p">)</span>
<span class="nb">p</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="ss">content: </span><span class="s1">'world'</span><span class="p">)</span></code></pre></div></div></div><div class="code-book__left"><div class="markdown-body"><p>You want posts that have at least one comment marked as spam.</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># distinct because joins can make duplicates</span>
<span class="c1"># I dislike using joins for this.</span>
<span class="n">posts</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">joins</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">distinct</span>
 <span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span></code></pre></div></div></div><div class="code-book__left code-book__left--has-after"><div class="markdown-body"><p>How many comments in this array?</p>
</div></div><div class="highlight code-book__right code-book__right--has-after"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="n">posts</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">to_a</span><span class="p">.</span><span class="nf">size</span></code></pre></div></div></div><div class="code-book__left code-book__left--after"><div class="markdown-body"><p><button class="spoiler-toggler btn btn-xs btn-default">Answer</button></p>
<div class="spoiler-target"><p>2, nothing special there.</p></div>
</div></div><div class="highlight code-book__right code-book__right--after"></div><div class="code-book__left code-book__left--has-after"><div class="markdown-body"><p>You will need to display the comments of the posts, so you <code>includes</code> them.</p>
<p>How many comments are in the array?</p>
</div></div><div class="highlight code-book__right code-book__right--has-after"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="n">posts</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">to_a</span></code></pre></div></div></div><div class="code-book__left code-book__left--after"><div class="markdown-body"><p><button class="spoiler-toggler btn btn-xs btn-default">Answer</button></p>
<div class="spoiler-target"><p>Only 1, the one with `spam = true`</p></div>
</div></div><div class="highlight code-book__right code-book__right--after"></div><div class="code-book__left code-book__left--has-after"><div class="markdown-body"><p>What's the <code>count</code>?</p>
</div></div><div class="highlight code-book__right code-book__right--has-after"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="n">posts</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">count</span></code></pre></div></div></div><div class="code-book__left code-book__left--after"><div class="markdown-body"><p><button class="spoiler-toggler btn btn-xs btn-default">Answer</button></p>
<div class="spoiler-target">Back to 2! <span class="with-tooltip" data-tippy-content="`count` does a new query, ignoring what is already loaded."> ? </span></div>
</div></div><div class="highlight code-book__right code-book__right--after"></div><div class="code-book__left code-book__left--has-after"><div class="markdown-body"><p>What about <code>size</code>?</p>
</div></div><div class="highlight code-book__right code-book__right--has-after"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="n">posts</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">size</span></code></pre></div></div></div><div class="code-book__left code-book__left--after"><div class="markdown-body"><p><button class="spoiler-toggler btn btn-xs btn-default">Answer</button></p>
<div class="spoiler-target">Back to 1! <span class="with-tooltip" data-tippy-content="`size` just returns the size of what is already loaded."> ? </span></div>
</div></div><div class="highlight code-book__right code-book__right--after"></div><div class="code-book__left"><div class="markdown-body"><p>To be clear, the <code>joins</code> isn't needed for this to happen.</p>
<p>It was just to show a progression.</p>
</div></div><div class="highlight code-book__right"><div class="code-book__right-wrapper"><div class="highlight"><pre class="highlight ruby"><code><span class="no">Post</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="ss">:comments</span><span class="p">)</span>
 <span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span>
 <span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">size</span>
<span class="c1"># => 1</span></code></pre></div></div></div></div>
<div class="markdown-body"><p>Personally, I find that unexpected.</p>
<p>Imagine if the <code>post</code> is passed to a view or a helper, which uses the <code>comments</code>. It
would only print the comments that matched the condition. Now if you try to debug from that helper,
you would see that <code>post.comments</code> only has 1 comment.</p>
<p>Hopefully, you know that this is how <code>includes</code> (and <code>eager_load</code>, see below) behaves, so you may look
up where the <code>post</code> comes from and figure it out. Good luck otherwise.</p>
<p>This is considered <a href="https://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations">a feature</a>.
The consequences of doing conditions on eager loaded association are not in the guide, but they
are in the <a href="https://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations">middle of this section of the documentation</a>.</p>
<p>I sometimes see this called "conditional eager loading", and the bug is doing it accidentally.</p>
<p>I consider the whole feature a maintenance burden. At least the guide doesn't recommend using it.</p>
<h2 id="tl-dr-my-recommendations">TL;DR: my recommendations</h2>
<p>Things will get more technical in the next sections, so my condensed and straightforward recommendations are:</p>
<ul>
<li>For new code, if you are only doing eager loading, use <code>preload</code> instead of <code>includes</code>. It does the same eager
loading as <code>includes</code>, and takes the same arguments, but it ignores the conditions in the query. With it, things
work as expected:</li>
</ul>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="n">posts</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">joins</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">distinct</span>
 <span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span>
<span class="n">posts</span><span class="p">.</span><span class="nf">preload</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">to_a</span><span class="p">.</span><span class="nf">size</span> <span class="c1">#=> 2</span>
<span class="n">posts</span><span class="p">.</span><span class="nf">preload</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">count</span> <span class="c1">#=> 2</span>
<span class="n">posts</span><span class="p">.</span><span class="nf">preload</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">size</span> <span class="c1">#=> 2</span></code></pre></div></div>
<div class="markdown-body"><ul>
<li>If you need to order by an association, then <code>eager_load</code> is basically the only simple way to do so.</li>
<li><p>If you need a condition (a <code>where</code>) which uses an association, avoid <code>includes</code>, <code>joins</code> and <code>eager_load</code>.<br>
Instead, I recommend my gem: <a href="https://github.com/MaxLap/activerecord_where_assoc">activerecord_where_assoc</a>.
Here's an <a href="https://github.com/MaxLap/activerecord_where_assoc/blob/master/INTRODUCTION.md">introduction to it</a>.<br>
It's made for this purpose, and will support many more use cases, such as:</p>
<ul>
<li>Recursive associations (parent/child)</li>
<li>Polymorphic belongs_to</li>
<li>Negative conditions (ex: posts without comments marked as spam)</li>
<li>Multiple conditions on different records of the same association</li>
</ul>
<p>Alternatively, there's another gem for this: <a href="https://github.com/EugZol/where_exists">where_exists</a></p></li>
</ul>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># Same as before, posts that have at least one comment marked as spam</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">where_assoc_exists</span><span class="p">(</span><span class="ss">:comments</span><span class="p">,</span> <span class="ss">spam: </span><span class="kp">true</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><ul>
<li><p>If <code>includes</code> seem to work somewhere that <code>preload</code> doesn't, you're probably doing a condition
on an association or ordering by an association. See the previous points for this.</p></li>
<li><p>For existing code, you can't mindlessly change all <code>includes</code> to <code>preload</code>, because some of it may rely on
<code>includes</code> adding a <code>JOIN</code> to the query (the <code>eager_load</code> way), which happens when the query refers to the table of the
included associations. So while it would be better to change everything to <code>preload</code> and sometimes <code>eager_load</code>, every such change must
be tested.</p></li>
<li><p>If you see an <code>includes</code> with a <code>references</code>, then that's just a call to <code>eager_load</code>. At this point, just use
<code>eager_load</code> to make your code shorter.</p></li>
</ul>
<p>So don't risk <code>includes</code> doing the wrong thing. <code>preload</code> means simple eager loading without the booby trap;
you should use it. Treat <code>eager_load</code> as a warning sign that this could be doing conditional eager loading and
be careful around it.</p>
<h2 id="down-the-rabbit-hole">Down the rabbit hole</h2>
<p>If you want to understand why I make those recommendations, we'll have to get technical...</p>
<p>Eager loading means loading associations of multiple records before they are needed. This is done to reduce
the number of queries executed, making execution faster.</p>
<p>There are actually 3 methods for eager loading in Rails:</p>
<ul>
<li><code>preload</code>: Executes one extra query per association being eager loaded. Same as <code>includes</code> usually does.</li>
<li><code>eager_load</code>: Adds <code>JOIN</code> to the SQL query and load the association without doing an extra query. This also
enables adding conditions on the table, which is the cause of the conditional eager loading bug from the
introduction.</li>
<li><code>includes</code>: Picks between <code>preload</code> and <code>eager_load</code> based on if there is a reference, in the query, to the
table of an association that was passed to <code>includes</code>. This can be from <code>where</code> or from <code>joins</code>.<br>
You may also specify an association with <code>references</code> to force the <code>eager_load</code> path, which is needed when
your conditions are specified with a <code>String</code> instead of a <code>Hash</code> (which, again, causes conditional eager loading).</li>
</ul>
<p>So out of the 3 methods, only one of them cannot trigger conditional eager loading: <code>preload</code>. It only does
full eager loading, always the same way.</p>
<h2 id="when-is-eager_load-needed">When is <code>eager_load</code> needed?</h2>
<p>The main reason to use <code>eager_load</code>, that I have no alternative for, is ordering by an association's field.</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># Ordering posts by created_at of last comment</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">eager_load</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">order</span><span class="p">(</span><span class="s2">"comments.created_at DESC"</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><p>Maybe some use it to reduce the number of queries when they do eager loading. I don't think it really saves much,
and there is a risk of slowing things down by making queries that are <span class="with-tooltip" data-tippy-content="If you use `eager_load` with multiple `has_many`, the rows get multiplied, and you end up with a massive amount of data that your DB is returning to your web server.">heavier</span>.</p>
</div>
<div class="markdown-body"><p>Some may use it to actually do conditional eager loading. I still heavily disagree with that use case.
<button class="spoiler-toggler btn btn-xs btn-aside btn-inline spoiler-toggler--always-displayed">Anecdote</button></p>
<div class="spoiler-target"><div class="markdown-body"><p>I've had to edit code that used this "feature" once...</p>
<p>You look at a method and it looks wrong; it can't be doing what it should be doing. It's using every
<code>project.users</code>, not just those we want! When I did an interactive console there (<code>binding.pry</code>
or <code>byebug</code>), I saw that users were missing from <code>project.users</code>.</p>
<p>Since I knew of this "feature", I started looking and, as expected, a condition on an <code>includes</code> was found...
3 method calls away from where the association was used, not a single comment to explain what is going on anywhere.</p>
<p>You should avoid code that looks wrong. Code that uses conditional eager loading looks wrong. In our case
the overall module was already something that we wanted to rewrite from scratch, so this was just another reason
to do so.</p>
</div></div>
<p>Other than ordering, I mostly see <code>eager_load</code> used to do a condition (a <code>where</code>) which uses an association. Let's dig into these.</p>
<h2 id="where-on-an-association-with-eager_load"><code>where</code> on an association with <code>eager_load</code></h2>
<p>It's a somewhat frequent need and there are many questions about this on stack overflow.</p>
<p>The bug from the introduction, accidentally doing conditional eager loading, started with such a need: "I want the
posts that have comments marked as spam".</p>
<p>You may see a recommendation to use <code>includes</code>, and then have a condition on its table. This actually
uses the <code>eager_load</code> path.</p>
<p>It looks like this:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># Please stop doing this :(</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span>
<span class="c1"># which is equivalent to this; don't do this either</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">eager_load</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span></code></pre></div></div>
<div class="markdown-body"><p>Again, this does conditional eager loading, which isn't what we asked for.</p>
<p>To be clear, the <code>where</code> on an association with <code>includes</code> / <code>eager_load</code> <strong>can</strong> be safe. But only <strong>if</strong>
the association is a <code>belongs_to</code>. When it is, there are only 2 possibilities: either load the
record and the associated <code>belongs_to</code> records, or don't load either. No conditional eager loading is possible.</p>
<p>But even when it's safe, there are risks:</p>
<ul>
<li>Using <code>includes</code> / <code>eager_load</code> increases the chance for a mistake, where you or someone else <em>just</em>
add another association to the existing eager loading call.</li>
<li>Every time a reader sees <code>includes</code> / <code>eager_load</code>, he may wonder if it is safe, or if there could be
accidental conditional eager loading.</li>
</ul>
</div>
<div class="markdown-body"><p>And as a tool, this isn't so great:</p>
<ul>
<li>If you don't need the associated records, then eager loading them is wasteful.</li>
<li>Doesn't handle recursive associations (ex: parent/children)</li>
<li><span class="with-tooltip" data-tippy-content="<div class="markdown-body"><p>A query for posts with &quot;a comment marked as spam&quot; and &quot;a comment by an admin&quot; will actually only return posts with &quot;a comment that is both marked as spam and made by an admin&quot; instead it being possible to be two different comments.</p> <p>So doing this kind of query in <code>scope</code> means they can interact incorrectly.</p> </div> ">Doesn't compose well</span></li>
<li>Looks potentially wrong when you know of the conditional eager loading "feature"</li>
</ul>
<h2 id="where-on-an-association-with-joins"><code>where</code> on an association with <code>joins</code></h2>
<p>The next option is to use <code>joins</code>. It also has downsides.</p>
<p>It looks like this:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># Please stop doing this :(</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">joins</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span>
<span class="c1"># and stop doing this</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">joins</span><span class="p">(</span><span class="ss">:comments</span><span class="p">).</span><span class="nf">distinct</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">comments: </span><span class="p">{</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">})</span></code></pre></div></div>
<div class="markdown-body"><p>Using <code>joins</code> like this is better than <code>includes</code> / <code>eager_load</code> since at least, there is no risk of conditionally
loading an association. But there are still problems with it:</p>
<ul>
<li>Doesn't handle recursive associations (ex: parent/children)</li>
<li>Requires a <code>distinct</code> to avoid duplicated records when used with <code>has_many</code> associations.<br>
This can be unexpected if you're doing a more complex query than j ust fetching records.</li>
<li><span class="with-tooltip" data-tippy-content="<div class="markdown-body"><p>A query for posts with &quot;a comment marked as spam&quot; and &quot;a comment by an admin&quot; will actually only return posts with &quot;a comment that is both marked as spam and made by an admin&quot; instead it being possible to be two different comments.</p> <p>So doing this kind of query in <code>scope</code> means they can interact incorrectly and change more things than just adding a condition (it adds a <code>distinct</code>).</p> </div> ">Doesn't compose well</span></li>
</ul>
<h2 id="where-on-an-association-with-arel"><code>where</code> on an association with Arel</h2>
<p>Truth is, this need for a <code>where</code> on an association isn't something that ActiveRecord supports well. So
leaving the ActiveRecord only solutions, you can do an actual <code>EXISTS</code> query with Arel. <code>EXISTS</code> is
the SQL tool that is meant to do this type of condition, not <code>JOIN</code>.</p>
<p>It looks like this:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># An OK way, but error prone</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="no">Comment</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="s2">"posts.id = comments.post_id"</span><span class="p">).</span><span class="nf">where</span><span class="p">(</span><span class="ss">spam: </span><span class="kp">true</span><span class="p">).</span><span class="nf">arel</span><span class="p">.</span><span class="nf">exists</span><span class="p">)</span></code></pre></div></div>
<div class="markdown-body"><p>This composes much better with other tools because all it does is add a single <code>WHERE</code> clause to the query.
It works as you would expect with <code>or</code>, <code>not</code> and with other conditions on the same association.</p>
<p>But there still are new downsides:</p>
<ul>
<li>You must manually write condition to link the <code>posts</code> to the <code>comments</code>. It's easy to forget it, and I've seen
StackOverflow answers that forgot to do so.<br>
You won't get any error for forgetting, your query will just be wrong, which may not even be obvious if all
you have is a little test data.
Bonus: This can get extra tedious for polymorphic associations, where you also need to this check: <code>foos.owner_type = #{Bar.base_class.name}</code>.</li>
<li>If a condition was given when defining the association, you must also manually rewrite it.</li>
<li>Only the models are named in the code, not the association of interest. This makes the intent less clear, especially
when non-trivial associations exist.</li>
<li>Extra work to handle recursive associations (ex: parent/children)</li>
<li>Quite a bit longer to write, and this is a short example.</li>
</ul>
<p>Other than writing the whole condition manually, which would have all the problems of the Arel way, but be
more verbose and more error-prone, I think we're out of built-in ways.</p>
<h2 id="where-on-an-association-with-activerecord_where_assoc"><code>where</code> on an association with activerecord_where_assoc</h2>
<p>What I recommend for conditions based on associations is a gem I made just for this purpose:
<a href="https://github.com/MaxLap/activerecord_where_assoc">activerecord_where_assoc</a>. It looks like this:</p>
</div>
<div class="highlight lone-highlighted-code"><div class="highlight"><pre class="highlight ruby"><code><span class="c1"># Please consider doing this:</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">where_assoc_exists</span><span class="p">(</span><span class="ss">:comments</span><span class="p">,</span> <span class="ss">spam: </span><span class="kp">true</span><span class="p">)</span>
<span class="c1"># Or using a scope such as is_spam:</span>
<span class="no">Post</span><span class="p">.</span><span class="nf">where_assoc_exists</span><span class="p">(</span><span class="ss">:comments</span><span class="p">)</span> <span class="p">{</span> <span class="n">is_spam</span> <span class="p">}</span></code></pre></div></div>
<div class="markdown-body"><p>The query it generates is the same as the Arel example, with the same benefits and more.
See for yourself:</p>
<ul>
<li>It just adds a single <code>where</code> condition, so it composes well and works with <code>or</code> and with
other conditions on the same association.</li>
<li>Handles recursive associations automatically (ex: parent/children)</li>
<li>Handles polymorphic belongs_to (<code>includes</code> and <code>joins</code> would simply refuse)</li>
<li>Easy to do a <code>NOT</code> of the condition (I.E.: where no comment is marked as spam)
with <code>where_assoc_not_exists</code>.</li>
<li>Composes with other such queries, even on the same association, even with negations</li>
<li>Unlike Arel, this uses the association's name, so the intent is clearer.</li>
</ul>
<p>So if you need to do this kind of condition, here are some references for my gem:</p>
<ul>
<li><a href="https://github.com/MaxLap/activerecord_where_assoc/blob/master/INTRODUCTION.md">Introduction to activerecord_where_assoc</a>.</li>
<li><a href="https://github.com/MaxLap/activerecord_where_assoc/blob/master/ALTERNATIVES_PROBLEMS.md">The problems of the other ways of doing such conditions</a>.</li>
<li><a href="https://github.com/MaxLap/activerecord_where_assoc/blob/master/EXAMPLES.md">Multiple example usages</a>.</li>
</ul>
<p>There's simply no way I could find to use builtin tools to have this query be clear, succinct and not booby trapped.
Either live with the booby traps, write your own methods to do this cleanly, or use one of the gems written for this purpose:</p>
<ul>
<li>Mine: <a href="https://github.com/MaxLap/activerecord_where_assoc">activerecord_where_assoc</a></li>
<li><a href="https://github.com/EugZol/where_exists">where_exists</a></li>
</ul>
<p>Seriously, try any of them, it's liberating how simple this once complex task becomes.</p>
</div>
<div class="markdown-body"><h2 id="but-includes-is-everywhere">But <code>includes</code> is everywhere</h2>
<p>It is! Let's explore the reasons I can think of.</p>
<p><strong><code>includes</code> is the "smart" function out of the 3, it will pick the "right" strategy when needed.</strong></p>
<p>Marketing-wise, this sounds like a good thing... Until you learn that the alternate path, <code>eager_load</code>, is
not always what you want and it can cause bugs due to conditional eager loading.</p>
<p><strong>For a long time, <code>includes</code> (and <code>eager_load</code>) were the only way to do a <code>LEFT JOIN</code></strong></p>
<p>The method <code>left_joins</code> was added in Rails 5.0. Before that, if you wanted one, you had to either do
<code>includes</code> / <code>eager_load</code>, or write the whole "LEFT JOIN" yourself like this: <code>joins("LEFT JOIN
comments ON comments.post_id = posts.id")</code>. The <code>includes</code> shortcut was often suggested.</p>
<p><strong><code>includes</code> has always been recommended, so most are familiar with it, and most recommend it.</strong></p>
<p>Everything is against <code>preload</code>, even it's <a href="https://apidock.com/rails/ActiveRecord/QueryMethods/preload">documentation</a> makes <code>preload</code>
sound like an <span class="with-tooltip" data-tippy-content="As of writing this post it only says: "Allows preloading of args, in the same way that #includes does", with one trivial example.">alias for <code>includes</code></span>, and the Rails guide only mentions <code>includes</code> for eager loading data.</p>
<p>I think not enough people were both harmed by <code>includes</code> and aware that you can just specify <code>preload</code> and <code>eager_load</code> for that knowledge to spread.</p>
<h2 id="recap">Recap</h2>
<p>Conditional eager loading:</p>
<ul>
<li><code>includes</code> and <code>eager_load</code> can accidentally eager load only part of an association, a good source of bugs.</li>
<li>Doing conditional eager loading voluntarily can be maintenance burden</li>
<li>If you do want conditional eager loading, using <code>eager_load</code> makes it a bit more obvious.</li>
</ul>
<p>Conditions based on associations:</p>
<ul>
<li>Using <code>includes</code> and <code>eager_load</code> for conditions based on associations can do conditional eager loading at
the same time, you will get bitten by the bugs it can causes.</li>
<li>if you don't need to load the association, eager loading it is wasteful</li>
<li>Using specialized gems to do conditions based on association is safer, clearer and easier.</li>
</ul>
<p>Order based on association:</p>
<ul>
<li><code>includes</code> and <code>eager_load</code> are the only simple way.</li>
<li>Using <code>eager_load</code> is explicit about the use case, and you don't need to also call <code>references</code>.</li>
</ul>
<p>Regular old eager loading:</p>
<ul>
<li>Just use <code>preload</code></li>
</ul>
<p>If you want to run the examples from this post, here is a <a href="/blog/2021/02/15/you-should-avoid-includes-in-rails.rb">self-contained ruby script</a>.</p>
</div>