Today I Learned

A Hashrocket project

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.

Multiline matches with ripgrep (rg)

Ripgrep has become the default file search tool in my development environment. It’s fast! It can also do multiline searches if given the correct set of flags.

First, let me introduce you to the dataset:

$ echo 'apple\norange\nbanana\nkiwi'
apple
orange
banana
kiwi

So what if I want all the lines from orange to kiwi?

$ echo 'apple\norange\nbanana\nkiwi' | rg 'orange.*kiwi'

This finds nothing! Never fear, there is a --multiline flag.

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline 'orange.*kiwi'

This also finds nothing! The problem is that . does not match \n in regex. You can change that behaviour however by using the dot all modifier which looks like (?s).

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline '(?s)orange.*kiwi'
orange
banana
kiwi

We did it! Alternately, you can use the --multiline-dotall flag to allow . to match \n.

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline --multiline-dotall 'orange.*kiwi'
orange
banana
kiwi

I prefer short incantations however, and we can shorten it by using -U instead of --multiline.

$ echo 'apple\norange\nbanana\nkiwi' | rg -U '(?s)orange.*kiwi'
orange
banana
kiwi

Access a shadowDOM node from Capybara

Using selenium_headless you can access a node from the shadowDOM using evaluate_script. The trick is knowing where in the showdowRoot.childNodes it’s going to be. If you have a web component called my-dope-component then you could do something like this:

shadow_host = find('my-dope-component')
shadow_child = evaluate_script('arguments[0].shadowRoot.childNodes[<magic-index>]', shadow_host)

The value for <magic-index> depends on the structure of the template that you’re using, as well as the library (if any) that you’re using. You could also write a find style script that iterates the children and locates the correct one based on the tagName property or similar.

For example:

FIND_NODE = <<-SCRIPT
Array.from(arguments[0].shadowRoot.childNodes).map(element => {
  if (element.tagName === arguments[1].toUpperCase()) return element;
}).filter( value => value != undefined)[0]
SCRIPT

Git Diff Matches Paths 📁

Today I learned that the git-diff command accepts a <path> argument. I used this knowledge to construct a command that runs only the RSpec test files on my branch that differ from master.

(other-branch)$ git diff master --name-only spec | xargs rspec

This is handy on a big test suite; when you can’t run every test, make sure you run the ones that you have changed.

See man git-diff for more info.

Turn on coordinates in Minecraft Realm

In a Minecraft Realm the option to turn on the coordinate system is not available without turning on cheats. That is not good because then you’ll lose achievements.

Many tutorials recommend downloading the realm locally, flipping the switch for coordinates, and then uploading that copy of your world back to the realm. Turns out that some server commands will work in the Realm… coordinates being one of them!

Enter this into your chat window to enable coordinates:

/gamerule showcoordinates true

You can, of course, turn them back off via:

/gamerule showcoordinates false

Auth with gh on remote machine

gh is a new github cli in the alpha phase right now hosted at https://github.com/cli/cli. It’s auth right now is through the browser which doesn’t work great on a remote machine.

When you try to do something with gh that should connect to github you’ll get a bunch of errors and this:

$ gh pr create
Please open the following URL manually:
https://github.com/login/oauth/authorize?client_id=XXXXXXXXXXXXXXXXXX&redirect_uri=http%3A%2F%2Flocalhost%3A34869%2Fcallback&scope=repo&state=XXXXXXXXXXXXXXXXXXXX

This is running a server in the background that is waiting for a request, don’t kill the process until the authentication is complete.

You can copy that url to your browser, authenticate with github and then you are redirected to a localhost url where the server is hosted

Copy the localhost url that you were redirected to, open a new window and then curl the localhost url.

$ curl http://localhost:34869/callback&scope=repo&state=XXXXXXXXXXXXXXXXXXXX

Now you should be authenticated and the gh process should continue.

Search in dotfiles with ripgrep

ripgrep is a very fast searching file system searching utility written in Rust. General usage is like this:

rg something directory-name

This is great, but when searching in my dotfiles for a configuration I don’t find what I’m looking for. Any file whose filename starts with . is considered hidden and these files are not searched by default with ripgrep.

To search hidden files (or dot files) use the --hidden flag.

rg --hidden "alias git" ~

Now, if there is a configuration that is overriding git in my dot files, I’ll find it!

Mac Notes Default Text Size 🔎

I use Mac Notes a lot during pairing and brainstorming. It works, and it’s right there on the Mac!

One issue I’ve had is that the default text size on Mac Notes is unreadably small on a big monitor or iMac. I always end up creating a note, then bumping the text size with CMD-SHIFT ..

Of course, there’s a better way. Set the default font size in preferences.

image

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 "https://exp.host/--/api/v2/push/send" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title":"New Message",
  "body": "Are you there?",
  "channelId": "chat-messages",
  "priority": "max"
}'

Byline Class and Time Tag 📰

I’ve been hacking on TIL to optimize our site for Safari Reader mode. A lot of this is black box, because Apple doesn’t have easy-to-find documentation about how the feature works.

One thing that seems to matter is a class called byline for your author information, and an HTML tag time for the publication date. Here’s an example, borrowed from this site:

<p class="byline">
  <%= name %>
  <time datetime="<%= inserted_at %>">
    <%= inserted_at %>
  </time>
</p>

It gives us relevant reader information, shown here in Safari:

image

Here’s the PR:

https://github.com/hashrocket/tilex/pull/650

Ergodox vim input for numpad keys can be wonky

I use the ergodox ez keyboard. When you’re setting it up you can select either the numpad value of a key, or the shifted/regular value. These will behave slightly differently depending on your settings for application keypad mode. This is something that is set at the application layer, so you may end up dealing with vim recieing <esc>Ok if you are trying to type +. You can either try to find the way to get your application (in my case Manjaro’s terminal emulator) to change the keypad mode (I couldn’t find that), or you can setup an innoremap section in your vimrc to cover the cases where you definitely don’t want the escape sequence.

:inoremap <Esc>Oq 1
:inoremap <Esc>Or 2
:inoremap <Esc>Os 3
:inoremap <Esc>Ot 4
:inoremap <Esc>Ou 5
:inoremap <Esc>Ov 6
:inoremap <Esc>Ow 7
:inoremap <Esc>Ox 8
:inoremap <Esc>Oy 9
:inoremap <Esc>Op 0
:inoremap <Esc>On .
:inoremap <Esc>OQ /
:inoremap <Esc>OR *
:inoremap <Esc>Ol +
:inoremap <Esc>OS -
:inoremap <Esc>OM <Enter>

This sequence is for PuTTY, but you can see which key to put after the <Esc>O by looking at what registers on keydown events. Alternatively, for ergodox users, only use the shifted/regular values for keys.

Meta Charset Top That! 🔝

When writing HTML, make sure that your meta charset tag is in the first 1024 bytes on the page.

Per MDN:

The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML as some browsers only look at those bytes before choosing an encoding.

What does this mean practically? Keep this tag near the top of your document, and use a website validator like https://validator.w3.org to check your tag placement.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta

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 (
    <View>
      <UserList users={users} loading={loading} />
    </View>
  );
};

Type "text/javascript" is Redundant

Have a script tag in your HTML annotated with <script type="text/javascript">? Per the HTML5 specification, the MIME type of a script tag, when omitted, is text/javascript. We are encouraged to omit it.

Delete that code and go green 💚.

Per MDN:

Omitted or a JavaScript MIME type: This indicates the script is JavaScript. The HTML5 specification urges authors to omit the attribute rather than provide a redundant MIME type. In earlier browsers, this identified the scripting language of the embedded or imported (via the src attribute) code. JavaScript MIME types are listed in the specification.

Source:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

Swap between STI classes in Rails

Rails allows you to swap between single table inheritance classes with the becomes method. This method creates a new instance of the desired class, and passes the attributes from the original record to the new instance.

So if I have two classes, User and Admin, where Admin inherits from User:

class User
  # ...
end

class Admin < User
  # ...
end

I can change between the two classes using becomes:

user = User.first # returns an instance of the User class
user.class # => User
user.id # => 1
user.name # => Joe Hashrocket

admin = user.becomes(Admin) # returns an instance of the Admin class
admin.class # => Admin
admin.id # => 1
admin.name # => Joe Hashrocket