Today I Learned

A Hashrocket project

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.

Run side effect when a prop changes w/Hooks

There is a React Hook for side effects useEffect. You can pass useEffect a function and that function will run after each render.

useEffect(() => console.log('rendered!'));

In many cases it’s inefficient and unnecessary to call the effect function after every render. useEffect has a second argument of an array of values. If passing in this second argument, the effect function will only run when the values change.

useEffect(() => console.log('value changed!'), [props.isOpen]);

Now, you will see “value changed!” both on the first render and everytime isOpen changes.

Reminder: React Hooks are for functional components not class components. Check out the hooks api here

Get a ref to a dom element with react hooks

React Hooks are now available in React 16.8. There are 10 different hooks, you can read about them here. When I needed a ref to a dom element yesterday I reached for useRef.

const containerRef = useRef(null);

This isn’t a ref to anything unless you pass the ref to a tag.

return (<div ref={containerRef}></div>);

Now the ref will be assigned a dom element that you can use. In this example I’m using the useEffect hook to execute a side effect after the render takes place. Use the current attribute to access the current dom node.

useEffect(() => {
    containerRef.current.style = 'background-color: green;'
})

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

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.

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

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]

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]

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

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

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

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

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

Get the return type of a function in TypeScript

Sometimes you only want to rely on the return type of a function instead of defining a new type for it.

To do that in TypeScript use ReturnType and typeof:

function extractStatusData(buffer: Buffer) {
  return {
    active: buffer[0] !== 1,
    totalErrors: buffer[1]
  }
}

function onStatusChange(callback: (data: ReturnType<typeof extractStatusData>) => void) {
  listenToSomeBinaryStuff(buffer => {
    const statusData = extractStatusData(buffer)
    callback(statusData)
  });
}


onStatusChange(({active, totalErrors}) => {
  if (active) {
    console.log(`Task is still running`)
  } else {
    console.log(`Task is completed with ${totalErrors} errors`)
  }
})

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.

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>

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

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

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

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.

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

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'

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

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.

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

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.

Viewing your test coverage in Elixir

Curious about test coverage in your Elixir application? Mix.Tasks.Test comes with a handy --cover option.

$ mix test --cover
...
Generating cover results ...

Percentage | Module
-----------|--------------------------
   ...
   100.00% | TilexWeb.PixelController
    75.00% | Mix.Tasks.Deploy
    30.00% | TilexWeb
   ...
-----------|--------------------------
    68.33% | Total

By default it uses a wrapper around OTPs built in cover, however that’s configurable. If you wanted to use something like excoveralls you could:

def project() do
  [
    ...
    test_coverage: [tool: ExCoveralls]
    ...
  ]
end

Other niceties, like color based thresholds, can be found in the docs

The paste event in browsers

Browsers allow you to capture a paste event in a DOM element [1] [2].

This event fires before any clipboard data is inserted into the document, which makes it ideal for manipulating the data and pasting the manipulated data instead.

In Today I Learned by Hashrocket we recently utilized this feature to enable pasting images straight into the post editor. The image is then uploaded to imgur.com and the resulting URL is pasted as a Markdown Image Tag into the textbox.

image

If you are interested in adding similar functionality to your site check out this PR.

Also consider hosting your own fork of TIL.

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

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')

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.

Destructuring Record in Fn Argument

Elm has destructuring/pattern matching that feels typical for an ML. One pattern matching feature I like is record destructuring in a function argument.

myRecord = {a = 1}

myFunc {a} = a

myFunc myRecord
# 1

Here, we destructure the key a out of the record. What’s cool about this is the record does not need to match exactly, but can be any record with the property of a.

myRecord = {a = 1, b = 2}

myFunc {a} = a

myFunc myRecord
# 1

This enables us to be able to grow the record over time without changing the signature of the function. In Elm, not having to accomodate that change across the entire program is great.

Formatting Elm Code

Elm comes with it’s own formatter.

elm-format src/

It’s got options like --upgrade which will help you get from 0.18 code to 0.19 code, and its got --validate which you can use in continuous integration to ensure all PRs are properly formatted.

In vim, formatting is enabled by default when you use elm-vim.

Where is List.zip in Elm?

unzip is a function available as part of the list package.

List.unzip [(1, 2), (3, 4)]
-- ([1,3],[2,4])

It’s defined as:

Decompose a list of tuples into a tuple of lists.

But there is no corresponding zip function to compose a tuple of lists into a list of tuples. If you just want a list to be zipped with it’s index, then you can use List.indexedMap.

List.indexedMap (\x y -> (x, y)) ["a", "b", "c"]
-- [(0,"a"),(1,"b"),(2,"c")]

And you could substitute (\x y -> (x, y)) with Tuple.pair which does the same thing.

List.indexedMap Tuple.pair ["a", "b", "c"]
-- [(0,"a"),(1,"b"),(2,"c")]

And if you don’t care about indexes but instead have two lists, you can zip those two lists together with List.map2.

List.map2 Tuple.pair [1, 3, 5] ["a", "b", "c"]
-- [(1,"a"),(3,"b"),(5,"c")]

Happy Zipping!

Random is not pure in Elm

Elm requires that functions be pure, that is, the same arguments should produce the same outputs every time. Random necessarily injects some uncertainty into what the outputs that way and Elm has decided to handle random differently than in other languages.

First, install the random package:

elm install elm/random

The Random package allows you to create numbers in a couple of different ways, but the most idiomatic is to create a generator:

generator = (Random.int 1 10)

And then create a message that will let the Elm runtime know to produce a random number with the parameters defined by the generator.

type Msg = ConsumeRandomValue

msg = Random.generate ConsumeRandomValue generator

This message can then be placed into the (model, msg) tuple that is returned from the update function. The update function is then called to respond to the message, using the message type to wrap the random value that has been produced.

import Random

type Msg = ProduceRandomValue | ConsumeRandomValue Int

update msg model =
    case msg of
        ProduceRandomValue -> 
            (model, Random.generate ConsumeRandomValue (Random.int 1 10))
        ConsumeRandomValue randomValue ->
            ({model | rValue = randomValue}, Cmd.none)

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

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

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://..." },
    },
  ]
}

Decorator Factory vs Decorator

I’ve discovered decorator factories and they are cool!

Decorating a function in python is easy. Watch as I yell when a function gets called.

def yell(func):
    def yeller(*args):
        print("YARGH")
        func(*args)
    return yeller

@yell
def hi(name):
    print(f'hi {name}')

hi("Bob")
# YARGH
# hi Bob

What if I always wanted to say hi to Bob, and wanted to configure that via a decorator. Could I do that?

@yell("Bob")
def hi(name):
    print(f'hi {name}')

# TypeError: 'str' object is not callable

Instead of just passing an argument to a decorator, I need to create a function that will return a decorator, a decorator factory.

def yell(name):
    def decorate(func):
        def yeller():
            print("YARGH")
            func(name) 
        return yeller
    return decorate

@yell("Bob")
def hi(name):
    print(f'hi {name}')

hi()
# YARGH
# hi Bob

So this time, I created a function that returned a decorator which in turn returns a function that wraps the decorated function and calls the decorated function with the argument passed in to the decorator factory. Very russion doll. Fun.

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

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.

Define Your Refs with React ElementRef Flow Type

If you’re using flow in your React project, chances are that you are probably using refs. In this case, you’ll need to define your ref types.

Let’s say we have ref on a FlatList component from React Native:

import React from 'react'
import { FlatList } from 'react-native'
import data from './data.json'

class MyList extends React.Component {
  renderItem = () ={
    // rendering stuff
  }
  render() {
  return(
    <FlatList
      ref={l => (this.l = l)}
      data={data}
      renderItem={this.renderItem}
    />)
  }
}

Instead of being lazy and just using any, we’ll use the React.ElementRef flow type. It takes an additional typeof argument, which is the component type of your ref.

// @flow
import * as React from 'react'
import { FlatList } from 'react-native'
import data from './data.json'

class MyList extends React.Component {
  l: React.ElementRef<FlatList>
  renderItem = () ={
    // rendering stuff
  }
  render() {
  return(
    <FlatList
      ref={l => (this.l = l)}
      data={data}
      renderItem={this.renderItem}
    />)
  }
}

Flow docs - ref functions

Compare Dates in Neo4j.rb

Neo4j.rb stores dates as timestamps so you will have to convert your date object into a timestamp.

To convert a date object into a timestamp first convert to utc time and then to integer:

Date.current.to_time(:utc).to_i

And in your cypher query you are safe to use the comparison operators:

where('post.published_at <= ?', Date.current.to_time(:utc).to_i)

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

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