Today I Learned

A Zero One initiative

15 posts by douglasgreyling

Beware of Stale Closure Data in React

Something we continually take for granted is that setState 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.

This is particularly painful when your next version of some state depends on the previous version (Eg. A counter + 1).

To ensure that closures don’t experience issues like this, you can provide a callback to setState instead of a fixed value.

Eg. Instead of:

[count, setCount] = useState(0);
const increment = () => setCount(count + 1);

Do this:

[count, setCount] = useState(0);
const increment = () => setCount((prevCount) => prevCount + 1);

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.

Add comments to package.json

Progressively upgrading dependencies in the JS world is a little different compared to Ruby.

When updating a Ruby gem inside a gemfile, you might want to leave notes. For example:

gem 'some-gem', '~> 1.2.3' # TODO: Update once we're on Ruby 3.1

The comment is useful since it shares knowledge related to upgrading this dependency.

Javascript, or JSON, works a little differently. No comments are allowed inside the package.json file. Instead to achieve roughly the same result you can do this:

...
"//": [
    "TODO: Upgrade stylelint-config-sass-guidelines once we're on Node 11+",
    "TODO: Upgrade mini-css-extract-plugin once we're on 12.13+ and webpack 5+",
    "TODO: Upgrade style-loader once we're on 12.13+ and webpack 5+"
  ],
"dependencies": {
  ....
}

"//" will never be used by npm for any purpose, and is reserved for comments.

Finding missing/associated records in Rails

Rails has some built in methods to help you find records which may have missing associated records.

For example, let’s assume we have an app where an Invoice has many Payments, and we wanted to find all invoices which have no payments:

Invoice.where.missing(:payments)

We’ll get back a collection of all invoices which have no payments.

Much cleaner than writing messy joins!

Also, on the flip side you can find all invoices with payments like this:

Invoice.where.associated(:payments)

Extending Rake Tasks

You can extend rake tasks to add extra functionality.

For example, let’s extend the rails db:migrate task to automatically include schema migrations and data migrations.

# Inside lib/tasks/db.rake
# Remove the old migrate task
Rake::Task['db:migrate'].clear

namespace 'db' do
  # Redefine the migrate task to invoke with data migrations
  task migrate: ['migrate:with_data']
end

Now whenever you execute rails db:migrate, the task will include schema + data migrations.

Additionally, you may want to invoke some extra task after another has been executed. To do this, you can enhance a task like this:

# Inside lib/tasks/db.rake
Rake::Task['db:migrate'].enhance do
  Rake::Task['db:do_some_custom_thing'].invoke
end

Now your custom task will be invoked after rails db:migrate is invoked.

Simpler conditional HTML classes with class_names

Coming from JS and React you may be familiar with a package called classnames. 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.

Eg. classnames(‘foo’, { bar: true, baz: false })

Returns: ‘foo bar’

Rails includes its own helper which does this same thing. You can call class_names or token_list:

Eg. class_names(‘foo’, { bar: true, baz: false })

Returns: ‘foo bar’

Default Controller Params

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:

def todo_params
  params
    .require(:todo)
    .permit(:subject, :description, :user_id)
    .tap { |pms|  pms.user_id ||= current_user.id }
end

However, TIL that you can actually make use of a method called with_defaults:

def todo_params
  params
    .require(:todo)
    .permit(:subject, :description, :user_id)
    .with_defaults(user_id: current_user.id)
end

Much cleaner!

Also, with_defaults is defined for ActionController::Parameters and Hash. I find with_defaults much cleaner, and more intention revealing, than reverse_merge!

Fun with Recursive Anonymous Functions

Did you know that an anonymous function can call itself?

All you need is something like this:

factorial_of = -> (n) { n <= 1 ? 1 : n * factorial_of.call(n - 1) }
factorial_of.call(5)
# => 120

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.

Instead of something as complex as that, let’s wrap our anonymous factorial function around an enumerator:

factorial_of = -> (n) { n <= 1 ? 1 : n * factorial_of.call(n - 1) }

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

f.next
=> 1
f.next
=> 1
f.next
=> 2
f.next
=> 6

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)

Prevent commits to main/master with git hooks!

You can use git hooks to keep you safe when you aren’t thinking properly and decide to commit something to main/master.

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 crimesjohnson prefix.

As a start I decided to make a global hook for all of my projects with this command:

git config --global core.hooksPath ~/githooks

I then created a file (within ~/githooks) for a special kind of git hook and named it commit-msg.

#!/bin/sh

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

if ([ "$branch" = "main" ] || [ "$branch" = "master" ]) && [[ "$commit_msg" != *"crimesjohnson:"* ]]; then
  echo "You can't commit directly to main/master branch"
  exit 1
fi

Don’t forget to make it executable: chmod +x ~/githooks/commit-msg

When you create a commit message, it seems like the message is saved to a temp file while the commit-msg hook runs. We get the commit message by cating it.

We also use a git command to get the name of our branch.

Using these 2 bits of info, we can then write a simple if statement to keep us safe!

Find your notes within your Rails app

Leaving TODOS and other notes sometimes feels like throwing trash onto a pile which you may never come back to.

You can find all notes you’ve left inside a Rails using rails notes.

This will search app, config, db, lib, and test directories for FIXME, OPTIMIZE, and TODO annotations.

Running rails notes will return something like:

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

You can see that we receive a list of notes including the file, line number and the note.

You can also filter for specific notes with --annotations. For example let’s filter FIXMEs:

rails notes --annotations FIXME

You can also add custom notes in the Rails config like this:

config.annotations.register_tags("DEPRECATEME", "TESTME")

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.

Combine Pry and RSpec

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.

For example, let’s say we wanted to have run pry whenever a spec includes some metadata like: pry: true. Instead of adding pry: true after each spec, we can add an alias by doing the following:

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 'pry'
    binding.pry
  end
end

We’ve registered 2 aliases. Whenever we prepend p onto a method’s name, we’ll now get the same functionality as adding pry: true to our spec’s metadata.

We’ve tied this alias into an after 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!

Simply prepend p to a spec and pry will run once the spec is finished!

We’ve now created a powerful pry alias to debug our specs in RSpec :)

DRY up kwargs using `with_options`

Rails has a really awesome helper to DRY up code which makes use of much of the same kwargs. It’s called with_options and is used like this:

class Account < ActiveRecord::Base
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
end

# Becomes this with `with_option`

class Account < ActiveRecord::Base
  with_options dependent: :destroy do
    has_many :customers
    has_many :products
  end
end

Your code is now a lil more DRY and its easier to add, or remove, pesky kwargs as needed.

Another quick example:

class AuthenticatedController < ApplicationController
  before_action :logged_in?,         only: [:show, :edit]
  before_action :create_paper_trail, only: [:show, :edit]

  # ...
end

# Becomes this with `with_option`

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

  # ...
end

Strict loading with Rails

Rails implements a feature called strict loading. This feature can help catch potential N+1 queries.

Did you know there are a couple of ways to enable strict loading?

1. Per Record

Rails let’s you enable strict loading on specific records at runtime:

post = Post.first

post.strict_loading! # Enable strict loading
post.strict_loading? # Check if strict loading is enabled on this object
# => true

2. Per model

Rails let’s you enable strict loading on all models of a specific type:

class Post < ApplicationRecord
  self.strict_loading_by_default = true

  has_many :comments, dependent: :destroy
end

post = Post.first
post.strict_loading?
# => true

3. Enable per association basis

Rails let’s you define strict loading for specific (or pesky) relations on a model:

class Post < ApplicationRecord
  has_many :comments, dependent: :destroy, strict_loading: true
  has_many :likes,    dependent: :destroy, strict_loading: false
end

4. Application wide

Rails also let’s you enable strict loading all across the board when you’re brave enough for the nuclear option:

# config/environments/development.rb

Rails.application.configure do
# ...
  config.active_record.strict_loading_by_default = true
# ...
end

Now that you know your options, you can enable it based on your appetite/distaste for N+1 queries!

Ruby prepend wrappers

Imagine we have some sort of object (which we don’t own) and we’d like to audit it. You could write an explicit wrapper around the object to audit it, but then you’ve got to keep in mind that you have to use this wrapper whenever you use the service.

You can add new functionality, whilst preserving the old interface and function like this:

class Foo # class we do not own
  def bar(arg)
    sleep arg
    "Running bar with #{arg}!"
  end
end

module FooWrapper
  def bar(arg)
    start_time = Time.now
    result = super
    time_taken = Time.now - start_time

    puts "bar() took more than #{time_taken} seconds" if 2 < time_taken
    result
  rescue
    "bar() failed"
  end
end

Foo.class_eval do
  prepend FooWrapper
end

Foo.new.bar 2.5
# bar() took more than 2.501257 seconds
# => "Running bar with 2.5!"

We’ve created a wrapper, opened up the class and prepended the wrapper. The wrapper adds new functionality, whilst preserving the old.

The downside to this approach is that whenever you call Foo#bar, you will get your added functionality each time. This may, or may not, be an issue depending your circumstance.

Make fancy enums with `enum_for`

Did you know you can make your own enumerators in Ruby?

e = Enumerator.new do |y|
 [1,2,3].each { |i| y << i }
end

e.next    # => 1
e.next    # => 2
e.rewind
e.next => # => 1

Methods like each help convert things to an enumerable (using to_enum), but there is another way to create enums in Ruby. enum_for takes one argument which points to a method to bind to for enumeration. It allows make something enumerable when it technically isn’t, or when we can’t/don’t want to.

class DoReMi
  def sing
    lyrics.each { |l| yield l }
  end

  private

  def lyrics
    [
      "Doe, a deer, a female deer",
      ...
    ]
  end
end

Use enum_for to enumerate at your own pace. Let’s learn to shout the song backwards!

d = DoReMi.new
d.sing { |l| puts l }
# => Doe, a deer, a female deer
# => ...
e = d.enum_for :sing
e.map { |l| l.upcase }.reverse.each { |l| puts l }
# => ME, A NAME I CALL MYSELF
# => ...

# Wait, what's the last lyric again?
e.rewind
e.next.upcase # => "DOE, A DEER, A FEMALE DEER"

`[]` is smarter thank you think!

You’re probably very familiar with using [] on objects which are enumerable, but [] has a few more tricks up it’s sleeve when it comes to strings which you may not know about:

string = "Ruby is a cool language"

Did you know that you can use [] to fetch a substring from a string, and return nil if it doesn’t exist?

string["cool lang"] # => "cool lang"
string["foo"] # => nil

You can even throw a Regex in that bad boy and you’re off to the Regex races!

And for the final trick we’ll make a pesky substring DISAPPEAR!

string["cool"] = "great" # => "great"
string # => "Ruby is a great language"

Next time you’re munging with strings, remember that [] is smarter than you think!