diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Make macOS remember your SSH keys after restart

We need to tell macOS to add the keys to the agent, in order to make them persistent after reboot.

We have to create new file in ~/.ssh/ called config

vi ~/.ssh/config

with the following content:

Host *
   AddKeysToAgent yes
   UseKeychain yes   

Then, we need to add our keys to the agent and macOS keychain (so your private key password is remembered).

ssh-add -D
ssh-add -K ~/.ssh/*

To verify that our keys are present in the agent:

ssh-add -l

LocalStorage event not triggering

Whenever a value is set in localStorage, a storage event will be dispatched on all other windows from the same origin. The event is not fired or catchable in the originator window when it is modified through a script.

This was a problem because I wanted to be able to have an Elm subscription as the only source of truth for my session.

In order to achieve this, I'm mimicking the storage event from within the command port as soon as the message is received through the port.

app.ports.commandPort.subscribe(session => {
  localStorage.session = JSON.stringify(session);
  app.ports.subscriptionPort.send(localStorage.session);
});
window.addEventListener(
    "storage",
    event => {
      if (event.storageArea === localStorage && event.key === "session") {
        app.ports.subscriptionPort.send(event.newValue);
      }
    },
    false
);

And in Elm:

port subscriptionPort : (Encode.Value -> msg) -> Sub msg
port commandPort : Maybe Encode.Value -> Cmd msg

Lists and Recursion in Elixir - Flatten a list

While reading Dave Thomas's book, Programming Elixir, I stumbled upon an exercise.

You're supposed to write a flatten(list) function that takes a list as a parameter. That list may contain any number of sublists, which themselves may contain sublists ... Your function should return all the elements of these lists in a single flattened list, all the while preserving the order of the elements.

In the beginning, it sounded a bit hard (especially since he mentioned in the book that the exercise it's hard), but after enough playing with the idea, I got to a fairly simple solution, simpler than what I thought it would be and the solutions I've found so far on the internet.

The result should look something like this:

iex> MyList.flatten([ 1, [ 2, 3, [4] ], 5, [[[6]]]])
[1,2,3,4,5,6]

Now the function:

defmodule MyList do
  def flatten([]), do: []
  def flatten([head | tail]) when is_list(head) do
	  flatten(flatten(head) ++ tail)
  end
  def flatten([head | tail]), do: [head | flatten(tail)]
end

At first I thought it wouldn't work, but tested it and got surprised.

The flatten function takes care of 3 cases:

  • the given list is empty
  • the head of the list is in itself a list, in which case it should be flattened
  • the head is not a list, in which case we continue with flattening the tail

Parsing a date string into a date sigil in Elixir

What I was trying to achieve, was to get a person's date of birth from a date_input and calculate the age of that particular person. The problem was that the input was giving back a string like "yyyy-mm-dd" with the date, while I needed a date sigil of the form ~D[yyyy-mm-dd] to use with the diff/2 function.

IO.inspect(date_of_birth) "1978-06-11"

So I learned that you can use the from_iso8601!/1 function to parse the date from a string to an Elixir Date sigil.

IO.inspect(Date.from_iso8601!(date_of_birth)) ~D[1978-06-11]
def calculate_age(date_of_birth) do
	date_of_birth
		|> Date.from_iso8601!()
		|> calculate_date_diff()
		|> div(365)
end
defp calculate_date_diff(date) do
	Date.diff(Date.utc_today(), date)
end

Why the private calculate_date_diff() function? Because the Date.diff() takes the oldest date as its second parameter, while using the Elixir pipeline adds the previous functions's result as a first parameter to the next function.

Upgrade your SSH Keys to the new Ed25519 standard

Why should you upgrade?

Your ssh key is most probably half a decade or more old, as with all technlologies, cryptographic algorithms evolve and all of them become less secure with time as vulnerabilites are discovered, or computing power increases. Good SysOps and DevOps often rotate their keys and you should too!

See all the benefits of Ed25519 here: https://ed25519.cr.yp.to

You need to upgrade right now if:

  • your key was generated using DSA you need to upgrade right now
  • your key was generated using RSA less than 3072bit length
  • your key was generated using ECDSA

Ed25519 is the public-key algorithm you should use today.

How to generate your key:

I like to have custom names for my keys, and I also add relevant information to key comments like: role, name and e-mail. The -o 100 option, increases the brute force resistance of your key by increasing the KDF rounds.

ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/zeno.popovici.ed25519 -C "Graffino Member :: Zeno Popovici (zeno@graffino.com)"

Don't forget to provide a strong password for your key.

Deploy (macOS)

You can now deploy your key. First, you need to add it to your keychain like this:

ssh-add -K ~/.ssh/zeno.popovici.ed25519

On macOS, to copy your public key to the clipboard and paste it into GitHub or other services you're using, just issue:

pbcopy < ~/.ssh/zeno.popovici.ed25519.pub

That's it!

How to fix a render-blocking video on Safari

This is related to the MOOV atom that contains all the metadata and information about the frame locations other important information the browser needs to know about the video. For some reason, all other major browsers load it with no issues but Safari prefers to wait until the whole resource has downloaded in order to decide what to do with it.

For mp4 files, downloading FFMPEG and running the following command solved my issue:

./ffmpeg -y -i source.mp4 -movflags faststart dest.mp4

What it does is that it takes the MOOV atom from the end and adds it right at the start.

Extend Ecto's query API with your own macros

Imagine for a second this is something you're using a lot when programming Elixir and Phoenix:

Repo.one(
	from u in User,
		where: fragment("lower(?)", u.email) == ^email
)

You can write your own Elixir macro to improve your flow.

defmacro lower(arg) do
	quote do: fragment("lower(?)", unquote(arg))
end

Then your query can be rewritten like this:

Repo.one(
	from u in User,
		where: lower(u.email) == ^email
)

How to change JPEG compression rate in WordPress

Our client wants pixel perfect images in his WordPress site. Truth is WordPress is a bit agressive in compressing JPEGs.

WordPress default is 75% compression quality. A higher setting will generate better looking images, to the expense of larger filesize.

Add this to your functions.php file:

// Change JPEG compression rate - 85 is much more reasonable setting
// You can also disable it by settign it to 100
$jpeg_compression = function() { 
	return 85; 
};

add_filter( 'jpeg_quality', $jpeg_compression );

The importance of clear namespaced HTML attributes

Here's another reminder why expressive, namespaced ids are important. This time is about a label's for attribute.

<input id="phone">
<label for="phone"></label

This would work just as expected unless, for example, we have a svg sprite at the beginning of the document that includes an svg with a "phone" id.

<symbol id="phone" viewBox="0 0 61.4 48">...</symbol>
...
<input id="phone">
<label for="phone"></label

In this case, the label's for will match the first in a top-down order that has the target id.

And you will never know why clicking the label won't trigger the input.