Today I Learned

A Hashrocket project

380 posts by jakeworth @jwworth

Override Markdown Listing 📝

Markdown parsers have a neat feature: a list of any numbers tends to be turned into an ordered lists.

I write:

99. First thing (?)
1. Second thing (??)
50. Third thing (??)
1. Fourth thing (????)

And TIL’s markdown parser (Earmark) produces this as HTML:

  1. First thing (?)
  2. Second thing (??)
  3. Third thing (??)
  4. Fourth thing (????)

Pretty cool! Sometimes I want to do my own thing, though. This Stack Overflow question elicits a bunch of techniques; the kind I learned today and used with success is:

99\. First thing (?)
1\. Second thing (??)
50\. Third thing (??)
1\. Fourth thing (????)

Escape the pattern; break the system! This perserved my unordered numbering. Now, I may have other problems, like losing automatic ol and li tags, but the hack works.

Find File Case-Insensitively 🔎

When using the find command, consider the -iname flag. It works like -name:

True if the last component of the pathname being examined matches pattern.

But with case insensitivity:

-iname pattern
  Like -name, but the match is case insensitive.

Here’s a sample command, which will match utils.js and Utils.js.

$ find . -iname Utils.js

See man find for more information. h/t Raelyn.

Reduce Depth of an Existing Git Repo ⚓️

git pull --depth works on an existing repo to set the depth of references retrieved. To quote the --depth docs:

Limit fetching to the specified number of commits from the tip of each remote branch history.

So, what happens when you run this? Here’s me experimenting with the Elixir repo:

$ git clone https://github.com/elixir-lang/elixir.git
$ cd elixir
$ git log --oneline | wc -l
   15670 # 15670 entries!
$ git pull --depth=1
# ...pulling the shallow repo
$ git log --oneline | wc -l
   1 # 15670 to 1... woah!
$ git log
# ...output from just one commit

Is .git/ a lot smaller now? Not yet, because there are many dangling references. This Stack Overflow answer shows you how to cleanup that directory. After setting a depth of one and following its instructions, I reduced the Elixir repo’s .git/ size by 90%.

Check out man git-pull for more information.

Zoom Hotkeys to Toggle Audio and Video 🎛

Zoom users, two new hotkeys for you on Mac: CMD-SHIFT-A to toggle audio and CMD-SHIFT-V to toggle video. The ability to control my A/V presentation in long video calls makes these hotkeys worth the muscle memory investment.

Zoom has another handy audio feature in preferences: temporarily unmute by pressing the spacebar. This is perfect for standups, where I mostly want to be muted, but need to be able to unmute quickly for short periods of time.

Dash Docset Keywords 🗝

Dash has a customizable keyword for each Docset. Here are the defaults for a random Hashrocket collection*:

image

These keywords scope search results to one Docset; use one in the Dash search bar like so:

elixir:String.split

And from Alfred:

dash elixir:String.split

This is useful when you have many Docsets, and are sure where you want to look, which should be true almost all the time. It’s nice when using two complementary technologies like Elixir and Elm that share a function namespace (such as String.split).

* Also note: this list order can be changed, as noted in the table header. Move libraries you use often above those you use rarely.

Dash Find in Page 🔎

To build upon my earlier post, Integrate Alfred and Dash: Dash supports find-in-page search via a whitespace. To search on a docs page, add a space after the function or keyword name, and then enter your search query.

Here’s a command you could run to find JavaScript’s Array.prototype.unshift() and search on that docs page for the ‘specification’ header, where the associated ECMAScript standards for unshift are found.

unshift specification

This command from Alfred could look like:

dash unshift specification

Integrate Alfred and Dash 🤝

Today I’m testing an intergration between Alfred, the powerful alternative to Finder, and Dash, the local-documentation tool for Mac. My goal is a single command, run from anywhere on a Mac, that searches one or many sets of docs for a named function.

This tutorials showed me how to do it. TL;DR, it’s a one-click setup in Dash:

https://github.com/Kapeli/Dash-Alfred-Workflow

Once complete, I can trigger Alfred (with CMD + SPACE, or any key combo that’s not assigned elsewhere), type dash List.reverse, hit enter, and Alfred loads all the List.reverse docs I’ve downloaded in Dash.

Send a Command's Output to Vim (Part II) ↪️

In a Hashrocket blog post, 10 Vim Commands for a Better Workflow, I wrote about :.! <command>, which replaces the output of <command> with your current line.

Today I learned what this technique is called: filtering. From the docs:

filter is a program that accepts text at standard input, changes it in some way, and sends it to standard output. You can use the commands below to send some text through a filter, so that it is replaced by the filter output.

An even shorter version is just !!<command> in normal mode. A use case for this would be writing docs, where command-line output (ls, curl, etc.) can help demonstrate an idea.

Check out :help !! to see all the permutations of this interesting command.

ALL CAPS SQL

A while ago I read The Mac Is Not a Typewriter by Robin Williams. In it, the author claims:

Many studies have shown that all caps are much harder to read. We recognize words not only by their letter groups, but also by their shapes, sometimes called the “coastline.” —pg. 31, The Mac Is Not a Typewriter

I’ve found this to be true. When we teach SQL, students are often surprised that we don’t capitalize PostgreSQL keywords, preferring this:

select * from posts limit 5;

To this, which you might see in an SQL textbook:

SELECT * FROM posts LIMIT 5;

My arguments against the latter syntax: it’s practically redundant in PostgreSQL, it’s harder to type, and it’s unnecessary because any good text editor highlights the keywords. Now I have another: such writing has been, in typesetting, shown to be harder to read. WHERE and LIMIT look similar from a distance in all-caps, but they mean and do different things.

It’s a style opinion each developer gets to refine for themselves. To quote Williams: “Be able to justify the choice.”

Delete a Command from ZSH history 📚

A while ago, I restored this site’s production database to a backup captured 24 hours earlier (the reason isn’t important). Now in my terminal history, heroku pg:backups:restore a719 DATABASE_URL -rproduction is just hanging out, ready for me to accidentally smash, sending our production database hurtling back into the past. How do I remove this destructive command from my history?

In my terminal, ZSH, there’s a file called ~/.zsh_history, and a similar one for Bash. To remove the command, open that file and remove the entry from the list.

cat somefile
heroku pg:backups:restore a719 DATABASE_URL -rproduction # delete me!
ls

Open a new terminal window, and the bad command is gone.

To avoid this situation, consider the following setting (thanks Dillon!):

# ~/.zshrc
setopt HIST_IGNORE_SPACE

With this, any command preceded by a space is excluded from history. One could alias a destructive command to itself, preceded by a space, and save themselves a headache. Note a weird bug here: the last command appears in history, even if it should be excluded, until another command is entered. Banish it to the ether by running just an empty space as a command.

What's the Trailing Underscore in Elm?

Variables such as model_, with a trailing underscore, are allowed and conventional in Elm. If you’re familiar with a language where _model can mean an unused variable, this can cause a double-take. The trailing underscore is a nod to Haskell, telling us the variable is related, or similar, to a prior variable.

Here’s an example: model_ is the argument of the update function in the Elm architecture, and model is the updated model we’ll return:

> model_ = { start = "now" }
{ start = "now" } : { start : String }
> model = { model_ | start = "tomorrow" }
{ start = "tomorrow" } : { start : String }

This Stack Overflow answer summarizes the convention:

https://stackoverflow.com/a/5673954/2112512

A single quote (model') was used similarly in the past; that syntax was deprecated in Elm 0.18. Here’s a GitHub issue describing that decision:

https://github.com/elm-lang/elm-plans/issues/4

PostgreSQL Triggers and Views 🐘

Today I learned a lot about PostgreSQL.

  1. Views can have triggers!
  2. Deleting such a view also deletes the trigger!

To demonstrate, let’s create a table and record, and a view that looks at our table.

create table some_table (name varchar);
insert into some_table values ('some name');
create view some_view as select name from some_table;

Now, we need a test function:

create or replace function always_null() returns trigger as $always_null$
  begin
    return null;
  end;
$always_null$ language plpgsql;

Let create a trigger for our function:

create trigger some_table_trigger
  instead of delete on some_view
  for each row execute procedure always_null();

Here’s our view… with a trigger attached!

backup=# \d some_view
                   View "public.some_view"
 Column |       Type        | Collation | Nullable | Default
--------+-------------------+-----------+----------+---------
 name   | character varying |           |          |
Triggers:
    some_table_trigger INSTEAD OF DELETE ON some_view FOR EACH ROW EXECUTE PROCEDURE always_null()

Our trigger can be inspected (lots of interesting info omitted):

backup=# select * from information_schema.triggers;
-[ RECORD 1 ]--------------+--------------------------------
trigger_name               | some_table_trigger

Drop the view, and lose the trigger:

backup=# drop view some_view;
DROP VIEW
backup=# select * from information_schema.triggers;
(0 rows)

Flunk Your ExUnit Tests ❌

Today I got to use the flunk/1 function in an ExUnit test. I like everything about this function, from its name to its signature.

Here’s how we used it:

case result do
  "bad" ->
    flunk("Your result is bad, and you should feel bad.")

  "good" ->
    assert result_passes_other_tests(result)
end

It fit well into a helper function that should fail if my result ever comes back a certain way.

flunk docs

PostgreSQL: Table or View?

I’m doing some database exploration today and needed to find out if a collection of tables are all tables, or if some of them are views. By querying the information schema via the following: select table_name, table_type from information_schema.tables;, I learned more about each table and its type:

> select table_name, table_type from information_schema.tables;
              table_name               | table_type
---------------------------------------+------------
 cats                                  | BASE TABLE
 dog                                   | BASE TABLE
 dogs_and_cats                         | VIEW

asdf Global Versions 🌏

Spend a bit of time with asdf, and you might see an error like this:

$ npm install
asdf: No version set for command npm
you might want to add one of the following in your .tool-versions file:

nodejs 10.15.3
nodejs 8.9.1

The project I’m trying to set up via npm install doesn’t specify a Node version in .tool-versions, and since I have multiple Nodes on my machine, asdf isn’t sure which to use.

I don’t want to edit .tool-versions in this project; I’d rather asdf had a global default.

Here’s how I made that happen:

$ asdf global node 10.15.3   

Print Calendar With Week Number 📅

I use the week number (1-52) in my notetaking. How do I know what week number it is? ncal.

$ ncal -w
    May 2019
Mo     6 13 20 27
Tu     7 14 21 28
We  1  8 15 22 29
Th  2  9 16 23 30
Fr  3 10 17 24 31
Sa  4 11 18 25
Su  5 12 19 26
   18 19 20 21 22

Though not shown in the output above, today’s date (the 10th) is highlighted in my terminal output. At the bottom of the column containing today’s date is the week number (19).

Friendly Zoom Personal Meeting URL ➡

If you have a Zoom account, you have a Personal Meeting URL— a permanent video conferencing room tied to your account. It looks like this:

https://zoom.us/j/5551112342

When another person with Zoom installed visits this link in a browser, Zoom starts a call in your room.

A pro move is to permenantly redirect a subdomain to this URL, like so:

chat.tomcruise.com

Now you have a memorable link you can share in online and offline conversation.

Hide an Elixir Module from Documentation

Recently I’ve noticed @moduledoc false sprinkled through some Elixir projects. What’s going on? To quote the docs:

Conveniently, Elixir allows developers to hide modules and functions from the documentation, by setting @doc false to hide a particular function, or @moduledoc false to hide the whole module. If a module is hidden, you may even document the functions in the module, but the module itself won’t be listed in the documentation…

Why would you see this in a project that doesn’t autogenerate docs? Possibly to satisfy a linter like Credo. In a codebase with public APIs, I think this kind of explicit statement is a good practice.

Writing Elixir Docs

Improve Your Stack Overflow Feed ⬆

I like answering questions on Stack Overflow. It helps me stay aware of what people are struggling with in a particular language or framework. To do this well, I manage my tags. Here’s a screenshot of some of the tags I’m watching (subjects I care about and can contribute answers to) and ignoring (the rest).

image

With these settings, my question feed is mostly signal rather than noise.

If you visit Stack Overflow often and haven’t signed up for an account, I recommend it.

killall 💀

Today while pairing I learned about the killall command.

This BSD program kills processes by name, unlike kill, which uses PID.

Here’s me killing my Mac calculator:

$ ps aux | grep Calc
grep Calc
/Applications/Calculator.app/Contents/MacOS/Calculator
$ killall Calculator
$ ps aux | grep Calc
grep Calc

Thanks for the tip, Mark!

Alphabetize Keys with jq

In a previous post, I wrote about how jq is great because it doesn’t alphabetize JSON keys by default. I still think that’s great, because sometimes the key order is meaningful, such as might be found in a package.json file.

We can add alphabetization to jq, however, using the -S flag. To format and and sort you current buffer in Vim, run the following:

:%!jq -S '.'

Happy JSON-ing!

Highlight Diffs With Markdown

Today I learned about the ‘diff’ syntax, supported by many syntax highlighting libraries including those used by Github and Today I Learned.

Add diff to your fenced code block, and this:

def example do
-  :ok
+  {:ok, :more_stuff}
end

Becomes this:

def example do
-  :ok
+  {:ok, :more_stuff}
end

A nice use case is a README or pull request description; show instead of tell the changes you made, or someone should make.

Send Tmux Pane to Window

A scenario I find myself in frequently: I’ve started a server in a Tmux pane, and realize I don’t need to see the server logging in my ‘home’ Tmux pane (pane 0 for me).

To send a Tmux pane to its own window, use :break-pane.

A nice addition is the -n flag, which lets you set the new window name while breaking the pane.

:break-pane -n frontend-elm

Python Help in REPL 🐍

Heading to the internet to get help with a Python function? Slow down! There’s help right in your terminal.

With the Python executable installed, it’s as easy as:

$ python
>>> help()
help> string.lower
Help on function lower in string:

string.lower = lower(s)
    lower(s) -> string

    Return a copy of the string s converted to lowercase.

help>

Reset Hub Credentials

On a shared computer, multiple users logged into Hub can lead to confusing Github activity, such as a PR opened from the command line with your name on it that you didn’t open.

These credentials are stored in ~/.config/hub. To reset your Hub credentials, delete this file and run a Hub command, and you will get an opportunity to reautheniticate.

Open Newest Email with Gmail

Part of my Gmail workflow includes hotkeys. I’d recommend them to anyone who wants to use their email more efficiently.

My current favorite command is gio, or gi (show inbox), combined with o (open focused message), which defaults to the newest message. gio lets me open my newest email without a mouse, faster than you can say ‘unsubscribe’.

Combine this with Vim motions (j and k) and you’re off to the races. 🏁

Build a CLI with Elixir

Elixir Mix ships with built-in CLI support, called escript. From the docs:

An escript is an executable that can be invoked from the command line. An escript can run on any machine that has Erlang/OTP installed and by default does not require Elixir to be installed, as Elixir is embedded as part of the escript.

Yesterday I built a CLI that reads an input file, does some calculations, and prints a result. For me, CLI’s are often the mark of a fully-realized project, and I appreciate how the Elixir community treats CLI’s as a first-class idea.

In lieu of a longer explanation, here are the docs:

mix escript.build

Set Vim Filetype or Syntax with Modeline

Here’s a problem I faced today: I’m writing a Thor CLI. The convention for that project is a file called cli, written in Ruby but with no .rb extension. Vim highlights it and otherwise treats it like a conf file, but it’s Ruby. I want my editor to progamatically recognize that and act accordingly.

One solution is Vim’s modeline. With modeline enabled, either of these settings will enable syntax highlighting on buffer read. They can both be used at the same time as shown.

# vi: syntax=ruby
# vi: filetype=ruby

def ruby_here
end

Gmail 'o'

In my quest to learn all the Gmail hotkeys, today I learned ‘o’.

‘o’ opens the focused email in your inbox. Combine this with ‘gi’ (go to inbox) and Vim-style JK (down/up throught the inbox list), and you’re cruising. 🏁

Hotkeys must be enabled in your settings.

List ImageMagick Fonts

ImageMagick drawing and annotating features rely on fonts on your computer to work. But what fonts are on your computer?

On newer versions of ImageMagick, here’s how you’d find out that information:

% identify -list font

Path: /usr/local/Cellar/imagemagick/7.0.8-24/etc/ImageMagick-7/type-apple.xml
  Font: AndaleMono
    family: Andale Mono
    style: Undefined
    stretch: Undefined
    weight: 0
    glyphs: /Library/Fonts//Andale Mono.ttf
...

AndaleMondo and ~60 other fonts are installed on my machine and accessible by ImageMagick.

Bootstrap CircleCI Setup with Docker 🐳

CircleCI maintains a collection of Docker containers, pre-installed with different languages and tools.

These include language image variants that can eliminate a lot of CI setup churn. I recently used the -browsers variant, which ships with Chrome, Firefox, Java 8, and Geckodriver.

Here’s how you could use such a container.

# .config/circle.yml

version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.5.1-browsers
    steps:
      - run: rake

Add an Empty Directory to Git

Have you ever seen a directory containing a single .gitkeep? Today I learned the history of that file.

Git won’t let us add an empty directory, but sometimes there’s a good reason to want to do that. For instance, I’m building a single-page app that requireds a src/data/ directory, even when there’s no data. Instead of each developer on the project making this directory by hand, I’d like to check it into version control.

There are two competing strategies to achieve this: adding a .gitkeep to the directory, or adding a .gitignore. I prefer .gitkeep because the name tells you what it does and it’s not conventionally used for another purpose.

$ touch src/data/.gitkeep

Stack Overflow

Gatsby 404 Static Sitemap

Iterating through a process of building many static Gatsby.js pages at once, I discovered a hacky way to see what pages have been built. Visit a page that doesn’t exist, and Gatsy’s development 404 page will provide a sitemap with links to all the static pages.

demo

It’s a little nicer than digging through the filesystem.

Prevent a User From Selecting Text

Today I learned about a CSS property called user-select. You might use it like this, to prevent a user from selecting text:

p {
  user-select: none;
}

I discovered this on an event booking website: the event description links were not anchor tags, and the content on the page was not selectable.

If you’re going to disable default behavior on a website, you need to have a good reason. My assumption (which could be wrong) is that the site creators don’t want me to select text on the page and start searching around for a better deal. I don’t think that’s a good enough reason, because there are plenty of cases where a user might copy text on a page that don’t hurt (or actually support) the event organizer’s business.

docs

Separated Table Borders

Today I learned how to make separated table borders, like this:

border-collapse

Each td has a top and bottom border, and these settings make the separation possible:

table {
  border-collapse: separate;
  border-spacing: 3rem 0rem;
}

The border-collapse property sets whether cells inside a table have shared or separate borders. The border-spacing property sets the distance between the borders of adjacent table cells, when border-collapse is set to separate.

More info: table properties

Open Bitbucket PR's from the Command Line

I’m a fan of Git command line tools. The more familiar I become with Git hosting platform’s website, the more I want to control it with a CLI.

When you push a branch to Bitbucket, part of the response is a pull request URL. That URL is a create page for a pull request against the default branch.

I’ve taken this idea a step further and created a script for my project that visits this page:

#!/bin/sh

BRANCH=`git rev-parse --abbrev-ref HEAD`
open "http://bitbucket.org/<organization>/<project>/pull-requests/new?source=${BRANCH}&t=1#diff"

This script reads my current branch name and visits the Bitbucket ‘create PR’ page in a diff view.

Work with a Gist locally

I like Gists, those fun-size Git repositories I fill with coding demos, scripts, and WIP markdown files. Today I learned you can edit these files locally and push them to a remote, just like any Git repo.

Grab your Gist URL:

https://gist.github.com/yourhandle/5bac61ee3fe0083

Alter it slightly, and clone:

$ git clone git@gist.github.com:5c61ee3fe0083.git
$ cd 5c61ee3fe0083/

Make changes, commit, and push away. Your commits will show up under the /revisions tab of your Gist.

Pass a Block on Console Load

Have some Ruby code you want to run as the Rails console loads? Here’s a technique.

In this example, I’m going to use a function from the hirb gem. Add the gem to your Gemfile and bundle.

Next, pass a block to console in your application configuration.

# config/application.rb

module MyApplication
  class Application < Rails::Application
    console do
      # Action(s) I want to run on console load...
      Hirb.enable
    end
  end
end

The next time you start the Rails console, Hirb.enable will run after load.

code / docs

The Outline Property 🖼

Today I learned about the CSS outline property.

Outlines are like borders, except they never take up space, because they’re drawn outside of an element’s content.

Some browsers have a default outline style for interactive elements (like buttons) on focus. This was how I came to learn about the property, because that style clashed visually with an element I was building.

Remove such an outline with either of these settings:

button:focus {
  outline: 0;
  outline: none;
}

Note that if an element can be interacted with, a11y best practices require a visible focus indicator. Provide obvious focus styling if the default focus style is removed.

Start Zoom on Mute 📣

We’ve been using Zoom for a while now; it’s an excellent video conferencing tool.

One setting I recommend enabling is ‘Always mute microphone when joining meeting’, under the ‘Audio’ settings. I’ve been using it for a week and love it. Pausing work in an open office to join a conference call can be a messy affair, and this setting helps me control how I enter the conversation (silently).

Resize Tmux Pane 🖥

Sometimes, after a long day of coding, I resize a Tmux pane using my mouse instead of my keyboard. It’s a habit from my GUI-informed past.

Here’s how to accomplish this without a mouse.

To resize the focused pane left one cell (from the Tmux prompt):

:resize-pane -L

Resize pane number 3 right 10 cells:

:resize-pane -t 3 -R 10

Etc.

Jump Back 🐇

A Vim motion I’ve really been loving lately is '' or ``, which returns you to the previous jump position. It references the previous jump, so using it twice in a row will toggle you back and forth between two locations.

I use this a lot after jumping to the next occurance of a word with *, and then changing that word in some way so I can’t return with *. If you’re using normal mode most of the time, this command can help you walk back through your thought process.

For more info:

:help ''

ES6 Nested Destructuring

I picked this up a while ago on Twitter, use it all the time in my React code, and wanted to document it here. If you’re destructuring a JS object like this:

const { username, zip } = props.user;

A valid (and slightly better, in my opinion) refactor is this:

const { user : { username, zip } } = props;

As you add more destructured variables pulled from props, the object you’re referencing stays the same.

Note that this doesn’t define user— if that’s something you want, you have to destructure it on its own:

const { user, user : { username, zip } } = props;

While a little more verbose, I still think this makes for more maintainable destructuring long-term.