Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

Easily Format Phone Numbers in Rails

Included as part of ActiveSupport, Rails has a handy helper for formatting phone numbers. Passing a number/string to the helper and some optional args and it will take care of the rest.

Here’s a few examples of how it works:

number_to_phone(8858846) # 885-8846
number_to_phone(8778858846) # 877-885-8846
number_to_phone(8778858846, area_code: true) # (877) 885-8846

To see all the options for the method, check out the Rails docs https://api.rubyonrails.org/classes/ActiveSupport/NumberHelper.html#method-i-number_to_phone

🚯 Prevent development logs from bloating on macOS

I rarely need to refer to development.log or test.log when working on rails applications, but yet I end up keeping weeks or even years of records [gigabytes]. I’m used to working with logrotate, and I wanted to find a similar solution that was preinstalled with macOS. macOS comes preinstalled with a program called newsyslog that can keep file sizes in check. I just created a new file at /etc/newsyslog.d/rails.conf which limits all rail’s log files to just 10MB

# /etc/newsyslog.d/rails.conf
# logfilename                                                   [owner:group]      mode count size(KB)     when  flags [/pid_file] [sig_num]
/Users/<username>/dev/<rails projects>/*/log/*.log              <username>:staff   644  0     10000         *     G
# Mono repos
/Users/<username>/dev/<rails projects>/*/*/log/*.log            <username>:staff   644  0     10000         *     G
# Deeper mono repos
/Users/<username>/dev/<rails projects>/*/*/*/log/*.log          <username>:staff   644  0     10000         *     G

You can also perform a dryrun of the config for testing:

sudo newsyslog -v -n -f /etc/newsyslog.d/rails.conf

Clean Paste 🧼

When you copy text from the internet, a bunch of formatting and HTML gets copied, too. Copy a link’s text and paste it into Mac Notes, and there’s some of the text formatting of the website, plus the link.

I almost never want this extra stuff, so I use an alternate paste dubbed the ‘Clean Paste.’ On MacOS, instead of CMD + V, try CMD + OPTION + SHIFT + V. You’ll get just the text, minus all the extras.

Note: I learned this via the newsletter Recomendo, which I (pause for effect) recommend. It’s so helpful me and I’ve looked it up so many times, that I’m re-sharing it here.

TypeScript Union Types

Today I got a chance to try out the TypeScript union type. It looks like this.

interface FlashMessageWithSuccess {
  state: 'success';
  message: string;
}

interface FlashMessageWithFailure {
  state: 'failure';
  message: string;
}

export type FlashMessageInterface =
  | FlashMessageWithSuccess
  | FlashMessageWithFailure;

This lets me tell consumers of FlashMessageInterface that it is allowed to have two shapes: one with a state key of success and one with a state key of failure. I can use this to change how I present the flash message.

Yarn Upgrade to Latest ⬆️

Sometimes I just want to blow away a JavaScript library’s versioning in the lockfile and go to the latest. This happens with projects still in development or requiring very stable libraries– I want to be on latest now, rather than creeping up the semantic versioning ladder. Here’s how this is done with Yarn.

$ yarn upgrade --latest react

Enjoy the latest features.

Ruby Itself

Today I stumbled across a neat Ruby object, itself. itself returns the receiver, itself.

string = "my string"
string.itself.object_id == string.object_id #=> true

What’s a use case for this object? I used it to divide an array into arrays of matching integers.

 > [1,99,99,1,2,3,2,5].group_by(&:itself)
 => {1=>[1, 1], 99=>[99, 99], 2=>[2, 2], 3=>[3], 5=>[5]}

Ruby Delete Prefix

Today I was working with a Ruby method that deletes the prefix from a string, turning --code into code. While there are several ways to get this done, a handy Ruby feature I discovered today is delete_prefix:

> "--code".delete_prefix("--")
=> "code"

Intention-revealing and very Ruby.

Automate Applescript via Accessibility Description

While automating a few tasks I do frequently it became apparent that many apps do not title their UI elements. This makes it more of a trial and error process to figure out which button is where.

This leads to script that is not very descriptive:

tell application "System Events"
    tell process "zoom.us"
        click button 3 of window 0
    end tell
end tell

I’ve always wanted to reference elements by their label or utilize accessibility attributes if they are available but could never figure out how.

I finally have!

tell application "System Events"
    tell process "zoom.us"
        click (first button where its accessibility description = "Copy Invite Link") of window 0
    end tell
end tell

This is obviously a bit more verbose but infinitly more descriptive when looking back at what a script is doing.

My future self is already thanking me.

Group Related ActiveRecord Validations

Today I learned about the with_options feature of ActiveRecord. It’s used to group validations together in a block. You could use it to transform code like this:

class User < ApplicationRecord
  validates :password, length: { minimum: 10 }, if: :is_admin?
  validates :email, presence: true, if: :is_admin?
end

Into this:

class User < ApplicationRecord
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end

A use case for this might be a collection of validations that should only be checked when a boolean feature flag like enabled? is true.

What directory is the parent of root 👨‍👦📁

I learned that in unix, root (e.g. /) is the only directory that is the parent directory of itself.

$ ls -lai / | grep '\./'
                  2 drwxr-xr-x   20 root  wheel   640 Jan  1  2020 ./
                  2 drwxr-xr-x   20 root  wheel   640 Jan  1  2020 ../

In the above example, the files . and .. both have the same i-node: 2

Source: Brian W. Kernighan, & Rob Pike (1984) The UNIX Programming Environment. Prentice-Hall, Inc

Reversed git log

I use git log -p a lot.

git log shows all the commits and messages in descending order and the -p flag includes the code that changed.

It helps me understanding why some changes happened in the codebase, to search when things got included and more.

Recently I wanted to actually see the history from the beginning and guess what, there’s a flag for that:

git log -p --reverse

What Is the Rails Notes Command?

While reading through the Rails 6 changelog, I noticed an entry for a rails command called notes. Having never seen this before, I took a quick look at the Rails Guides.

The command rails notes will return a list of all instances of the following annotations in your codebase - FIXME, OPTIMIZE, and TODO.

You can optionally search for your own custom annotations with the --annotations (-a) flag:

rails notes -a NOTE
app/controllers/admin/blog_posts_controller.rb:
  * [10] [NOTE] Only return the last 10 blog posts

README.md:
  * [ 1] [NOTE] Set the following env variables

There’s also a way to register your own custom annotations for use with the default runner

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

Rails Guides - Rails Notes

Kill a Program with pkill

I have a cronjob to open macOS’s Photo Booth every weekday so I can take a picture of my work life. Unfortunately, it opens the program every weekday; I’d rather it quickly closes itself if I’m not there or otherwise occupied. Today I used pkill in the cronjob to terminate the program five minutes after opening:

20 9 * * 1-5  pkill "Photo Booth"

pkill kills a process by name. You can figure out how to make pkill effective using pgrep, a companion program that searches for running processes by name. Using it, I learned that the string “Photo Booth” was specific enough to find and kill Photo Booth:

$ pgrep -l "Photo"
292 Photo Booth

“Photo Booth”, PID 292 (today), is the process I programmatically kill every weekday at 9:20 AM.

accepts_nested_attributes_for in Rails 5.2

Getting accepts_nested_attributes_for to work can be a bit tricky, and most of the walkthroughs I found are a bit dated, so here’s a quick update.

Our models will be User and Address:

class User < ApplicationRecord
  has_many :addresses
  accepts_nested_attributes_for :addresses
end

class Address < ApplicationRecord
end

Now, assuming we have a UsersController, with all the standard resource actions omitted, we try to add addresses to our permitted params:

class UsersController < ApplicationController
  def user_params
    params.require(:user).permit(:name, :email, :addresses)
  end
end

Logically this seems like it would work, but if we look in the rails server logs, we will see:

Unpermitted parameter: :addresses_attributes

So we add it to our permitted params:

  def user_params
    params.require(:user).permit(:name, :email, :addresses_attributes)
  end

and refresh. Lo and behold, we get the same error!

Unpermitted parameter: :addresses_attributes

The secret is that address_attributes is a hash, and we need to communicate that to Rails:

  def user_params
    params.require(:user).permit(:name, :email, addresses_attributes: {})
  end

Et voilà!

Classnames Computed Keys

Here’s some fun code:

import React from 'react';
import cn from 'classnames'

const App = ({ appClass }) => <div className={cn({[appClass]: !!appClass})} />

export default App

Notice the object inside className– what’s going on here?

This is a dynamic class name via ES2015+ computed keys. If appClass is provided as a truthy prop, the class is enabled; if it is not provided or provided as a falsy prop, the class is not enabled.

Tmux Clear Server Pane

Here’s a situation: you’re watching a server log in Tmux, about to trigger an action that will produce log data you care about. You hit return a bunch of times to create a visual break in the server log. Then you can scroll up and see the beginning of your revelant history.

What actually happens? Sometimes, the server logs tons and tons of information, and your visual break gets buried way above the fold. It’s hard to find the break, and you’re searching through all that information, plus anything that happened before.

There’s a better way! Tmux’s clear-history command “removes and frees the history of the specified pane.” In the Hashrocket Dotmatrix, we combine that with send keys -R, which “causes the terminal state to be reset.” Here’s the mapping:

 bind-key C-k send-keys -R \; clear-history

Type the Tmux leader, then C-k, and your terminal pane will be visually cleared and cleared of its history, making reading and reverse searching much easier.

h/t Gabe Reis

prevent execution when creating materialized views

When working with foreign data wrappers, one uses a materialized view to store the downloaded foreign table data. The process of downloading could be very expensive and managed by another process or program, but your program needs to define the parameters of materialzied view/foreign table. Maybe the data is loaded out of band by cron:

@daily /usr/bin/psql -d cool_db -c 'refresh materialized view big_view;'

So to create the materialized view without loading the data we use the WITH NO DATA clause:

create foreign table measurement_y2016m07
    partition of measurement for values from ('2016-07-01') to ('2016-08-01')
    server server_07;

create materialized view big_view
  as select *
  from measurement_y2016m07
  with no data;

This way we are able to execute the create foreign table and create materialized view statements in a very short amount of time. A different process can start the download with refresh materialized view

Alias a Git Branch Name 🏷

I’m working on a long-running Git branch with a long name, and I’d like to have an alias for that long branch name. The best solution I’ve found so far is to use a Git symbolic ref:

$ git symbolic-ref refs/heads/epic refs/heads/long-epic-branch-name

Once in place, this alias can be used to reference the longer branch name:

$ git checkout epic
Switched to branch 'epic'
$ git branch --show-current
long-epic-branch-name

Resources:

git-symbolic-refs
Answer: Creating aliases for Git branch names

Tmux not starting after an upgrade

Recently upgraded my version of Tmux and after could no longer start a session. After banging my head on tmux’s config files it turned out that sessions that were currently in play were the problem.

So if you find yourself in this situation… make sure to stop all sessions and most likely you’ll have Tmux working again.

An easy way to kill everything would be tmux kill-server.

There's a "whereami" alias in Pry

Since version 0.10.0, the pry gem has shipped with a built in alias for whereami: the extremely convenient @

Other useful aliases can be found using the help command:

[1] pry(main)> help

Aliases
  !!!                Alias for `exit-program`
  !!@                Alias for `exit-all`
  $                  Alias for `show-source`
  ?                  Alias for `show-doc`
  @                  Alias for `whereami`
  clipit             Alias for `gist --clip`
  file-mode          Alias for `shell-mode`
  history            Alias for `hist`
  quit               Alias for `exit`
  quit-program       Alias for `exit-program`
  reload-method      Alias for `reload-code`
  show-method        Alias for `show-source`

Finding a Data Attribute

Some codebases use data attributes for automated testing, and today I learned how to access and manipulate these attributes from a DevTools console.

document.querySelector(`[data-test-attribute="submit-button"]`).click()

This technique lets me visually verify that the attribute is in the right place and behaves as test software expects it should, without reading the HTML at all.

10x Your Playback Rate

Consuming pre-recorded conference talks, video tutorials, and podcasts at accelerated speeds is possible and addictive. Each week I send a newsletter containing at least one recent conference talk I’ve watched, and I wouldn’t be able to do this without the following hack:

Most videos players (YouTube, Vimeo, QuickTime) let you increase the playback rate, but often these controls are hard to find or have an arbitrary ceiling like 2x. If there’s a video element in the DOM, I like to bump up the rate in the DevTools console like this:

document.querySelector('video').playbackRate = 3

‘3’ equals three-times the normal speed, and that seems to be my limit for the moment.

playbackRate MDN docs

The Zen of Python 🐍

$ python
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>>

Gonna be pondering these for some time. Thanks Phil!

All Jest Describes Run First

When writing a Jest test, the setup code inside any top-level describe function gets run before any scenario. So, if you setup a world in one describe, and a competing world in another, the last one that is run wins.

This even applies when you’ve marked a describe as ‘skip’ via xdescribe. The code inside that setup will still be run.

This is a first pass at a TIL, because I’m not sure the behavior I’m seeing is expected, and if so, why it is expected. It certainly surprised me 😳. I think one solution is to limit your tests to one top-level describe which has the important benefit of being more readable, too.

Disallow Large Jest Snapshots Using ESLint

eslint-plugin-jest has a handy rule to limit the size of snapshots - no-large-snapshots. This is especially useful for maintainability of snapshot tests. Snapshots of a large component are cumbersome to maintain as it requires the dev to have a deep knowledge of this components DOM and contained logic.

Github - eslint-plugin-jest/no-large-snapshots

Bonus Round: React Native has updated their documentation and added some great testing guidelines for mobile apps.

Google Font Popular Pairings

Google fonts are an incredible resource, and I use them a lot because of how easy they are to set up. Today I learned that Google has a ‘popular pairings’ feature at the bottom of each font page. Here’s that page for the Spectral font:

image

The Spectral/Open Sans pairing shown above gives design newbies like myself a nice serif/sans-serif combination to play with. When you click the ‘+’ icon, your paired font is added to the import statement, so you can get both with one link tag.

Design for everyone.

Fix poor type support in immutablejs

Immutablejs doesn’t work very well with typescript. But I can patch types myself for some perceived type safety:


import {Map,fromJS} from 'immutable';

interface TypedMap<T> extends Map<any, any> {
  toJS(): T;
  get<K extends keyof T>(key: K, notSetValue?: T[K]): T[K];
}

interface User {
  name: string;
  points: number;
}

const users = List() as List<TypedMap<User>>;

users.forEach(u => {
  const aString = u.name
  const aNumber = u.points
});

How to convert JSON to CSV with jq

I had this json file that was an array of objects and some of those objects had different keys. I wanted to visualize that data in a spreadsheet (data science, AI, machine learning stuff) so I thought about having a CSV file where each JSON key would become a CSV column.

// file.json
[
  {
    "type": "Event1",
    "time": 20
  },
  {
    "type": "Event2",
    "distance": 100
  }
]

jq to the rescue:


cat file.json | jq -r '(map(keys) | add | unique) as $cols | map(. as $row | $cols | map($row[.])) as $rows | $cols, $rows[] | @csv' > file.csv

Here is the output:

"distance","time","type"
,20,"Event1"
100,,"Event2"
Screen Shot 2020-08-07 at 4 39 16 PM

Typescript Bang

The “non-null assertion operator” or “!” at the end of an operand is one way to tell the compiler you’re sure the thing is not null, nor undefined. In some situations, the compiler can’t determine things humans can.

For example, say we have a treasure map with some gold in it.

type TreasureMap = Map<string, number>

const map: TreasureMap = new Map()
const treasure = 'gold'
map.set(treasure, 10)

And we’re cranking out some code that tells us if

function isItWorthIt(map: TreasureMap) { 
  return map.has(treasure) && map.get(treasure) > 9
}

Obviously we are checking if the map has gold first. If it doesn’t, the execution returns early with false. If it does, then we know we can safely access the value.

But the compiler gets really upset about this:

Object is possibly ‘undefined’. ts(2532)

In this case, the compiler doesn’t keep map.has(treasure) in context when evaluating map.get(treasure). There is more than one way to solve this, but for now we can simply “assert” our superiority over the compiler with a bang.

return map.has(treasure) && map.get(treasure)! > 9

Typscript Docs - non-null-assertion-operator

Count Occurrences in Elixir

Elixir recently introduced an useful couple of functions to count how many times a value appears in an Enumerable. It comes in two formats: frequencies/1 and frequencies_by/2. Here’s an example:

iex> [
...>   %{name: "Falcon", power: "Flight"},
...>   %{name: "Titan Spirit", power: "Flight"},
...>   %{name: "Atom Claw", power: "Strength"},
...>   %{name: "Electro", power: "Electricity Control"},
...>   %{name: "Loki Brain", power: "Telekinesis"},
...> ] |> Enum.frequencies_by(& &1.power)
%{
  "Electricity Control" => 1,
  "Flight" => 2,
  "Strength" => 1,
  "Telekinesis" => 1
}

Generic React Components

When props are generic like this:

inteface SelectProps<T> {
  options: T[];
  onChange: (value: T) => void;
}

function CoolSelect<T> (props: SelectProps<T>) {
    // ...
}

The generic part can be specified in JSX like this:

interface Fruit {
  name: string;
  isFruit: boolean
}

const fruits = [
  { name: 'Pumpkin', isFruit: true },
  { name: 'Avocado', isFruit: true },
  { name: 'Cucumber', isFruit: true },
  { name: 'Bell Pepper', isFruit: true },
]

funciton App() {
  return <CoolSelect<Fruit> options={fruits} />
}

See it? <CoolSelect<Fruit> options={fruits} />

Now when crafting the onChange function in this example, it’s type will be infered as this:

type OnChange = (value: Fruit) => void
funciton App() {
  return (
    <CoolSelect<Fruit>
      options={fruits}
      onChange={value => {
        if (value.isFruit && value.name === 'Bell Pepper') {
          console.log("You're blowing my mind dude!")
        }
      }}
    />
  )
}

*This syntax is available in Typescript v2.9+

Git + NPM: Resolving Lockfile Conflicts 🤝

Here’s a challenging real-world scenario: you’re doing a big merge or rebase on a JavaScript project, and you keep getting conflict after conflict in your package-lock.json. These conflicts are tough to resolve, because your package-lock.json is not easy to read, and is, say, 30,000 lines long. What do you do?

When you hit to conflict, on the the conflicting Git SHA, run the following command. Your lockfile will be regenerated, conflict resolved:

$ npm install --package-lock-only

💥

From the resolving lockfile conflicts docs:

image

Tmux Send Keys to Pane

I wrote a script the other day designed to help me download and edit files faster. In part of the script, I wanted to open Vim in an existing Tmux pane, and in the process I learned about the tmux send-keys command. Here’s how it works:

$ tmux send-keys -t 3 "vim" Enter

send-keys, aliased send, sends your string of commands to your pane (t) of choice. Running the above opens Vim in pane #3.

The -t flag accepts negative numbers, too, like an array index. In my version of the above command, I send the keys to pane -1, the last pane on the screen, which is where I keep Vim in my Tmux session.

Use Enzyme's `wrappingComponent` option in `mount`

Also works with shallow:

const provided = {super: 'cool', object: ['of', 'things']};

// This means that the root of the tree is going to be the provider
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(
      <CoolProvider thing={provided}
        <Component changeMe={false} />
      </CoolProvider>
  })
})

// This means that the root of the tree will be your Component
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(<Component changeMe={false} />, {
      wrappingComponent: CoolProvider,
      wrappingComponentProps: {super: 'cool', object: ['of', 'things']}
  })
})

This pattern is particularly meaningful when you want to call setProps or other methods that are only valid on the root of the component tree, because dive can’t help you there. If you want to change the props on both, you can use target.getWrappingComponent() to get at the wrapping component in the same way!

https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/getWrappingComponent.html

Enzyme debug() 🐞

Debugging React unit tests can be tricky. Today I learned about the Enzyme debug() function. Here’s the signature:

.debug([options]) => String

This function:

Returns an HTML-like string of the wrapper for debugging purposes. Useful to print out to the console when tests are not passing when you expect them to.

Using this in a log statement will dump a ton of valuable data into your test runner’s output:

console.log(component.debug());

debug() docs