mirror of
https://github.com/jbranchaud/til
synced 2026-01-15 21:18:02 +00:00
Compare commits
7 Commits
dfe9c002ee
...
63bb627716
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63bb627716 | ||
|
|
21385f4491 | ||
|
|
5b47326ab3 | ||
|
|
c16d80fd94 | ||
|
|
edf38308da | ||
|
|
dc7159c16c | ||
|
|
33f780a69f |
@@ -10,7 +10,7 @@ pairing with smart people at Hashrocket.
|
||||
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||
|
||||
_1485 TILs and counting..._
|
||||
_1491 TILs and counting..._
|
||||
|
||||
---
|
||||
|
||||
@@ -212,6 +212,8 @@ _1485 TILs and counting..._
|
||||
### Drizzle
|
||||
|
||||
- [Create bigint Identity Column For Primary Key](drizzle/create-bigint-identity-column-for-primary-key.md)
|
||||
- [Drizzle Tracks Migrations In A Log Table](drizzle/drizzle-tracks-migrations-in-a-log-table.md)
|
||||
- [Get Fields For Inserted Row](drizzle/get-fields-for-inserted-row.md)
|
||||
|
||||
### Elixir
|
||||
|
||||
@@ -522,6 +524,7 @@ _1485 TILs and counting..._
|
||||
- [Open Global npm Config File](javascript/open-global-npm-config-file.md)
|
||||
- [Parse A Date From A Timestamp](javascript/parse-a-date-from-a-timestamp.md)
|
||||
- [Pre And Post Hooks For Yarn Scripts](javascript/pre-and-post-hooks-for-yarn-scripts.md)
|
||||
- [Prevent Hidden Element From Flickering On Load](javascript/prevent-hidden-element-from-flickering-on-load.md)
|
||||
- [Purge Null And Undefined Values From Object](javascript/purge-null-and-undefined-values-from-object.md)
|
||||
- [Random Cannot Be Seeded](javascript/random-cannot-be-seeded.md)
|
||||
- [Reach Into An Object For Nested Data With Get](javascript/reach-into-an-object-for-nested-data-with-get.md)
|
||||
@@ -723,6 +726,7 @@ _1485 TILs and counting..._
|
||||
- [Compute Hashes With pgcrypto](postgres/compute-hashes-with-pgcrypto.md)
|
||||
- [Compute The Levenshtein Distance Of Two Strings](postgres/compute-the-levenshtein-distance-of-two-strings.md)
|
||||
- [Compute The md5 Hash Of A String](postgres/compute-the-md5-hash-of-a-string.md)
|
||||
- [Concatenate Strings With A Separator](postgres/concatenate-strings-with-a-separator.md)
|
||||
- [Configure The Timezone](postgres/configure-the-timezone.md)
|
||||
- [Constructing A Range Of Dates](postgres/constructing-a-range-of-dates.md)
|
||||
- [Convert A String To A Timestamp](postgres/convert-a-string-to-a-timestamp.md)
|
||||
@@ -1002,6 +1006,7 @@ _1485 TILs and counting..._
|
||||
- [Select Value For SQL Counts](rails/select-value-for-sql-counts.md)
|
||||
- [Serialize With fast_jsonapi In A Rails App](rails/serialize-with-fast-jsonapi-in-a-rails-app.md)
|
||||
- [Set A Timestamp Field To The Current Time](rails/set-a-timestamp-field-to-the-current-time.md)
|
||||
- [Set DateTime To Include Time Zone In Migrations](rails/set-datetime-to-include-time-zone-in-migrations.md)
|
||||
- [Set default_url_options For Entire Application](rails/set-default-url-options-for-entire-application.md)
|
||||
- [Set Schema Search Path](rails/set-schema-search-path.md)
|
||||
- [Set Statement Timeout For All Postgres Connections](rails/set-statement-timeout-for-all-postgres-connections.md)
|
||||
@@ -1440,6 +1445,7 @@ _1485 TILs and counting..._
|
||||
- [Generate Random 20-Character Hex String](unix/generate-random-20-character-hex-string.md)
|
||||
- [Get A List Of Locales On Your System](unix/get-a-list-of-locales-on-your-system.md)
|
||||
- [Get Matching Filenames As Output From Grep](unix/get-matching-filenames-as-output-from-grep.md)
|
||||
- [Get The SHA256 Hash For A File](unix/get-the-sha256-hash-for-a-file.md)
|
||||
- [Get The Unix Timestamp](unix/get-the-unix-timestamp.md)
|
||||
- [Global Substitution On The Previous Command](unix/global-substitution-on-the-previous-command.md)
|
||||
- [Globbing For All Directories In Zsh](unix/globbing-for-all-directories-in-zsh.md)
|
||||
|
||||
39
drizzle/drizzle-tracks-migrations-in-a-log-table.md
Normal file
39
drizzle/drizzle-tracks-migrations-in-a-log-table.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Drizzle Tracks Migrations In A Log Table
|
||||
|
||||
When I generate (`npx drizzle-kit generate`) and apply (`npx drizzle-kit
|
||||
migrate`) schema migrations against my database with Drizzle, there are SQL
|
||||
files that get created and run.
|
||||
|
||||
How does Drizzle know which SQL files have been run and which haven't?
|
||||
|
||||
Like many SQL schema migration tools, it uses a table in the database to record
|
||||
this metadata. Drizzle defaults to calling this table `__drizzle_migrations`
|
||||
and puts it in the `drizzle` schema (which is like a database namespace).
|
||||
|
||||
Let's take a look at this table for a project with two migrations:
|
||||
|
||||
```sql
|
||||
postgres> \d drizzle.__drizzle_migrations
|
||||
Table "drizzle.__drizzle_migrations"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
------------+---------+-----------+----------+----------------------------------------------------------
|
||||
id | integer | | not null | nextval('drizzle.__drizzle_migrations_id_seq'::regclass)
|
||||
hash | text | | not null |
|
||||
created_at | bigint | | |
|
||||
Indexes:
|
||||
"__drizzle_migrations_pkey" PRIMARY KEY, btree (id)
|
||||
|
||||
postgres> select * from drizzle.__drizzle_migrations;
|
||||
id | hash | created_at
|
||||
----+------------------------------------------------------------------+---------------
|
||||
1 | 8961353bf66f9b3fe1a715f6ea9d9ef2bc65697bb8a5c2569df939a61e72a318 | 1730219291288
|
||||
2 | b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805 | 1730224013018
|
||||
(2 rows)
|
||||
```
|
||||
|
||||
Notice that Drizzle stores each migration record as [a SHA256 hash of the
|
||||
migration
|
||||
file](https://github.com/drizzle-team/drizzle-orm/blob/526996bd2ea20d5b1a0d65e743b47e23329d441c/drizzle-orm/src/migrator.ts#L52)
|
||||
and a timestamp of when the migration was run.
|
||||
|
||||
[source](https://orm.drizzle.team/docs/drizzle-kit-migrate#applied-migrations-log-in-the-database)
|
||||
56
drizzle/get-fields-for-inserted-row.md
Normal file
56
drizzle/get-fields-for-inserted-row.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Get Fields For Inserted Row
|
||||
|
||||
With Drizzle, we can insert a row with a set of values like so:
|
||||
|
||||
```typescript
|
||||
await db
|
||||
.insert(todoItems)
|
||||
.values({
|
||||
title,
|
||||
userId,
|
||||
description,
|
||||
})
|
||||
```
|
||||
|
||||
The result of this is `QueryResult<never>`. In other words, nothing useful is
|
||||
coming back to us from the database.
|
||||
|
||||
Sometimes an insert is treated as a fire-and-forget (as long as it succeeds) or
|
||||
since we know what data we are inserting, we don't need the database to
|
||||
response. But what about values that are generated or computed by the database
|
||||
-- such as an id from a sequence, timestamp columns that default to `now()`, or
|
||||
generated columns.
|
||||
|
||||
To get all the fields of a freshly inserted row, we can tack on [the
|
||||
`returning()` function](https://orm.drizzle.team/docs/insert#insert-returning)
|
||||
(which likely adds something like [`returning
|
||||
*`](https://www.postgresql.org/docs/current/dml-returning.html)) to the insert
|
||||
query under the hood).
|
||||
|
||||
```typescript
|
||||
await db
|
||||
.insert(todoItems)
|
||||
.values({
|
||||
title,
|
||||
userId,
|
||||
description,
|
||||
})
|
||||
.returning()
|
||||
```
|
||||
|
||||
This will have a return type of `Array<type todoItems>` which means that for
|
||||
each inserted row we'll have all the fields (columns) for that row.
|
||||
|
||||
Alternatively, if we just need the generated ID for the new row(s), we can use
|
||||
a partial return like so:
|
||||
|
||||
```typescript
|
||||
await db
|
||||
.insert(todoItems)
|
||||
.values({
|
||||
title,
|
||||
userId,
|
||||
description,
|
||||
})
|
||||
.returning({ id: todoItems.id })
|
||||
```
|
||||
55
javascript/prevent-hidden-element-from-flickering-on-load.md
Normal file
55
javascript/prevent-hidden-element-from-flickering-on-load.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Prevent Hidden Element From Flickering On Load
|
||||
|
||||
Here is what it might look like to use [Alpine.js](https://alpinejs.dev/) to
|
||||
sprinkle in some JavaScript for controlling a dropdown menu.
|
||||
|
||||
```html
|
||||
<div x-data="{ profileDropdownOpen: false }">
|
||||
<button
|
||||
type="button"
|
||||
@click="profileDropdownOpen = !profileDropdownOpen"
|
||||
>
|
||||
<!-- some inner html -->
|
||||
</button>
|
||||
<div x-show="profileDropdownOpen" role="menu">
|
||||
<a href="/profile" role="menuitem">Your Profile</a>
|
||||
<a href="/sign-out" role="menuitem">Sign Out</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Functionally that will work. You can click the button to toggle the menu open
|
||||
and closed.
|
||||
|
||||
What you might notice, however, when you refresh the page is that the menu
|
||||
flickers open as the page first loads and then disappears. This is a quirk of
|
||||
the element being rendered before Alpine.js is loaded and the
|
||||
[`x-show`](https://alpinejs.dev/directives/show) directive has a chance to take
|
||||
effect.
|
||||
|
||||
To get around this, we can _cloak_ any element with an `x-show` directive that
|
||||
should be hidden by default.
|
||||
|
||||
```html
|
||||
<div x-data="{ profileDropdownOpen: false }">
|
||||
<button
|
||||
type="button"
|
||||
@click="profileDropdownOpen = !profileDropdownOpen"
|
||||
>
|
||||
<!-- some inner html -->
|
||||
</button>
|
||||
<div x-cloak x-show="profileDropdownOpen" role="menu">
|
||||
<a href="/profile" role="menuitem">Your Profile</a>
|
||||
<a href="/sign-out" role="menuitem">Sign Out</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
This addition needs to be paired with some custom CSS to hide any _cloaked_
|
||||
elements.
|
||||
|
||||
```css
|
||||
[x-cloak] { display: none !important; }
|
||||
```
|
||||
|
||||
[source](https://alpinejs.dev/directives/cloak)
|
||||
53
postgres/concatenate-strings-with-a-separator.md
Normal file
53
postgres/concatenate-strings-with-a-separator.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Concatenate Strings With A Separator
|
||||
|
||||
I was putting together an example of using a generated column that concatenates
|
||||
string values from a few other columns. I used manual concatenation with the
|
||||
`||` operator like so:
|
||||
|
||||
```sql
|
||||
create table folders (
|
||||
id integer generated always as identity primary key,
|
||||
user_id integer not null,
|
||||
name text not null,
|
||||
parent_folder_id integer references folders(id),
|
||||
path text generated always as (
|
||||
user_id::text || ':' || lower(name) || ':' || coalesce(parent_folder_id::text, '0')
|
||||
) stored
|
||||
);
|
||||
```
|
||||
|
||||
Instead of doing that manual concatenation for the `path` generated column, I
|
||||
can use
|
||||
[`concat_ws`](https://www.postgresql.org/docs/current/functions-string.html).
|
||||
|
||||
```sql
|
||||
create table folders (
|
||||
id integer generated always as identity primary key,
|
||||
user_id integer not null,
|
||||
name text not null,
|
||||
parent_folder_id integer references folders(id),
|
||||
path text generated always as (
|
||||
concat_ws(
|
||||
':',
|
||||
user_id::text,
|
||||
lower(name),
|
||||
coalesce(parent_folder_id::text, '0')
|
||||
)
|
||||
) stored
|
||||
);
|
||||
```
|
||||
|
||||
The first argument to `concat_ws` is the separator I want to use. The remaining
|
||||
arguments are the strings that should be concatenated with that separator.
|
||||
|
||||
One other things that is nice about `concat_ws` is that it will ignore `null`
|
||||
values that it receives.
|
||||
|
||||
```sql
|
||||
> select concat_ws(':', 'one', 'two', null, 'three');
|
||||
+---------------+
|
||||
| concat_ws |
|
||||
|---------------|
|
||||
| one:two:three |
|
||||
+---------------+
|
||||
```
|
||||
45
rails/set-datetime-to-include-time-zone-in-migrations.md
Normal file
45
rails/set-datetime-to-include-time-zone-in-migrations.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Set Datetime To Include Time Zone In Migrations
|
||||
|
||||
When using Rails and PostgreSQL, your migrations will contain DSL syntax like
|
||||
`t.datetime` and `t.timestamps` which will produce columns using the
|
||||
`timestamp` (`without time zone`) Postgres data type.
|
||||
|
||||
While reading [A Simple Explanation of Postgres' <code>Timestamp with Time
|
||||
Zone</code>](https://naildrivin5.com/blog/2024/10/10/a-simple-explanation-of-postgres-timestamp-with-time-zone.html),
|
||||
I learned that there is a way to configure your app to instead use
|
||||
`timestamptz` by default. This data type is widely recommended as a good
|
||||
default, so it is nice that we can configure Rails to use it.
|
||||
|
||||
First, add these lines to a new initializer (`config/initializers/postgres.rb`)
|
||||
file.
|
||||
|
||||
```ruby
|
||||
require "active_record/connection_adapters/postgresql_adapter"
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
||||
```
|
||||
|
||||
Alternatively, you can configure this via `config/application.rb` per the
|
||||
[Configuring ActiveRecord
|
||||
docs](https://guides.rubyonrails.org/configuring.html#activerecord-connectionadapters-postgresqladapter-datetime-type).
|
||||
|
||||
Then, if you have a new migration like the following:
|
||||
|
||||
```ruby
|
||||
class AddEventsTable < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
create_table :events do |t|
|
||||
t.string :title
|
||||
t.text :description
|
||||
t.datetime :start_time
|
||||
t.datetime :end_time
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
you can expect to have four `timestamptz` columns, namely `start_time`,
|
||||
`end_time`, `created_at`, and `updated_at`.
|
||||
|
||||
Here is the [Rails PR](https://github.com/rails/rails/pull/41084) that adds
|
||||
this config option.
|
||||
34
unix/get-the-sha256-hash-for-a-file.md
Normal file
34
unix/get-the-sha256-hash-for-a-file.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Get The SHA256 Hash For A File
|
||||
|
||||
Unix systems come with a `sha256sum` utility that we can use to compute the
|
||||
SHA256 hash of a file. This means the contents of file are compressed into a
|
||||
256-bit digest.
|
||||
|
||||
Here I use it on a SQL migration file that I've generated.
|
||||
|
||||
```bash
|
||||
$ sha256sum migrations/0001_large_doctor_spectrum.sql
|
||||
b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805 migrations/0001_large_doctor_spectrum.sql
|
||||
```
|
||||
|
||||
Each file passed to this utility gets output to a separate line which is why we
|
||||
see the filename next to the hash. Since I am only running it on a single file
|
||||
and I may want to pipe the output to some other program, I can clip off just
|
||||
the part I need.
|
||||
|
||||
```bash
|
||||
sha256sum migrations/0001_large_doctor_spectrum.sql | cut -d ' ' -f 1
|
||||
b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805
|
||||
```
|
||||
|
||||
We can also produce these digests with `openssl`:
|
||||
|
||||
```bash
|
||||
$ openssl dgst -sha256 migrations/0001_large_doctor_spectrum.sql
|
||||
SHA2-256(migrations/0001_large_doctor_spectrum.sql)= b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805
|
||||
|
||||
$ openssl dgst -sha256 migrations/0001_large_doctor_spectrum.sql | cut -d ' ' -f 2
|
||||
b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805
|
||||
```
|
||||
|
||||
See `sha256sum --help` or `openssl dgst --help` for more details.
|
||||
Reference in New Issue
Block a user