Today I Learned

hashrocket A Hashrocket project

Echo substrings of a variable

Say you have a variable set in a shell script:

export testPath='/Users/mary/code/thingy.rb'

You can echo that variable as usual:

echo $testPath
# /Users/mary/code/thingy.rb

But you can also get substrings of that variable as you echo it, using substring extraction. The substring extraction syntax is ${variable:offset:length}

echo ${testPath:11}
# /code/thingy.rb

echo ${testPath:12:4}
# code

You can also change the prefix (${variable#prefix}) and suffix (${variable%suffix}):

echo ${testPath#/Users/mary/code/}
# thingy.rb

echo ${testPath%.rb}
# /Users/mary/code/thingy

Random Sort Text

You can sort to sort text in a file or STDIN. But what if you want to embrace the chaos and sort randomly? Turns out, there's a few ways to do that.

  • sort -R file.txt - sort's -R option does a random sort rather than lexicographically. The caveat here is if you have duplicates, they will be grouped together in the resulting output.
  • shuf file.txt - shuf is a standalone utility to generate random permutations.

Read their man pages to learn more!

Autosave belongs_to Associations

By default, if you make a change to a belongs_to associated model and save the parent model, the associated model won't be saved. This is probably a good default, but it is overridable with the :autosave option.

class Book < ApplicationRecord
  belongs_to :author
end

book = Book.first
book.author.name # => "J.R.R. Tolkein"

book.author.name = "John Tolkein"

book.save
book.reload
book.author.name # => "J.R.R. Tolkein"

But we can change this behaviour by setting :autosave to true.

class Book < ApplicationRecord
  belongs_to :author, autosave: :true
end

book = Book.first
book.author.name # => "J.R.R. Tolkein"

book.author.name = "John Tolkein"

book.save
book.reload
book.author.name # => "John Tolkein"

While this is off by default for belongs_to associations, has_one and has_many associations have :autosave true because the foreign keys are on the associated records to ensure those FKs are kept up to date.

Build rows from jsonb data in PostgreSQL

PostgreSQL has a function called jsonb_to_recordset that will return your jsonb data as if it were rows in the database.

Say we have the following example table:

create table notes (title varchar, bullets jsonb);

insert into notes (title, bullets)
values ('Inspection', '[{"field": "Tires", "measured": "Tread Height", "value": "3mm"},{"field": "Brakes", "measured": "Caliper Boot", "value": "cracked"}]');

To use the jsonb_to_recordset function, we can do the following:

select title, field, measured, value
from notes
cross join lateral jsonb_to_recordset(notes.bullets) 
as temp(field varchar, measured varchar, value varchar);

The function gives us the following output:

   title    | field  |   measured   |  value  
------------+--------+--------------+---------
 Inspection | Tires  | Tread Height | 3mm
 Inspection | Brakes | Caliper Boot | cracked

Place a cursor at ALL occurrences in Visual Studio

One of the nice things about working with Visual Studio based text editors is its multi-cursor abilities. For those who don't know, you can highlight a word and pressing CMD+D you can place another cursor at the next occurrence of that string.

This is helpful for making a lot of changes at once. Here's an example where we can fix a typo that was made all throughout a file:

typo = getTypo

if(typo)
    # do something with the typo
else
    # do something else
end

def getTypo()
  typo
end

Well, what if we want a cursor at every occurrence of a typo, but we want a faster way than pressing CMD+D a bunch of times?

TLDR
Just highlight the word you want to grab and press CMD+Shift+L and it will grab all occurrences found in that file. (Just be careful and make sure that's what you actually want!)

Reload All Buffers in Vim

In vim :e edits the current file (reloads from disk), which is useful if the file has been changed outside of vim and you want to load those changes into your buffer in vim.

I ran into a scenario where I ran a formatter outside of vim, and wanted to reload all files open in buffers in my vim session. I took a guess that :ea would edit all (like :wa writes all, :qa quits all) - but alas, swing and a miss.

The way to do this is with :bufdo e. bufdo executes the command - in this case e - in each buffer in the buffer list.

Read Man Pages in (n)vim

Reading man pages in (n)vim is my new favorite way to read man pages. (n)vim gives you syntax highlighting, plus you can follow the links with Shift + K (yes, there are links between man pages - I never knew!).

You can override the default pager for man by setting the MANPAGER environment variable (the default is less -sR) - per the nvim help page, you can use nvim with export MANPAGER='nvim +Man!'.

Happy manual reading!

h/t Josh Branchaud, the TIL GOAT.

Validating Keyword lists in Elixir

I found out that Keyword.validate/2 is a great way to guarantee that a function would be called with the right keyword options. Check this out:

def do_something(%User{} = user, opts \\ []) when is_list(opts) do
  {:ok, opts} = Keyword.validate(opts, [:preload])
  preload = Keyword.get(opts, :preload, [])
  #...
end

Here's some interesting cases:

# fails if you pass an invalid option
Keyword.validate([one: "1", two: "2"], [:one])
# => {:error, [:two]}

# fails if you pass a valid option that is not expected that many times
Keyword.validate([one: "1", one: "again"], [:one])
# => {:error, [:two]}

# it can set default values
Keyword.validate([one: "1"], [:one, two: "5"])
# => {:ok, [two: "5", one: "1"]}

# it succeeds if you pass the right options
Keyword.validate([one: "1", two: "2"], [:one, :two])
# => {:ok, [two: "2", one: "1"]}

Prevent display from falling asleep on MacOS

If you want to prevent your display from going to sleep while you are doing something that doesn't require input, you can use caffeinate -d

For me this was useful when running a longer script that I wanted to keep an eye on while I was debugging. By running caffeinate -d in a terminal I was able to prevent the display from sleeping, saving me from pressing a button or moving the mouse all the time.

git commit -C

Today I learned about the -C flag for git commit. It's used in Hashrocket's dotmatrix in our git cheddar alias - git commit --amend -CHEAD, but I never actually dug into what it does.

From the manual:

-C <commit>, --reuse-message=<commit>

Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit.

Important to note, -C doesn't open your editor to edit the message, so it's great if you don't need to edit the existing message (if you do want to edit, lowercase -c will open your editor with the existing message).

-CHEAD....ohhhh that's why it's called cheddar 🤯

ActiveRecord List All Enums

Today I learned a way to view all the enums defined on an ActiveRecord Model.

Suppose I have a class Post with enums status and category:

class Post < ApplicationRecord
  enum status: [:draft, :published]
  enum category: [:ruby, :rails, :lord_of_the_rings]
end

I can view the enum values for statuses with Post.statuses and category with Post.categories, but what if I want to see all the enums on Post? defined_enums will do that:

Post.defined_enums
# => {"status"=>{"draft"=>0, "published"=>1}, "category"=>{"ruby"=>0, "rails"=>1, "lord_of_the_rings"=>2}}

You can of course key in to each enum here too:

Post.defined_enums["status"]
# => {"draft"=>0, "published"=>1}

Extract Secrets from 1Password for Kamal Deploy

Kamal, the default deployment tool for Rails, has some really great features. One that I just discovered today is kamal secrets.

You can use this utility to expand sensitive credentials from external sources during deployment. Out of the box, it supports 1Password, Bitwarden (and Secrets Manager), LastPass, AWS Secrets Manager, Doppler, and GCP.

You can run the command for SECRETS from the .kamal/secrets file manually to test everything out.

A pre-req for using 1Password is that you will need to install the OP CLI and login to your vault:

brew install 1password-cli

op signin

Next you'll need your account id. You can get that with -

op whoami

Then verify you can read your secrets. The output of the command inside the $(...) is a stringified JSON -

SECRETS=$(kamal secrets fetch --adapter 1password --account op_account_id --from "op://Example/ApiKeys" KAMAL_REGISTRY_PASSWORD)

The output will look something like this -

\{\"Example/ApiKeys/KAMAL_REGISTRY_PASSWORD\":\"xxxxxxxxxxxxx\"\}

The last part is expanding this. You can pass this JSON string to kamal secrets extract to extract the value from the key in the JSON.

kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS}

Change your current context

You can change the context of your console session by using the cd just like you would change the context of your directory.

Here is an example.

# Grab an object, in our example a customer.
c = Customer.all.sample

# Now, 'cd' in to that object.
cd c

# From here you will notice that we are now within 
# the context of our object and can call methods on it directly.

pry(<Customer>):1> first_name
=> "Peter"
pry(<Customer>):1> last_name
=> "Parker"

I found this cool tip, as well as some others in this awesome blog post.

Screenshot Single Window in macOS

It feels like there are an absurd number of options with the built in macOS screenshot tool.

We've seen before you can screenshot the entire screen or select a rectangle and save them as a file or to your clipboard.

Today I Learned there's an option to select an individual window and screenshot the entire window.

You can do this by hitting CMD + shift + 4 (with or without ctrl), then tap space. The cursor will change to a camera, and then you can click on the windows you wish to screenshot!

image

Looks much better than when I try to crop by hand.

Handy Commands for Raspberry Pi Sensors

I recently got an Enviro hat for my Raspberry Pi Zero W. And in tinkering with it, I found there's a ruby driver for interacting with the BME280 sensor.

You can use it like this -

require "i2c/device/bme280"
require "i2c/driver/i2c-dev"

I2CDevice::Bme280.new(driver: I2CDevice::Driver::I2CDev.new("/dev/i2c-1")).read_raw

In debugging, I learned a few useful commands to check the i2c port. Basically, I just needed to figure out what i2c port to use for the ruby driver.

Check if the i2c port is enabled (raspberry pi os)

sudo raspi-config nonint get_i2c # 0 if enabled, 1 if disabled

List all the i2c interfaces by lsing the dev node.

ls /dev/i2c*

Check if the i2c board interface is in the list of kernel modules

lsmod | grep i2c

3 ways to clear the terminal

Option 1:
Simply typing the terminal command clear will clear your terminal. This method preserves your current terminal session, meaning you can scroll up to see what was cleared away.

Option 2:
Pressing CTRL+L to clear away the terminal. This behaves essentially the same as the clear command, preserving your terminal session.

Option 3:
Pressing CMD+K to clear away the terminal. This behaves similarly to the other 2 options, except that it does not preserve your session.


h/t Vinicius Negrisolo

Run a rake task from within your Rails application

I was curious if I could trigger a rake task from within my Rails application.
After some digging, I found Rails::Command.invoke() which lets you run rake tasks from inside your Rails app.

Here's an example using db:migrate:

Rails::Command.invoke('db:migrate')

Just pass it the name of your task (db:migrate, your_custom_task, etc) and it will begin processing it.

This is great for automating or triggering rake tasks during bootstrapping or background operations without needing to leave the Rails environment.

Deleting Words in the Terminal

I stumbled upon this earlier today, if you're in a terminal and have typed some things in, pressing Esc, then Backspace will delete the word on or before your cursor. Ctrl + W will do the same thing.

So if I start typing a word and want to

$ one two three four 
                    ^ cursor here

Then Esc, Backspace will produce

$ one two three
               ^ cursor here

It deletes through the beginning of the word, but starts where the cursor is, so you can delete partial if your cursor is in the middle of a word

$ longer command here
            ^ cursor here

Then Esc, Backspace will produce

$ longer and here
         ^ cursor here

Confirmed both work in recent versions of bash and zsh.

Tables in Markdown

To create tables in a markdown file, you can use this syntax:

| Header 1 | Header 2 | Header 3|
| --- | --- | --- |
| Content 1 | Content 2 | Content 3 |
| Content 4 | Content 5 | Content 6 |

And this will output something like this, depending on what you are using to interpret the markdown:

image


I find this to be useful when displaying side-by-side images in a Github PR

Here is a silly example:

Post Office (B&W) Post Office (Colorized)
image image

h/t Vinicius Negrisolo

Run Script on Login/Start in Windows

You can automatically run a script on Windows during login by placing files in your users "Programs\Start Up" folder. It's useful for running *.bat scripts.

You can easily access this location by opening the "Run" dialog - Windows + r and typing shell:startup.

If you're an admin, you can also set this for all users by typing shell:common startup, which will open the shared Start Up folder.

If this is too much, you can also accomplish a similar thing by creating a "Scheduled Task". In the Windows search bar, type "Task Scheduler". Open this then click "Actions" -> "Create Task"

Count Commits within Time Range in Git

git rev-list lists commit objects in reverse chronological order, and as such is an incredibly powerful tool for inspecting your repository.

For example, if you wanted to count all your commits, you can with git rev-list --count HEAD.

You can also drill down to a specific time range with the --before and --after options. To get the count of all commits during 2024, you can run:

git rev-list --count --after="2024-01-01" --before="2025-01-01" HEAD

There are tons of other options you can use to filter this down too, by author, committer, etc.

Omitting specific fields in TypeScript types

TypeScript has an Omit utility type that lets you create a new type by excluding specific keys from an existing type. It's perfect for cases where you need a type that's almost the same but without certain properties.

Here's an example:

type User = {
  id: number;
  name: string;
  password: string;
};

type PublicUser = Omit<User, 'password'>;

Now we have a PublicUser type that includes id and name, but the password field is excluded.

h/t Vinicius Negrisolo

Change font size in iPhone simulator from keyboard

We found it very annoying to navigate to settings>accessibility>display>larger text just to verify that things still look good on phones with larger font sizes.

Turns out you can adjust the font size of the simulator much quicker using the following keyboard shortcuts:
CMD+Option++ to increase the font size of the device
CMD+Option+- to decrease the font size of the device

NOTE!
Just keep in mind, even if you have Larger Accessibility Sizes toggled off, you can still get to the larger font sizes this way.

Get React Native Component's Dimensions

In React Native you can use the onLayout prop along with the nativeEvent's layout to get the dimensions (in pixels) of a rendered component.

const [viewDimensions, setViewDimensions] = useState({height: 0, width: 0})
return (
  <View
     onLayout={(e) => setViewDimensions(e.nativeEvent.layout)}
  />
)

Now we have an object containging our view's height and width saved as viewDimensions

ActiveRecord.invert_where

Ever been debugging in ActiveRecord and wanted to inverse a query? You can do that without changing the whole query!

User.where(active: true)
# => "select * from users where active = 'true'"

User.where(active: true).invert_where
# => "select * from users where active != 'true'"

It also works with multiple conditions:

User.where(active: true, subscribed: false)
# => "select * from users where active = 'true' and subscribed = 'false'"

User.where(active: true, subscribed: false).invert_where
# => "select * from users where active != 'true' and subscribed != 'false'"

It works on scopes, too:

class User < ActiveRecord::Base
  scope :active, -> { where(active: true) }
end

User.active.invert_where
# => "select * from users where active != 'true'"

Managing Return Value of ActiveRecord Transactions

I recently ran into a situation where I wanted to know the return state of a transaction in ActiveRecord. Basically, I wanted to know if it succeeded or failed, then return true/false.

There's 3 scenarios to keep in mind to when trying to use this approach:

  1. Transaction succeeds - the last line of the transaction is the return
    class TestModel
     def save
       result =
         ActiveRecord::Base.transaction do
           SomeModel.last.touch
         end
    
       result
     end
    end
    
    > TestModel.new.save
    => true
  2. Transaction rollbacks - transactions handle ActiveRecord::Rollback by default, so the transaction block returns nil
    class TestModel
     def save
       result =
         ActiveRecord::Base.transaction do
           raise ActiveRecord::Rollback
         end
    
       result
     end
    end
    
    > TestModel.new.save
    => nil
  3. Other Errors - any errors raised inside the transaction will be raised by the block
    class TestModel
     def save
       result =
         ActiveRecord::Base.transaction do
           raise ActiveRecord::Rollback
         end
    
       result
     end
    end
    
    > TestModel.new.save
    StandardError: StandardError from .....

Putting it all together now, we can return a true/false for all scenarios -

class TestModel
    def save
      result =
        ActiveRecord::Base.transaction do
          SomeModel.new(attrs).save!
          OtherModel.new(other_attrs).save!
        end

      result

      did_save = !!result
      did_save
    rescue => e
       false
    end
end

Rails Generate Tasks

Andrew's TIL from the other day got me thinking about rails generators, and I never knew there was a generator for rake tasks!

There's not a ton of boilerplate in rake tasks, but that's never stopped me from making a typo.

bin/rails g task hello will generate just the namespace hello

# lib/tasks/hello.rake
namespace :hello do
end

If you have one or more tasks within the namespace, you can include them and the generator will add a task for each one:

bin/rails g task say hi bye good_day will generate

# lib/tasks/say.rake
namespace :say do
  desc "TODO"
  task hi: :environment do
  end

  desc "TODO"
  task bye: :environment do
  end

  desc "TODO"
  task good_day: :environment do
  end
end

Rails 8 Generate One Off Scripts

Rails 8 added a new generator command for creating 1 script files. The docs encourage the use of this folder for one off tasks, data migrations, etc. Previously, you could have used rake tasks to accomplish a similar thing.

Running this generator command will create the script dir if one does not already exist.

❯ rails g script migration_script
      create  script/migration_script.rb

Ruby 3.4 Warn on Method Unused Blocks

A change to Ruby 3.4 will print a warning while using the verbose flag -w when a method is passed a block and the block is not used by that method.

Here's our example file scratch.rb -

def print_message(message) = puts message

print_message("hello_world") { puts "hello anonymous" }
> ruby scratch.rb
hello world

> ruby -w scratch.rb
scratch.rb:3: warning: the block passed to 'Object#print_message' defined at scratch.rb:1 may be ignored
hello world

https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/

Start a Rails Console in the Browser

When you generate a new Rails app, it comes with the web-console gem, a handy helper for embedding a rails console in the browser.

This can be useful in development for debugging things in the same process as your webserver. In my case, I used it to test out some action cable related things.

In your controller, just add console to the action -

class AppointmentsController < ApplicationController
  def index
    console
  end
end

image

https://github.com/rails/web-console

Ensure you're always using the same connection

In Rails, if you want to ensure that you are using the same database connection from the connection pool to execute a number of commands, you can use the ActiveRecord::Base.with_connection method.

This method yields a single connection from the pool for you to execute your commands against:

ActiveRecord::Base.with_connection do |conn|
  conn.execute("select * from sessions")
end

It is important to note that the connection yielded is taken out of the pool until the processing in the block is complete.

Convert a Keyword List to a Struct

You can use Kernel.struct/2 to convert a keyword list (or Enumerable) into a struct.

I found this useful when working with a new social media integration. I read some key-values out of the config environment and needed to convert these into a named struct.


> creds = Application.get_env(:tilex, __MODULE__)
[username: "foo", password: "bar"]

> struct(BlueskyEx.Client.Credentials, creds)
%BlueskyEx.Client.Credentials{
  username: "foo",
  password: "bar"
 }

When you pass keys that are not part of the struct, they are discarded for you. If you want to raise an error, you can use the related struct!/2 function

https://devdocs.io/elixir~1.17/kernel#struct/2

Vim Stores the Current Buffer's Path in a Register

Registers in (n)vim contain a lot of super useful data. To wit, the % register stores the path of the current buffer. Some useful things we can do with this register:

  • Write the path of the current buffer into the file in Insert Mode: Ctrl + R, %
  • If in Normal Mode: "%p
  • Copy the path to the clipboard: run the command "let @+=@% (copy the % buffer into the + buffer, which is usually used by the system clipboard), or echo it

ANSI Escape Codes

Today I learned that ANSI Escape Codes are a thing.

I was writing some Rails migrations and wanted nice readable output and a quick way to tell how things were going. So, I wanted some colored text to be displayed as output. This was how I learned about ANSI Escape Codes.

In short, you want to escape your string using \e then give it the prefix [ and then give it a series of codes to format your text.

For example, you can color all further text green using the code 32m or red using 31m.
If you want text to go back to normal give it the code 0m.

Here is an example: image

You can do other formatting than just color, but I found these to be helpful for what I needed and I didn't realize how simple it was.

FactoryBot Traits inside Traits

Today I learned you can nest FactoryBot traits within other traits. Traitception?!

Say you have a blog post model with a deleted_at attribute, and an optional deleted_by attribute. You could have:

FactoryBot.define do
  factory :post do
    trait :deleted do
      deleted_at { Time.current }
    end

    trait :deleted_by_admin do
      deleted
      deleted_by { :admin }
    end
  end
end

There the deleted in deleted_by_admin references the deleted trait above it.

You could alternatively define a new factory that composes the two traits, but it's always nice to have options.

factory :admin_deleted_post, traits: [:deleted, :deleted_by_admin]

Rails Environment Task

In a Rails task if you need to load in your rails application (to get access to your models, etc.), you have to call :environment in the task:

task task_name: :environment do 
  ...
end

I kind of always took this for granted, and never thought much about it. Then, in Xavier Noria's 2024 Rails World talk he mentioned that :environment itself is a task - and that syntax is actually saying your task depends upon :environment and run that task before your task runs. 🤯

So I decided to look up what the environment task actually does:

task :environment do
  ActiveSupport.on_load(:before_initialize) { config.eager_load = config.rake_eager_load }

  require_environment!
end

require_environment! ... requires your environment, specifically it requires your config/environment.rb which runs Rails.application.initialize! - which is what actually starts your rails app.

Cool!

RSpec expect not_to change from

I use the rspec change matcher a lot to check the before and after values of something while the subject under test executes. A convoluted example:

RSpec.describe "expect change from to" do
  it do
    x = 1
    expect{ x = x + 1 }.to change { x }.from(1).to(2)
  end
end

And sometimes I use to it to verify something doesn't change.

RSpec.describe "expect not to change" do
  it do
    x = 1
    expect{ nil }.not_to change { x }
  end
end

Which is great - but sometimes I want to make sure my understanding of the initial state is correct and want to verify it didn't change from its initial value - in this case 1.

RSpec.describe "expect not to change from" do
  it do
    x = 1
    expect{ nil }.not_to change { x }.from(1)
  end
end

And if that from value is wrong, I'll get a nice message explaining what's wrong.

RSpec.describe "expect not to change from" do
  it do
    x = 1
    expect{ nil }.not_to change { x }.from(0)
  end
end
# => expected `x` to have initially been 0, but was 1

Case Regex Matching with Capture Groups

I ran into a problem where I wanted to use a case statement to match against a regex, but also capture some values from the matched data.

Say I want to match on a pair of numbers wrapped in parens like (2,4) and add them together. If I match without capture groups I have to grab the numbers again in the when block:

x = "(2,4)"
case x
when /\(\d+,\d+\)/
  a,b = x.scan(/\d+/)
  a.to_i + b.to_i
end
# => 6

Seems a shame to have perform another op to get a and b. What if we used capturing groups like /\((\d+),\(d+)\)/? How do we refer to those captured values inside the when? We can use some of ruby's special variables, namely $1 and $2 to refer to the captured groups of the last regexp.

x = "(2,4)"
case x
when /\((\d+),(\d+)\)/
  $1.to_i + $2.to_i
end
# => 6

$1 and $2 feel a little magic, so we can name our capture groups group1 and group2, and access those from $~ (the MatchData of the last regexp).

x = "(2,4)"
case x
when /\((?<group1>\d+),(?<group2>\d+)\)/
  $~[:group1].to_i + $~[:group2].to_i
end
# => 6

Count Occurrences of Elements in List

The Enum Module in Elixir has the frequencies/1 function, which is useful for counting the occurrences of each element in a list.

> Enum.frequencies(["dog", "cat", "dog", "bird", "dog", "cat"])
%{"bird" => 1, "cat" => 2, "dog" => 3}

There's also frequencies_by/2, where the 2nd argument is a function that normalizes the element/key before counting.

> Enum.frequencies_by(["a", "b", "a", "C", "A"], fn key ->
  String.upcase(key)
end)
%{"A" => 3, "B" => 1, "C" => 1}

https://devdocs.io/elixir~1.17/enum#frequencies/1