Today I Learned

Always something.

Using tee To Split CLI Output

Store the output of some CLI operation and see the results in real-time

tail -f /var/log/postgresql/postgresql-13-main.log | grep deadlock | tee ~/postgres-deadlocks.log

This command will stream the logs written to /var/log/postgresql/postgresql-13-main.log, filter for lines containing deadlock, writes those lines to ~/postgres-deadlocks.log, and finally display that output to the terminal.

Use tee --append to append to the specified file instead of the default of overwriting it.

Grouping by Partial Date in PostgreSQL

To be able to count by some subset of a timestamp, date_trunc(field, source [, time_zone ]) can be used to modify the date.

With a table users that only has columns id, created_at, the number of users created per month can be queried with

SELECT date_trunc('month', created_at), COUNT(*) as user_count
  FROM users
 GROUP BY date_trunc('month', created_at)

Which produces

     date_trunc      | count
---------------------+-------
 2021-02-01 00:00:00 |    96
 2021-07-01 00:00:00 |    49
 2021-04-01 00:00:00 |    76
 2021-08-01 00:00:00 |    49
 2021-09-01 00:00:00 |    83
 2021-01-01 00:00:00 |    89
 2021-05-01 00:00:00 |    59
 2021-03-01 00:00:00 |    62
 2021-10-01 00:00:00 |    47
 2021-06-01 00:00:00 |    40

The various available time intervals that can be truncated to are

  • microseconds
  • milliseconds
  • second
  • minute
  • hour
  • day
  • week
  • month
  • quarter
  • year
  • decade
  • century
  • millennium

Idiomatic Ruby Type Conversion

Built-in Ruby types include a method with the same name as the type to idempotently convert some value into that type.

irb(main):001:0> Array('some value')
=> ["some value"]
irb(main):002:0> Array(['some value'])
=> ["some value"]

This can be done with custom types/objects, but there are a couple of gotchas.

  1. The converter method is named after a class, it’s not a class method, so it must be defined outside the class. Unless something like Email.Email(object) is acceptable 😅.
  2. Since the converter method is not directly linked to the class it belongs to, there can be autoloading issues in Rails applications.

These two gotchas allow for a few different ways to manage these converter methods.

One strategy is avoiding copying the built-in converter methods.

Although defining uppercased methods for your custom classes to create instances of it looks like an interesting idea at first glance, it is rather confusing.
Consider defining class.[] instead, which enables a very similar syntax, but uses the real constant it belongs to. An example of such usage is Set

class Email
  def self.[](obj)
    return obj if obj.is_a?(self)

    new(obj)
  end
end

In a situation where constant-based autoloading is not a concern, the converter method could be defined in the same file as the class.

# lib/email.rb

class Email
  def initialize(email)
    @email = email
  end
end

def Email(obj)
  return obj if obj.is_a?(Email)

  Email.new(obj)
end

In a Rails application, another option to allow for Email() that might be controversial but still works: define the converters in an initializer.

# config/initializers/vale_objects.rb
def Email(obj)
  return obj if obj.is_a?(Email)

  Email.new(obj)
end
# app/value_objects/email.rb
class Email
  def initialize(email)
    @email = email
  end
end

Tmux Basics Cheatsheet

These are the most common commands I use in Tmux, all with the default configuration.

List existing tmux sessions

tmux ls

Create a new tmux session names statamic

tmux new -s statamic

The default command prefix is C-b (Ctrl and ‘b’ simultaneously).

Attach to the most recent session

tmux attach

Attach to the session named statamic

tmux attach -t statamic

Enable mouse control by entering the tmux command line with C-b : and enter

set -g mouse on

Mouse control allows selecting windows with the mouse and scrolling to work as expected.

Important key commands

  • C-b c - create a new window
  • C-b , - rename the current window
  • C-b $ - rename the current session
  • C-b d - detach from tmux session
  • C-b 1 through C-b 9 - switch to the corresponding window
  • C-b ' - will prompt for the window number to switch to
  • C-b p and C-b n - switch to the previous and next windows, respectively

Fix Debian Emacs Ispell

After using the testing repo for Debian Buster to install Emacs 27 I ran into an issue where spellcheck through ispell would not work

debian-ispell-set-startup-menu: Symbol's value as variable is void: ispell-menu-map-needed

Turns out the issue was in dictionaries-common, and fixed ages ago. Debian doesn’t like to keep with the times though, so I had to also install dictionaries-common from the testing repo to finally fix the issue!

sudo apt --target-release=testing install dictionaries-common

😎

Starting new Ispell process ispell with default dictionary...done
Checking spelling of THIS...
THIS is correct

Using Ruby Proc Shorthand With Arguments

When following the Ruby Style Guide on single operation blocks, there are times where that operation is a method that needs to take an argument. To still use the Proc shorthand in these cases, the Object#method method allows passing each element of the enumerable to the method.

LETTERS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r']

def number_to_letter(n)
  LETTERS[n]
end

# Lame
[17, 0, 12, 8].map { |s| number_to_letter(s) }

# Cool
[17, 0, 12, 8].map(&method(:number_to_letter))

When To Use Single Vs. Double Splats In Ruby

Use a single splat to pass an array of arguments to a method.

args = ['Rami', 'J', 'Massoud']

def say_name(given_name, middle_initial, family_name)
  puts "#{family_name}, #{given_name}, #{middle_initial}"
end

say_name(*args)

Use a double splat to pass a hash of arguments to a method with keyword arguments

args = [middle_initial: 'J', family_name: 'Massoud', given_name: 'Rami']

def say_name(given_name:, middle_initial:, family_name:)
  puts "#{family_name}, #{given_name}, #{middle_initial}"
end

say_name(**args)