1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-15 21:18:02 +00:00

Compare commits

...

7 Commits

Author SHA1 Message Date
jbranchaud
63bb627716 Add Prevent Hidden Element From Flickering On Load as a JavaScript TIL 2024-10-30 15:11:41 -05:00
jbranchaud
21385f4491 Add Drizzle Tracks Migrations In A Log Table as a Drizzle TIL 2024-10-29 16:18:55 -05:00
jbranchaud
5b47326ab3 Add Get The SHA256 For A File as a Unix TIL 2024-10-29 16:09:58 -05:00
jbranchaud
c16d80fd94 Add Get Fields For Inserted Row as a Drizzle TIL 2024-10-28 15:40:24 -05:00
jbranchaud
edf38308da Add Set DateTime To Include Time Zone In Migrations as a Rails TIL 2024-10-28 11:34:34 -05:00
jbranchaud
dc7159c16c Add a bit more to the latest TIL 2024-10-27 22:45:13 -05:00
jbranchaud
33f780a69f Add Concatenate Strings With A Separator as a Postgres TIL 2024-10-27 22:38:59 -05:00
7 changed files with 289 additions and 1 deletions

View File

@@ -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)

View 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)

View 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 })
```

View 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)

View 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 |
+---------------+
```

View 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.

View 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.