Today I Learned

A Hashrocket project

Make numbers more readable in a Rails view

Rails has a number of handy helper methods.

Specifically, in ActionView::Helpers::NumberHelper there is the method number_with_delimiter

If you want to display the number 100000 and have people know right away whether it is 1 million or 100 thousand, just pass it to number_with_delimiter and it will return "100,000".

Some useful optional keyword parameters:
delimiter:"πŸ”" replaces the default , delimiter. (100000 becomes 100πŸ”000)
separator:"πŸ•" replaces the default . separator. (100.56 becomes 100πŸ•56)

Add primary key to table

You can add a primary key to a table with alter an alter table statement:

Table with no primary key:

create table no_pks (id int generated by default as identity not null);
insert into no_pks select from generate_series(0,999);
[local] dillon@dillon=# \d no_pks
                           Table "public.no_pks"
 Column |  Type   | Collation | Nullable |             Default
--------+---------+-----------+----------+----------------------------------
 id     | integer |           | not null | generated by default as identity

You can add it later:

alter table no_pks add primary key (id);
[local] dillon@dillon=# \d no_pks
                           Table "public.no_pks"
 Column |  Type   | Collation | Nullable |             Default
--------+---------+-----------+----------+----------------------------------
 id     | integer |           | not null | generated by default as identity
Indexes:
    "no_pks_pkey" PRIMARY KEY, btree (id)

Print unknown exceptions in PL/pgSQL

When trying to figure out why a function raised an exception you can print the error code raised to lookup in the table Appendix A-1.

One method is to capture others and then raise the magic sqlstate variable (only available in exception handlers)

create or replace function do_it(name text)
  returns void
as $$
begin
  select 42 from nothing;
exception
  when others then
    raise '%: %', sqlstate, sqlerrm;
end;
$$
  security definer
  language plpgsql
;

Then you can view the error:

select do_it('hi');
ERROR:  42P01: relation "nothing" does not exist
CONTEXT:  PL/pgSQL function do_it(text) line 6 at RAISE

Proxy Development Requests with Create-React-App

Create-React-App supports an optional proxy option in development, which can be added to your package.json. In my case, I was dealing with CORS issues locally because our frontend and backend run on the same server in production, but our development setup is different.

My backend was running on http://localhost:4000, and frontend on http://localhost:3000.

In my package.json, I specified that I wanted to proxy my requests to backend development server:

{
  ...
  "proxy": "http:localhost:4000"
  ...
}

Then I changed my fetch call to be a relative URL:

// Was fetch("http://localhost:4000/api/graph", ...params)
fetch("/api/graphql")

Create-react-app will recognize that this path is not a static asset and β€œwill proxy the request (http://localhost:4000/api/graphql) as a fallback”.

Create-React-App Docs - Proxying API Requests in Development

curl with a progress bar

You can download files with a nice progress bar using curl’s -# flag:

curl -# -O https://files.example.com/large/long_video.mp4
#################                               38.6%

This might be preferable to the verbose output:

curl --no-progress-meter -O https://files.example.com/large/long_video.mp4
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 17  433M   17 75.6M    0     0  28.7M      0  0:00:15  0:00:02  0:00:13 28.8M

Use encrypted env vars with direnv

Direnv can execute shell scripts, so given that your env file is encrypted, you can automatically have it become decrypted for you:

───────┬──────────────────────
       β”‚ File: .env
───────┼──────────────────────
   1   β”‚ STRIPE_PK="123456789"
   2   β”‚ API_KEY="qwertyuiop"
───────┴──────────────────────

Say is was encrypted:

ansible-vault encrypt --vault-password-file config/master.key .env
cat .env
───────┬─────────────────────────────────────────────────────────────────────────────────
       β”‚ File: .env
───────┼─────────────────────────────────────────────────────────────────────────────────
   1   β”‚ $ANSIBLE_VAULT;1.1;AES256
   2   β”‚ 35306466356632363334643432343132356662376462333964366534393462366333623764336161
   3   β”‚ 6131336435323834623539323462626235383330346562660a323534656133653237656634346235
   4   β”‚ 30653635663438313931393966383266663535313361613339396234373164323830373262633661
   5   β”‚ 6262356131306530350a643362623636323762656132326363323736633431396463616137343139
   6   β”‚ 66666438623230333636373563393165333562633964616536663363323334343235386465346663
   7   β”‚ 3365643263643766323835356230636539353034643034346136
───────┴─────────────────────────────────────────────────────────────────────────────────

Now that we have an encrypted .env file, we just need direnv to decrypt it whenever we’re in our directory:

───────┬────────────────────────────────────────────────────────────────────────────────────────────────
       β”‚ File: .envrc
───────┼────────────────────────────────────────────────────────────────────────────────────────────────
   1   β”‚ export $(ansible-vault decrypt --vault-password-file config/master.key --output - .env | xargs)
───────┴────────────────────────────────────────────────────────────────────────────────────────────────

Output:

$ cd rails_app
direnv: loading ~/dev/rails_app/.envrc
direnv: export +API_KEY +STRIPE_PK
echo $API_KEY
qwertyuiop

Now whenever we enter the directory, we will have the unencrypted env vars, but the file remains encrypted on disk. For whatever that’s worth.

Use + as a closure in Array reduce

In swift you can pass a method as the closure:

import Foundation

let numbers = [1, 2, 3, 4, 5]
let total = numbers.reduce(0, +)
print("Average: \(total / numbers.count)")

=> "Average: 3"

You can also use the generic closure:

import Foundation

let numbers = [1, 2, 3, 4, 5]
let total = numbers.reduce(0, { accumulator, number in 
  accumulator + number
})
print("Average: \(total / numbers.count)")

=> "Average: 3"

String concatenation in Ruby

My first instinct when it comes to concatenating strings would be to use the += operator:

name = "Peter"
=> "Peter"

name.object_id
=> 15320

name += " Parker"
=> "Peter Parker"

name.object_id
=> 34480

However, as you can see, doing this creates a new object. Instead, << should be used to maintain the same string object and also improve performance when working on larger batches of strings.

name = "Peter"
=> "Peter"

name.object_id
=> 54960

name << " Parker"
=> "Peter Parker"

name.object_id
=> 54960

Prevent rails' file server from serving index.html

You can prevent rails’ file server from serving index.html, while continuing to serve other files from the public directory by changing the index_name:

class Application
  config.public_file_server.index_name = "other-index.html"
end

Rails.application.routes.draw do
  get :dashboard, to: "application#dashboard"
  root to: redirect("/dashboard")
end

Visiting the root path / will no longer serve public/index.html if you define another route for /

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 <Proc (lambda)> with brackets Proc[]:

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
=> #<User id: 123, ...>

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
=> #<User id: 123, ...>

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<Props, State> {
  onSubmit = (e) => {
    e.preventDefault();
    const firstName = document.newCustomer.firstName.value;
    const lastName = document.newCustomer.lastName.value;
    console.log({firstName, lastName});
  };

  render() {
    return (
      <form name="newCustomer" onSubmit={this.onSubmit}>
        <input name="firstName" type="text" />
        <input name="lastName" type="text" />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

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}"
  )
%>

# <a href="#" class="class-1 class-2 class-3">Somewhere</a>

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})
  )
%>

# <a href="#" class="class-1 class-2 class-3">Somewhere</a>

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)})
  )
%>

# <a href="/" class="active">Home</a>

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

<ul>
  {
    [0,1,2].map((n) => (
      <li><button tabindex={n}>{n}</button></li>
    )
  }
</ul>

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