Today I Learned

hashrocket A Hashrocket project

Importing Multiline Strings in Rails Config

Say I have a multiline string for an environment variable

MY_VAR="hi
bye"

If I try to import that in a Rails config yml file, I'm going to have a bad time.

config:
  my_value: <%= ENV.fetch("MY_VAR") %>

If this config file is autoloaded, rails is going to blow up on startup:

YAML syntax error occurred while parsing config.yml. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Error: (<unknown>): could not find expected ':' while scanning a simple key at line 15 column 1 (Psych::SyntaxError)

This is happening because the ERB output is not writing the line breaks in a way that Psych (ruby YAML parser) knows how to handle. We can use String#dump to return the quoted version of the string to make Psych happy.

"hi
bye".dump

=> "\"hi\\nbye\""

So the resulting config would look like so:

config:
  my_value: <%= ENV.fetch("MY_VAR").dump %>

And then our app can start and in console:

Rails.configuration.config.my_value

=> "hi\nbye"

Elixir's Info function

The elixir Kernel module has an interesting function i/1 that returns information about whatever you pass to it.

It will provide the argument's data type, byte size, raw representation, a description, and reference modules

i("hello world")

#=>
# Term
#  "hello world"
# Data type
#  BitString
# Byte size
#  11
# Description
#  This is a string: a UTF-8 encoded binary. It's printed surrounded by
#  "double quotes" because all UTF-8 encoded code points in it are printable.
# Raw representation
#  <<104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100>>
# Reference modules
#  String, :binary
# Implemented protocols
#  Collectable, IEx.Info, Inspect, List.Chars, String.Chars

Manually update Apollo Graphql Cache

Usually you structure your graphql queries and mutations in a way that the cache just works out of the box. But there are times that you need to manually update the graphql cache then you can call updateQuery:

const client = useApolloClient();
const preferences = {abc: "xyz"};

function updateCache() {
  client.cache.updateQuery({ query: MY_QUERY }, (data) => {
    if (!data.preferences) {
      return { ...data, preferences };
    }
  });
}

Note that the return is optional, so if your update function returns undefined then it will keep the cache intact, without updating anything.

Yield a double to a Block in RSpec

TIL in rspec you can yield a double to a block with and_yield, similar to how you return a double with and_return.

With and_return you can write a test like this:

sftp = Net::SFTP.start(args)
sftp.upload!(content, path)

# Test
client = double
allow(Net::SFTP).to receive(:start).and_return(client)
expect(client).to receive(:upload!)

However, if your code has a block like below and_return won't work. Instead, you can use and_yield to yield the double to the block:

Net::SFTP.start(args) do |sftp|
  sftp.upload!(content, path)
end

# Test
client = double
allow(Net::SFTP).to receive(:start).and_yield(client)
expect(client).to receive(:upload!)

Docs

How to debug in Elixir

If you want to debug an elixir code execution you can use the dbg/2 macro. I was already using that nice macro to show the results on each step of a pipe execution on the logs, but I learned that if we use iex to start phoenix server then the dbg/2 calls will act like adding a breakpoints so we can pry on it. We need to start the server with:

iex -S mix phx.server

As a side note this debugging worked on the elixir 1.14.3 version but I saw that on the latest versions there's a new option to be passed to iex --dbg pry in order to swap the dbg implementation from the fancy IO output to the IEx.pry one.

Execute (n)vim Commands on Startup

Today I Learned you can have (n)vim execute a command on startup.

For example - I often open vim and immediately run :Git to open vim-fugitive so I can easily navigate my modified and staged files. But that's 2 steps - 1 more than I want.

To achieve this, I can run nvim -c ":Git", where the -c is the command you want to execute on startup. For even fewer keystrokes you could create an alias!

Using slice_after to split arrays by a value

Given you have an array of objects that you may want to split apart based on a value on one of the objects, you can use slice_after (there's also slice_before, which behaves the same way).

array = [
  {activity: "traveling", ticket: "123"},
  {activity: "working", ticket: "123"},
  {activity: "awaiting_assignment", ticket: ""},
  {activity: "traveling", ticket: "234"},
  {activity: "refueling", ticket: "234"},
  {activity: "traveling", ticket: "234"},
  {activity: "working", ticket: "234"},
  {activity: "awaiting_assignment", ticket: ""}
]

array.slice_after { |i| i.activity == "awaiting_assignment" }
# Returns:
[
  [
    {activity: "traveling", ticket: "123"},
    {activity: "working", ticket: "123"},
    {activity: "awaiting_assignment", ticket: ""}
  ],
  [
    {activity: "traveling", ticket: "234"},
    {activity: "refueling", ticket: "234"},
    {activity: "traveling", ticket: "234"},
    {activity: "working", ticket: "234"},
    {activity: "awaiting_assignment", ticket: ""}
  ]
]

Elixir Data Type Comparison

Elixir has an interesting defined sorting order for its data types. The order is as follows:

number < atom < reference < function < port < pid < tuple < map < list < bitstring

For example:

random_data = [["Hello World"], {:hello, :world}, 16, %{key: "value"}, :atom]
Enum.sort(random_data)
#=> [16, :atom, {:hello, :world}, %{key: "value"}, ["Hello World"]]

Or even

4 < :atom
#=> true

Conditional link_to

In your Rails view, you can conditionally render links thanks to ActionView::Helper using #link_to_if

<%= link_to_if(current_user.nil?, "Login", login_path) %>

When this condition is true it would output:

<a href="/sessions/new/">Login</a>

When it is false it just outputs the text:

Login

If you want a custom rendering for false values, just pass that into a block:

<%= link_to_if(current_user.nil?, "Login", login_path) do
  link_to("Logout", logout_path)
end %>

NOTE: There is also an inverse link_to_unless method available

Here's the docs if you're interested.

Unix Timestamps in Elixir

Unix timestamps are a very simple way to compare times in an integer format. They are the total seconds elapsed since January 1st, 1970 UTC (The Unix Epoch).

In elixir, if you want to do some operations on a DateTime struct, and want to keep things simple, you can convert a DateTime struct to an integer with to_unix/2. By default, it will use seconds as the unit.

Skip auto updates for brew install/upgrade/tap

I think we've all been there. You're right in the middle of something, you need to brew install a formula, you go to install the formula, and then all of a sudden you're greeted with:

Running `brew update --auto-update`...

and so now you have to wait for all of your formulae to update, just so you can use the one that you need.

Well, if you're in a rush or on a slow internet connection, you can skip the auto update by setting the env var HOMEBREW_NO_AUTO_UPDATE to 1.

HOMEBREW_NO_AUTO_UPDATE=1 brew install <formula>

You might find it helpful to throw this into an alias so that you can brew install without updates when you're in a rush, without needing to remember the name of the env var.

From the official docs:

HOMEBREW_NO_AUTO_UPDATE:
If set, Homebrew will not auto-update before running brew install, brew upgrade or brew tap.

convice typescript your literally in the union

We want to pass a string prop that is defined as a union.

function Alert({
  level,
  children,
}: {
  level: "info" | "warn";
  children: ReactNode;
}) {
  const iconName = { info: "info", warn: "alert-triangle" }[level];
  return (
    <View>
      <Feather name={iconName} />
      {children}
    </View>
  );
}

info and alert-triangle are both in the union. Should be groovy, right?

❯ npx tsc
src/components/Alert.ts:666:69 - error TS2322: Type 'string' is not assignable to type '"key" | "type" | ... 267 more ... | undefined'.

Well shoot.

We need to convince typescript that this is no average string. Enter const assertions

const iconName = { info: "hashrocket" as const, warn: "alert-triangle" as const }[level];

And now we get a beautiful error:

❯ npx tsc
src/components/Alert.ts:666:69 - error TS2322: Type "hashrocket" | "alert-triangle" is not assignable to type '"key" | "type" | ... 267 more ... | undefined'.

Change the name back to "info" and we get no errors, with good reason this time. 😎

Command Line Wildcards

The * character can be used as a wildcard to match sequences of unknown characters in the command line.

For example, lets say my elixir project has a few tests that I want to run in a directory: MyProject/tests. The folder is filled with a bunch of random files, but the ones that i want to run have a similar name, tests/user_views_home and tests/user_views_show. We could use a wild card to match on both of these file names and run the tests (assuming there are no other files that match) like this:

mix test MyProject/tests/user_views*

Place screenshot straight to clipboard on Mac

Taking a screenshot is super helpful and there are many ways to do it. My go to on the Mac is the keyboard shortcut cmd+shift+4. This brings up a crosshair to select which part of the screen you want.

When you're done selecting you the image will be added to wherever you've configured screenshots to go. Normally the default is directly to your Desktop.

To add this screenshot directly to your pastebin you can do an alternate shortcut of cmd+ctrl+shift+4. Then you can just paste the image wherever as soon as you're done. This is awesome but is a hard keystroke for me. However I discovered that you can use the original cmd+shift+4 to activate the crosshair but it can be redirected to the pastebin if you hold ctrl before you click to finalize the screenshot'd section.

Hooray!

How to get javascript params in LiveView?

I found out that Phoenix LiveView has a function get_connect_params to be used when you want to get some value sent at the connection time from the javascript.

This way I can send params like:

// app.js
const params = {
  _csrf_token: csrfToken,
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
};

const liveSocket = new LiveSocket('/live', Socket, { params });

And get those like on my LiveView:

# my_live_view.ex
@impl true
def mount(_params, _session, socket) do
  tz = get_connect_params(socket)["timezone"] || "America/New_York"
  {:ok, assign(socket, :timezone, tz)}
end

ActiveRecord Query with `and`

ActiveRecord has an and method for querying.

Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
# SELECT "posts".* FROM "posts" WHERE "posts"."id" IN (1, 2) AND "posts"."id" IN (2, 3)

More likely you'd write that by chaining wheres:

Post.where(id: [1, 2]).where(id: [2, 3])

However, and really shines if you have more complicated querying with nesting of ANDs and ORs:

SELECT "posts".* 
FROM "posts" 
WHERE "posts"."id" IN (1, 2) 
  AND ("posts"."title" = 'title'
    OR "posts"."body" IS NOT NULL)

How would you write this in ActiveRecord? A first pass with where and or doesn't get us what we want:

Post.where(id: [1,2])
  .where(title: 'title')
  .or(Post.where.not(body: nil))

# SELECT "posts".* 
# FROM "posts" 
# WHERE ("posts"."id" IN (1, 2) 
# 	AND "posts"."title" = 'title' 
# 	OR "posts"."body" IS NOT NULL)

Instead of A AND (B OR C) we get A AND B OR C. We can use a well-placed and to get the right grouping of conditions:

Post.where(id: [1,2])
  .and(Post.where(title: 'title')
    .or(Post.where.not(body: nil)))

# SELECT "posts".*
# FROM "posts"
# WHERE "posts"."id" IN (1, 2)
#   AND ("posts"."title" = 'title'
#     OR "posts"."body" IS NOT NULL)

Docs

h/t Craig Hafer

Git checkout to previous branches

In Git you can check out previous branches using @{-N}, where N is how many "branches ago" you want to go back.

For example, say you were in main then checked out branch-1 then checked out branch-2, and finally you checked out branch-3.

If you wanted to checkout the branch you were on "2 branches ago" you would just checkout @{-2}

> git checkout @{-2}
Switched to branch 'branch-1'

If you are using this to just go back to your previous branch @{-1}, there is a nice shortcut:

> git checkout -
Switched to branch 'branch-2'

H/T Matt Polito

The Word Count Command

By using the wc command, you can print out word count information to the terminal. By default, if you use the wc command along with a file path, the command will return 3 values: the line count, word count, and character count of that file.

You can also pipe the wc command into any other terminal command to receive the word count information about the previous command. For instance, you could use the command ls | wc to see the word count info for the current directory's file list.

If you want the wc command to only output either the line count, word count, or char count, you can pass it the following flags: wc -l for line count, wc -w for word count, and wc -c for the char count.

Clone a Github Repository Wiki

A Github repository's wiki can be an extremely useful tool with amazing information. However not everyone is best at organizing information and I've had mixed results with Github search.

I knew that each repo wiki was versioned and figured it was git but it turns out you can also get access to that.

Just add .wiki to the end of your repo when cloning.

gh repo clone hashrocket/decent_exposure.wiki

Now you have all of the wiki files locally and can search however you feel.

Decomposing Nested Arrays

As you probably already know, in Ruby, you can decompose a nested array into variables like so:

letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
letters, numbers = letters_and_numbers

>> letters
=> ["a", "b", "c", "d", "e"]

>> numbers
=> [1, 2, 3, 4, 5]

However, did you also know that you can add parentheses () to decompose specific values from a nested array?

letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
(a, b, *other_letters), numbers = letters_and_numbers

>> a
=> "a"

>> b
=> "b"

>> other_letters
=> ["c", "d", "e"]

>> numbers
=> [1, 2, 3, 4, 5]

Note: You can also grab values from either the beginning or end of the array!

letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
(a, *other, d, e), _ = letters_and_numbers

>> a
=> "a"

>> other
=> ["b", "c"]

>> d
=> "d"

>> e
=> "e"

NPM script hooks

When writing script commands in your package.json you can have additional scripting before & after your script with a special incantation of script commands.

Say you have a test script

{
	"scripts": {
  	"test": "SOME TEST COMMAND"
  }
}

If you want to add a prerunner you just need to append the text pre to your command.

{
	"scripts": {
  	"pretest": "SOME COMMAND THAT HAPPENS BEFORE",
  	"test": "SOME TEST COMMAND"
  }
}

As you may have expected by now you'll do the same to get scripting after your command.

{
	"scripts": {
  	"pretest": "COMMAND THAT HAPPENS BEFORE",
  	"test": "TEST COMMAND",
    "posttest": "COMMAND THAY HAPPENS AFTER"
  }
}

This all happens via a naming scheme, so just remember pre & post.

More info in the documentation.

Debug a Jest test

Need to debug a test in Jest but can't figure out how? Or possibly you have a react-native app and you can't figure out how to debug a component?

Run jest via the node command so that the flag of --inspect-brk can be added.

node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand

From the docs:

The --runInBand cli option makes sure Jest runs the test in the same process rather than spawning processes for individual tests. Normally Jest parallelizes test runs across processes but it is hard to debug many processes at the same time.

Then open your chromium based browser (chrome, brave, etc...) and go to about:inspect. This will open the dev tools where you can select 'Open dedicated DevTools for Node'.

chrome dev tools

Then you'll see the node dev tools window open.

node dev tools

Now just enter a debugger whereever you need and run the jest command from above.

Import Github User Public SSH Keys

You can use this command to add keys to the current user authorized_keys file. This command works for public keyservers, but in my case, I used it for Github. Handy when setting up a new machine or adding a new user's keys to a system.

# Example - ssh-import-id gh:GITHUB_USERNAME

ssh-import-id gh:avogel3

If the PROTOCOL (gh above) portion is absent, it defaults to lp. Acceptable values are lp = launchpad and gh = github. You can also configure this command to handle a specific URL when not specifying a protocol.

https://manpages.ubuntu.com/manpages/xenial/man1/ssh-import-id.1.html

Add partial lookup prefixes in Rails

Utilizing partials in Rails can be super powerful, primarily when you use them in other templates. However, using a partial outside of its intended resource when it contains additional partials can give you a bad day.

# app/views/foos/_foo.html.erb

Foobity doo
<%= render "bar" %>
# app/views/dews/show.html.erb

Dewbie doo
<%= render "foos/foo" %>

After requesting the "dews/show" template, you'll probably find yourself with a disappointing error page saying the "bar" partial cannot be found.

Missing partial dews/_bar, application/_bar

As you can gather, partial lookup is trying to find the "bar" partial in the context it was called from. We declared the "foo" partial explicitly, so it did not error there. Since the "foo" partial calls the "bar" partial internally without being fully qualified, Rails is trying to find where it lives but cannot find it.

We can use a trick to help the template finder out. In your controller, we can define a local_prefixes method and add the prefix where the partial lives.

class DewsController < ApplicationController
  def self.local_prefixes
    super << "foos"
  end
  
  ...
end

Rails can now find the sub-partial once you've added "foos" to the lookup context.

Ruby Rightward Assignment -> JS-like Destructuring

It's not often there's a javascript feature I wish was available in ruby, but here we are. But, it turns out ruby has the functionality as of 2.7 and I was just out of the loop.

In javascript you can use destructuring assignment to unpack a bunch of variables in a single line:

const obj = { a: 1, b: 2, c: 3, d: 4 }
const { a, b, d: newName } = obj
console.log([a, b, newName]) 
// => [1, 2, 4]

With rightward assignment you can do a similar thing with hashes, though with slightly different syntax:

hsh = { a: 1, b: 2, c: 3, d: 4 }
hsh => { a:, b:, d: new_name }
puts [a, b, new_name]
# => [1, 2, 4]

Nice!

Excluding view templates for Spina resources

When creating a view_template in a Spina CMS Rails app there is an option of exclude_from that will take an array of strings. For any resources included in this array, that view template will not be available to pages of that resource.

Here is an example:

theme.view_templates = [
  {
    # The rest of your view template...
    exclude_from: ["main", "blog_resource"]
  }
]

Note: If you want to exclude templates from being used on main spina pages, you can just exclude from the implicit "main" resource.

Modifier Keys with Hot Corners

If you don't know what Hot Corners are, check out this post.

Say you want to utilize the Hot Corner for Lock Screen, but don't want to type in your password every time you accidentally hit the corner of your screen. Well, you can add modifier keys when selecting a hot key.

To do this, simply hold down your preferred modifier key (Command, Shift, Option, or Control , or a combination of these keys) while selecting a shortcut from the dropdown. Now whenever you move your pointer into the corner of the screen, your shortcut won't fire unless you're also holding down that modifier key.

MacOS Hot Corners

On macOS you can take advantage of Hot Corners, which are shortcuts activated by moving your pointer into a corner of the screen.

Currently, you can only select from a handful of options, such as Mission Control, Launchpad, Lock Screen, Quick Note and a few others.

To customize each of the four corners, on macOS Ventura 13, it can be found under System Settings->Desktop & Dock->Hot Corners

If you're on a different version, you can see exactly where the setting is located here.

Quote a SQL Value in Rails

If you saw my last post about Geocoding, you'll notice that the value passed to the geocode sql function is an address. To properly pass that value, we need to make sure that we quote it for SQL land.

❌ Bad

ActiveRecord::Base.connection.execute(<<~SQL)
  select
    rating
  from geocode('#{address}', 1)
SQL

Passing a mundane address like 100 O'Connel Ave will cause the above to throw an error in the database

But if we use the quote function from ActiveRecord, we properly quote our strings for SQL:

✅ Better

quoted_address = ActiveRecord::Base.connection.quote(address)

ActiveRecord::Base.connection.execute(<<~SQL)
  select
    rating
  from geocode(#{quoted_address}, 1)
SQL

Doing this ensures that we mitigate SQL Injection attacks and we properly account for things like single quotes in our values.

https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Quoting.html#method-i-quote

Geocode an Address in PostgreSQL with PostGIS

I recently learned that you can use PostGIS alongside the Tiger Geocoder extension to geocode an address in Postgres. This is especially handy if you have a specific locale (US or state level) that you need to Geocode. In my case, I need lat/long coordinates for addresses in Florida and Illinois.

Another reason I like this is because it is free - no need to pay for an additional service.

Here's what the API looks like:

select
  result.rating,
  ST_X(result.geomout) as longitude,
  ST_Y(result.geomout) as latitude
from geocode('320 1st St N, Jacksonville Beach FL', 1) as result;

 rating |     longitude      |      latitude
--------+--------------------+--------------------
      1 | -81.39021163774713 | 30.291481272126084
(1 row)

https://postgis.net/docs/manual-3.3/postgis_installation.html#loading_extras_tiger_geocoder

h/t Mary Lee

Gem pristine <gem_name> restores installed gems

If you have been directly working or debugging within your gems and wish to revert any changes made, instead of manually undoing all of the changes in each file, you can simply run gem pristine <gem_name>. This command restores installed gems to their original, pristine state using the files stored in the gem cache.

If you want to check out what options you can pass to it, here is some documentation.
H/T Matt Polito

Skip hidden fields on Phoenix inputs_for

I just found out that Phoenix Component inputs_for accepts a skip_hidden attr which was very useful today. We have a has_many relationship that we are using <.inputs_for> to decompose the nested fields and as it was generating some hidden fields the style was breaking.

We are actually using a Tailwind divide on the parent tag and those hidden fields were adding extra separation borders. A way to solve that was calling <.inputs_for twice as this:

<div>

  <.inputs_for field={@form[:posts]} />

  <div class="divide-y">
    <.inputs_for :let={form} field={@form[:posts]} skip_hidden>
      <.input type="text" field={form[:title]} label="Title" />
    </.inputs_for>
  </div>
</div>

On-disk size of a table/view in Postgres

In Postgres if you want to see how much disk space your relation (including data and any indexes) is taking, you can use pg_total_relation_size(<relation_name>)

SELECT pg_total_relation_size('<relation_name>') as size;

image

This can be used in conjunction with pg_size_pretty() to give a more readable output

SELECT pg_size_pretty(pg_total_relation_size('<relation_name>')) as size;

image

Use `is distinct from` to match `null` records

Let's say you want to find all purchases that don't match a specific coupon. You can use != to filter them:

select * from purchases where coupon != 'JULY4';

The problem with that is that it doesn't match records that have null values. One way to solve that is by doing a or:

select * from purchases where coupon != 'JULY4' or coupon is null;

Better than that is to use is distinct from:

select * from purchases where coupon is distinct from 'JULY4';

Delete A Rule In UFW

Say you want to delete a rule in your uncomplicated firewall (ufw). You run ufw help and see that the entry for delete is

delete RULE|NUM                 delete RULE

But when you check the status with sudo ufw status, all you see is this

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
443                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)

Where's the rule number?

What you need is sudo ufw status numbered, which has the rule number in the left hand column.

Status: active

     To                         Action      From
     --                         ------      ----
[ 1] OpenSSH                    ALLOW IN    Anywhere
[ 2] 443                        ALLOW IN    Anywhere
[ 3] OpenSSH (v6)               ALLOW IN    Anywhere (v6)
[ 4] 443 (v6)                   ALLOW IN    Anywhere (v6)

So if you want to delete your OpenSSH rule for IPv6, you know to run sudo ufw delete 3.

Rails config hosts accepts regular expressions

Imagine you're running your Rails server on an ngrok instance. If you want to access your ngrok instance, you're going to need to add your specific ngrok subdomain to your config hosts.

If you ever restart your ngrok instance and get a new subdomain, you now have to change your config hosts again to match the new one.

Well, not anymore! Because now that you know you can use regular expressions, you can just do this:

Rails.application.config.hosts << /.*\.ngrok\.app/

Technically, if your use-case is just for subdomains, you can just do this:

Rails.application.config.hosts << ".ngrok.app"

But it's still nice to know you can utilize regular expressions if needed!

Macro Guard Clauses in Elixir

Have you ever wanted to pass your own custom function into a guard clause?

Lets start by looking at a super basic guard clause here:

def greater_than_10(num) when num > 10, do: true
def greater_than_10(_), do: false

Let's say we want to get more specific with the operation inside of the when guard. This is overkill for our situation, but lets say we want the when to use a custom function of ours instead of a basic operation, but when guards don't allow you to directly pass functions to them.

Elixir has a fancy macro called defguard that allows you to define custom guards like functions. Let's change our function up and define a guard that checks if our argument is both a integer and even.

defmodule Guards do
  defguard is_even_num(num) when is_integer(value) and rem(value, 2) == 0
end

Let's use that guard in our function

import Guards

def even_num_greater_than_10(num) when is_even_num(num) do
  case num > 10 do
    true -> true
    _ -> false
  end
end

Even though this example is a bit overkill, the option to use custom guards is a cool elixir feature.