Today I Learned

hashrocket A Hashrocket project

Implicit order column

ActiveRecord has a class method implicit_order_column= that allows you to override the behavior of the .first and .last methods.

class User < ApplicationRecord
  self.implicit_order_column = "email"
end

User.first
User Load (3.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."email" ASC, "users"."id" ASC LIMIT $1  [["LIMIT", 1]]

Migrating Data in Ecto

I usually have created a mix task for data migrations to avoid putting the app down, but today I learned that ecto migrations accept an arg --migrations-path to their commands which allow us to have 2 separate folders for migrations.

With that we can easily use the default priv/repo/migrations folder for automatic migrations (for running on deployments) and a separate folder, let’s say priv/repo/data_migrations that we can run when it’s more convenient.

So in prod we run migrations on deploy and data_migrations on a quite time for the app to avoid downtime. And in dev and test env we just run them all as we usually have smaller dataset, so not a big deal.

Here’s a good post about this strategy.

Ignore ~/.psqlrc when using psql

You can ignore your ~/.psqlrc when running psql commands by using the -X or --no-psqlrc flags.

So when you have all this in your rc file:

/* ~/.psqlrc */
\x auto
\timing
\set PROMPT1 '%[%033[1m%]%M %n@%/%R%[%033[0m%]%# '
\set PROMPT2 '[more] %R > '
\pset null '☢'
\setenv PSQL_PAGER pspg
\setenv PAGER pspg

This command becomes quite noisy:

psql -c 'select 1'
Expanded display is used automatically.
Timing is on.
Null display is "☢".
Time: 0.210 ms
 ?column?
----------
        1
(1 row)

Time: 0.297 ms

If you run without the config file:

psql -X -c 'select 1'
 ?column?
----------
        1
(1 row)

on-line manual pages:

-X,
--no-psqlrc
    Do not read the start-up file (neither the system-wide psqlrc file nor the
    user's ~/.psqlrc file).

Get database value of model instance

AR instances have a method #attribute_in_database. Sometimes things don’t make sense, someone has created a method with the same name as the column:

create table users (id int, email citext);
class User < ApplicationRecord
  def email
    Time.current.strftime("%A, %B %C")
  end
end

User.first.email
=> "Thursday, March 20"

But you actually wan the email:

User.first.attribute_in_database("email")
=> "admin@example.com"

About EST versus EDT

Here in the US we have the system of daylight savings time (DST), at least for now.

But did you know that the timezone you use includes that? Cause I sure didn’t.

EST/CST/MST/etc are for the standard time version of the time zone EDT/CDT/MDT/etc are for the DST adjusted periods!

Largely, I prefer to let libraries handle this for me, so I’ve never dug into the details. But this one is also interesting from the perspective of communications with others.

Invoke procs with brackets

You can invoke a ` with bracketsProc[]`: ruby UriService = lambda do |username:, password:| def scream(n) n.upcase end "http://#{scream(username)}:#{password}@api.example.com" end UriService.call(username: "password", password: "123") => "http://PASSWORD:123@api.example.com" UriService[username: "password", password: "123"] => "http://PASSWORD:123@api.example.com"

Ruby Private Class Methods

Today I learner that Ruby Module has private_class_method, this way we can for example make the new method as private on service objects:

class MyServiceObject
  private_class_method :new

  def self.call(*args)
    new(*args).call
  end

  def initialize(foo:)
    @foo = foo
  end

  def call
    @foo
  end
end

Note that in this example the new method is private, so the .call will work, but the .new().call will raise an error:

irb> MyServiceObject.call(foo: "FOO")
=> "FOO"

irb> MyServiceObject.new(foo: "FOO").call
NoMethodError: private method `new' called for MyServiceObject:Class

Rails composed_of

I learned that Rails ActiveRecord has composed_of method which allow us to group model attributes under a common PORO like object. Let’s say we have an User with address_street and address_city fields, stored in the same table, so we can still group the address attributes:

class User < ActiveRecord::Base
  composed_of :address, mapping: [%w(address_street street), %w(address_city city)]
end

This way, besides the user.address_street and user.address_city, we can also access the same values as:

address = user.address
puts address.street
puts address.city

Rails AR readonly!

ActiveRecord has some readonly features that marks a model (or relation) not to be updated. This way if we try:

irb> user = User.last
=> #

irb> user.readonly!
=> true

irb> user.update(updated_at: Time.current)
   (0.3ms)  SAVEPOINT active_record_1
   (0.3ms)  ROLLBACK TO SAVEPOINT active_record_1
ActiveRecord::ReadOnlyRecord: User is marked as readonly

Then we get a rollback with the readonly error.

An interesting thing is that the touch AR method does not respect this readonly feature (I tested on rails 6.0.4.1), check this out:

irb> user = User.last
=> #

irb> user.readonly!
=> true

irb> user.touch
   (0.3ms)  SAVEPOINT active_record_1
  User Update (1.4ms)  UPDATE "users" SET "updated_at" = $1 WHERE "users"."id" = $2  [["updated_at", "2022-03-08 20:39:40.750728"], ["id", 123]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> true

Add TypeScript support to forms

When working with form names, it’s nice to have typescript support:

interface CustomerFormType extends HTMLFormElement {
  firstName: HTMLInputElement;
  lastName: HTMLInputElement;
}


declare global {
  interface Document {
    newCustomer: CustomerFormType;
  }
}

class CustomerForm extends Component {
  onSubmit = (e) => {
    e.preventDefault();
    const firstName = document.newCustomer.firstName.value;
    const lastName = document.newCustomer.lastName.value;
    console.log({firstName, lastName});
  };

  render() {
    return (
      
        
        
        Submit
      
    );
  }
}

Git show on merge commit

I always use git show to see the changes for the last commit or git show sha1 to see the changes from a specific commit.

I rarely use git merge commits and recently I was working on a project that had a merge commit. When I did git show sha1 on that merge commit nothing showed up. Then I discovered that you can pass the -m option and it shows all the diff on that merge commit.

git show -m sha1 

Pretty neat!

Reject blank input with graphql-ruby

The graphql-ruby gem has a built-in blank validator:

class Mutations::UserUpdate < Mutations::BaseMutation
  null true

  argument :user_id,
    String,
    "Identifier of user",
    required: true,
    validates: {allow_blank: false}

  field :user_id, String, null: false

  def resolve(user_id:)
    {user_id:}
  end
end

So now a mutation with a user_id of " " will cause the graphql response to have an error:

mutation UserUpdate($userId: String!) {
  userUpdate(userId: $userId) {
    userId
  }
}

Only fk constraints may be altered in PostgreSQL

Only foreign key constraints may be altered in PostgreSQL:

create extension citext;
create table users (id int generated by default as identity primary key);
create table user_emails (
  user_id int not null references users,
  email citext primary key
);

Now we can see the constraint:

[local] dillon@test=# \d user_emails
             Table "public.user_emails"
 Column  |  Type   | Collation | Nullable | Default
---------+---------+-----------+----------+---------
 user_id | integer |           | not null |
 email   | citext  |           | not null |
Indexes:
    "user_emails_pkey" PRIMARY KEY, btree (email)
Foreign-key constraints:
    "user_emails_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)

But now we can change the foreign key to be deferrable:

alter table user_emails
  alter constraint user_emails_user_id_fkey deferrable initially immediate;

After:

[local] dillon@test=# \d user_emails
             Table "public.user_emails"
 Column  |  Type   | Collation | Nullable | Default
---------+---------+-----------+----------+---------
 user_id | integer |           | not null |
 email   | citext  |           | not null |
Indexes:
    "user_emails_pkey" PRIMARY KEY, btree (email)
Foreign-key constraints:
    "user_emails_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) DEFERRABLE

Easy conditional style class names in Rails

How many times do you see something like this?

<%= 
  link_to("Somewhere", "#",
    class: "class-1 class-2#{" class-3" if true}"
  )
%>

# Somewhere

Gross right!?

Take advantage of a token list instead.

It’s also got a great alias in class_names

<%= 
  link_to("Somewhere", "#", 
    class: class_names("class-1 class-2", {"class-3": true})
  )
%>

# Somewhere

As you see it can be passed different types and still generates down to a string list.

It works great with the current_page helper.

<%= 
  link_to("Home", root_path, 
    class: class_names({"active": current_page?(root_path)})
  )
%>

# Home

Check if string starts with character

In ruby I would use something like

"PostgreSQL".downcase.start_with?("p")
=> true

and the equivalent in a query would be:

select lower(left('PostgreSQL', 1)) = 'p';

 ?column?
----------
 t
(1 row)

and if you have the citext extension enabled you could do:

select left('PostgreSQL', 1)::citext = 'p';

 ?column?
----------
 t
(1 row)

Other things:

select *
from users
where left(display_name, 1)::citext = 'a'
;

Resize Window Keyboard Shortcuts in Windows 10

After using Magnet, a window manager in OSX, I remembered hearing that there was a similar facility built into Windows. Turns out, they are very easy-to-remember shortcuts for resizing windows.

  • Win + Right Arrow - Snap to right half of screen**
  • Win + Left Arrow - Snap to left half of screen**
  • Win + Up Arrow - Maximize current window
  • Win + Down Arrow - Minimize window if not currently maximized

**(will maximize the window if it is currently split on the opposite side of the screen)

There’s a few others too, but these 4 I’m finding super useful. If you’re interested in other Windows shortcuts, I found some other cool ones in this Lifewire article

Select first element from array_agg

It’s as simple as:

select zip_code, (array_agg(company_name))[1]
from locations
group by zip_code
;

 zip_code |        array_agg
----------+--------------------------
 90210    | In-N-out
 46368    | Johnny's Round the Clock
(2 rows)

source:

create table locations (
  id bigint generated by default as identity primary key,
  zip_code text not null,
  company_name text not null
);

insert into locations (zip_code, company_name) values 
  ('46368', 'Johnny''s Round the Clock'),
  ('90210', 'In-N-out'),
  ('46368', 'Albanese Candy');

Add global variables in typescript

In this example, we need to put placeholder values on global.window to allow us to use Ruby on Rails’ ActionCable websocket framework where no window exists:

// Fix to prevent crash from ActionCable
global.window.removeEventListener = () => {};
global.window.addEventListener = () => {};

But we need to add a type:

declare global {
	var window: {
		addEventListener(): void;
		removeEventListener(): void;
	};
}

HTML Tab Index Gotcha

The tabindex attribute on HTML elements behaves differently than one might first expect.

The default tabindex for an element is 0, all elements with this index will be tabbed to in the order they appear in the document.

Giving an element a positive tabindex will prioritize it over all elements with a 0 index, in the order they appear.

Consider the following code

    { [0,1,2].map((n) => (
  • {n}
  • ) }

The order that the buttons will be focused when tabbing is 1, then 2, and finally 0.

Control video playback with keyboard controls

For a macOS app to show up in the Now Playing media center (which enables media keys [F7, F8, F9]) you just need to configure the MPNowPlayingInfoCenter‘s playbackState

func play() {
  self.player.play()
  MPNowPlayingInfoCenter.default().playbackState = .playing
}

And then subscribe to RemoteCommand changes:

let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
  self.player.pause()
  MPNowPlayingInfoCenter.default().playbackState = .paused
  return .success
}
commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
  self.player.play()
  MPNowPlayingInfoCenter.default().playbackState = .playing
  return .success
}
Screen Shot 2021-12-26 at 12 05 47

Keep 5 most recent files in a directory

One can keep the most recent n files in a directory with just three shell programs: ls, tail, and xargs.

Here is an example to use in a nightly database backup cron job:

#!/bin/bash
# Keep last 5 files ending in .dump
# Don't forget to 

# Installation
# 1. cp pg-backups.sh /usr/local/bin/
# 2. chmod u+x /usr/local/bin/pg-backups.sh
# 3. Set the DB variable
# 4. Set the BACKUP_DIR variable

# Example usage for cron to run at 4:05 am every day:
# 5 4 * * * /usr/local/bin/pg-backups.sh

DB=mydatabase
BACKUP_DIR=/mnt/object/production/db-backups

DATE=$(date "+%Y-%m-%d_%H%M")
pg_dump -Fc $DB > $BACKUP_DIR/$DATE.dump

/bin/ls -t $BACKUP_DIR/*.dump | tail +6 | xargs rm

Be Careful with JavaScript Numbers

Today I Learned that you need to be careful when working with numbers in JavaScript. This is because of the way that JavaScript implements the Number type.

> The JavaScript Number type is a double-precision 64-bit binary format IEEE 754 value, like double in Java or C#. This means it can represent fractional values …

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number

Take for example the following Ruby snippet:

> 2 / 5
=> 0

Our division here returns 0, which is what I expected. And if you want the remainder, you can get it with the modulus operator %

Now here’s the same snippet in JavaScript, which returns a double-precision number that is not zero

> 2 / 5
0.5

If you’re looking to do integer-like division in JavaScript, here’s a few ways you can accomplish that:

> Math.floor(2 / 5)
0

> Math.trunc(2 / 5)
0

> (2 / 5) >> 0
0

Docs

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc

Here’s a callback to another JavaScript number TIL 😅

https://til.hashrocket.com/posts/e04ffe1d76-because-javascript

Use created_at in Ecto

You can use created_at in Ecto/phoenix app with timestamps/1. When migrating data from a rails application to a phoenix application you will have many tables with a created_at column.

defmodule Phoenix.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :email, :string
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :utc_datetime

    timestamps(inserted_at: :created_at, type: :utc_datetime)
  end

end

Ruby Squeeze

Ruby has a method to remove repeating characters in a string called squeeze

"foobar".squeeze
# => "fobar"

"foo foo bar".squeeze
# => "fo fo bar"

It also accepts args to narrow down the specific characters for which you would want to remove repeats:

"foobar hello world".squeeze("o")
# => "fobar hello world"

"foobar  hello  world".squeeze(" ")
# => "foobar hello world"

How to install sqlite3 on heroku

Using sqlite to persist data is superfluous on heroku, duh, but sometimes a third party service wants my rails app to read configuration in a sqlite db file. In order to read the read-only database file, I need to install the sqlite3 gem. To get this to work on heroku I needed to do two things:

  1. Install the apt buildpack
  2. Add an Aptfile in the root of the project
heroku buildpacks:add --index 1 heroku-community/apt

Then create an apt file:

# Aptfile
libsqlite3-dev
libsqlite3-0

Export CSV of query without COPY

Clients task me to export data to a CSV from time to time. My favorite workflow for authoring queries is a split tmux session, vim (with the excellent coc-sql) on one side, and a psql session on the other. I modify my query and then re-execute via \i.

This is great, until I’m ready to export the CSV. historically I’ve edited the query, cumbersomely breaking my REPL flow by turning it into a COPY command. Now I do this:

\pset format csv
\o output-file.csv
\i my-query.sql

To reset my psql session output…

\pset format aligned
\o

And thus my-query.sql remains a working query.

Elixir Compilation Cycles with `--fail-above`

Elixir 1.13.0 introduced a --fail-above flag for the mix xref task which will fail that task execution under a certain criteria.

With that we can verify, for example, that our code is ok to have 1 cycle that goes over length of 4, but not 2 cycles. Let’s see how it works:

$ mix xref graph --format cycles --min-cycle-size 4 --fail-above 1
2 cycles found. Showing them in decreasing size:

Cycle of length 6:

    lib/my_app_web/endpoint.ex
    lib/my_app_web/router.ex
    lib/my_app_web/views/layout_view.ex
    lib/my_app_web/live/components/sorting_link.ex
    lib/my_app_web/live/components/icon.ex
    lib/my_app_web/endpoint.ex

Cycle of length 5:

    lib/my_app_web/live/components/sorting_link.ex
    lib/my_app_web/live/components/icon.ex
    lib/my_app_web/router.ex
    lib/my_app_web/views/layout_view.ex
    lib/my_app_web/live/components/sorting_link.ex

** (Mix) Too many cycles (found: 2, permitted: 1)

In this case xref found 2 cycles with a length greater than 4, and as I allowed only 1 then we can see the error.

PostgreSQL Indexes on Partition tables

When we CREATE INDEX in a partitioned table, PostgreSQL automatically “recursively" creates the same index on all its partitions.

As this operation could take a while we can specify the ONLYparameter to the main table index to avoid the index to be created on all partitions and later creating the same index on each partition individually.

CREATE INDEX index_users_on_country ON ONLY users USING btree (country);

CREATE INDEX users_shard_0_country_idx ON users_shard_0 USING btree (country);
CREATE INDEX users_shard_1_country_idx ON users_shard_0 USING btree (country);

Elixir __struct__/0

When we define a struct via defstruct macro we end up getting a __struct__/0 function on both struct module definition and on each struct map. The intriguing part is that the implementation of the module and the map are different, check this out:

iex(1)> defmodule Book do
...(1)>   defstruct title: nil, pages_count: 0
...(1)> end

iex(2)> Book.__struct__()
%Book{pages_count: 0, title: nil}

iex(3)> Book.__struct__().__struct__()
Book

As we can see Book.__struct__() returns a new %Book{} struct with its defaults, meanwhile %Book{}.__struct() returns the Book module.

Elixir IEX multi-line command

A change on Elixir 1.12.0 made possible to pipe |> multi-line commands in iex where the |> operator is in the beginning of new lines.

That means that we can:

iex(1)> :foo
:foo
iex(2)>       |> to_string()
"foo"
iex(3)>       |> String.upcase()
"FOO"

The docs also mention that all other binary operators works the same way, except+/2 and -/2, so that’s also valid:

iex(1)> [:foo]
[:foo]
iex(2)> ++ [:bar]
[:foo, :bar]
iex(3)> |> Enum.join(" ")
"foo bar"
iex(4)> |> String.upcase()
"FOO BAR"

Each line will run at a new command, so if you assign a variable in the first line you may not have what you expect, so watch out.