Today I Learned

A Hashrocket project

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

108 posts by dillonhafer

How to change the request body size in Phoenix

Plug.Parsers accepts a length: key with a value of approximate bytes to accept and can be used in an endpoint file.

# endpoint.ex
plug Plug.Parsers,
  pasrsers: [:urlencoded, :multipart],
  length: 100_000_000 # 100 MB body size (approximately)

You can also configure the length for a specific parser:

plug Plug.Parsers,
  parsers: [:urlencoded, {:multipart, length: 100_000_000}] # 100 MB body size (approximately)

Ecto can only execute 1 sql statement at a time

Ecto can only ever execute 1 sql statement at a time, by design. For performance concerns, every statement is wrapped in a prepared statement
*some “security” is a fringe benefit of prepare statements.

In regards to the performance concerns of the prepared statement, PostgreSQL will force re-analysis of the statement when the objects in the statement have undergone definitional changes (DDL), making its use in a migration useless.

An example of a migration if you need to perform multiple statements:

def up do
  execute("create extension if not exists \"uuid-ossp\";")
  execute("alter table schedules add column user_id uuid;")
  execute("create unique index on schedules (id, user_id);")

def down do
  execute("alter table schedules drop column user_id;")
  execute("drop extension if exists \"uuid-ossp\";")

Convert .mov to .gif with ffmpeg

Sometimes putting a gif in a pull request is really helpful for reviewers. If you’ve recorded a movie on an iOS simulator with xcrun simctl or just QuickTime, it’s very simple to convert them to animated .gifs


ffmpeg -i \
       -s 415x925 \
       -pix_fmt rgb24 \
       -r 10 \
       -f gif \

Now it would be nice to have a function that could extract the video dimensions automatically 🤔 mdls 🤯

1 function gif-mov() {
2   movie=$1
3   height=$(mdls -name kMDItemPixelHeight ${movie} | grep -o '[0-9]\+')
4   width=$(mdls -name kMDItemPixelWidth ${movie} | grep -o '[0-9]\+')
5   dimensions="${width}x${height}"
6   ffmpeg -i ${movie} -s ${dimensions} -pix_fmt rgb24 -r 10 -f gif ${movie}.gif
7 }


$ gif-mov ~/Desktop/

Tuples are never inferred in TypeScript

If I have a function that takes a number tuple:

type Options = {
  aspect: [number, number],

const pickImage = (imageOptions: Options) => (

This will give a type error:

const myOptions = {
  aspect: [4, 3],

// ❌ Expected [number, number], got number[]

I must use type assertion when passing tuples:

const myOptions = {
  aspect: [4, 3] as [number, number],


Apollo-React hooks can easily refetch queries

React-apollo hooks allow you to easily refetch queries after a mutation, which is useful for updating a list when you have create/update/delete mutations.

useMutation takes an argument called refetchQueries that will run queries named in the array.

import React from "react";
import { useMutation } from "@apollo/react-hooks";
import gql from "graphql-tag";
import { Button } from "react-native";
import { Item } from "./types";

const ITEM_DELETE = gql`
  mutation ItemDelete($id: ID!) {
    itemDelete(id: $id) {

interface Props {
  item: Item;

const DeleteItemButton = ({item}: Props) => {
  const [deleteItem] = useMutation(ITEM_DELETE, {
    variables: { id: },
    refetchQueries: ["GetItemList"],

  return (
    <Button title="Delete" onPress={() => deleteItem()} />

Push git branch to another machine

Whenever I’m working in the same git repo on multiple machines, and I need to work on a branch on both machines I usually push the branch to a shared remote for the sole purpose of pulling it down on the other machine. This third machine can be avoided by push directly between machines:

$ myproject(feature-branch): git remote add machine2 ssh://machine2:/Users/dillon/dev/myproject
$ myproject(feature-branch): git push machine2 feature-branch

Over on machine2:

$ myproject(master): git branch
* master
  feature-branch       <= 😯

How to expose entire Swift class to Objective-C

Use the @objcMembers annotation in your Swift class. When working on a project that is migrating from Objective-C to Swift you will most likely be sharing all Swift functions with Objective-C, but that could start to look ugly:

class MyClass: NSObject {
  @objc func fancy() -> String {
    return "fancy"
  @objc func tooFancy() -> String {
    return "🤵"

But if everything needs to be available to Objective-C, we simply do:

class MyClass: NSObject {
  func fancy() -> String {
    return "fancy"
  func tooFancy() -> String {
    return "🤵"

How to remove NotificationCenter observer in Swift

Use the method deinit. In Objective-C classes I would always remove NSNotificationCenter observers in the -dealloc method, but a Swift class doesn’t have a -dealloc method. Instead, Swift has a deinit method.

Here is an example of Objective-C:

@implementation MyClass
  - (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];

And the equivilant in Swift:

class MyClass {
  deinit {

Open iOS simulator app data directory

If you need to quickly open an app’s document directory on the iOS Simulator, you can quickly get the path from the xcrun CLI.

xcrun simctl get_app_container booted ${bundle_id} data

Here’s some ruby you can throw into a Rakefile of your iOS app 😉

# Rakefile
desc "Open the simulator document directory"
task :docs do
  bundle_id ="MyAppName/MyAppName.xcodeproj/project.pbxproj")
    .scan(/PRODUCT_BUNDLE_IDENTIFIER = "(.*)"/)

  app_directory = `xcrun simctl get_app_container booted #{bundle_id} data`

  puts "Opening simulator directory: #{app_directory}"
  `open #{app_directory}`

🔍 Using NSArray with CONTAINS NSPredicates

NSPredicate’s predicateWithFormat method takes a va_list of arguments, so it’s not possible to pass an array to your format string. But, the same result can be achieved by combining multiple NSPredicates together using an NSCompoundPredicate:

Given a space-separated array of search words:

NSArray<NSString*> *words = [@"my search terms" componentsSeparatedByString:@" "];

You can combine them by first creating multiple predicates:

NSMutableArray<NSPredicate *> *predicates = [NSMutableArray new];
for (NSString *word in words) {
  NSPredicate *predicate = [NSPredicate predicateWithFormat:@"attribute CONTAINS[cd] %@", word];
  [predicates addObject:predicate];

And finally create one NSPredicate via NSCompoundPredicate

NSPredicate *finalPredicate =
        [NSCompoundPredicate andPredicateWithSubpredicates:predicates];

Shell parameter expansion

Shells can perform variable expansion, this is really useful for default argument variables in shell functions.

Here is a simple example of assigning a variable from an argument or defaulting to running a different shell command:

clog() {
  log_date=${1-`date +%Y-%m-%d`}
  git log --after="$log_date 00:00" --before="$log_date 23:59"

This allows me to write

clog to default to today’s date or

clog 2018-10-02 to look at the logs from yesterday

The date in the example clog 2018-10-02 would come through as $1 in the function, so ${1-`date`} means run this shell command if there is no $1.


Client Connection Vars with Ecto

Postgres has dozens of connection variables it will take, a couple of my favorite ones are statement_timeout and application_name.

Ecto takes a parameters connection option, which is a keyword list of connection parameters.

# dev.exs


# Configure your database
config :myapp, Myapp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "myapp_dev",
  socket_dir: "/var/run/postgresql",
  parameters: [application_name: "My App Development", statement_timeout: "5000"],
  pool_size: 10

Example of statement_timeout being respected in Ecto:

Ecto.Adapters.SQL.query!(Myapp.Repo, "select pg_sleep(86400)")

** (Postgrex.Error) ERROR 57014 (query_canceled): canceling statement due to statement timeout
    (ecto) lib/ecto/adapters/sql.ex:200: Ecto.Adapters.SQL.query!/5

Client Connection Variables references:

Use a unix socket with Ecto

Ecto allows the use of a unix socket to connect to postgres, instead of TCP. In order to use a unix socket in a exs file, you should remove the hostname list item, and provide the socket_dir list item.

# dev.exs


# Configure your database
config :myapp, Myapp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "myapp_dev",
  socket_dir: "/var/run/postgresql",
  pool_size: 10

NODE_OPTIONS without node

I was using storybook and found it had grown so large that its node process would run out of memory before it could start. The downside was that the storybook command did not accept node arguments, like --max_old_space_size=4096

Starting with node 8, node will look for options in an environment variable named NODE_OPTIONS to be interpreted as if they had been specified on the command line before the actual command line (so they can be overridden).

Example to give storybook more memory:

NODE_OPTIONS=--max_old_space_size=4096 storybook start


🆙 Update macOS from cli

Opening the App Store and navigating to the update tab and clicking and confirming the update buttons is just too many steps for me.

macOS has a cli to the software update process:

$ /usr/sbin/softwareupdate

With some fancy arguments I can update everything in one command:

$ sudo softwareupdate -ia

the -ia flag means install all the available updates.

🔢 CLI to bump iOS target versions

Bumping Info.plist versions by hand can be very error prone (e.g. remembering to bump multiple targets in a project)

Fortunately Xcode’s cli has an easy solution

xcrun agvtool new-marketing-version <versString>

So if my Application targets are at version 2.0.4 and I need them to be at 2.0.5 I simply run this command at the terminal:

xcrun agvtool new-marketing-version 2.0.5


🔑 Set foreign keys to null

Sometimes, in certain circumstances, it is reasonable to have a foreign key value of null.

ActiveRecord’s .has_many method has an argument to set the foreign key column on referencing rows to null when that record is deleted.

dependent: :nullify


class Post < ApplicationRecord
  belongs_to :author
  belongs_to :category

class Category < ApplicationRecord
  has_many :posts, dependent: :nullify

In this example whenever a category is deleted, any posts referencing the categories table will have their foreign key set to null.


Custom Browser with Create React App

Sometimes Google Chrome isn’t your default browser for surfing the internet, but it is your default browser during development. With create-react-app you can specify an alternate browser to automatically open (or no browser at all), to avoid opening the wrong development browser all the time.

Just pre-pend the browser to the start script:

  "name": "custom-browser",
  "version": "0.1.0",
  "scripts": {
    "start": "BROWSER='Google Chrome' react-scripts start",

Find host and port in development

Rails 5.1 is a little different the pre 5.1

require 'rails/commands/server/server_command'

In rails 5.0 or below

require 'rails/commands/server'
# lib/host_and_port.rb

def __host_and_port__
  options =!(ARGV)
  options.values_at(:Host, :Port)

You can then find the host and port for various configuration files.

# config/initializers/carrier_wave.rb
require Rails.root.join('lib/host_and_port').to_s

CarrierWave.configure do |config|
  config.asset_host = "http://" + __host_and_port__.join(":")


# config/environments/development.rb
require Rails.root.join('lib/host_and_port').to_s

host, port = __host_and_port__
config.action_mailer.default_url_options = { host: host, port: port }

Rails/PG Statement Timeout 🐘⏰

By default, Rails does not set a timeout on database statements. For example, this will run for a full day, even if your ruby process goes away.

  select pg_sleep(86400);

The good news is that Rails provides a way to prevent this from happening from the database.yml with the statement_timeout variable.

default: &default
  adapter: postgresql
    statement_timeout: 5000

I’m starting to think that this should always be set low. And then explicitly set higher on a per-query basis, when one is expecting something to take a long time.

group by 1, 2, 3 🔢

Postgres allow group by and order by to reference column order in a sql statement. This is particularly useful when an aggregate result needs to be referenced in the group by or order by statement.

-- group by aggregate
  (created_at at time zone 'UTC' at time zone 'America/Chicago')::date,
from posts
group by (created_at at time zone 'UTC' at time zone 'America/Chicago')::date
order by (created_at at time zone 'UTC' at time zone 'America/Chicago')::date


-- group by 1, 2, 3
  (created_at at time zone 'UTC' at time zone 'America/Chicago')::date,
from posts
group by 1
order by 1

Open images in vim with iTerm 🖼

iTerm 3 has a built in shell script called imgcat for displaying images in the terminal. With one simple autocmd in my vim configuration I can open images*:

:autocmd BufEnter *.png,*.jpg,*gif exec "! ~/.iterm2/imgcat ".expand("%") | :bw

In my command I wipe the image buffer(:bw) because I don’t want large images sitting around in buffers, but this is easy to change.


*imgcat does not work with tmux

Use PostgreSQL socket in database.yml 🐘🔌

When using the host: configuration option in the database.yml set to localhost or, I would need to add an entry to PostgreSQL’s pg_hba.conf file to allow my ip address access. But, if you give the host: option the directory of your PostgreSQL sockets, rails will be able to use the socket, without needing to add an entry to the PostgreSQL configuration file.

  database: fancy_things
  host: '/var/run/postgres'

Drop connections with Nginx

You can ignore requests sent to nginx by responding with the status 444

server {
  listen 80;
  listen [::]:80;
  listen 443;
  listen [::]:443 ssl http2;

  location /no-response {
    return 444;

The primary use for this is to prevent processing requests with undefined server names:

# block for requests with undefined server names
server {
  listen 80;
  listen [::]:80;

  return 444;

Respect Do Not Track 👁‍🗨

Before I put google analytics, adwords, or other third party scripts on my sites I can respect the DNT headers a user has set. Here’s how I can do it with Google Analytics:

  if (window.navigator.doNotTrack !== '1') {
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

    ga('create', 'UA-xxxxxxx-5', 'auto');
    ga('send', 'pageview');

In the above snippet I check if the user has enabled DNT. If they haven’t set the header or they have turned it off the script will run. Checking for window.navigator.doNotTrack !== '1' ensures that the user purposely set the header.

Chaining TLS Certificates 🐱

Apache allows you to declare an intermediate TLS certificate along with your regular certificate in your configuration, but many web servers only allow you to provide one certificate option. Like Nginx, go, or heroku.

In those cases, you will need to concatenate the entire certificate chain into one certificate file. This may sound daunting, but the process is very simple. Let me introduce cat -- concatenate and print files.

cat is normally used for printing files, but in this case we actually want to concatenate files. Below is a simple example on how we can do this:

cp example_com.crt example_com.chained.crt
cat AddTrustExternalCARoot.crt >> example_com.chained.crt
cat COMODORSAAddTrustCA.crt >> example_com.chained.crt
cat COMODORSADomainValidationSecureServerCA.crt >> example_com.chained.crt

To shorten it, we only need to use cat once:

cp example_com{,.chained}.crt &&
cat AddTrustExternalCARoot.crt COMODORSAAddTrustCA.crt COMODORSADomainValidationSecureServerCA.crt >> example_com.chained.crt

Verify TLS cert with private key

Hopefully you’re never in a situation where you don’t know what private key you used to generate your TLS certificate, but if you do… here’s how you can check.

Note: this is better than uploading the certs to production to check on them 😉

Assuming we have generated a private key named and a certificate named we can use openssl to check that the MD5 hashes are the same:

openssl x509 -noout -modulus -in | openssl md5
openssl rsa -noout -modulus -in | openssl md5

To make things better, you can write a script:

CERT_MD5=$(openssl x509 -noout -modulus -in | openssl md5)
 KEY_MD5=$(openssl rsa  -noout -modulus -in | openssl md5)
if [ "$CERT_MD5" == "$KEY_MD5" ]; then
  echo "Private key matches certificate"
  echo "Private key does not match certificate"

Trust Issues 🤔

Our computers trust a scary amount of Root Certificate Authorites, and sometimes I have trust issues with some of them. Most recently being the StartCom bug, which allowed anyone to get a certificate for any domain they wanted.

I can’t trust them. Period. And I don’t have to.

Here is how you can revoke trust for any Root CA in OSX:

  1. Open Keychain Access.
    open /Applications/Utilities/Keychain\
  2. Click on System Roots from the left Keychains sidebar.
  3. Typestartcom in the search bar.
  4. Select all the root certificates and press ⌘i.
  5. Expand the Trust section
    and change the option
    When using this certificate
    Never Trust.

Put -L in your ssh config

If you’ve used ssh you’ve probably had to use local port forwarding before. If you use complex flags often, you will find remembering what you need is tiresome. Stop. Write it down so you can forget all about it. Save your memory.

Instead of doing this:

ssh -L 3000:localhost:3002

Add those flags to the ~/.ssh/config file instead:

# /home/dillon/.ssh/config

Host example
  User         not_dillon
  LocalForward 3000 localhost:3002

From now on, all you need to type is:

ssh example

The Case of the Default 🕵

In Apple’s Swift language switch statements must be what apple calls “exhaustive”. I’ve felt the term to be very literal. Literally exhaustive?

Example that does not work:

let count = 42

switch count {
case 1:
case 7:

The above statement does not work because it’s missing a default case. Why? What if I don’t want to do anything else? Why do I need to write something that won’t be used? Don’t worry, there is an amazing and less “exhaustive” way to handle these situations; simply default: ()

Correct example:

let count = 42

switch count {
case 1:
case 7:
default: ()