Today I Learned

hashrocket A Hashrocket project

617 posts by joshbranchaud twitter @jbrancha

Create Named Structs With Struct.new

I often see Struct used to create some one-off anonymous data structure like so:

> person = Struct.new(:name, :age)
=> #<Class:0x007fc6c89112e8>
> person.new("Alice", 33)
=> #<struct name="Alice", age=33>
Ruby

This will often get the job done, but on its own the resulting data structure doesn't tell us as much as it could.

We can say more with a named struct:

Struct.new("Person", :name, :age)
=> Struct::Person
> Struct::Person.new("Bob", 24)
=> #<struct Struct::Person name="Bob", age=24>
Ruby

When the first argument is a string that can be converted to a constant, then we'll get a named struct that is subclassed under Struct.

We can also assign the struct initialization to a constant to do a similar thing:

> Person = Struct.new(:name, :age)
=> Person
> Person.new("Jerry", 45)
=> #<struct Person name="Jerry", age=45>
Ruby

source

Compute Intermediate Values In A With Construct

The expressions you use in a with construct do not have to contain the <- syntax. You can pattern match and bind values along the way as well.

with %{id: id} <- get_user(),
     url = "/api/#{id}/blogs",
     %{status_code: 200, body: body} <- HTTPoison.get(url),
     {:ok, decoded_body} <- Poison.decode(body) do
  {:ok, decoded_body}
end
Elixir

In the above (sorta contrived) example we were able to construct a URL in the middle of the series of expressions.

The values we compute inline will be closed into the with construct, so they won't leak.

See the with docs for more details.

Check List Membership In Elixir

You can use the in operator to check if something appears in a list. This is a handy way of checking if a variable is one of a few acceptable or expected values.

For instance, a common DateTime comparison pattern relies on this to check if a DateTime is >= or <= to another DateTime.

{:ok, datetime} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")

DateTime.compare(datetime, DateTime.utc_now()) in [:lt, :eq]
Elixir

Alternatively, you can check that something does not have membership in a list by also including the not operator.

DateTime.compare(datetime, DateTime.utc_now()) not in [:lt, :eq]
Elixir

Open FZF Result In A Split In Vim

The fzf.vim plugin allows you to do speedy fuzzy searches for filenames and line-by-line content.

Once you've narrowed down the results and found what you're interested in, you can hit <enter> and a new buffer will open over what was already in the window. You can also open that file as a split.

Hitting Ctrl-x will open the file under the cursor as a horizontal split.

Hitting Ctrl-v will alternatively open that file as a vertical split.

Using When Clauses In A With Construct In Elixir

Because Elixir's with construct supports the full power of the language's pattern matching, we can use when clauses to further narrow down our matches.

For instance, if we want to match against the response to an API request, but only for response status codes in the 2xx range, we can do something like the following:

with %{status_code: code, body: body}
       when code >= 200 && code < 300 <- HTTPoison.get!(url),
     {:ok, decoded_body} <- Poison.decode(body) do
  {:ok, decoded_body}
end
Elixir

See the docs for with for more details.

Pulling In Changes During An Interactive Rebase

My standard workflow when doing feature development is to checkout a feature branch and commit changes as I go. When the feature is finished, I clean up the commit history with an interactive rebase and then integrate those changes with master.

I initiate the interactive rebase like this (while on the feature branch):

$ git rebase -i master

This allows me to squash, fixup, and delete commits that I've made since checking out this branch from master.

It is important to note that an another thing will happen seemingly behind the scenes. Any commits on master since the feature branch was checked out will be applied to the feature branch before the effects of the interactive rebase are applied.

If you want to strictly do an interactive rebase of the commits on the feature branch ignoring what is on master, then reference the commit you checked out from -- put another way, reference the commit before the first commit on this branch.

$ git rebase -i <sha-of-first-commit-on-this-branch>~

The tilde (~) will go back one commit from the specified commit sha.

See man git-rebase for more details.

Edit A File Starting On The Last Line

Generally when you start editing a file whether as a new Vim session (vim file.txt) or in an existing Vim session (:e file.txt), your cursor will be positioned at the beginning of the file.

You can start editing a file with the cursor positioned at the end of a file using an edit command -- include + with no line number. This may be useful for a large file or even if you just know that you'll be adding content directly to the bottom of the file.

If you are starting a new Vim session:

$ vim + file.txt
Bash

or if you are already in a Vim session:

:e + file.txt

See man vim or :h +cmd for more details.

Match On A Map In A With Construct In Elixir

Many usage example of the with construct show a series of matches on a tuple.

with {:ok, width} <- Map.fetch(opts, :width),
     {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
end
Elixir

You can match on more than just tuples though. Here is how you might match on a map.

with %{status_code: 200, body: body} <- HTTPoison.get!(url),
     {:ok, decoded_body} <- Poison.decode(body) do
  {:ok, decoded_body}
end
Elixir

In fact, you have the full power of Elixir's pattern matching available to you in your series of matches for a with construct.

Edit A File At A Specific Line Number In Vim

I use long-running Vim sessions where I constantly open files as new buffers with the :edit (or :e) command. Generally, when I open a new file I end up with the cursor at the top of the buffer and go from there. But what if I have a specific line number in mind and I want the cursor to start there?

The :edit command can receive arguments, including a line number argument. To open up to line 159, I can include the +159 argument in the command.

:edit +159 path/to/the/file.txt

See :h :edit and :h +cmd for more details about how :edit works and what the different arguments can do.

Comparing DateTime Structs In Elixir

Remember, comparisons in Elixir using ==/2, >/2, </2 and friends are structural and based on the DateTime struct fields. For proper comparison between datetimes, use the compare/2 function.

As the DateTime docs say, you'll want to use compare/2 in order to accurately compare two DateTime structs.

{:ok, older} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")
{:ok, newer} = DateTime.from_naive(~N[2017-11-24 13:26:08.003], "Etc/UTC")

DateTime.compare(older, newer)
#=> :lt

DateTime.compare(newer, older)
#=> :gt

DateTime.compare(newer, newer)
#=> :eq
Elixir

When using compare/2, you'll get one of :lt, :gt, or :eq as a result, meaning less than, greater than, or equal respectively.

Access Unsupported Resolutions With RDM On Mac

If you visit the Display Settings for your Mac, you'll find that you only have a handful of screen resolution options. For standard use, you'll get by with these. If you need a specific, unsupported resolution you'll need help from a 3rd party tool. There are many options out there. RDM is a free and open-source option.

Once you have it installed and have given it Accessibility permissions, open the menu from your top toolbar and select the resolution you are looking for.

I use RDM to adjust my screen resolution to 1280x720 for optimal screencasting.

Search Backward Through A File

There are a number of ways to search for a match in a file. One I use quite often is hitting * while the cursor is over the word I want to find matches for. It searches forward jumping to the next occurrence of that word.

It turns out there is a way of doing the same thing, but searching backward to the previous occurrence of the word. If you hit # with the cursor over a word, it will jump backward through the file until it finds an occurrence of that word. Keep hitting # to keep searching backward.

See :h # for more details.

Resize App Windows With AppleScript

I showed in a previous TIL how we can run AppleScript commands inline from the terminal. Here is an inline command for positioning and resizing your iTerm2 window.

osascript -e 'tell application "iTerm2"
  set the bounds of the first window to {50, 50, 1280, 720}
end tell'
Bash

The first two values tell the command the x and y coordinates of where to position the upper left corner of the window relative to the upper left corner of your screen. The next two values are the width and height that the window should be resized to.

source

Delete Paranoid Records In Rails

The ActsAsParanoid gem provides soft delete functionality to ActiveRecord objects in Rails. You can enhance a model with its functionality like so:

class User < ActiveRecord::Base
  acts_as_paranoid
end
Ruby

This gem hijacks ActiveRecord's standard destroy and destroy! functionality. If you call either of these methods, instead of the record being deleted from the database, it's deleted_at column is updated from nil to the current timestamp. Resulting in a soft deleted record.

If you call destroy or destroy! a second time (i.e. on a record that has already been soft deleted), it will be actually deleted from the database. Alternatively, you can call destroy_fully! from the beginning to skip the soft delete.

Clear Out The Jump List In Vim

Vim uses a jump list to track all they jumps you've made during a session. Vim can even be configured to keep a record of those jumps between sessions. This is really handy for a long-lived project, but what if you want those jumps cleared out?

You can clear them out for the current and subsequent windows using :clearjumps. The jump list for existing windows will be unchanged and once you start a new session, the full jump list will be restored.

See :h :clearjumps for more details.

Connect To An RDS PostgreSQL Database

You can connect to an RDS PostgreSQL database remotely via psql. First, you need to tell AWS that connections from your IP address are allowed. This is done by configuring the VPC (Virtual Private Cloud) that is associated with the RDS instance. You'll need to add an inbound rule to the Security Group associated with that VPC. You'll add an inbound rule that allows a Postgres connection on port 5432 from your IP address -- which is identified by a CIDR address.

image

Once this rule has been added to the security groups associated with the VPC that is associated with your RDS instance, you'll be able to connect from your machine with psql.

$ psql my-rds-endpoint.cbazqrhkmyo.us-east-1.rds.amazonaws.com \
    --port 5432 \
    --user rdsusername \
    postgres
Bash

Assuming the database username is rdsusername and the specific database is named postgres, you'll be prompted for that user's password and then connected.

Navigate To The Nth Column On A Line In Vim

You can navigate the cursor to a specific column of the current line using the | character. For instance typing

45|

will navigate your cursor to the 45th column of the current line. If you type a number that exceeds the number of columns on the line, your cursor will be placed on the last column.

Here is what the help files have to say about |:

|           To screen column [count] in the current line.
            |exclusive| motion.  Ceci n'est pas une pipe.

Generate A Signed JWT Token

The jwt gem is a Ruby library for encoding and decoding JWT tokens. You can create a signed JWT with the #encode method by specifying a secret and a hash algorithm.

payload = { id: 1, email: 'user@example.com' }
secret = Rails.application.credentials.secret_key_base

token = JWT.encode(payload, secret, 'HS256')
Ruby

This will create a JWT token that contains some JWT headers, application data, and an encrypted secret that signs the data. This can be passed to and from your client app as a way of identifying and authenticating a user.

See the jwt-ruby docs or jwt.io for more details.

Serialize With fast_jsonapi In A Rails App

Netflix put out a Ruby gem for super fast JSON serialization -- fast_jsonapi. It is great for serializing JSON responses for Rails API endpoints.

First, add gem 'fast_jsonapi' to your Gemfile and bundle install.

Then create the app/serializers directory for housing all of your JSON serializers.

Next you can create a serializer that corresponds to the model you want to serialize:

# app/serializers/recipe_serializer.rb
class RecipeSerializer
  include FastJsonapi::ObjectSerializer

  set_id :id
  attributes :name, :source_url
end
Ruby

Last, use it to generate a JSON response in your controller:

# app/controllers/recipes_controller.rb
class RecipesController < ApiController
  def index
    render json: RecipeSerializer.new(@current_user.recipes)
  end
end
Ruby

Requests to that endpoint will receive a response that looks something like this:

{
  data: [
    {
      id: 1,
      attributes: { name: "Old Fashioned", source_url: "http://..." },
    },
    {
      id: 2,
      attributes: { name: "Sazerac", source_url: "http://..." },
    },
  ]
}
JSON

Get Matching Filenames As Output From Grep

Standard use of the grep command outputs the lines that match the specified pattern. You can instead output just the names of the files where those matches occur. To do this, include the -l flag.

$ grep -Rl hashrocket .
./elixir/run-exunit-tests-in-a-deterministic-order.md
./git/show-file-diffs-when-viewing-git-log.md
./git/single-key-presses-in-interactive-mode.md
./internet/enable-keyboard-shortcuts-in-gmail.md
...
Bash

This recursive grep finds all the files where hashrocket appears. It only looks for the first match in a file, so each file will only be listed once even if there may have been multiple matches.

See man grep for more details.

Copy Some Data From The Chrome Console

Sometimes you have some data that you are playing around with in the console, something you logged from an API response. You then want to share it, so you try to copy the whole thing into your system copy buffer. There are a couple hacky ways of doing this, but Chrome supports a really smooth way.

Use the copy function.

characters
> (8) [{}, {}, {}, {}, {}, {}, {}, {}]
copy(characters[1])
JavaScript

My system copy buffer now contains the entire object that makes up the second entry in that list. I can then paste it into Slack or wherever.

source

Secure Passwords With Rails And Bcrypt

If you are using bcrypt (at least version 3.1.7), then you can easily add secure password functionality to an ActiveRecord model. First, ensure that the table backing the model has a password_digest column. Then add has_secure_password to your model.

class User < ActiveRecord::Base
  has_secure_password

  # other logic ...
end
Ruby

You can now instantiate a User instance with any required fields as well as password and password_confirmation. As long as password and password_confirmation match then an encrypted password_digest will be created and stored. You can later check a given password for the user using the authenticate method.

user = User.find_by(email: user_params[:email])

if user.authenticate(user_params[:password])
  puts 'That is the correct password!'
else
  puts 'That password did not match!'
end
Ruby

Access Secrets In A Rails 5.2 App

For a long time the access chain for getting at secrets in your Rails app stayed the same. For instance, getting at the secret_key_base value looked something like this:

Rails.application.secrets.secret_key_base
Ruby

In the world of Rails 5.2, secrets are no longer secrets. They are now credentials. This means they are under the credentials key instead of the secrets key. Here is how you can access secret_key_base now:

Rails.application.credentials.secret_key_base
Ruby

source

Configure cd To Behave Like pushd In Zsh

The Zsh environment has a setting that allows you to make the cd command behave like the pushd command. Normally when you use cd the remembered directory stack is not effected. However, if you add the following setting to your ~/.zshrc file:

setopt auto_pushd
Bash

then using cd to navigate directories will cause those directories to be added to the dirs stack.

This is the default in the oh-my-zsh configuration of zsh.

Scripting With RVM

Because of how RVM works under the hood, you have to do a couple things to get it to work in a script.

First, you need to ensure that your script is using bash instead of sh, so add this to the top of your scripts:

#!/bin/bash
Bash

You'll then want to make sure that RVM is sourced. Their docs recommend sourcing in a script like this:

# Load RVM into a shell session *as a function*
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then

  # First try to load from a user install
  source "$HOME/.rvm/scripts/rvm"

elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then

  # Then try to load from a root install
  source "/usr/local/rvm/scripts/rvm"

else

  printf "ERROR: An RVM installation was not found.\n"

fi
Bash

After that, you can utilize any of the capabilities of RVM in your script as you'd like.

List The Stack Of Remembered Directories

When you open a new Unix shell, you start in some directory, probably your home (~/) directory. If you use pushd to navigate to different directories, there is a paper trail of your movements, a listing of where you've been. You can view this listing of directories with the dirs command.

$ dirs
~/
$ pushd code
$ dirs
~/code ~/
$ pushd /usr/bin
$ dirs
/usr/bin ~/code ~/

Each time you pushd, the directory you have moved to is pushed onto the stack of visited directories. Alternatively, you can use the popd command to return to the previous directory, removing the current directory from the stack.

source

for...in Iterates Over Object Properties

I don't reach for for loops very often, so when I needed one recently I thought I'd check out the newer for...in construct. It didn't behave quite how I was expecting. I thought it would iterate over the values in the target list, instead it seemed to be iterating over the indices.

The MDN docs explain what is going on:

The for...in statement iterates over all non-Symbol, enumerable properties of an object.

An array is an object whose properties are the indices of the values stored in the array.

const fruits = ["apple", "banana", "orange"];
for (let fruit in fruits) {
  console.log(fruit, fruits[fruit]);
}
// => "0" "apple"
// => "1" "banana"
// => "2" "orange"
JavaScript

The iteration value wasn't what I was looking for, but in this case I can use it to access the value from the list. I'd be better off using a standard for loop though.

Quit When There Is An Argument List

To start a Vim session with multiple files in the argument list, name multiple files when invoking Vim:

$ vim README.md LICENSE
Bash

The first file in the argument list, and the current buffer, is README.md. The last file in the argument list is LICENSE.

At this point if you try to quit, Vim will prevent you saying 1 more file to edit. If we look at the docs for :q and :wq, we see something along the lines of:

This fails when the last file in the argument list has not been edited.

Vim wants to ensure that you've paid attention to every file that you loaded up into your argument list. If you'd like to quit regardless. then this is where the :q! and :wq! variants come in handy. This commands will skip the argument list check.

Show Only Commits That Touch Specific Lines

When you run git log, you are listing all commits in reverse-chronological order for the current branch. There are ways of filtering the commits that get output from git-log. As of Git 1.8.4, git-log output can be filtered by commits that touch a range of line numbers.

This is done with the -L flag.

For instance, if I want to see all commits that touched the 13th line of my README.md file, then I can do this:

$ git log -L13,13:README.md
Bash

I can alter the command to show commits that touched a range of lines like so:

$ git log -L19,45:README.md
Bash

I used the -L flag recently to find when a dependency was added to my package.json file even though the most recent changes to that line were version bumps.

source

Show Changes In The Compose Commit Message View

When you execute git commit, you'll be dropped into your default editor and prompted to compose a commit message. By default you'll only see the status and names of the files involved in the commit.

To also see the line-by-line-changes in this view, you'll want to commit in verbose mode.

$ git commit --verbose
Bash

You can also set verbose mode as the default by updating your ~/.gitconfig file.

[commit]
    verbose = true

source

Open Slack's Keyboard Shortcuts Reference Panel

Slack has a ton of keyboard shortcuts, but you might only be familiar with some of the more obvious ones. When I started using Slack, I quickly discovered that Cmd+} and Cmd+{ allow you to navigate forward and backward through your different Slack workspaces.

But there is a whole world of Slack keyboard shortcuts that I didn't know about. By hitting Cmd+/ a Keyboard Shortcuts reference panel will be toggled open. This lists all of the keyboard shortcuts you didn't know you needed.

Gesture To View All Windows Of Current Mac App

I use a Mac as my primary development machine. When working on a web application, I often end up with a couple Chrome windows open and sometimes even a couple terminal windows open. I generally use the Cmd+~ key binding to toggle through them. This can occasionally get confusing though. To get the fuller picture, I can get a birds-eye view of all windows for the current app using a track pad gesture.

Swiping down with three fingers on the track pad will provide a zoomed out view of all windows. Click on the window you care about to view that or swipe back up with three fingers to dismiss it.

source

Find The Date That A File Was Added To The Repo

The git log command has a bunch of flags that you can use to filter commits and format their output.

We can get git log to only show the date for a commit in the short format with the following flags:

$ git log --pretty=format:"%ad" --date=short
Bash

We can also get git log to filter commits to just those that have files being added:

$ git log --diff-filter=A
Bash

Like many git commands, we can restrict the output to those that match a path or file.

$ git log -- README.md
Bash

If we put all of these together, then we have a one-line command for getting the date a specific file was added to the repository:

$ git log --pretty=format:"%ad" --date=short --diff-filter=A -- README.md
2015-02-06
Bash

See man git-log for more details.

Enable Keyboard Shortcuts In Gmail

In these modern times of asynchronous communication and paperless receipts, a person's email inbox can get mighty full. Keeping that influx of emails at bay is a challenge. If you'd like to start unburying yourself and get on the path to inbox zero -- Gmail's keyboard shortcuts can help.

Keyboard shortcuts are not enabled by default, so you'll need to turn them on in settings. Click the gear icon, select Settings and then scroll down under the General tab until you find the Keyboard shortcuts section. Select the Keyboard shortcuts on radio button and then Save changes.

You'll now have access to a variety of shortcuts, such as using the j and k keys to move up and down the list of emails in your inbox. Find the one you want and hit o to open it up.

Keep exploring -- there is a whole world of keyboard shortcuts out there.

h/t Jake Worth

Navigate With State Via @reach/router

With @reach/router, you can programmatically change your route using the navigate function. This utilizes the Context API, so its available anywhere nested under your router. To provide some data to the destination location, include a state option in the navigate call.

const onSubmit = ({ data }) => {
  /* submit logic ... */

  navigate(nextPath, { state: { data }});
}
JavaScript

The component that renders in response to this navigation will have access to this state.

const NextComponent = ({ location }) => {
  const { data } = location.state;

  return (
    /* ... */
  )
}
JavaScript

Prevent reach/router Redirect Error Screen In Dev

When using @reach/router's <Redirect> with tools like create-react-app and Gatsby, you'll get those tools' development-mode error screen overlays whenever a redirect happens. This has to do with how @reach/router utilizes componentDidCatch to change the path without a render. That error screen overlay can get annoying though. Prevent it with the noThrow prop.

return (
  <Redirect to={anotherPath} noThrow />
);
JavaScript

Destructured Access To Nested Value & Parent Value

A destructuring pattern that I often see (especially in React code) is to peel off a nested value in the argument declaration of a function.

const Component = ({ data: { name: displayName }}) => {
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent />
    </div>
  );
};
JavaScript

On its own this works quite well, but what happens when you need access to the full set of data as well as the nested name value? I often see this.

const Component = ({ data }) => {
  const { name: displayName } = data;
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent data={data} />
    </div>
  );
};
JavaScript

ES6 destructuring is flexible. You can skip the const line and keep everything in the argument declaration.

const Component = ({ data: { name: displayName }, data }) => {
  return (
    <div>
      <h1>{displayName}</h1>
      <SubComponent data={data} />
    </div>
  );
};
JavaScript

You can re-reference the data value after the nested destructuring.

Submit A Form With A Button Outside The Form

You can tie a submit button to a form that the button doesn't live inside of. The trick is to give the form an id and then reference that id with the button's form property.

<div>
  <form id="my-form">
    <label for="name">Name:</label>
    <input type="text" name="name"></input>
  </form>

<!-- ... -->


  <button type="submit" form="my-form">Submit</button>
</div>
HTML

With this setup, clicking the Submit button will cause the form to be submitted.

See the MDN Button docs for more details.

Matching A Computed Property In Function Args

The computed property name feature of ES6 allows you to reference a variable in object assignments and destructurings. This syntax is flexible enough that it can be used in the arguments portion of a function declaration. In fact, it can even be matched against another argument -- allowing the creation of some handy, yet terse functions.

const get = (key, { [key]: foundValue }) => foundValue;
JavaScript

Notice that the first argument, key, will match against the computed property name in the second argument. The foundValue will correspond to whatever key maps to in the given object.

This get function can then be used like so.

const stuff = { a: 1, b: 2, c: 3 };

console.log("Get a:", get("a", stuff)); // Get a: 1
console.log("Get d:", get("d", stuff)); // Get d: undefined
JavaScript

h/t @sharifsbeat

Check The Type Of A Child Component

There is a simple way to check the type of a child component.

import React from 'react';

const Hello = () => <h1>Hello</h1>;

const Greeting = ({ children }) => {
  let hello;
  React.Children.forEach(children, child => {
    if(child.type === Hello) {
      hello = child;
    }
  });

  return hello;
};
JavaScript

This is a comparison of the child's type to the component constant we are looking for.

This comparison is not the most robust. For instance, Gatsby does something internally that throws off this comparison. Here is a more robust comparison.

if(child.type === Hello || child.type === <Hello />.type)
JavaScript

source

Wrap The Root Of A Gatsby App In A Component

Each component that is defined in the pages directory of a Gatsby app will be generated into a separate static page. Each of these pages is meant to stand on its own. Nevertheless, there is still a behind-the-scenes root component above all of these pages. There are cases where'd you like to wrap this root component with some other component, such as a Redux Provider.

This can be done using the wrapRootElement hook from the Browser API in the gatsby-browser.js file.

// gatsby-browser.js
import React from 'react';
import { Provider } from 'react-redux';

import store from './src/store';

export const wrapRootElement = ({ element }) => {
  return (
    <Provider store={store}>{element}</Provider>
  );
}
JavaScript

Each page and each component in your Gatsby app will now be downstream from a Redux provider meaning that they can connect to the Redux store as needed. You can use this technique for any top-level component that need to be wrapped around the entire app.

source