Today I Learned

hashrocket A Hashrocket project

Linewise Searching in Vim

Today I learned about the \%l atom, which can be used to search on the current line, or a specific line, and also above or below the current or specific line. That's kind of a lot in one sentence, so lets break it down:

Say you want to search for a search_term on line 23 of your buffer, you can do this by

/\%23lsearch_term

If you want to search for search_term only on the current line, you can use . instead of a line number

/\%.lsearch_term

You can use the <and > modifiers to match above (<) or below (>) the current line

/\%<.lsearch_term

/\%>.lsearch_term

But you can also replace . with a line number to match above or below that line:

/\%<23lsearch_term

/\%>23lsearch_term

Search within Selection in Vim

\%V is a useful atom (TIL it's called an atom - and there are a lot of them, see :h pattern-atoms) that can be used to match inside a Visual selection.

Say you have a large file open and want to search for something only within a single area of text, like a method. You can visually select that block of text, enter command mode with :, and prefix the search with \%V to scope the search so that you only get matches inside the selection:

:'<,'>/\%Vsearch_text

You can also use \%V to search and replace.

Switch Android emulator devices

If you want to switch which android device you are emulating on Android Studio,

  1. Open the app and click the dropdown menu More Actions
  2. Select Virtual Device Manager
  3. Click the + icon to add a new device, and select the android model you'd like to emulate
  4. Choose a system image
  5. After adding the device you should be able to click the play button to open the new emulator

Ensure Correct Redirect Action with 303 Status

Let's say I have a Post model, and send a PATCH XHR request for an instance of Post, handled by the PostsController. Normally, if I want to redirect to the posts_path at the end of the controller action I end the method like so:

def patch
  # patch-y things done here
  redirect_to posts_path
end # => redirects to PATCH /posts ❌

It's possible, depending on your browser, that instead of redirecting as a GET request to posts_path like you would expect, the original PATCH method is forwarded instead. Most times, this probably isn't what you want. To force the redirect to use the appropriate method for the path you're redirecting to, you can use the 303 See Other status code:

def patch
  # patch-y things done here
  redirect_to posts_path, status: :see_other
end # => redirects to GET /posts ✅

It's specifically called out in the docs (ActionController::Redirecting) so it seems to be fairly well-known/widespread, but it was a new to me occurrence.

Show Commit from Other Branch

I wanted to view a commit on a different branch without having to checkout/switch to that branch. It turns out git show can do that!

Say I'm on branch-a, but I want to see the latest commit I made on branch-b. It's actually really straightforward:

$ git show branch-b

This follows a lot of the git norms, too! git show branch-b~ will show the previous commit on branch-b (and git show branch-b~2 the commit before that). To view a single file in that commit, git show branch-b:path/to/file.rb!

Group together console logs

In Javascript you can group together console logs by using console.group()

This will output all of the following console logs in a nicer indented form.

console.group("Food");
console.log("Apple");
console.log("Banana");
console.log("Cookie");

image

You can nest groups inside of one another. To end a group just call console.groupEnd()

// ✨ indented for a better visual experience ✨
console.group("Food");
  console.group("Fruit");
    console.log("Apple");
    console.log("Banana");
  console.groupEnd();
  console.group("Dessert");
    console.log("Cookies");
  console.groupEnd();
console.groupEnd();

image

PostgreSQL conditional upserts

So in order to do an "upsert" we use an INSERT command with a CONFLICT statement. Then if we need to do some conditional setting in the case of existing data then we can use the "temp table" EXCLUDED that PostgreSQL provide to us. They have a nice example how to use that in their docs and here's mine example:

INSERT INTO users AS u (email, fullname) VALUES
  ('darth@example.com', 'Darth Vader'),
  ('luke@example.com', 'Luke Vader')
ON CONFLICT (email) DO UPDATE
SET fullname = EXCLUDED.fullname || ' (formerly known as ' || u.fullname || ')';

For this to work we have to have an unique constraint on the column email and the outcome of this could be:

SELECT email, fullname
FROM users;
| email              | fullname                                      |
| ---                | ---                                           |
| darth@example.com  | Darth Vader                                   |
| luke@example.com   | Luke Vader (formerly known as Luke Skywalker) |

Use git show-ref to Check for Branch Existence

Today I learned how to check in git if a branch exists on my local repo. I bounce between projects that use a main or master branch and wanted to automate away checking which to use.

git show-ref lists references in a local git repository. It can show any ref, be it tag or branch or whatever. But we want to use it for branches:

# let's say this repo has `main` branch, not `master`
$ git show-ref --branches master
$ git show-ref --branches main
2067645fb4d7b6ab07215c025dee95b872150db2 refs/heads/main

Note it's important to filter by branches, otherwise you can get false positives from remotes or tags

$ git show-ref main
2067645fb4d7b6ab07215c025dee95b872150db2 refs/heads/main
2067645fb4d7b6ab07215c025dee95b872150db2 refs/remotes/origin/main

You can use the existence of output from this command in scripts to, for example, determine which branch to rebase against:

#!/bin/sh

main_exists=`git show-ref --branches main`
if [ -n "$main_exists" ]; then
  branch="main"
else
  branch="master"
fi;

git rebase -i $branch

Create New Rails App with Old Rails Version

Sometimes you need to spin up a rails app using an older version of rails. Running rails new will spin up an app with the latest version (which is what you want most of the time). But if you have older gems of rails installed you can create new rails apps by specifying the version wrapped in underscores.

Say your rails entry in your gem list looks like this: rails (7.2.2.2, 7.2.1, 7.1.5.2, 7.1.2, 7.0.8.7). If you want to create a new rails app using 7.0.8.7, you can run (_ before and after the version number!):

$ rails _7.0.8.7_ new new_app

And boom, you've got a new_app created running rails 7.0.8.7.

Add/Subtract Months on a Date in Ruby

With ActiveSupport you can easily add months (or days, years, etc) to a date with:

pry(main)> Date.today
# => Fri, 05 Sep 2025
pry(main)> Date.today + 2.months
# => Wed, 05 Nov 2025
pry(main)> Date.today - 1.month
# => Tue, 05 Aug 2025

But what if you're working outside of Rails and without ActiveSupport? You can use the shovel (<<) operator on dates to return the date n months earlier.

pry(main)> Date.today
# => Fri, 05 Sep 2025
pry(main)> Date.today << 1
# => Tue, 05 Aug 2025

If you want to go forwards, you can use >>, or negate the value of n using <<:

pry(main)> Date.today
# => Fri, 05 Sep 2025
pry(main)> Date.today >> 2
# => Wed, 05 Nov 2025
pry(main)> Date.today << -2
# => Wed, 05 Nov 2025

Docs for << and docs for >>

Setup Your Rails Console with .pryrc

Sometimes there are variables I want to initialize or setup I want to run each time I enter a rails console, and I don't want to have to type it out every time. I'm using pry-rails, so my rails console is a pry session, and I can add ruby code to my .pryrc file to be run on console startup. Conveniently, this runs after the environment is loaded, so I have access to all my ActiveRecord models.

# .pryrc
author = Author.find(1)

If I put the above in my .pryrc, then I've got author available to me whenever I'm in a rails console (but not when I drop into a pry session in rails server (which is fine, I think it's less useful there)).

You can use a local .pryrc in your project's directory, or a global config in ~/.pryrc.

Add a Suffix to ActiveRecord Enums

Today I learned you can add suffixes to ActiveRecord Enums. I knew it was possible to prefix enums with the _prefix option:

class Answer < ApplicationRecord
  enum selection: {yes: "yes", no: "no", idk: "i don't know"}, _prefix: true
end

And you get helper methods like answer.selection_yes?, but that reads a little awkwardly. I think it reads better as answer.yes_selection?. Turns out you can do this with _suffix:

class Answer < ApplicationRecord
  enum selection: {yes: "yes", no: "no", idk: "i don't know"}, _suffix: true
end

answer = Answer.new selection: "yes"
answer.yes_selection? # => true
answer.no_selection? # => false

ActiveRecord::Enum Docs

Git Branch Completion in Bash

You can get git branch auto-completion in bash. Sometimes this is configured out of the box, but it didn't work for me using homebrew's git.

If you are using git installed via homebrew, git-completions.bash is located at /opt/homebrew/etc/bash_completion.d/git-completions.bash (or better yet $(brew --prefix)/etc/bash_completion.d/git-completions.bash for reliability). You just need to source it in your .bashrc or .bash_profile with:

test -f $(brew --prefix)/etc/bash_completion.d/git-completion.bash && . $_

(This is a cool shorthand syntax to test that the file exists, and if it does, source it. $_ refers to the last argument in the previous command, in this case the full path to git-completion.bash)

Now you can tab complete all your branch names in git checkout, git rebase, and more!

Normalization Attributes in Rails

In Rails 7.1, attribute normalization was introduced. This gives us an easy way to clean up data before persistence, or other actions.

class Contact < ApplicationRecord
  normalizes :phone, with: -> (phone) { phone.gsub(/\D/, "") } # remove non-number chars
end

The above ensures that before save, a phone number like 904-123-4567 gets converted to 9041234567.

In Rails 8, attribute normalization was backported to ActiveModel. So you can also use these methods in ActiveModels (re: Form objects) -

class ContactForm
  include ActiveModel::Attributes
  include ActiveModel::Attributes::Normalization

  attribute :phone
  
  normalizes :phone, with: -> (phone) { phone.gsub(/\D/, "") }
end

contact = ContactForm.new
contact.phone = "904-123-4567"
contact.phone # 9041234567

Ecto: Preload with Joins to Save Database Trips

Today I learned that you can combine preload with join in Ecto to fetch related data in a single query instead of making multiple database trips.

For example, instead of doing this:

from(
  u in User,
  where: u.status == :active,
  preload: [:emails]
) |> Repo.all()

Which runs 2 queries in db, you can do this in a single query:

from(
  u in User,
  join: e in assoc(u, :emails),
  where: u.status == :active,
  preload: [emails: e]
) |> Repo.all()

ActiveModel Error Details as a Hash

Today I Learned about a new way to list the errors for an invalid ActiveRecord model. Let's say I have a blog post class with title and author attributes, both of which are required:

class BlogPost < ApplicationRecord
  validates :title, presence: true
  validates :author, presence: true
end

Normally I interact with the errors in an invalid model through the nice ActiveModel Errors interface:

pry(main)> blog_post = BlogPost.new
pry(main)> blog_post.valid?
pry(main)> blog_post.errors
# => <ActiveModel::Errors [#<ActiveModel::Error attribute=title, type=blank, options={}>, #<ActiveModel::Error attribute=author, type=blank, options={}>]>

pry(main)> blog_post.errors.where(:title)
# => [#<ActiveModel::Error attribute=title, type=blank, options={}>]

But you can also return a hash of attributes containing arrays of their error details and use your favorite hash methods:

pry(main)> blog_post.errors.details
# => {:title=>[{:error=>:blank}], :author=>[{:error=>:blank}]}

pry(main)> blog.errors.details.dig(:title, 0, :error)
# => :blank

Git: Quickly Rebase Onto Previous Branch

Today I learned that you can use a dash - with git rebase to quickly rebase your current branch onto the previous branch you were on. This is a handy shortcut if you’re hopping between branches and want to rebase without typing the full branch name.

For example, after switching between branches:

git checkout main
git pull --rebase
git checkout new-feature-branch
git rebase -

The - here ^ refers to the last branch you were on (main in this case), so this command is equivalent to git rebase main.

It's funny that I used this all the time with git checkout but it never occurred to me that I could use for other git commands.

Use Brace Expansion to Rename Files

Today I Learned you can use brace expansion to rename files in the command line (it works in both bash and zsh).

Brace expansion allows you to generate strings sharing a common prefix or suffix - perfect for moving/renaming files:

To copy someFile.js to someFile.ts, you can run:

$ cp someFile.{js,ts}
# expands to:
$ cp someFile.js someFile.ts

To rename app/model/blog_test.rb to app/model/blog_spec.rb:

$ mv app/model/blog_{test,spec}.rb
# expands to:
mv app/model/blog_test.rb app/model/blog_spec.rb

See Which Process Has a File Open with fuser

Today I learned you can see which processes have a file open with fuser. For example, if I wanted to see what process was writing to log/development.log in my rails app, I'd run

$ fuser log/development.log
log/development.log: 76170

So the process with PID 76170 has the file open. Running ps will show me the process details:

$ ps 76170
  PID   TT  STAT      TIME COMMAND
76170 s008  S+     0:02.86 ruby bin/rails console

Turns out I had a rails console running!

You could also turn this into a one-liner with xargs:

$ fuser log/development.log | xargs ps

Edit Source Code from Within Pry

Yesterday I learned (YIL?!) how to override instance methods within pry. But what if you like the change and want make it more permanent? Typically what I would do is edit the appropriate file in my editor, and either reload! within pry or restart the session.

I learned you can also edit the code directly from within pry - if I want to edit the User class, I can run edit User and pry will open the file that defines User in my $EDITOR ((n)vim, natch). When you close the file, you'll be dropped back into your pry session and the code will be reloaded (including any instances of the class you already initialized in your session).

class User
  def name = "tony"
end

pry(main)> me = User.new
pry(main)> me.name
# => "tony"

pry(main)> edit User # and let's say we edit name to return 'not-tony'
pry(main)> me.name
# => "not-tony"
pry(main)> User.new.name
# => "not-tony"

Override Methods in a Pry Session

Today I learned you can override methods on an object from within pry! Super helpful if you want to test something out but not change it in code yet. It's important to note this only overrides at the instance level, not the class level, so other instances won't be affected.

class User
  def name = "tony"
end

pry(main)> me = User.new
pry(main)> me.name
# => "tony"
pry(main)> def me.name = "who now?"
pry(main)> me.name
# => "who now?"
pry(main)> other_me = User.new
pry(main)> other_me.name
# => "tony"

Use the norm Command for Repetitive Text Tasks

Vim macros are great, but they can become tedious if you need to repeat the macro over and over again on multiple lines. norm comes in super handy in these cases! You can use it to operate on every line, or you can apply it to a visual select range.

* one,
* two,
* three,
* four,

Given a file like above and I want to change just the lines with text two, three, by replacing the * with a - and change the ending comma to a semicolon, I can visual select those lines (Shift + V), enter command mode with : and run:

:'<,'>norm 0s-^[$s;

Let's break it down:

  • '<,'> is the visual selection range
  • norm is the norm command
  • 0s- replaces the first character of the line with -
  • ^[ is actually not typed directly but is an escaped ESC char inserted by Ctrl + V, Esc. This allows you to re-enter command mode inside norm mode.
  • $s' replaces the last character of the line with ;

After running the command, the file should look like:

* one,
- two;
- three;
* four,

Disable single table inheritance column in Rails

Due to ActiveRecord single table inheritance in Rails, the the attribute type is a reserved word by default. However, I like to use type when STI is not in the picture. We can do this per model by setting inheritance_column. Much like most things in Rails there is a sane default for convention but can be overrdiden. In this case we will be 'disabling' is by setting to nil

class Foo < ApplicationRecord
  self.inheritance_column = nil
end

Now you're free to use the type column as wish.

Modify JS code in the browser

Have you ever needed to debug a production JavaScript issue but couldn't modify the source code directly? Chrome's Overrides feature is here to save the day! This powerful tool allows you to persist JavaScript changes between page reloads, even when you don't have direct access to the original source files.

Here are the steps:

  1. Open DevTools > Sources panel
  2. Click "Overrides" tab (>> if hidden)
  3. Select a local folder (grant permission when prompted)
  4. Edit any JS file in Sources > Page
  5. Save (Cmd+S) - a purple dot indicates override active

You can reload the page and your change will be persisted locally and executed in the browser.

Remember to delete your overrides once you're done.

Happy debugging! 🛠️

image

Firefox Custom Search Shortcuts

In addition to the built in search shortcuts in firefox, you can also add custom shortcuts in the settings.

This lets you override your default search engine by prepending a keyword in your address bar (like @google, @duckduckgo, @kagi). It's a quick shortcut if you want to search for something on a specific site. The wikipedia one (@wiki) is especially neat in that it'll take you straight to the wikipedia page if there's a direct hit, no results page you need to go through first.

image

Firefox Builtin Search Shortcuts

Firefox has built in search shortcuts in the address bar, making it easy to search through your bookmarks, tabs, history and actions you can perform.

image

In the address bar (which you can focus with Cmd + L), including the following characters in your search will:

  • * search within your bookmarks
  • % search within your tabs
  • ^ search within your history
  • > search within actions - like Manage bookmarks, View downloads, Take a screenshot

The neat thing is you don't need to start your search with the shortcut, you can add it anywhere. So you can start typing something in the address bar, remember you visited the page yesterday, and add ^ at the end to filter down to your history.

Docs

Subtly between enumerable pluck and map in Rails

Did you know that pluck was not just available in ActiveRecord? Turns out Rails added it to Enumerable so you can use it on most collections.

But wait...

Before you go and do something like this:

all("nav a").pluck(:text)

Beware that pluck's implementation does a key lookup on the object being enumerated (reference). So in this particular case because text is a method on these Capybara elements you'd need to use map

all("nav a").map(&:text)

But you could use it for things that respond to hash/key lookup syntax like:

all("nav a").pluck(:href)

Report Code Statistics in Rails

You can run rails stats to print out statistics around lines of code, classes, methods, code to test ratio, etc for your rails app. It'll print out a table like this:

image

(Don't mind the code to test ratio...it's a proof of concept app 😅)

h/t Joel Hawksley, I learned about this from the excellent talk he gave at Chicago Ruby last week.

Nested Expectations in RSpec

Today I learned you can write nested expectations in rspec. I find this is approach useful with writing request-style tests where I want to ensure the request returned successfully and also ensure the expected effects happened.

it "creates the post" do
  expect {
    expect(request.response_code).to eq(200)
  }.to change { Post.count }.by(1)
end

If either the response is not 200, or the Post count doesn't change, then the test fails.

There is a gotcha that I run into when I build these tests iteratively - first the inner expectation on it's own, wrap it in the outer block, and then add the outer matcher. If you wrap the inner request in an expect block, but don't have any assertions on that block, it will always pass - because we're not matching against anything.

RSpec.describe do
  # ❌ - as expected
  it do
    expect(true).to eq(false)
  end

  # ❌ - as expected, outer expectation passes but inner fails, so test fails
  it do
    expect {
      expect(true).to eq(false)
    }.to not_change { 0 }
  end

  # ✅ - :-? watch out for this one, even though the inner expectation fails, test passes
  it do
    expect {
      expect(true).to eq(false)
    }
  end
end