Today I Learned

A Hashrocket project

91 posts by dillonhafer

🚯 Prevent development logs from bloating on macOS

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

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

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

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

What directory is the parent of root 👨‍👦📁

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

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

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

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

prevent execution when creating materialized views

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

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

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

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

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

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

Fix poor type support in immutablejs

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

import {Map,fromJS} from 'immutable';

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

interface User {
  name: string;
  points: number;

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

users.forEach(u => {
  const aString =
  const aNumber = u.points

Postgres Identity Column

The Postgres wiki recommends not using the serial type, and instead added identity columns to replace them.

Old way:

create table todos (
  id bigserial primary key,
  todo text not null

The new way with identity columns:

create table todos (
  id bigint generated by default as identity primary key,
  todo text not null


insert into todos (todo) values
  ('write a til'),
  ('get some coffee');

select *
from todos;
 id |      todo
  1 | write a til
  2 | get some coffee
(2 rows)

Source: PG wiki: Don’t use serial

Expo PushNotifications with pop-ups on Android

Expo has an amazing push notification service. In order to display push notifications in a pop-up style (the default on iOS) on Android, you must do two things:

  1. Create an Android push notification channel
  2. Set the priority and channelId in the push notification payload to expo.

Create channel example:

import React from "react";
import { Platform } from "react-native";
import { Notifications } from "expo";

class App extends React.Component<Props> {
  componentDidMount() {
    if (Platform.OS === "android") {
      Notifications.createChannelAndroidAsync("chat-messages", {
        name: "Messages",
        priority: "max",
        sound: true,
        vibrate: [0, 250, 500, 250]

  render() {
    // ...

Send PN with priority and channel example:

curl -H "Content-Type: application/json" -X POST "" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title":"New Message",
  "body": "Are you there?",
  "channelId": "chat-messages",
  "priority": "max"

Prefetch images in ReactNative

When a react native app boots and there is a known set of user/group avatar urls received from an API, it may be a better user experience to load them before hand.

React Native’s Image component has a prefetch method that will save the image into the device’s image cache:

import { Image } from "react-native";

const App = () => {
  const { users, loading } = useUserFetcher();

  users.forEach(u => Image.prefetch(u.avatarURL));

  return (
      <UserList users={users} loading={loading} />

RN fetch on Android requires mime type

ReactNative on Android requires a mime type when uploading files with FormData. But the only way to get the mime type of a user-chosen file is to read the file or files into memory after a user select one or more. In all of my use cases, my server didn’t care if the mimetype was in the FormData.

The easy solution is to just set the mimetype to a binary file type (e.g. application/octet-stream):

let data = new FormData();
data.append("file", {
  name: "cool.pdf",
  uri: "///files/cool.pdf",
  // MIME-type required on Android
  type: "application/octet-stream"

fetch("", data);

Ecto queries can be combined

Queries can take other queries to build on top of, which is very useful for conditional query logic:

def find_posts(group_id, title) do
  posts =
    from(posts in Phoenix.Post,
      where: posts.group_id == ^group_id,
    |> title_search(title)
    |> Phoenix.Repo.all()

defp title_search(query, title) do
    |> where([posts], posts.title == ^title)

defp title_search(query, nil) do

💅 styled-components limit prop names

💅 styled-components is one of my absolute favorite libraries! ❤️ I just have to be careful not use prop names for my components that could be interpreted as styles.

For example:

interface Props {
  position: "left-side" | "right-side";
  item: Item;

const ListItem= styled.View<{position: "left-side" | "right-side"}>(({position}) => ({
  backgroundColor: position === "left-side" ? "burlywood" : "skyblue";

const ListItemContainer: React.FC<Props> = ({position, item}) => {
  return (
    <ListItem position={position}>

<ListItemContainer item={item} position="left-side" />

This will return an error because in StyleSheet, position must be one of absolute or relative

How to manually edit ufw rules

Usually you don’t want to manually edit ufw, but in this case I just needed to update an ip address across multiple ip4 and ip6 rules. Turns out there is config file that can be very carefully edited:

$ sudo vim /lib/ufw/user.rules
$ sudo vim /lib/ufw/user6.rules

Very carefully update the ip addresses and then reload ufw:

$ sudo ufw reload

See git history of a renamed file

If you rename a file, git won’t show history of the previous name:

$ git log --pretty=oneline things/text.txt
8567d... Move file into things directory

However, the --follow flag will allow you to see the history of commits beyond the rename:

$ git log --follow --pretty=oneline things/text.txt
8567d... Move file into things directory
1458a... Fix something
0aac5... Add something


  Continue listing the history of a file beyond renames (works only for a single file).

How to typescript react native list refs

VirtualLists (i.e. SectionList, FlatList) have always been dificult for me to describe in typescript, but the following seems to work:

interface Props {
  list: MutableRefObject<SectionList<any> | undefined>;


import React, { MutableRefObject } from "react";
import { SectionList, View, TouchableOpacity, Text } from "react-native";

interface Props {
  listRef: MutableRefObject<SectionList<any> | undefined>;

const Button = (props: Props) => {
  return (
      onPress={() => {
          itemIndex: 0,
          sectionIndex: 0,
          animated: true
      <Text>Scroll Up</Text>

const Screen = () => {
  const listRef = useRef<SectionList<any>>();

  return (
      <SectionList ref={listRef} />
      <Button listRef={listRef} />

How to redirect standard error

2> Allows us to redirect standard error.

Taking advantage of rm’s ability to not delete files recursively can come in handy, especially when writing clean up scripts, but it can be noisy when you don’t care.

Give this file structure:


I may want to delete all the files, but not touch the directories (to keep file removal simple)

$ rm *
rm: cannot remove 'do-not-delete': Is a directory

$ ls

Because the directory error message comes over stderr, we can simply redirect it to /dev/null to ignore it:

rm * 2> /dev/null

How to only stage deleted files

If I delete a bunch of files and just want to stage the deleted ones, I can use xargs to add them:

$ git ls-files --deleted | xargs git add

Example output:

$ git status

  deleted: lib/to/deleted_1.txt
  deleted: lib/to/deleted_2.txt
  modified lib/that/changed_1.txt
  deleted: lib/to/deleted_3.txt
  deleted: lib/to/deleted_4.txt
  modified lib/that/changed_2.txt
  deleted: lib/to/deleted_5.txt
  modified lib/that/changed_3.txt
$ git ls-files --deleted | xargs git add
Changes to be committed:

  deleted: lib/to/deleted_1.txt
  deleted: lib/to/deleted_2.txt
  deleted: lib/to/deleted_3.txt
  deleted: lib/to/deleted_4.txt
  deleted: lib/to/deleted_5.txt

Changes not staged for commit:

  modified lib/that/changed_1.txt
  modified lib/that/changed_2.txt
  modified lib/that/changed_3.txt

How to change the placeholder text color

The TextInput component in react-native has a property of placeholderTextColor that allows you to configure the placeholder text color! However, react-native-web does not have this property and it must be done in css. So if you were using react-native-web you would need to include .css file:

// screen.tsx
import "./styles.css";
import { TextInput } from "react-native";

const Screen = () => (
    placeholder="enter some text"
*  styles.css
[data-testid="change-placeholder-color"]::-webkit-input-placeholder {
  color: skyblue;
[data-testid="change-placeholder-color"]::-moz-placeholder {
  color: skyblue;
[data-testid="change-placeholder-color"]::-ms-placeholder {
  color: skyblue;
[data-testid="change-placeholder-color"]::placeholder {
  color: skyblue;

Browsers have a Web Cryptography API

All major versions of browser implement a Web Cryptography API for obtaining random numbers:

const numbers = new Uint32Array(1);
// => [1627205277]

However, the methods are overridable, not read-only, and are vulnerable to polyfill attacks. This shouldn’t be used yet and is still being developed, but I found out the API exists.

How to remove unused deps from mix.lock

When you remove a dep from the mix.exs file it will remain in the mix.lock file. Unused deps need to be “unlocked” in order to be removed from the lock file.

For example:

mix deps.clean --unused

will only remove the unused dependencies’ files, but will keep the library in the mix.lock file. If you run the clean command the --unlock option, it will also remove it from the lock file.

For example:

mix deps.clean --unlock --unused

What a cursor is in postgres

SQL has a structure called a CURSOR that, according to the docs:

Rather than executing a whole query at once, read the query result a few rows at a time

This is mainly for solving memory usage issues, but probably not very applicable to web applications. Here’s an example syntax to periodically fetch a limited amount of rows:

declare posts_cursor cursor for select * from posts;
fetch 10 from posts_cursors;
fetch 10 from posts_cursors;

This is not advised due to leaving a transaction open, but a simple example. More powerful use cases would be iterating a query in a function for updating a small amount of records at a time.

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];