Today I Learned

A Hashrocket project

126 posts by viniciusnegrisolo @vfnegrisolo

Ruby yield as keyword args default

Today I learned that you can use yield as a default value for a keyword arg.

def foo(bar: yield)
  "Received => #{bar}"
end

Then you can call this method using the keyword syntax:

foo(bar: "Hello world!")
#=> "Received => Hello world!"

or by using the block syntax:

foo do
  "Hello world!"
end
#=> "Received => Hello world!"

I am not sure why I’d use such flexible syntax for a single method, but we have to know what’s possibly in Ruby. Anyway, just a sanity check here, is the block evaluated if we pass the arg?:

foo(bar: "Hello") do
  puts "Block was evaluated!!!"
  "world!"
end
#=> "Received => Hello"

Cool, so ruby does not evaluate the block if this keyword is passed into, so we are cool.

Use assigned variables on its own assign statement

Javascript allows you to use an assigned variables into the same assignment statement. This is a non expected behavior for me as the first languages I’ve learned were compiled languages and that would fail compilation, at least on the ones I’ve experienced with.

Here’s an example:

Javascript ES2015:

const [hello, world] = [
  () => `Hello ${world()}`,
  () => "World",
]
hello()
//=> "Hello World"

In this example I’m assigning the second function into the world variable by using destructuring assignment and I’m using this by calling world() in the first function.

Ruby:

To my surprise ruby allows the same behavior:

hello, world = [
  lambda { "Hello #{world.call}" },
  lambda { "World" },
]
hello.call
#=> "Hello World"

Elixir:

Well, Elixir will act as I previously expected, so assigned variables are allowed to be used only after the statement that creates them.

[hello, world] = [
  fn -> "Hello #{world.()}" end,
  fn -> "World" end,
]
hello.()
#=> ERROR** (CompileError) iex:2: undefined function world/0

I won’t start using this approach, but it’s important to know that’s possible.

PostgreSQL index with NO lock on Rails

Rails allow us to create a PostgreSQL index that it would not lock the table for writing meanwhile it’s being calculated.

That’s usually handful if a table has millions of rows and this operation could take hours to release the INSERT/UPDATE/DELETE lock. Maybe the lock downtime is critical to the application.

There are caveats to this approach so please read the PostgreSQL documentation.

In order to do that you’ll need to add { algorithm: :concurrently } to the add_index on a Rails migration. Additionally PostgreSQL uses DB transactions to manage this new index creation so we need to disable the Rails migration transaction. Take this example:

class AddIndexOnEventsName < ActiveRecord::Migration[5.0]
  disable_ddl_transaction!

  def change
    add_index(:events, :name, algorithm: :concurrently)
  end
end

In this case I’m using disable_ddl_transaction! to disable the DB transaction that wraps each Rails migrations and I am using algorithm: :concurrently.

This option it will generate an index like that:

CREATE INDEX CONCURRENTLY "index_events_on_name" ON "events" ("name"));

Rails will change the `not` behavior

Yay Rails will change the behavior of the ActiveRecord#not on rails 6.1. That’s great as the current behavior sometimes leads to confusion. There will be a deprecation message on the version 6.0 so let’s watch out and change our code accordingly.

As I already wrote on this TIL this function does not act as a legit boolean algebra negation. Let’s say that we have this query:

User.where.not(active: true, admin: true).to_sql

Prior to this change this will produce this query:

SELECT users.*
  FROM users
 WHERE users.active != 't'
   AND users.admin != 't'

The problem is that negating an AND clause is naturally a NAND operator, but Rails have implemented as a NOR. Some developers might wonder why regular active users are not returning.

On Rails 6.1 this will be fixed hence the new query will be:

SELECT users.*
  FROM users
 WHERE users.active != 't'
    OR users.admin != 't'

Here’s the rollout plan:

  • Rails 5.2.3 acts as NOR
  • Rails 6.0.0 acts as NOR with a deprecation message
  • Rails 6.1.0 act as NAND

This is the PR in case you want to know more.

Javascript arguments on ES2015 Arrow functions

Javascript function arguments can be accessed by functions defined using the function keyword such as:

function logArgsES5 () {
  console.log(arguments)
}
logArgsES5('foo', 'bar')
// => Arguments(2) ["foo", "bar"]

But ES2015 Arrow functions does not bind this variable, so if you try this you will see an error:

let logArgsES2015 = () => {
  console.log(arguments)
}
logArgsES2015('foo', 'bar')
// => Uncaught ReferenceError: arguments is not defined

So if we want to have similar variable we can add an ...arguments as the function argument:

let logArgsES2015 = (...arguments) => {
  console.log(arguments)
}
logArgsES2015('foo', 'bar')
// => Array(2) ["foo", "bar"]

React Testing Library => within nested queries

Wow, React Testing Library has a within helper to get nested searches on the dom. Check this out with this example:

const DATA = {
  movies: ["The Godfather", "Pulp Fiction"],
  tv: ["Friends", "Game of Thrones"],
};

const MyContainer = () => (
  <>
    {Object.keys(DATA).map(category => (
      <MyCategory name={category} list={DATA[category]} />
    ))}
  </>
);

const MyCategory = ({name, list}) => (
  <ul data-testid={name} role="list">
    {list.map(item => <li role="listitem">{item}</li>)}
  </ul>
);

Then let’s say if we want to assert the list of movies that this MyContainer renders and in the same order as it’s been rendered we can:

...
import { render, within } from "@testing-library/react";
import MyContainer from "../MyContainer";

describe("MyContainer", () => {
  it("tests movies", () => {
    const { getByTestId, getAllByRole } = render(<MyContainer />);

    const moviesCategory = getByTestId("movies");
    const movies = within(moviesCategory).getAllByRole("listitem");

    expect(items.length).toBe(2);
    expect(items[0]).toHaveTextContent("The Godfather");
    expect(items[1]).toHaveTextContent("Pulp Fiction");
  });
});

Real code is way more complex than this example, so this within helper function turns to be very convenient.

Proposing new time on Google Calendar invitations

Today I learned that we can respond to an invitation using google calendar proposing a new time. Check this out:

First when we open an invitation on gmail I can hit the link more options:

image

This will open the event on google calendar. Then we can expand more options with the caret down and hit Propose a new time.

image

Finally choose a time and send it back to the event owner. And that’s how they will receive our request to change the event time:

image

Change PostgreSQL psql prompt colors

Today I learned how to change psql prompt to add some color and manipulate which info to show:

$ psql postgres
postgres=# \set PROMPT1 '%[%033[1;32m%]@%/ => %[%033[0m%]%'
@postgres => \l
                                           List of databases
            Name            |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
----------------------------+------------+----------+-------------+-------------+-----------------------
 postgres                   | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0                  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                            |            |          |             |             | postgres=CTc/postgres
 template1                  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                            |            |          |             |             | postgres=CTc/postgres
(3 rows)

image

Check this documentation if you want to know more.

Make dry run

make has an great option to validate the commands you want to run without executing them. For that just use --dry-run.

#!make
PHONY: test
test:
    echo "running something"

Then:

$ make test --dry-run
echo "running something"
$ make test
echo "running something"
running something

Thanks @DillonHafer for that tip!

How to clear a Mac terminal and its scroll-back?

Just type this: clear && printf '\e[3J'

Or even better create an alias for that, here’s mine:

alias clear='clear && printf "\e[3J"';

Here’s what I’ve learned today:

On Mac a regular clear is pretty much the same as typing Control + L on iTerm2. This clears the screen what’s good but sometimes I want to clear all the scroll-back to clean the noise and find things faster.

In order to clean the scroll-back I was performing a Command + K. This cleans the screen and the scroll-back. That’s great, except that it messes up with tmux rendering and tmux holds all the scroll-back per pane, so it won’t work at all.

So my new alias solves that as after clear the screen it also sends a terminal command to reset the scroll back through printf '\e[3J' and this keeps tmux working just fine!

Rails 6 new ActiveRecord method pick

Rails 6 will be released with a new convenient method on ActiveRecord pick. This method picks the first result value(s) from a ActiveRecord relation.

Person.all.pick(:name)
# SELECT people.name FROM people LIMIT 1
# => 'John'

Person.all.pick(:name, :email)
# SELECT people.name, people.email FROM people LIMIT 1
# => ['John', 'john@mail.com']

So pick(:name) is basically equivalent to limit(1).pluck(:name).first.

Watch out for rails inconsistencies as pick(:name) returns a single string value and pick(:name, :email) returns an array of values.

Jest toEqual on js objects

jest toEqual compares recursively all properties of object with Object.is. And in javascript an object return undefined for missing keys:

const obj = {foo: "FOO"}

obj.foo
// "FOO"

obj.bar
// undefined

So it does not make sense to compare an object against another one that has a undefined as value, so remove them all from the right side of the expectation to avoid confusion. In other words, this test will fail:

test("test fails => objects are similar", () => {
  expect({
    planet: "Mars"
  }).not.toEqual({
    planet: "Mars",
    humans: undefined
  });
});

// Expected value to not equal:
//   {"humans": undefined, "planet": "Mars"}
// Received:
//   {"planet": "Mars"}

Watch out!

Elixir sigil for DateTime with timezone

Elixir have a new sigil_U to be relased in the upcomming 1.9.0 version.

~U[2019-05-18 21:25:06.098765Z]

This new sigil creates a UTC DateTime.

Now Elixir will have all these sigils for dates & times:

Date.new(2019, 5, 18)
=> {:ok, ~D[2019-05-18]}

Time.new(23, 55, 6, 98_765)
=> {:ok, ~T[23:55:06.098765]}

NaiveDateTime.new(2019, 5, 18, 23, 55, 6, 98_765)
=> {:ok, ~N[2019-05-18 23:55:06.098765]}

DateTime.from_iso8601("2019-05-18T23:55:06.098765+02:30")
=> {:ok, ~U[2019-05-18 21:25:06.098765Z], 9000}

Install the last Elixir version with asdf

If you are using asdf for managing Elixir version through asdf-elixir then you can install the master version. This way we can use new features yet to be release as 1.9. Let’s see how:

asdf install elixir master
asdf global elixir master

Then:

elixir --version
Erlang/OTP 21 [erts-10.3.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.9.0-dev (6ac1b99) (compiled with Erlang/OTP 20)

ActiveRecord not is not boolean algebra negation

Today I learned that rails ActiveRecord not query is not to be considered a boolean algebra negative. Let’s see by an example:

User.where(name: "Jon", role: "admin")

This will produce a simple sql query:

SELECT "admins".*
FROM "admins"
WHERE "admins"."name" = $1
  AND "admins"."role" = $2
[["name", "Jon"], ["role", "admin"]]

If we get the same where clause and negate it:

User.where.not(name: "Jon", role: "admin")

Then we get:

SELECT "admins".*
FROM "admins"
WHERE "admins"."name" != $1
  AND "admins"."role" != $2
[["name", "Jon"], ["role", "admin"]]

But I was expecting a query like:

SELECT "admins".*
FROM "admins"
WHERE ("admins"."name" != $1
   OR "admins"."role" != $2)
 [["name", "Jon"], ["role", "admin"]]

So if you want to produce a query like that you’ll have to build on your own:

User.where.not(name: "Jon").or(User.where.not(role: "admin"))

Just beaware of that when using not with multiple where clauses.

Elixir ExDoc has version dropdown

ExDoc released a new version that allow developers to show a version dropdown on their documentation.

Here’s how I added to my library:

Open the mix.exs file and add javascript_config_path to the docs option on your project function.

def project do
  [
    ...
    docs: [
      main: "readme",
      extras: ~w(README.md),
      javascript_config_path: "../.doc-versions.js"
    ],
    ...
end

And on my Makefile I have this:

docs: ## Generate documentation.
docs: setup
    echo "var versionNodes = [" > .doc-versions.js
    app=`mix run -e 'IO.puts(Mix.Project.config()[:app])'`; \
    for v in $$(git tag | tail -r); do echo "{version: \"$$v\", url: \"https://hexdocs.pm/$$app/$$v/\"}," >> .doc-versions.js; done
    echo "]" >> .doc-versions.js
    mix docs

So if I run make docs this will generate or update a file .doc-versions.js from what I have on my git tag

And here is how it looks like:

image

Here’s the ExDoc changelog.

How to assert Elixir doctest raises an error

Today I learned how to assert an Elixir doctest raises an error. Check this out:

defmodule MyModule do
  @doc """
  This function raises ArgumentError.

  ## Examples

      iex> MyModule.my_func()
      ** (ArgumentError) something is wrong
  """
  def my_func() do
    raise(ArgumentError, "something is really wrong")
  end
end

The previous doctest will fail with this message:

  1) doctest MyModule.my_func/0 (1) (MyModuleTest)
     test/my_module_test.exs:3
     Doctest failed: wrong message for ArgumentError
     expected:
       "something is wrong"
     actual:
       "something is really wrong"
     code: MyModule.my_func()
     stacktrace:
       lib/my_module.ex:10: MyModule (module)

Rails protects production database

Rails has a mechanism to protect production databases to be dropped (and other destructive commands). In order to do that rails database tasks use a private database rake task check_protected_environments. Here’s a db task code sample from rails code databases.rake:

task drop: [:load_config, :check_protected_environments] do
  db_namespace["drop:_unsafe"].invoke
end

Under the hood it checks the database environment from a metadata table ar_internal_metadata created by rails on the first load schema attempt

SELECT * FROM ar_internal_metadata;
     key     |  value 
-------------+---------
 environment | staging 

Parallel shell processing with xargs

Today I learned how to parallel run a slow command on my shell. We can use xargs combined with the flags -n and -P flags. Let’s see how this works:

find . -type f | xargs -n1 -P8 slow_command
  • slow_command your slow command that receives a file as the first arg
  • -n to specify how many arguments are passed to the slow_command
  • -P how many parallel workers xargs will spawn to run the slow_command

Check this out watch -d -n 0.1 "seq 10 | xargs -n2 -P8 echo":

watch-xargs

On this example xargs are spawning up to 8 workers to run the echo command and for each echo execution xargs will pass 2 arguments. The arguments are produced by a seq 10 and as multiple executions of echo runs in parallel we can highlight the output changes with watch.

Show better image diff on git

Today I learned how to improve my git diff with images. For context check this TIL first.

I gave a step further on my git config to allow a better diff of my images using imagemagick compare features.

git config --global diff.image.textconv 'imgcat'
git config --global diff.image.command 'imgdiff'

So textconv will be used by git show commands and it’s using iTerm2 imgcat.

And command will be used by git diff command and it uses my new shell script imgdiff added to my PATH:

#!/bin/bash

if [[ -f "$1" ]] && [[ -f "$2" ]]; then
  compare "$2" "$1" png:- | montage -geometry +4+4 "$2" - "$1" png:- | imgcat
else
  if [[ -f "$1" ]]; then
    echo "+ Image Added"
    imgcat "$1"
  else
    echo "- Image Removed"
    imgcat "$2"
  fi
fi

exit 0

With that I can have a diff like that:

git diff

So previous image to the left, new to the right and imagemagick comparison in the middle.

Rails with_options

Rails has a convenient method with_options for reusing options on rails methods.

On the documentation we can see how to use on ActiveRecord classes but as this method is under Rails Object it actually can be used on several other places, like controllers.

Check this out:

before with_options

class PostsController < ApplicationController
  before_action :require_login, only: [:show, :edit]
  before_action :load_post_extra_info, only: [:show, :edit]

  ...
end

using with_options

class PostsController < ApplicationController
  with_options only: [:show, :edit] do
    before_action :require_login
    before_action :load_post_extra_info
  end

  ...
end

Thanks @mattpolito for that!

Show images on git diff

Today I learned how to show binary files, more specifically images, using git diff or git show CLI. For that I am using iTerm2 imgcat.

In order to get there I had to configure git to allow a custom diff command to specific file types.

So I used git attributes to do that:

echo "*.gif diff=image" >> ~/.gitattributes
echo "*.jpg diff=image" >> ~/.gitattributes
echo "*.png diff=image" >> ~/.gitattributes
git config --global core.attributesfile '~/.gitattributes'

Then I had to change the git diff text converter to use iterm2 imgcat:

git config --global diff.image.textconv 'imgcat'

Check this out:

git-diff-images

Notes:

  • imgcat does not work with new versions of tmux
  • git pagers like less or more won’t work either, so you can run git --no-pager diff or you can pipe with cat like:
git diff | cat

Check also Open images in vim

Delimiters for sed shell command

Today I learned that the sed shell command accepts other delimiters than /. I was trying to run the following:

folder="screens/components"
echo "~/FOLDER/index.js" | sed -e "s/FOLDER/${folder}/g"
# sed: 1: "s/{{folder}}/screens/co ...": bad flag in substitute command: 'c'

But I got a bad flag error. So I changed my delimiter to | and all works fine:

folder="screens/components"
echo "~/FOLDER/index.js" | sed -e "s|FOLDER|${folder}|g"

Javascript Default Named Parameters

I wanted to create a function with some named parameters as optional values, but I wanted them to have a default value for each option. I could use function({max = 10, min = -10}){} but this only works if you pass a js object as argument. So I added a default object into that and it just works great. Check this out:

avg = function({max = 10, min = -10} = {max: 11, min: -11}) {
  let avg = (max + min) / 2;
  console.log('min', min, 'max', max, 'avg', avg);
  return avg;
}

avg();
// => min -11 max 11 avg 0

avg({});
// => min -10 max 10 avg 0

avg({max: 5});
// => min -10 max 5 avg -2.5

avg({min: 6});
// => min 6 max 10 avg 8

avg({max: 5, min: 6});
// => min 6 max 5 avg 5.5

Default values are different on this example for illustration purposes.

Git Push Force but with lease

If you need to run a git push --force to push a fixup or an amended commit you can try the --force-with-lease tag first for safety. It will protect you to overwrite a commit made by other dev.

alias gplease='git push --force-with-lease'

Check Git push documentation

--force-with-lease will protect all remote refs that are going to be updated by requiring their current value to be the same as the remote-tracking branch we have for them.

Serialize an Elixir pid

I was wondering how to serialize/deserialize a pid for some tricks with Ecto Sandbox and I found out some nice code on phoenix_ecto code. Here it goes my version:

defmodule MyApp.Serializer do
  @spec serialize(term) :: binary
  def serialize(term) do
    term
    |> :erlang.term_to_binary()
    |> Base.url_encode64()
  end

  @spec deserialize(binary) :: term
  def deserialize(str) when is_binary(str) do
    str
    |> Base.url_decode64!()
    |> :erlang.binary_to_term()
  end
end

And the test:

defmodule MyApp.SerializerTest do
  use ExUnit.Case, async: true

  alias MyApp.Serializer

  describe "serialize/1" do
    test "serializes a pid" do
      pid = self()
      assert pid |> Serializer.serialize() |> is_binary()
    end

    test "serializes a map" do
      map = %{foo: :bar}
      assert map |> Serializer.serialize() |> is_binary()
    end
  end

  describe "serialize/1, deserialize/1" do
    test "serializes and deserializes a pid" do
      pid = self()
      assert pid |> Serializer.serialize() |> Serializer.deserialize() == pid
    end

    test "serializes and deserializes a map" do
      map = %{foo: :bar}
      assert map |> Serializer.serialize() |> Serializer.deserialize() == map
    end
  end
end

Redirect outputs with subshell

Today I learned how to use a Subshell to redirect multiple outputs as a single one. This way I can run a bunch of commands and unify all the results as it was a single command.

(git diff --name-only; git diff --staged --name-only) | sort -u

The above example will show me a sorted list of changed files (including the staged ones) in a git project without duplicates.

Here’s another example:

$ echo "world"; echo "hello" | sort -u
#=> world
#=> hello

$ (echo "world"; echo "hello") | sort -u
#=> hello
#=> world

From VIM to Tmux

There’s a really good plugin tslime for sending text from VIM to another Tmux session/window/pane. In addition, sometimes it’s super useful to map short cuts like this:

:vmap <buffer> \t <Plug>SendSelectionToTmux

In this example I’m mapping \t for sending all visual selected text in VIM to Tmux.

Check this out:

tmux

h/t @joshuadavey

PostgreSQL "RETURNING"

Today I learned that PostgreSQL INSERT/UPDATE/DELETE has a RETURNING clause that returns the computed values.

We can use it to get values that are generated by database such as sequences or defaults.

Check this out:

DROP TABLE IF EXISTS users;

CREATE TABLE users (
  id    SERIAL PRIMARY KEY,
  email VARCHAR NOT NULL UNIQUE
);

INSERT INTO users (email) VALUES ('user1@example.com');
-- INSERT 0 1

INSERT INTO users (email) VALUES ('user2@example.com') RETURNING *;
--  id |       email
-- ----+-------------------
--   2 | user2@example.com
-- (1 row)

h/t @joshuadavey

Vim undo tree

Vim has an undo tree and there’s two different ways to iterate with your changes.

One way to undo/redo changes is with: u and <Ctrl>r. This moves the current state based on the current branch in the tree.

The other way is using g- and g+. In this case it moves based on the timestamp of that change.

So here’s an example to follow:

  1. add a line: added_line_1
  2. update this line: updated_line_1
  3. undo this change: u
  4. add another line: added_line_2

If you run through these steps your undo tree will be something like:

 o  [3] "added_line_2" <= "current_state"
 | *  [2] "updated_line_1"
 |/
 *  [1] "added_line_1"
 *  [0] "empty_file"

Now, if you undo with u will be in this state:

 *  [3] "added_line_2"
 | *  [2] "updated_line_1"
 |/
 o  [1] "added_line_1" <= "current_state"
 *  [0] "empty_file"

Otherwise if you undo with g- will be in this part of the tree:

 *  [3] "added_line_2"
 | o  [2] "updated_line_1" <= "current_state"
 |/
 *  [1] "added_line_1"
 *  [0] "empty_file"

You can check gundo vim plugin for tree visualization.