Today I Learned

Using destructuring assignment to dynamically create new functions in JavaScript

// www.codewars.com/kata/525f3eda17c7cd9f9e000b39

const curry = (operand, operator) => (!operator) ? operand : operator(operand);

const [zero, one, two, three, four, five, six, seven, eight, nine] =
  [0,1,2,3,4,5,6,7,8,9].map(operand => ((operator) => curry(operand, operator)));

const plus = (first) => (second) => second + first;
const minus = (first) => (second) => second - first;
const times = (first) => (second) => second * first;
const dividedBy = (first) => (second) => Math.floor(second / first);

vue-cli-service environment variables

While you can find detailed information in the official docs, I dare to spoil the fun for you:

1. All app variables are pulled in if they are prefixed with VUE_APP_.

Example: VUE_APP_API_URL.

You can now access the variables in your application like this: process.env.APP_URL.

2. All e2e variables are fetched if they are prefixed withCYPRESS_.

Example: CYPRESS_APP_URL.

You can now access the variables in your tests like this: Cypress.env('APP_URL').

Make LiveView template variables readable

If you ever seen this in the wild:

{:ok, assign(socket,
      %{my_variable: @my_variable
        my_other_variable: nil.
        yet_another_variable: nil 
      })}

Remember that applying elixir's pipe operator makes everything better:

def mount(_session, socket) do
    socket =
      socket
      |> assign(:my_variable, @my_variable)
      |> assign(:my_other_variable, nil)
      |> assign(:yet_another_variable, nil)
    {:ok, socket}
end

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

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
)