Today I Learned

A Hashrocket project

420 posts by jakeworth @jwworth

Tmux Clear History

I’ve been doing an experiment lately that requires reading and searching lots of logs in Tmux. Something that makes this harder is that we collect a deep history there. Sometimes I’ll ‘find’ a query I’ve been looking for 😀, only to realize it is hours or days old ️😐.

Tmux clear-history has been our solution. Running this command on an already cleared Tmux pane clears your history for that pane; you can’t scroll up or search the previous output.

We’re doing this enough that I put this alias in our terminal dotfiles.

alias tc='clear; tmux clear-history; clear'

tmux clear-history is the CLI version of the Tmux meta command :tmux clear-history.

Vim Mark That Test! 🔖

This week Chris Erin taught me a technique that has changed my testing game: Vim mark your current Ruby test with mA.

When writing TDD-style code in a test-crazy language like Ruby, I tend to follow a predictable workflow: create a test, watch it fail, bounce around the code making changes, and run/edit the test again. This loop can repeat for hours on a complex feature.

There are many ways you could navigate back to your test from elsewhere; a very efficient method is to mark it in Vim with mA. Like any mark, you can return to it with `A. Why ‘A’? Capitalized marks are file marks and are valid between files. With this command, you can mindlessly jump back to your test from anywhere in the jumplist stack.

Git Diff Matches Paths 📁

Today I learned that the git-diff command accepts a <path> argument. I used this knowledge to construct a command that runs only the RSpec test files on my branch that differ from master.

(other-branch)$ git diff master --name-only spec | xargs rspec

This is handy on a big test suite; when you can’t run every test, make sure you run the ones that you have changed.

See man git-diff for more info.

Mac Notes Default Text Size 🔎

I use Mac Notes a lot during pairing and brainstorming. It works, and it’s right there on the Mac!

One issue I’ve had is that the default text size on Mac Notes is unreadably small on a big monitor or iMac. I always end up creating a note, then bumping the text size with CMD-SHIFT ..

Of course, there’s a better way. Set the default font size in preferences.

image

Byline Class and Time Tag 📰

I’ve been hacking on TIL to optimize our site for Safari Reader mode. A lot of this is black box, because Apple doesn’t have easy-to-find documentation about how the feature works.

One thing that seems to matter is a class called byline for your author information, and an HTML tag time for the publication date. Here’s an example, borrowed from this site:

<p class="byline">
  <%= name %>
  <time datetime="<%= inserted_at %>">
    <%= inserted_at %>
  </time>
</p>

It gives us relevant reader information, shown here in Safari:

image

Here’s the PR:

https://github.com/hashrocket/tilex/pull/650

Meta Charset Top That! 🔝

When writing HTML, make sure that your meta charset tag is in the first 1024 bytes on the page.

Per MDN:

The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML as some browsers only look at those bytes before choosing an encoding.

What does this mean practically? Keep this tag near the top of your document, and use a website validator like https://validator.w3.org to check your tag placement.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta

Type "text/javascript" is Redundant

Have a script tag in your HTML annotated with <script type="text/javascript">? Per the HTML5 specification, the MIME type of a script tag, when omitted, is text/javascript. We are encouraged to omit it.

Delete that code and go green 💚.

Per MDN:

Omitted or a JavaScript MIME type: This indicates the script is JavaScript. The HTML5 specification urges authors to omit the attribute rather than provide a redundant MIME type. In earlier browsers, this identified the scripting language of the embedded or imported (via the src attribute) code. JavaScript MIME types are listed in the specification.

Source:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

Git Interactive Single Key 👇

Today I became familiar with the Git configuration interactive.singleKey. In the Hashrocket dotfiles this is enabled by default, and I’m not sure how common it is out in the wild. To quote the docs:

In interactive commands, allow the user to provide one-letter input with a single key (i.e., without hitting enter).

This allows git add --patch to be very powerful by enabling a quick vote up or down on a patch with a single keystroke.

Make sure you have the Perl module Term::ReadKey installed; it is a dependency.

Linking to a PayPal Transaction Page ⛓

This is a follow up to PayPal Transaction Pages aren’t Linkable. We figured out a way to build this feature!

When you process a PayPal transaction with the SDK (i.e. as a seller), you get a token that could be considered a transaction ID. It is something like a primary key on an object that is the parent to several other transactions: the seller’s transaction, any currency conversions, etc.

With this ID in hand, you can link to the seller’s dashboard and see a summary of this parent transaction. It’s not a documented feature, so the link could be broken at any time.

Thanks for sticking with me, PayPal support.

PayPal Transaction Pages aren't Linkable ⛓

I spent some time this week trying to create a link directly to a PayPal Merchant transaction show page. This is the page you’d see as a merchant when somebody has paid you via PayPal.

These pages use the transaction’s ID as a lookup, so I’d need the know that ID in order to dynamically create the link. After some Googling, reading GitHub issues, and finally the source code of my PayPal SDK of choice, I’d like to report: PayPal does not expose this ID in the normal course of business.

To quote my PayPal caseworker:

“While there are ways of retrieving the Transaction ID… you will not be able to link directly to the PayPal transaction details page, for security. The only way that your buyers can view the transaction details page is by accessing PayPal themselves, logging in with their credentials, and navigating to the Activity.”

I hope this spares another developer from the same rabbit-hole I just emerged from. Sometimes things with technology don’t work. 🤷‍♀️

Edit: we figured it out!

Class.new with Superclass

Today I encountered the following syntax (observed in Ruby 2.7):

SpecialError = Class.new(StandardError)

What’s going on here?

According to the Class docs, Class.new takes a superclass as an argument, creating an anonymous unnamed class. So this could be written as:

class SpecialError < StandardError
end

We’d lose the anonymity of the SpecialError class, but in the first example, we are assigning it to a Pascal-cased variable name anyway.

I think the first syntax would make sense when the code looks just like this example: a simple class that inherits some behavior, where the name is needed only in the scope where the class is declared.

Sort Git Branches by Latest Commit

Some of my repos have a lot of branches, and a command I discovered this week sorts them by the most recent committer date:

$ git branch --sort=-committerdate
* new-branch
  not-so-new-branch
  super-old-branch

The - reverses the order, putting the most-recently updated branch at the top, rather than the bottom, of my output. That’s more intuitive to me, but it certainly works in reverse.

Capitalize Hashtags for #a11y

At RubyConf 2019, I learned that social media hashtags are more easily read by screen readers if they are capitalized. So #rubyconf is more accessible when written as #RubyConf, because the screen reader can more easily make sense of the word breaks. Keep this in mind when posting and designing marketing campaigns.

I saw #rubyfriends and #rubyconf migrate to #RubyFriends and #RubyConf at the conference in realtime as this idea spread.

more info

h/t Coraline Ada Ehmke

Multiline HAML, One Way or Another 🥞

HAML uses meaningful whitespace identation, so in general, multiline code doesn’t fly. The prevailing wisdom is that ‘it’s better to put that Ruby code into a helper’ rather than support multiline blocks. But what if I really want to?

Here’s an example of a multiline HAML block using pipes.

= link_to(           |
  'someplace',       |
  some_path,         |
  class: 'someclass'

The pipe character preceded by whitespace signifies a multiline block. Make sure you don’t end your final line with a pipe; this can break your templating.

Tmux Attach Sessions with Working Directory

Your Tmux working directory is the root directory of the session. You can figure out what it is by opening a new window or pane in your session. The directory you start in is your working directory.

Sometimes it’s not the best directory for the type of project you’re developing. For instance, it could be set to the root directory of an umbrella app, when you’re working exclusively in one of the subdirectories.

Reset it with the -c flag:

$ tmux attach-session -t my_session -c ~/my_project

Force Code Block Length with Non-Breaking Space 📏

Services that render fenced code blocks tend to remove whitespace. They’ll display these two code blocks the same way, trimming the blank lines from the second example.

let something;
let something;

GitHub does this, as does Deckset. Deckset also adjusts your code’s font size based on how many lines are in the block, which means these two code blocks would have different font sizes on their slides. Sometimes I don’t want that to happen, like when building a series of slides that fill in different pieces of an example and should look consistent.

I cheat this feature putting a non-breaking space on the last line. On Mac, I can do this with OPTION + SPACE. I can see the character in Vim (‘+’), but it’s invisible on the slide, and it prevents Deckset from collapsing the line it’s on, forcing the code block to the length I choose.

SO: Use non-breaking spaces

Vim Tags in Visual Mode 🏷

This is my 400th TIL! 🎉

I’ll file this under ‘Vim is endlessly composable’. Today I learned that Vim tags can be used to define a range in visual mode. Here’s how you’d fold your code between two Vim tags.

Go to the first tag. If you marked it 1, here’s how you’d do that:

m1

Enter visual mode and extend to your second tag 2:

m2

Enter command mode and fold the range:

:fold

Which automatically extends to:

:'<,'>fold

I use this in big markdown files to hide all but the thing I’m currently working on. Enjoy.

Vim Reverse Sort

I use Vim’s :sort all the time. If I can’t have a more meaningful algorithm than alphabetical, at least my lists are in some kind of order.

This function comes in surprisingly useful while writing GitHub-style checkbox lists.

- [x] Check mail
- [ ] Play guitar
- [x] Write TIL

Sorting this list alphabeticaly puts the undone items at the top.

- [ ] Play guitar
- [x] Check mail
- [x] Write TIL

Reverse the order (in classic Vim style) with a bang:

:sort!

GitHub PR Team Reviews

Pull request openers on GitHub can request a review from a GitHub user, or a team that has at least read-access to the repository. In the screenshot below, deploying the ‘Ninjas’ to review this PR is effectively the same as requesting a review from each ninja on the team. If they have notifications for such things, they’ll be notified, and they’ll see a banner on the PR’s show page requesting their input on the change.

image

This is a handy way to request a lot of reviews at once.

About Pull Request Reviews

Mechanical Keyboard DIP Switches

I’ve owned a WASD keyboard for a while but totally forgot about the DIP switches on the bottom. What’s a DIP switch? From Wikipedia:

A DIP switch is a manual electric switch that is packaged with others in a group in a standard dual in-line package (DIP). The term may refer to each individual switch, or to the unit as a whole. This type of switch is designed to be used on a printed circuit board along with other electronic components and is commonly used to customize the behavior of an electronic device for specific situations.

DIP switch

On a WASD V2 87-key, these switches let you enable Mac mode (switch command and option), swap caps lock with CTRL, activate function commands, and more. These can be set in the OS or via different applications, but setting them at the hardware level is very convenient.

DIP Switch

Change Mechanical Keyboard Keycaps Faster ⌨️

I change the keys on my mechanical keyboards often. Sometimes I’m upgrading from stock, or trying an experiment, or just pursuing a look. It’s a reality of being really into keyboards.

Something I’ve learned along the way: removing all your old caps at once is a bad idea. When you do this, you have to figure out where every key goes. Some keycaps have a code on the underside telling you the row it belongs to, most do not. I end up pulling up my Mac virtual keyboard, or looking at another keyboard, to sort it out.

A better techique: take the new keys and line them up in the correct order for each row. Some keycaps ship in separate rows, making this easy. If that’s the case, slide each row out of its shrinkwrap like a sleeve of Thin Mints.

Then, change one row at a time. Remove all the keycaps in one row, and then put all the new ones in place. This is fast, foolproof, and if you get interrupted in between rows, you still have a functioning keyboard.

image

Tmux New Window and Process ⏩

This is a command I am continually a huge fan of. Here’s one way to open a new Tmux window, from Tmux command mode:

:new-window <program> <arguments>

My practical example from today:

:new-window psql my_database

This opens a new Tmux window psql with the arguments supplied, connecting me to my_database. When I terminate database connection, the window closes. For web development, this is a great way to quickly connect to a program, run some commands, then close the connection and cleanup the Tmux session.

Open the Vim Quickfix

Today I learned a new Vim command, :copen or :cope. The headline for this command is that it “open[s] a window to show the current list of errors”. The side benefit is that if you already have quickfix window in your buffers, like you would after greping the codebase, it will open or reopen that quickfix buffer.

See :help :copen for more info.

Reset a Vim Split

When I make a Vim horizontal split, the two panes are evenly sized. I then often use :resize n to make one pane larger or smaller. To restore the panes to their evenly sized split, use <vim-leader> =. In the Hashrocket dotfiles, this translates to CTRL + W =.

IO.inspect Label

When doing puts-driven-development in Elixir, IO.inspect/1 and IO.inspect/2 are very useful. These functions return their items unchanged, allowing us to spy on a value in our Elixir code.

I’m a fan of the :label option, which decorates our output with a label, making it easier to understand.

def channel_name_split(channel) do
  channel.name
  |> IO.inspect(label: "channel name before")
  |> String.split("")
  |> IO.inspect(label: "channel name after")
end

And here’s the output in our server log:

channel name before: "workflow"
channel name after: ["", "w", "o", "r", "k", "f", "l", "o", "w", ""]

Check out h IO.inspect in IEX for more info.

Set Splits Strings

Examples of JavaScript’s Set usually include an array as the argument:

const arraySet = new Set([1, 2, 3, 4, 5]);

However, the Set constructor also accepts a string, splitting it into a set for you:

const text = 'India';

const stringSet = new Set(text);  // Set ['I', 'n', 'd', 'i', 'a']

Enjoy those extra keystrokes!

docs

GitHub Insert a Suggestion ☝

Today I learned that GitHub has allows you to insert a one-line suggestion while conducting a pull request review. Click on the button circled in red, and you’ll get a suggestion fenced code block, as shown.

image

Inside the code block is the current code; replace it with your proposed change. GitHub will present your suggestion in a nice diff format.

When the pull requester views your suggestion, they can accept the change with one click. Efficient!

Multiple-lines are not yet supported, according to this GitHub blog post it is a frequently requested feature.

h/t Jed and Raelyn

Pivotal Tracker Advanced Search by Owner

On a big project, Pivotal Tracker contains a wealth of data. Let’s use it!

Today I filtered all the stories assigned to my teammate with the owner keyword:

owner, owned_by
owner: “Obi Wan Kenobi”
Search using the full name, initials, the username, user id or part of the user’s name.

This filtered out hundreds of stories, leaving the four in progress that I care about.

Here’s the official advanced search guide. Try a new keyword today!

https://www.pivotaltracker.com/help/articles/advanced_search/

Research Software Alternatives

Today I discovered AlternativeTo, which provides ‘crowdsourced software recommendations’. It’s available here:

https://alternativeto.net/

With this resource you can find alternatives to websites, tools, operating systems, and more, and learn about each alternative you find.

For reference, I checked out the results for open source alternatives to Ruby. The list includes no-brainers like Python, JavaScript, and Java, but also Go, C, and Lua. If I were a CTO who didn’t know Ruby and needed to quickly understand its position in the ecosystem, this would be a pretty useful starting point.

image

Tmux Kill All Other Sessions

Ok, you can kill a session in Tmux from the command line with tmux kill-session -t <session-name>. Did you know you can add a flag to kill every session other than your target? Here’s that command.

$ tmux kill-session -a -t <session-name>
$ tmux kill-session -at <session-name> # short version

All sessions but the target will die. This is my ‘clear the desk’ operation when I come back to my computer after some time away and want to shut down a bunch of random servers and processes.

Git Log Relative Committer Date 📝

Git log format strings are endlessly interesting to me. There are so many different ways to display the data.

Today I learned a new one, %cr. This formats your commit from the committer date, relative to now. After pulling from the remote, use it like this to see how long ago the latest commmit was committed:

$ git log -1 --format=%cr
8 hours ago

I use this command when surveying a Github organization, or an old computer with lots of dusty projects laying around.

Firefox Built-in JSON Tools ⛏

Recent versions of Firefox (I’m on 67.0.1) have built-in tools to explore JSON.

Here’s a screenshot of a JSON file from the React Native docs, viewed in Firefox:

image

We get syntax highlighting, the ability to save, copy, expand, or collapse the JSON, right in the browser, with no extra plugins or tools required. Another great Firefox feature!

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.”