<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Today I Learned</title>
    <description>TIL is an open-source project by Hashrocket that exists to catalogue the sharing and accumulation of knowledge as it happens day-to-day.
    </description>
    <link>http://localhost/</link>
    <atom:link href="http://localhost/rss" rel="self" type="application/rss+xml" />
      <item>
        <title>
          <![CDATA[`has_secure_password` can slow test suites down - #ruby]]>
        </title>
        <link>http://localhost/posts/bsfxfzmdpz-hassecurepassword-can-slow-test-suites-down</link>
        <description>
          <![CDATA[<p>TL;DR - A small BCrypt config tweak improved our test suite runtime from 3.5mins to 15s!</p><p>We have a project that had unusually slow tests compared to other systems.</p><p>~1000 tests took 3.5 minutes to run, which is unacceptably slow.</p><p>We did some profiling using <a href="https://github.com/test-prof/test-prof">Test Prof</a> which pointed to factories being the culprit, but it was difficult to drill down further and get a clear answer.</p><p>After some further digging, we found the problem:</p><pre><code class="ruby language-ruby"># in spec/factories/users.rb

password_digest { BCrypt::Password.create(Faker::Internet.password) }</code></pre><p>BCrypt is slow by design, mainly to make offline rainbow table attacks unfeasible, but in most cases during testing, we don’t need to worry about that.</p><p>We can configure BCrypt to “spend less” resources when creating a password for a user in a factory by adding the following config to either <code class="inline">config/environments/test.rb</code> or <code class="inline">spec/rails_helper.rb</code>:</p><pre><code class="ruby language-ruby"># Configure BCrypt to use minimum cost in tests to avoid needless slowdowns
BCrypt::Engine.cost = BCrypt::Engine::MIN_COST</code></pre><p>This is similar to configuring Devise’s “stretches” to <code class="inline">1</code> for testing.</p><p>Our test suite runtime improved ~14x from 3.5mins to 15secs from this one simple change.</p>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Mon, 27 Oct 2025 09:27:03 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/bsfxfzmdpz-hassecurepassword-can-slow-test-suites-down</guid>
      </item>
      <item>
        <title>
          <![CDATA[Time to get .lazy, then .eager - #ruby]]>
        </title>
        <link>http://localhost/posts/ulqdkvbbcc-time-to-get-lazy-then-eager</link>
        <description>
          <![CDATA[<h3>.lazy loading</h3><p>If you need to lazy load an associated set of data in an API client (or at any other time) in an object, you can use the <code class="inline">.lazy</code> method on an enumerator:</p><pre><code class="ruby language-ruby">hogwarts.wizards = wizard_ids.lazy.map { |wizard_id| find_wizard(wizard_id) }</code></pre><p>The above will return an <code class="inline">Enumerator::Lazy</code> that will only perform the <code class="inline">map</code> once the data is touched, for example by performing <code class="inline">hogwarts.wizards.first</code>.</p><h3>Get .eager if  necessary</h3><p>There’s a snag here though, if you would like to perform a <code class="inline">map</code> on the actual data down the line, it will then return another <code class="inline">Enumerator::Lazy</code>, instead of performing your <code class="inline">map</code>.</p><p>To circumvent this, tack on a <code class="inline">.eager</code> to the end of the returned <code class="inline">Enumerator::Lazy</code>:</p><pre><code class="ruby language-ruby">hogwarts.wizards = wizard_ids.lazy.map { |wizard_id| find_wizard(wizard_id) }.eager</code></pre><p>This will then return the enumerated values when performing a <code class="inline">.map</code> down the line!</p>]]>
        </description>
        <dc:creator>aarondelate</dc:creator>
        <pubDate>Tue, 29 Oct 2024 13:02:12 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/ulqdkvbbcc-time-to-get-lazy-then-eager</guid>
      </item>
      <item>
        <title>
          <![CDATA[How to speed up Rubocop's startup time - #ruby]]>
        </title>
        <link>http://localhost/posts/staucx4zua-how-to-speed-up-rubocops-startup-time</link>
        <description>
          <![CDATA[<p>If you have a fairly complex <code class="inline">.rubocop.yml</code> file, you may find that later versions take forever to start up or report results, and it seems like it hanging when you run it manually from the CLI.</p><p>The cause for this <strong>may</strong> be due to you having custom exclusion rules. When you exclude files from Rubocop ala:</p><pre><code class="yaml language-yaml">AllCops:
  Exclude:
    - &apos;db/fixtures/**/*&apos;
    - &apos;db/migrate/*.rb&apos;
    - &apos;db/schema.rb&apos;
    - &apos;db/seeds.rb&apos;
    - &apos;Gemfile.lock&apos;
    - &apos;bin/*&apos;
    # ... etc... etc... </code></pre><p>Rubocop will then ignore any of its own exclusion rules, and land up scanning <code class="inline">node_modules</code>, cache dirs, log dirs, and even your <code class="inline">.git</code> folder!</p><p>You can fix this by adding the following into your <code class="inline">.rubocop.yml</code> file.</p><pre><code class="yaml language-yaml">inherit_mode:
  merge:
    - Exclude</code></pre><p>Rubocop will now merge in its default exclusion list alongside your own.</p><p>Why this is not the default behaviour eludes me. :|</p>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Mon, 18 Dec 2023 13:05:18 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/staucx4zua-how-to-speed-up-rubocops-startup-time</guid>
      </item>
      <item>
        <title>
          <![CDATA[Debugging PDFKit and WKHTMLToPDF - #ruby]]>
        </title>
        <link>http://localhost/posts/lqn2vmhrro-debugging-pdfkit-and-wkhtmltopdf</link>
        <description>
          <![CDATA[<p>When PDFKit errors out with an unhelpful error like: <code class="inline">RuntimeError (command failed (exitstatus=1): /bin/wkhtmltopdf --page-size A4 - -):</code>, try the following:</p><ul><li>Add <code class="inline">config.verbose = true</code> to its configuration
</li><li>Add <code class="inline">load_error_handling: &apos;ignore&apos;</code> to its <code class="inline">config.default_options</code></li><li>Save the rendered HTML to a file by modifying pdfkit.rb’s <code class="inline">to_pdf</code> method with this just before the <code class="inline">invoke</code> declaration.
</li></ul><pre><code class="ruby language-ruby">open(&apos;/tmp/pdfkit-source.html&apos;, &apos;a&apos;) { |f| f.puts @source.to_s }</code></pre><p>Once you’ve got the html saved, you can inspect it for errors, and manually run <code class="inline">wkhtmltopdf</code> on the CLI using it as input like so:</p><pre><code class="shell language-shell">cat /tmp/pdfkit-source.html | &lt;the wkhtmltopdfbin command that is failing&gt; out.pdf</code></pre><p>Hopefully one of these techniques can highlight where your problems lie, and get you to a resolution.</p>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Tue, 30 May 2023 12:08:36 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/lqn2vmhrro-debugging-pdfkit-and-wkhtmltopdf</guid>
      </item>
      <item>
        <title>
          <![CDATA[Beware of Stale Closure Data in React - #vuejs]]>
        </title>
        <link>http://localhost/posts/eu6i63ad8p-beware-of-stale-closure-data-in-react</link>
        <description>
          <![CDATA[<p>Something we continually take for granted is that <code class="inline">setState</code> is actually an asynchronous method, and closures in JavaScript can get end up getting a stale version of state (if they’re using any) because of this.</p><p>This is particularly painful when your next version of some state depends on the previous version (Eg. A counter + 1).</p><p>To ensure that closures don’t experience issues like this, you can provide a callback to <code class="inline">setState</code> instead of a fixed value.</p><p>Eg. Instead of:</p><pre><code class="javascript language-javascript">[count, setCount] = useState(0);
const increment = () =&gt; setCount(count + 1);</code></pre><p>Do this:</p><pre><code class="javascript language-javascript">[count, setCount] = useState(0);
const increment = () =&gt; setCount((prevCount) =&gt; prevCount + 1);</code></pre><p>Your increment closure is now a little more resilient to asynchronous events, and since it doesn’t refer to some stale version of state, it will update the next version accordingly.</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Wed, 18 Jan 2023 09:11:51 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/eu6i63ad8p-beware-of-stale-closure-data-in-react</guid>
      </item>
      <item>
        <title>
          <![CDATA[Add comments to package.json - #ruby]]>
        </title>
        <link>http://localhost/posts/4axwc83kod-add-comments-to-packagejson</link>
        <description>
          <![CDATA[<p>Progressively upgrading dependencies in the JS world is a little different compared to Ruby.</p><p>When updating a Ruby gem inside a gemfile, you might want to leave notes. For example:</p><p><code class="inline">gem &apos;some-gem&apos;, &apos;~&gt; 1.2.3&apos; # TODO: Update once we&apos;re on Ruby 3.1</code></p><p>The comment is useful since it shares knowledge related to upgrading this dependency.</p><p>Javascript, or JSON, works a little differently. No comments are allowed inside the <code class="inline">package.json</code> file. Instead to achieve roughly the same result you can do this:</p><pre><code class=" language-">...
&quot;//&quot;: [
    &quot;TODO: Upgrade stylelint-config-sass-guidelines once we&apos;re on Node 11+&quot;,
    &quot;TODO: Upgrade mini-css-extract-plugin once we&apos;re on 12.13+ and webpack 5+&quot;,
    &quot;TODO: Upgrade style-loader once we&apos;re on 12.13+ and webpack 5+&quot;
  ],
&quot;dependencies&quot;: {
  ....
}</code></pre><p><code class="inline">&quot;//&quot;</code> will never be used by npm for any purpose, and is reserved for comments.</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Tue, 10 Jan 2023 10:04:28 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/4axwc83kod-add-comments-to-packagejson</guid>
      </item>
      <item>
        <title>
          <![CDATA[Quickly lock MacOS with the TouchID button - #macos]]>
        </title>
        <link>http://localhost/posts/l223c6gx7a-quickly-lock-macos-with-the-touchid-button</link>
        <description>
          <![CDATA[<p>If you press the TouchID button with your main finger, it does nothing, but did you know that if you press the TouchID button with a finger that MacOS does not have a fingerprint record for, your screen will immediately lock?</p><p>This is much quick than using hot corner — which, let’s be honest — gets triggered accidentally more often than not.</p><p>It’s also one less shortcut key-combo you’ll need to remember going forward.</p><p>This works with both laptop and external keyboard TouchID buttons.</p>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Wed, 04 Jan 2023 09:26:01 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/l223c6gx7a-quickly-lock-macos-with-the-touchid-button</guid>
      </item>
      <item>
        <title>
          <![CDATA[A simple way to keep the latest n docker images - #docker]]>
        </title>
        <link>http://localhost/posts/hktuenc0ve-a-simple-way-to-keep-the-latest-n-docker-images</link>
        <description>
          <![CDATA[<p>We recently had a CI server run out of space because it had too many images on it that were no longer relevant. </p><p>This handy command can be used to delete all but the newest number of images. Slap it into cron and never worry about your disk filling up from too many images</p><pre><code class="shell language-shell">docker images --format &quot;{{.Repository}}:{{.Tag}}&quot; | grep my-fancy-image | tail --lines +21 | xargs --no-run-if-empty docker image rm</code></pre><p>The example above will remove all but the latest 20 versions of <code class="inline">my-fancy-image</code>.</p><p>Take note to add 1 to the amount you want to keep when passing that value to <code class="inline">tail</code>, e.g. if you want to keep the latest 5, pass <code class="inline">+6</code> to <code class="inline">tail --lines</code>.</p>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Thu, 27 Oct 2022 09:01:35 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/hktuenc0ve-a-simple-way-to-keep-the-latest-n-docker-images</guid>
      </item>
      <item>
        <title>
          <![CDATA[Check if you are running arm64 or amd64 brew - #bash]]>
        </title>
        <link>http://localhost/posts/qwktfoepdl-check-if-you-are-running-arm64-or-amd64-brew</link>
        <description>
          <![CDATA[<p>With the switch to m1. Most people will still be running the amd64 version of brew, which will be going through a translation layer all the time.</p><p>Run </p><pre><code class="shell language-shell">which brew</code></pre><p>If you get <code class="inline">/opt/homebrew/bin/brew</code></p><p>Then you are using the correct arm version.</p><p>If you get <code class="inline">/usr/local/bin/brew</code></p><p>You are still using the amd64 version.</p><p>You can follow the following if you want to switch.</p><p><a href="https://earthly.dev/blog/homebrew-on-m1">https://earthly.dev/blog/homebrew-on-m1</a>/</p>]]>
        </description>
        <dc:creator>timhaak</dc:creator>
        <pubDate>Wed, 26 Oct 2022 12:56:14 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/qwktfoepdl-check-if-you-are-running-arm64-or-amd64-brew</guid>
      </item>
      <item>
        <title>
          <![CDATA[Finding missing/associated records in Rails - #ruby]]>
        </title>
        <link>http://localhost/posts/fn5y0wslvl-finding-missingassociated-records-in-rails</link>
        <description>
          <![CDATA[<p>Rails has some built in methods to help you find records which may have missing associated records.</p><p>For example, let’s assume we have an app where an <code class="inline">Invoice</code> has many <code class="inline">Payment</code>s, and we wanted to find all invoices which have no payments:</p><p><code class="inline">Invoice.where.missing(:payments)</code></p><p>We’ll get back a collection of all invoices which have no payments. </p><p>Much cleaner than writing messy joins!</p><p>Also, on the flip side you can find all invoices with payments like this:</p><p><code class="inline">Invoice.where.associated(:payments)</code></p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Thu, 20 Oct 2022 14:28:02 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/fn5y0wslvl-finding-missingassociated-records-in-rails</guid>
      </item>
      <item>
        <title>
          <![CDATA[Extending Rake Tasks - #ruby]]>
        </title>
        <link>http://localhost/posts/fjkugqkfes-extending-rake-tasks</link>
        <description>
          <![CDATA[<p>You can extend rake tasks to add extra functionality.</p><p>For example, let’s extend the <code class="inline">rails db:migrate</code> task to automatically include schema migrations and data migrations.</p><pre><code class="ruby language-ruby"># Inside lib/tasks/db.rake
# Remove the old migrate task
Rake::Task[&apos;db:migrate&apos;].clear

namespace &apos;db&apos; do
  # Redefine the migrate task to invoke with data migrations
  task migrate: [&apos;migrate:with_data&apos;]
end</code></pre><p>Now whenever you execute <code class="inline">rails db:migrate</code>, the task will include schema + data migrations.</p><p>Additionally, you may want to invoke some extra task after another has been executed. To do this, you can enhance a task like this:</p><pre><code class="ruby language-ruby"># Inside lib/tasks/db.rake
Rake::Task[&apos;db:migrate&apos;].enhance do
  Rake::Task[&apos;db:do_some_custom_thing&apos;].invoke
end</code></pre><p>Now your custom task will be invoked after <code class="inline">rails db:migrate</code> is invoked.</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Fri, 14 Oct 2022 09:34:33 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/fjkugqkfes-extending-rake-tasks</guid>
      </item>
      <item>
        <title>
          <![CDATA[Simpler conditional HTML classes with class_names - #ruby]]>
        </title>
        <link>http://localhost/posts/vtslu0txmw-simpler-conditional-html-classes-with-classnames</link>
        <description>
          <![CDATA[<p>Coming from JS and React you may be familiar with a package called <code class="inline">classnames</code>. It takes an object where the keys represent CSS classes, and the values are booleans which determine if the key should be loaded or not.</p><p>Eg. classnames(‘foo’, { bar: true, baz: false })</p><p>Returns: ‘foo bar’</p><p>Rails includes its own helper which does this same thing. You can call <code class="inline">class_names</code> or <code class="inline">token_list</code>:</p><p>Eg. class_names(‘foo’, { bar: true, baz: false })</p><p>Returns: ‘foo bar’</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Tue, 04 Oct 2022 07:15:15 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/vtslu0txmw-simpler-conditional-html-classes-with-classnames</guid>
      </item>
      <item>
        <title>
          <![CDATA[Configure Tmux to use 1 as its first window index - #bash]]>
        </title>
        <link>http://localhost/posts/jlcjbtafge-configure-tmux-to-use-1-as-its-first-window-index</link>
        <description>
          <![CDATA[<p>Tmux will start its first window at index 0, which can be unintiutive when you visually associate your windows with the number row on your keyboard. i.e. your first Tmux window is the last number on the number row (0), and the second window is the first number on the row (1).</p><p>This config in <code class="inline">~/.tmux.conf</code> will make 1 the starting index so that Tmux windows and the number row both grow from left to right in an intuitive manner.</p><pre><code class=" language-"># Start windows and panes at 1, not 0
set  -g base-index      1
setw -g pane-base-index 1</code></pre>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Wed, 21 Sep 2022 07:58:37 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/jlcjbtafge-configure-tmux-to-use-1-as-its-first-window-index</guid>
      </item>
      <item>
        <title>
          <![CDATA[Stimulus Use - #ruby]]>
        </title>
        <link>http://localhost/posts/iiawfbofiy-stimulus-use</link>
        <description>
          <![CDATA[<p>I was getting rather annoyed when using modals with stimulus, especially when it came to actually closing them. I didnt like the way I wrote the general function to close the modals so I decided to refactor it. In doing so I came across a package that would solve it all. The package is called stimulus-use (<a href="https://github.com/stimulus-use/stimulus-use">https://github.com/stimulus-use/stimulus-use</a>). It enables new lifecycle behaviors to your stimulus controllers. Now I can close my modals like so, instead of trying to check if my modal has a click event outside of it:</p><pre><code class="javascript language-javascript">import { Controller } from &apos;stimulus&apos;
import { useClickOutside } from &apos;stimulus-use&apos;

export default class extends Controller {

  connect() {
    useClickOutside(this)
  }

  clickOutside(event) {
    event.preventDefault()
    this.modal.close()
  }
}</code></pre>]]>
        </description>
        <dc:creator>dominicsanto</dc:creator>
        <pubDate>Fri, 16 Sep 2022 14:00:51 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/iiawfbofiy-stimulus-use</guid>
      </item>
      <item>
        <title>
          <![CDATA[Default Controller Params - #ruby]]>
        </title>
        <link>http://localhost/posts/hmfjjbn3ms-default-controller-params</link>
        <description>
          <![CDATA[<p>On occasion you may need to assign default values to the params required within a controller. For example, assume we’re working with todos and we want to assign a todo to the current user (by default, unless another user is specified), then I’ve usually seen some people handle default param values like this:</p><pre><code class="ruby language-ruby">def todo_params
  params
    .require(:todo)
    .permit(:subject, :description, :user_id)
    .tap { |pms|  pms.user_id ||= current_user.id }
end</code></pre><p>However, TIL that you can actually make use of a method called <code class="inline">with_defaults</code>:</p><pre><code class="ruby language-ruby">def todo_params
  params
    .require(:todo)
    .permit(:subject, :description, :user_id)
    .with_defaults(user_id: current_user.id)
end</code></pre><p>Much cleaner! </p><p>Also, <code class="inline">with_defaults</code> is defined for <code class="inline">ActionController::Parameters</code> and <code class="inline">Hash</code>. I find <code class="inline">with_defaults</code> much cleaner, and more intention revealing, than <code class="inline">reverse_merge</code>!</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 18 Jul 2022 08:30:41 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/hmfjjbn3ms-default-controller-params</guid>
      </item>
      <item>
        <title>
          <![CDATA[Fun with Recursive Anonymous Functions - #ruby]]>
        </title>
        <link>http://localhost/posts/ql4n7ip5sk-fun-with-recursive-anonymous-functions</link>
        <description>
          <![CDATA[<p>Did you know that an anonymous function can call itself?</p><p>All you need is something like this:</p><pre><code class="ruby language-ruby">factorial_of = -&gt; (n) { n &lt;= 1 ? 1 : n * factorial_of.call(n - 1) }
factorial_of.call(5)
# =&gt; 120</code></pre><p>This came in handy when I had a recursive method which needed to be wrapped inside an enumerator to navigate a very complex hierarchy of objects.</p><p>Instead of something as complex as that, let’s wrap our anonymous factorial function around an enumerator:</p><pre><code class="ruby language-ruby">factorial_of = -&gt; (n) { n &lt;= 1 ? 1 : n * factorial_of.call(n - 1) }

f = Enumerator.new do |y|
  num = 0
  loop do
    y &lt;&lt; factorial_of.call(num)
    num += 1
  end
end

f.next
=&gt; 1
f.next
=&gt; 1
f.next
=&gt; 2
f.next
=&gt; 6</code></pre><p>Now we can fetch the next number whenever we want, or enumerate over the numbers until we don’t have any left (which is infinite in this case)</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 04 Jul 2022 12:13:26 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/ql4n7ip5sk-fun-with-recursive-anonymous-functions</guid>
      </item>
      <item>
        <title>
          <![CDATA[Slide an Organiser into another Organiser's DM's - #ruby]]>
        </title>
        <link>http://localhost/posts/9fwaw0ckxo-slide-an-organiser-into-another-organisers-dms</link>
        <description>
          <![CDATA[<p>When calling an Organiser within another Organiser with light-service, a really neat way to do it is to splat the actions from the desired organiser into the other like this:</p><pre><code class="ruby language-ruby">module Users
  class UpdateProfile
    extend ::LightService::Organizer

    def self.call()
      with(
        # some params go here
      ).reduce(actions)
    end

    def self.actions
      [
        # some actions go here
        *Users::UpdatePreferencesAndNotify.actions,
        # some more actions go here
      ]
    end
  end
end</code></pre><p>I needed to call an organiser responsible for updating a completeness score on the user and also running the organiser responsible for updating the users attributes and awarding a badge based on the score. This allowed me to still segment the logic while not cluttering the organiser with more actions.</p><p>See <a href="https://github.com/adomokos/light-service/issues/160">https://github.com/adomokos/light-service/issues/160</a> for reference. Also it’s not necessary to use the <code class="inline">extend LightService::Action</code> in the organisers</p>]]>
        </description>
        <dc:creator>michealforbes</dc:creator>
        <pubDate>Thu, 09 Jun 2022 11:05:27 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/9fwaw0ckxo-slide-an-organiser-into-another-organisers-dms</guid>
      </item>
      <item>
        <title>
          <![CDATA[Prevent commits to main/master with git hooks! - #bash]]>
        </title>
        <link>http://localhost/posts/mqrdf43gmo-prevent-commits-to-mainmaster-with-git-hooks</link>
        <description>
          <![CDATA[<p>You can use git hooks to keep you safe when you aren’t thinking properly and decide to commit something to main/master.</p><p>It would be nice if we could create a hook which would prevent commits to main/master unless we give it some override. Like a <code class="inline">crimesjohnson</code> prefix.</p><p>As a start I decided to make a global hook for all of my projects with this command:</p><p><code class="inline">git config --global core.hooksPath ~/githooks</code></p><p>I then created a file (within <code class="inline">~/githooks</code>) for a special kind of git hook and named it <code class="inline">commit-msg</code>.</p><pre><code class="sh language-sh">#!/bin/sh

commit_msg=$(cat &quot;${1:?Missing commit message file}&quot;)
branch=&quot;$(git rev-parse --abbrev-ref HEAD)&quot;

if ([ &quot;$branch&quot; = &quot;main&quot; ] || [ &quot;$branch&quot; = &quot;master&quot; ]) &amp;&amp; [[ &quot;$commit_msg&quot; != *&quot;crimesjohnson:&quot;* ]]; then
  echo &quot;You can&apos;t commit directly to main/master branch&quot;
  exit 1
fi</code></pre><p>Don’t forget to make it executable: <code class="inline">chmod +x ~/githooks/commit-msg</code></p><p>When you create a commit message, it seems like the message is saved to a temp file while the <code class="inline">commit-msg</code> hook runs. We get the commit message by <code class="inline">cat</code>ing it.</p><p>We also use a git command to get the name of our branch.</p><p>Using these 2 bits of info, we can then write a simple if statement to keep us safe!</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 14 Mar 2022 07:04:21 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/mqrdf43gmo-prevent-commits-to-mainmaster-with-git-hooks</guid>
      </item>
      <item>
        <title>
          <![CDATA[How to correctly specify files when building gems - #ruby]]>
        </title>
        <link>http://localhost/posts/6j0p6oxy0z-how-to-correctly-specify-files-when-building-gems</link>
        <description>
          <![CDATA[<p>I recently found one of my docker images had 19MB of test files in the Ruby gems directory. When building a docker image, you want it as small as possible.</p><p>I wondered why some gems in there had tests included, and others did not? Here’s what I discovered…</p><p>When building a gem, it looks in your <code class="inline">gemname.gemspec</code> file for a list of files to include.</p><p>The default is a command that includes every file. It’s better to be specific about what files you include, instead of the default shotgun approach</p><p>In your <code class="inline">gemspec</code> file:</p><p><strong>BAD</strong></p><pre><code class="ruby language-ruby">s.files = `git ls-files -z`.split(&quot;\0&quot;)</code></pre><p><strong>GOOD</strong></p><pre><code class="ruby language-ruby">s.files = Dir.glob(&quot;{lib}/**/*&quot;) + %w(LICENSE README.md CHANGELOG.md)</code></pre>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Tue, 15 Feb 2022 14:58:46 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/6j0p6oxy0z-how-to-correctly-specify-files-when-building-gems</guid>
      </item>
      <item>
        <title>
          <![CDATA[Find your notes within your Rails app - #ruby]]>
        </title>
        <link>http://localhost/posts/jrjtguvtjn-find-your-notes-within-your-rails-app</link>
        <description>
          <![CDATA[<p>Leaving <code class="inline">TODOS</code> and other notes sometimes feels like throwing trash onto a pile which you may never come back to.</p><p>You can find all notes you’ve left inside a Rails using <code class="inline">rails notes</code>.</p><p>This will search <code class="inline">app</code>, <code class="inline">config</code>, <code class="inline">db</code>, <code class="inline">lib</code>, and <code class="inline">test</code> directories for <code class="inline">FIXME</code>, <code class="inline">OPTIMIZE</code>, and <code class="inline">TODO</code> annotations.</p><p>Running <code class="inline">rails notes</code> will return something like:</p><pre><code class="shell language-shell">app/actions/auth/creates_new_user_from_open_id_payload.rb:
  * [22] [TODO] Find a way to create users linked to accounts?
  * [27] [TODO] Not using these right now, and they should move to the tokens model</code></pre><p>You can see that we receive a list of notes including the file, line number and the note.</p><p>You can also filter for specific notes with <code class="inline">--annotations</code>. For example let’s filter <code class="inline">FIXME</code>s:</p><pre><code class="shell language-shell">rails notes --annotations FIXME</code></pre><p>You can also add custom notes in the Rails config like this:</p><pre><code class="ruby language-ruby">config.annotations.register_tags(&quot;DEPRECATEME&quot;, &quot;TESTME&quot;)</code></pre><p>Adding your own custom notes could be handy when you want to add a note for something like gem/app upgrades in code which might not be tested particularly well, or just difficult to test in general.</p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 07 Feb 2022 06:41:54 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/jrjtguvtjn-find-your-notes-within-your-rails-app</guid>
      </item>
      <item>
        <title>
          <![CDATA[How to use TouchID for sudo access - #bash]]>
        </title>
        <link>http://localhost/posts/9qg65xepvk-how-to-use-touchid-for-sudo-access</link>
        <description>
          <![CDATA[<p>If you’re on a Mac with TouchID, you can easily configure it to prompt for a TouchID scan alongside normal password auth when running a command via <code class="inline">sudo</code>.</p><ul><li>Edit <code class="inline">/etc/pam.d/sudo</code> with sudo
</li><li>Add this as the first line underneath the comment at the top:
</li></ul><pre><code class=" language-">auth sufficient pam_tid.so</code></pre><ul><li>Save the file, and the next time you require sudo, you should be greeted with a TouchID prompt.
</li></ul><h3>Notes</h3><ul><li>I haven’t tested this on the new magic keyboards with integrated TouchID, but there’s no reason to believe it won’t work with these keyboards too.
</li><li>You may need to reapply this config when MacOS updates
</li></ul>]]>
        </description>
        <dc:creator>gabrielfortuna</dc:creator>
        <pubDate>Wed, 02 Feb 2022 10:36:50 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/9qg65xepvk-how-to-use-touchid-for-sudo-access</guid>
      </item>
      <item>
        <title>
          <![CDATA[Where clause with Association - #ruby]]>
        </title>
        <link>http://localhost/posts/5b4dqeiznk-where-clause-with-association</link>
        <description>
          <![CDATA[<p>TIL that you can use a where clause when defining model associations</p><pre><code class="ruby language-ruby">class Foo &lt; ActiveRecord::Base
  has_many :bars, -&gt; {where(active: true)}
end</code></pre>]]>
        </description>
        <dc:creator>dominicsanto</dc:creator>
        <pubDate>Tue, 01 Feb 2022 11:55:05 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/5b4dqeiznk-where-clause-with-association</guid>
      </item>
      <item>
        <title>
          <![CDATA[Combine Pry and RSpec - #ruby]]>
        </title>
        <link>http://localhost/posts/t0kn0p63pb-combine-pry-and-rspec</link>
        <description>
          <![CDATA[<p>RSpec provides a way for you to register aliases for the methods you use to create your specs. You can alias these methods and tie them into predefined hooks using metadata.</p><p>For example, let’s say we wanted to have run pry whenever a spec includes some metadata like: <code class="inline">pry: true</code>. Instead of adding <code class="inline">pry: true</code> after each spec, we can add an alias by doing the following:</p><pre><code class="ruby language-ruby">RSpec.configure do |config|
  config.alias_example_group_to :pdescribe, pry: true
  config.alias_example_to       :pit,       pry: true

  config.after(:example, pry: true) do |e|
    require &apos;pry&apos;
    binding.pry
  end
end</code></pre><p>We’ve registered 2 aliases. Whenever we prepend <code class="inline">p</code> onto a method’s name, we’ll now get the same functionality as adding <code class="inline">pry: true</code> to our spec’s metadata.</p><p>We’ve tied this alias into an <code class="inline">after</code> hook. This hook only runs when pry is set to true in the spec’s metadata. RSpec after hooks always run, even if the spec fails!</p><p>Simply prepend <code class="inline">p</code> to a spec and pry will run once the spec is finished!</p><p>We’ve now created a powerful pry alias to debug our specs in RSpec :) </p>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 24 Jan 2022 06:03:09 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/t0kn0p63pb-combine-pry-and-rspec</guid>
      </item>
      <item>
        <title>
          <![CDATA[Ensure clauses - #ruby]]>
        </title>
        <link>http://localhost/posts/kjt0bupsif-ensure-clauses</link>
        <description>
          <![CDATA[<p>Today I learned about the <code class="inline">ensure</code> clause when raising exceptions. I was having trouble cleaning up certain operations, now within the <code class="inline">ensure</code> block the code will always get executed whether an exception is raised or not.</p><pre><code class="ruby language-ruby">def foo
  begin
     raise Exception, &quot;Oh no&quot;
   ensure
     return &quot;Clean me up Scotty&quot;
  end
end</code></pre>]]>
        </description>
        <dc:creator>dominicsanto</dc:creator>
        <pubDate>Tue, 18 Jan 2022 05:48:11 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/kjt0bupsif-ensure-clauses</guid>
      </item>
      <item>
        <title>
          <![CDATA[DRY up kwargs using `with_options` - #ruby]]>
        </title>
        <link>http://localhost/posts/vayp4orofe-dry-up-kwargs-using-withoptions</link>
        <description>
          <![CDATA[<p>Rails has a really awesome helper to DRY up code which makes use of much of the same kwargs. It’s called <code class="inline">with_options</code> and is used like this:</p><pre><code class="ruby language-ruby">class Account &lt; ActiveRecord::Base
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
end

# Becomes this with `with_option`

class Account &lt; ActiveRecord::Base
  with_options dependent: :destroy do
    has_many :customers
    has_many :products
  end
end</code></pre><p>Your code is now a lil more DRY and its easier to add, or remove, pesky kwargs as needed.</p><p>Another quick example:</p><pre><code class="ruby language-ruby">class AuthenticatedController &lt; ApplicationController
  before_action :logged_in?,         only: [:show, :edit]
  before_action :create_paper_trail, only: [:show, :edit]

  # ...
end

# Becomes this with `with_option`

class AuthenticatedController &lt; ApplicationController
  with_options only: [:show, :edit] do
    before_action :logged_in?
    before_action :create_paper_trail
  end

  # ...
end</code></pre>]]>
        </description>
        <dc:creator>douglasgreyling</dc:creator>
        <pubDate>Mon, 17 Jan 2022 07:29:47 GMT</pubDate>
        <guid isPermaLink="true">http://localhost/posts/vayp4orofe-dry-up-kwargs-using-withoptions</guid>
      </item>
  </channel>
</rss>
