Today I Learned

A Hashrocket project

Output directories in Parcel v1 and Parcel v2

Parcel stated a nice piece of philosophy in their v2 README.

Instead of pulling all that configuration into Parcel, we make use of their own configuration systems.

This shows up in the difference in how output directories are handled in v1 and v2.

In v1, dist is the default output directory and can overridden with the -d flag for the build command, like this:

npx parcel build index.js
// writes to dist/index.js
npx parcel build index.js -d builds
// writes to builds/index.js

In v2, parcel reads the main key from your package.json file and uses that to configure the output path and file.

With the configuration:

// package.json
{
...
"main": "v2_builds/index.js"
...
}

parcel outputs to the specified location.

npx parcel build index.js
// writes to v2_builds/index.js

DNS Lookup with host

Today while doing some sleuthing, I learned about the host command. host “is a simple utility for performing DNS lookups.” It helped me connect a series of domains to their respective AWS EC2 servers, without a visit to the domain registrar.

Example:

$ host jakeworth.com
jakeworth.com has address 184.168.131.241
jakeworth.com mail is handled by 10 mailstore1.secureserver.net.
jakeworth.com mail is handled by 0 smtp.secureserver.net.

More info: man host

Parcel hot module reloading over ssh

A really great feature of parcel is hot module reloading, or rather, when you make a change to a file that change is manifested in the browser where you have your app open.

My parcel project was setup on a remote server that I had ssh’d into port forwarding the default parcel port, 1234, but in the console of Firefox I got this error:

Firefox can’t establish a connection to the server at ws://localhost:41393/. hmr-runtime.js:29:11

Turns out I needed to lock my hmr port to a fixed port and then port forward that hmr port over ssh.

Lock the hmr port when you start the parcel server like this:

parcel index.html --hmr-port=55555

And make sure you’re port forwarding that port over ssh:

ssh chris@myserver -L 1234:localhost:1234 -L 55555:localhost:55555

Now your app will establish a web socker connection to the parcel server!

:set backupcopy=yes to trigger fs events for node

While trying to get hot module reloading working with parcel today I noticed that sometimes parcel’s server didn’t register when a file was saved.

It turns out that vim has some weird behaviour around writing files that prevents node’s filesystem watcher from getting events that the file changed.

You can read about that weird behaviour here

You can get around this behaviour with:

:set backupcopy=yes

according to vim help backupcopy this will:

make a copy of the file and overwrite the original one

triggering the fs event.

Production mode tree shaking in webpack by default

I’ve been experimenting with noconfig webpack (version 4.43) recently and was pleased to see tree shaking is on by default.

If I have a module maths.js:

export const add = (a, b) => a + b;

export const subtract = (a, b) => a - b;

And in my index.js file I import only add:

import { add } from './maths'

Then when I run webpack in production mode with -p, choose to display the used exports with --display-used-exports and choose to display the provided exports with --display-provided-exports then I get an output for the maths module that indicates tree shaking is taking place:

$ npx webpack -p --display-used-exports --display-provided-exports
    | ./src/maths.js 78 bytes [built]
    |     [exports: add, subtract]
    |     [only some exports used: add]

The output [only some exports used: add] indicates that subtract has not been included in the final output.

Prefer lodash-es when using webpack

The lodash package needs to be able to support all browsers, it uses es5 modules. The lodash-es package uses es6 modules, allowing for it to be tree shaked by default in webpack v4.

This import declaration:

import {join} from 'lodash';

brings in the entire lodash file, all 70K.

This import declaration:

import {join} from 'lodash-es';

brings in just the join module, which is less than 1K.

With both lodash builds you can just import the function directly:

import join from 'lodash/join';

But when using multiple lodash functions in a file you may prefer the previous import declarations to get it down to one line:

import {chunk, join, sortBy} from 'lodash-es';

If you have these declarations throughout your app, consider aliasing lodash to lodash-es in your webpack config as a quick fix.

Count the number of objects in memory

Let’s say you’re having memory issues in your Ruby app and you want to get a feel for how many objects are instantied. You can use ObjectSpace!

require 'objspace'
ObjectSpace.each_object(Object).count
# in my typical rails app: 394683

Everything is a child of Object (for the most part), so you have about 394683 objects in memory! That’s a lot! You can narrow this down if you like to just String.

ObjectSpace.each_object(String).count
# in my typical rails app: 295261

But let’s check something more interesting, like TZInfo::CountryTimezone:

ObjectSpace.each_object(TZInfo::CountryTimezone).count
# in my typical rails app: 348

Neat!

Safe Navigation on nil gotcha

tldr; Beware, both ActiveSupport’s try and Ruby’s safe navigation operator &. will always return nil when operating on a nil.

For example, I would have expected either to return a response like so:

> nil.try(:to_s) => ""
> nil&.to_s => ""

However this is not the case:

> nil.try(:to_s) => nil
> nil&.to_s => nil

Get actual file size with du on Linux

You can use du, to report on the size of directories or files, but when my file is smaller than the block size I don’t see the output I expect.

With a small file, this should be the size of the number of characters.

$ echo 'Every Good Boy Deserves Fudge' > staff.txt
$ cat staff.txt | wc -c
30

But when I use du to examine file, I don’t see 30.

$ du -h staff.txt
4.0K    staff.txt

du measures in block sizes because in general if any part of a block is used, then for the purposes of the operating system the entire block is used.

You can tell du to care only about the size of the file with --apparent-size which is only apparent because between the beginning and end of the file the OS can’t tell which bytes are in use or are not in use.

$ du --apparent-size staff.txt
1       staff.txt

When reporting apparent size it rounds up to kilobytes, or --block-size=1k

To get the actual size of the file, you can use -b which is the same as --apparent-size --block-size=1

$ du -b staff.txt
30      staff.txt

UUIDs in Vim with Ruby/JavaScript 🆔

UUID’s (universally unique identifiers) are really handy. I use them for all sorts of things, like key props in a React map, and I like to have them easily accessible when writing code.

Today I wrote two Vim mappings to support this workflow. I can shell out to Ruby and use SecureRandom:

" Insert a UUID with Ruby
nnoremap ruid :read !ruby -e "require 'securerandom'; p SecureRandom.uuid"<cr>

Or, if you’re happier in the JavaScript ecosystem, here’s a similar command using Node and the uuid library. I did not have to install uuid, it was already available to me.

" Insert a UUID with JS
nnoremap juid :read !node -e "var uuid = require('uuid'); console.log(uuid.v4())"<cr>

Object construction with jq

jq is a powerful command-line tool to help you parse, analyze and script json output.

My current problem in jq is to turn this:

{
  "modules": [
  {
    "name": "x",
    "size": 10
  },
  {
    "name": "y",
    "size": 20
  }
  ]
}

into this:

{x: 10}
{y: 20}

This is possible using object construction:

jq '.modules[] | {(.name): .size}'

You pipe the result of the initial attribute as an array syntax .modules[] to an object {}. To use an attribute as a key you put parens around the attribute (.name) and declare that the value should be a different attribute .size.

Read more in the jq docs

Rails has an Array#exclude? method

In my vendetta against the unless expression in ruby, I came across a use case where I wanted to execute code only if an item was missing from an array.

I could easily do:

unless ['a', 'b', 'c'].include?('d')
  # do a thing
end

But I wanted to replace the unless with an if, which led me to wonder if there was an exclude? method for arrays in ruby.

Turns out, ActiveSupport extended the Enumerable class to introduce exactly what I was looking for!

if ['a', 'b', 'c'].exclude?('d')
  # do a thing
end

Hooray!

Scan local network for hosts with nmap

If you want to connect to a computer on your network but don’t know the ipaddress, you can use nmap to help you.

nmap -sn 192.168.1.0/24

The above command will conduct a “Ping Scan” on all addresses that start with 192.168.1.

192.168.1.0/24 is a subnet mask. It takes 24 bits to define the first 3 sections of an ip4 address, so that’s what the /24 is all about. Learn more about subnet masks here.

Change up to next underscore "_" in vim

ct_ will change up to the underscore and leave it be.

cf_ will change up to the underscore and eat it as well.

Alternatively, you can use set iskeyword-=_ which will make the “_” character a valid word boundary. This might be preferable if you, like me, tend to use ciw more often than just cw.

EDIT: Because iskeyword is how the syntax highlighting is managed, you’ll probably notice that for methods/functions that contain keywords the highlighting is strange after playing with this. I found that I like nnoremap <leader>e :set iskeyword-=_<cr>diw:set iskeyword+=_<cr>i as a way to allow me to more easily edit the words I want without messing with the highlighting.

Elixir Inspecting Big Structs and Lists

IO inspects limits your data by default in 50, so 50 Map keys and values, or 50 List items, etc. Sometimes you have a larger data, so you can play with this number by setting an option to the IO.inspect/2 function, or you can set it to :infinity. Use :infinity with moderation of course:

big_map = 1..100 |> Enum.map(&{ &1, "value #{&1}"}) |> Enum.into(%{})
IO.inspect(big_map)
IO.inspect(big_map, limit: 100)
IO.inspect(big_map, limit: :infinity)

Include vs. Includes 🤷‍♂️

A hallmark of switching back and forth between Ruby and JavaScript: I mistype the respective array inclusion function a lot. In JavaScript, it’s includes, and in Ruby, it’s include (with a question mark). Each language does not care what I meant.

The way I remember which function to use: in Ruby, I say: “does this array include this item?”. Then when I’m writing JavaScript, I remember I’m not writing Ruby. This technique is… lacking.

Chris Erin’s pneumonic is better: “JavaScript has an ‘s’ in it, so it includes. Ruby does not, so it is include.”

Heroku Psql Oneline 🐘

I cooked up this Heroku/subshell command today, and I like it:

$ psql $(heroku config:get DATABASE_URL)

This will connect me to the primary PostgreSQL database for my Heroku application in the psql client. Now I’m ready to query!

NOTE: why not $ heroku pg:psql? I’m not sure. I think Heroku was reporting status issues today, and I wanted to bypass any infrastructure I could.

Firefox Profiles for Work and Home 👤

I’m managing two Firefoxes on my laptop: one for work and one for after work. A feature that supports this well is Firefox profiles. Visit them in Firefox by typing about:profiles into the browser bar.

These work like Chrome People— separate sessions, histories, bookmarks, etc. I have styled the two browsers a little differently so I can quickly tell them apart.

I used to use Incognito/Private Windows for this setup, but those aren’t great for long-running browser sessions, because when you close or crash the browser, anything you’ve done in that browser is gone. Profiles give me the isolation I need with some persistence, too.

Rails Collection#exists?

I’ve been using Rails for almost a decade, and find I still don’t know even close to all it has to offer. Today, I was browsing through an open source app, and stumbled across this code:

unless feed.posts.exists?(@post.id)
  feed.posts << @post
end

I’d never seen or used exists? on a collection before, so I looked it up. And of course, there it is, right in the Rails Guides Association Basics - methods added by has many!

Digging in a bit, what arguments can exists? take? Here’s the reference.

Turns out there a lot of options!. Integer, string, array, hash, false, or no args.

Well that’s kinda neat! So what does the no args do?

Imgur

It seems to behave similar to any?, and return true if there are any members of the collection, false if it’s empty.

TLDR: Read the Rails Guides. A decade in, you’ll probably still learn something!

Convert HTTPoison Request into curl command

Today I learned how to convert a HTTPoison Request into a curl command, useful for logs. This might be useful to someone else, so I am sharing:

defmodule MyHTTP do
  def curl(%HTTPoison.Request{} = req) do
    headers = req.headers |> Enum.map(fn {k, v} -> "-H \"#{k}: #{v}\"" end) |> Enum.join(" ")
    body = req.body && req.body != "" && "-d '#{req.body}'" || nil
    params = URI.encode_query(req.params || [])
    url = [req.url, params] |> Enum.filter(& &1 != "") |> Enum.join("?")

    [
      "curl -v -X",
      to_string(req.method),
      headers,
      body,
      url,
      ";"
    ]
    |> Enum.filter(& &1)
    |> Enum.map(&String.trim/1)
    |> Enum.filter(& &1 != "")
    |> Enum.join(" ")
  end
end

And here’s an example of usage:

MyHTTP.curl(%HTTPoison.Request{
  url: "https://til.hashrocket.com",
  params: [q: "elixir"]
})

"curl -v -X get https://til.hashrocket.com?q=elixir ;"

Perform on Zoom 🎸

Planning to use Zoom for something other than a meeting, like a reading or musical performance, that would benefit from room noise and a softer audio experience? If so, read on!

Zoom has several default audio features in place to make your business calls sound great. We don’t want those features in place in a performance setting. Jettison them by enabling ‘original sound’ under advanced audio settings:

image

All you need to do from here is turn on your best (quality and/or position) microphone option. For me, that’s my display audio.

h/t Suzanne Erin

Git Checkout in Patches

Something I’ve learned pairing with Chris Erin is to checkout code you don’t want in patches. Here’s the command:

$ git checkout --patch

Like the git add --patch command many developers know, this puts you an interactive workflow where you can decide how to handle each chunk, or patch, of your changes. It works opposite of git add --patch, in that y means check out the patch (abandon the changes), and n means keep the changes.

This is a great flow to go through before trying to commit code; use it to get rid of debugger statements, pseudocode, unwanted autoformatter changes, etc.

Toggle Dark Reader 🌓

I really love the Dark Reader Browser plugin (h/t Chris Erin). It turns most websites into a dark mode that is really easy on my eyes. However, some sites just don’t look right in dark mode. Luckily, the plugin can be toggled on and off with ALT + SHIFT + D on Firefox and Chrome. I use it a couple of times a day on websites I encounter on the internet.

Bonus: Here’s an example of this plugin in action, that is very meta.

image

Dark Reader

Git 101

There are lot of tutorials about Git. One that is built right into Git is the ‘everyday help’. Print this help with:

$ git help everyday

The output is a series of must-know commands for a variety of roles in a technical organization: individual developer (standalone), individual developer (participant), integrators, and admins.

Pick a lane and go! 🏁

Run Git Commands From Any Dir

Git has one feature that I find irksome; you have to be in the directory with the Git repository to run commands against it.

…Or do you?

Git provides the -C flag for this scenario. Follow it with a directory name, and Git will execute your command as if it was run from <path>.

$ git -C ~/tilex status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   lib/tilex_web/templates/layout/app.html.eex

no changes added to commit (use "git add" and/or "git commit -a")

How to fight the dragon

Tonight I learned how to fight the dragon in #Minecraft.

Preparation

Nice to have:

  • armor with protection
  • bow with power
  • sword with sharpening
  • stength/health/regen potions
  • pumpkin head
  • snowballs

Must have:

  • golden apple, speed potion,
  • pickaxe, bow, arrows, sword & shield, armor
  • stack of dirt/cobblestone, buckets of water, ladders

I proceeded to The End. I picked a fight with a dragon and its pesky posse of ender men (pumpkin head helps). I learned to:

  • dodge the purple dragon breath
  • dodge or block dragon dives with the sheild
  • destroy crystals

    • with bow or snowballs for shorter towers
    • for taller towers, dump water at tower base, climb with ladder, then break crystal and fall into water
  • with a sword strike dragon’s feet when it sits on the pillar of bedrock
  • use looted ender pearls to teleport dodge

In The End, I died.

VSCode Select All Matches

I’ve been using VSCode for a while, and really love the shortcut CMD+Shift+D: if you have something selected, it will find and select the next match.

Imgur

Today I accidentally pressed CMD+Shift+L, and serendipitously found out that it “Selects All Occurrences of Find Match.” Now I can remove those pesky quotations marks in this Ruby hash all at once! As written, the hash keys will be turned into symbols anyway, so the quote marks are noise.

Imgur

Hat tip 🎩to Licecap for straightforward recording of gifs on any platform.

Default Hash values are super easy in Ruby

Want a list?

list_default = Hash.new([])

That’s pretty darn easy.

Edit: Thanks to @kaokun on twitter for catching this and correcting me. This will return the same array each time (having flashbacks to the first time I used mutable keyword args in python). To get a different array, you need need to pass a block with the behavior:

list_default = Hash.new { |hash, key| hash[key] = [] }

Just because your language hides pointers and references doesn’t mean you can ignore them I guess.

Tmux Clear History

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

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

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

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

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

Rounding Time in Increments

I Recently needed to round down a time in certain increments. This is not something I’ve ever been asked to do previously so it was an interesting problem to figure out. The resulting formula is simple:

TIME_MIN - (TIME_MIN % INCREMENT)

This will give you the previous incremental minute. ex: Given a 5 minute increment @ 3:12, the result would be 10.

time = Time.now
new_min = time.min - (time.min % 5)

I happen to be solving this in a project with ActiveSupport so there was access to Time#change so I finished this up with:

time.change(min: new_min)

AddEventListener doesn't duplicate named functions

When adding an event listener to a target in javascript, using the EventTarget.addEventListener function allows you to add a listener without clobbering any others that may be registered to that target. This is really nice and all, but if you have a script that may or may not be reloaded multiple times you can end up with this function getting called quite a bit. If you’re like me and you use anonymous functions like they’re going out of style, then you can end up attaching multiple handlers, because they aren’t recognized as the same function, eg

const div = document.createElement('div');
div.addEventListener('click', event => (fetch('mutate/thing'));

This could end up running the fetch the same number of times that the script is loaded. If instead you do:

function mutateMyThings(event) { fetch('mutate/thing') };
div.addEventListener('click', mutateMyThings);

Then you don’t have to worry about it, since adding a named function will automatically only add it once.

Proc Composition Operator in Ruby #functionalruby

Function composition is really nice, and having a clean way to do it is important. Ruby 2.6 introduced a really neat way of handling it with procs:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self << mult_self
add_and_mult[3] # 18

If you want the application to be reversed:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self >> mult_self
add_and_mult[3] # 36

This can be made into a way of building up pipelines for single argument procs by declaring them in an Array and using #inject with an identity proc:

calculations = [
->x { x / 3 },
->x { x + 5 },
->x { x * 2 }
]
pipeline = calculations.inject(->x {x}) do |built, func|
build << func
end
pipeline[3] # 8

This can be super cool for things like ActiveRecord objects where you want to chain certain operations without necessarily making them explicit methods via scopes or similar.

Print stacktrace in Elixir #ElixirLang

When you are trying to debug something it is usually helpful to see the stacktrace of what called a function.

If you’re inside of a try expression, and within the catch or rescue block, that’s easy - use the __STACKTRACE__ Special Form.

try do
    # NOPE
rescue
  ArgumentError ->
    IO.puts("Stacktrace #{inspect(__STACKTRACE__)}")
catch
  value ->
    IO.puts("Stacktrace #{inspect(__STACKTRACE__)}")
else
  # NOPE
after
  # NOPE
end

For all other cases the incantation is a little more complicated (and if you don’t think you can remember it, perhaps set up a snippet):

self()
|> Process.info(:current_stacktrace)
|> IO.inspect(label: "---------> stacktrace")

Change the CodeLens annotation color in CoC

CoC is the fanciest Language Server Protocol client for (neo)vim. It even implements Code Lens annotations, like reference counts in Javascript:

code lens reference counts

Ok neat. But by default, virtual text in neovim is in the normal highlight group. That means it looks just like code, and I’m already confused enough. After much source diving, I found that the highlight group used by CoC for this is CocCodeLens, and used like so:

:hi CocCodeLens guifg=White

code lens reference counts with better color

My code is much more readable.

Smooth animations with Math.sin()

Remember geometry class? Well, I don’t.

But I do know that when you’re making a synthesizer, a sine wave produces a nice smooth sound.

And as it turns out, a sine wave has a smooth curve. So, you can use Math.sin() to animate smooth motions like so:

const domNode = document.querySelector('#waveEmoji')
function animate() {
  const a = Math.sin(Date.now() / speed) * range + offset;
  domNode.style = `transform: rotate(${a}deg);`;
  requestAnimationFrame(animate);
}
animate();

Edit Sine Wave

Tmux Shortcuts I use

Create a new window

<tmux-leader>c

Think ‘c’ as in create. Rename window

<tmux-leader>,

That’s a comma. I don’t have anything clever for this one. Create a new pane

<tmux-leader>%

Maybe you’re clever enough to come up with something for this? I just memorized it. Kill a window ( like with a hung terminal )

<tmux-leader>&

You can also create a new session without calling new-session in full, you just use

tmux new -s <session-name>

Which is a bit quicker to type. Unless you like to type, you know, you do you.

Variable arguments and map in JS can hurt

Given some array of numbers as strings:

const someNumStrings = ['1', '2', '05', '68'];

If you want them to be numbers, then you might be tempted to do something like:

someNumStrings.map(parseInt)

Which would be fine if parseInt didn’t allow multiple arguments, and if map only sent in the single element. But that’s not how it works, so what you end up getting is a bit of a mess.

[1, NaN, 0, NaN]

The parseInt function takes a radix as the second argument (and realistically anything else you want to pass to it won’t cause it to explode). The Array.map method takes a callback (in this case parseInt) and gives that little sucker all the data you could want! In this case, the index is being passed as the radix, and parseInt doesn’t care about the others.

TL;DR: map(el => parseInt(el)) >>>>>>>>>> map(parseInt) and if you ever intentionally encode the radix as the index of the element you’re parsing… may god have mercy on your soul.

Vim Mark That Test! 🔖

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

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

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

Using zsh functions with xargs

I want to call a zsh function with xargs, but the arguments passed to xargs don’t run in your environment.

$ function hi() { echo "hello world $@" }
$ hi person!
hello world person!
$ seq 3 | xargs hi
xargs: hi: No such file or directory

No such file or directory!? hi is a function, but xargs doesn’t see it. With a combination of environment variables, function output and zsh command execution, we can use that function with xargs.

First let’s read the definition of our function into an environment variable.

$ FUNCS=$(functions hi); echo $FUNCS
hi () {
  echo "hello world $@"
}

Now we can use that in combination with zsh -c to execute the function with xargs.

$ FUNCS=$(functions hi); seq 3 | xargs -I{} zsh -c "eval $FUNCS; hi {}"
hello world 1
hello world 2
hello world 3

This solution is messy but workable.