mirror of
https://github.com/jbranchaud/til
synced 2026-01-17 05:58:01 +00:00
Compare commits
9 Commits
abbc1b1114
...
265861e249
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
265861e249 | ||
|
|
43ea7acd74 | ||
|
|
d7d331b688 | ||
|
|
b743dc2ac0 | ||
|
|
4a72c63e42 | ||
|
|
431507fd0e | ||
|
|
fb153f35bf | ||
|
|
f4ba6a9ef7 | ||
|
|
295fe153ad |
@@ -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).
|
||||
|
||||
_1472 TILs and counting..._
|
||||
_1479 TILs and counting..._
|
||||
|
||||
---
|
||||
|
||||
@@ -326,6 +326,7 @@ _1472 TILs and counting..._
|
||||
- [List Untracked Files](git/list-untracked-files.md)
|
||||
- [List Untracked Files For Scripting](git/list-untracked-files-for-scripting.md)
|
||||
- [Move The Latest Commit To A New Branch](git/move-the-latest-commit-to-a-new-branch.md)
|
||||
- [Override The Global Git Ignore File](git/override-the-global-git-ignore-file.md)
|
||||
- [Pick Specific Changes To Stash](git/pick-specific-changes-to-stash.md)
|
||||
- [Pulling In Changes During An Interactive Rebase](git/pulling-in-changes-during-an-interactive-rebase.md)
|
||||
- [Push To A Branch On Another Remote](git/push-to-a-branch-on-another-remote.md)
|
||||
@@ -434,6 +435,7 @@ _1472 TILs and counting..._
|
||||
- [Add Emoji To GitHub Repository Description](internet/add-emoji-to-github-repository-description.md)
|
||||
- [Check Your Public IP Address](internet/check-your-public-ip-address.md)
|
||||
- [Enable Keyboard Shortcuts In Gmail](internet/enable-keyboard-shortcuts-in-gmail.md)
|
||||
- [Exclude AI Overview From Google Search](internet/exclude-ai-overview-from-google-search.md)
|
||||
- [Exclude Whitespace Changes From GitHub Diffs](internet/exclude-whitespace-changes-from-github-diffs.md)
|
||||
- [Figure Out Your Public IP Address](internet/figure-out-your-public-ip-address.md)
|
||||
- [Focus The URL Bar](internet/focus-the-url-bar.md)
|
||||
@@ -743,6 +745,7 @@ _1472 TILs and counting..._
|
||||
- [Duplicate A Local Database](postgres/duplicate-a-local-database.md)
|
||||
- [Edit Existing Functions](postgres/edit-existing-functions.md)
|
||||
- [Enable Logging Of Database Activity](postgres/enable-logging-of-database-activity.md)
|
||||
- [Enforce Uniqueness On Column Expression](postgres/enforce-uniqueness-on-column-expression.md)
|
||||
- [Escaping A Quote In A String](postgres/escaping-a-quote-in-a-string.md)
|
||||
- [Escaping String Literals With Dollar Quoting](postgres/escaping-string-literals-with-dollar-quoting.md)
|
||||
- [Export Query Results To A CSV](postgres/export-query-results-to-a-csv.md)
|
||||
@@ -795,11 +798,13 @@ _1472 TILs and counting..._
|
||||
- [Max Identifier Length Is 63 Bytes](postgres/max-identifier-length-is-63-bytes.md)
|
||||
- [Open Heroku Database In Postico From Terminal](postgres/open-heroku-database-in-postico-from-terminal.md)
|
||||
- [pg Prefix Is Reserved For System Schemas](postgres/pg-prefix-is-reserved-for-system-schemas.md)
|
||||
- [Postgres Does Not Support Unsigned Integers](postgres/postgres-does-not-support-unsigned-integers.md)
|
||||
- [Prepare, Execute, And Deallocate Statements](postgres/prepare-execute-and-deallocate-statements.md)
|
||||
- [Pretty Print Data Sizes](postgres/pretty-print-data-sizes.md)
|
||||
- [Pretty Printing JSONB Rows](postgres/pretty-printing-jsonb-rows.md)
|
||||
- [Prevent A Query From Running Too Long](postgres/prevent-a-query-from-running-too-long.md)
|
||||
- [Print The Query Buffer In psql](postgres/print-the-query-buffer-in-psql.md)
|
||||
- [Put Unique Constraint On Generated Column](postgres/put-unique-constraint-on-generated-column.md)
|
||||
- [Remove Not Null Constraint From A Column](postgres/remove-not-null-constraint-from-a-column.md)
|
||||
- [Renaming A Sequence](postgres/renaming-a-sequence.md)
|
||||
- [Renaming A Table](postgres/renaming-a-table.md)
|
||||
@@ -1717,6 +1722,7 @@ _1472 TILs and counting..._
|
||||
- [Prune The Excess From node_modules](workflow/prune-the-excess-from-node-modules.md)
|
||||
- [Rotate An Image To Be Oriented Upright](workflow/rotate-an-image-to-be-oriented-upright.md)
|
||||
- [See Overlaps For A Set Of Time Zones](workflow/see-overlaps-for-a-set-of-time-zones.md)
|
||||
- [Send A Message To A Discord Channel](workflow/send-a-message-to-a-discord-channel.md)
|
||||
- [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md)
|
||||
- [Toggle Between Stories In Storybook](workflow/toggle-between-stories-in-storybook.md)
|
||||
- [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md)
|
||||
@@ -1752,6 +1758,7 @@ _1472 TILs and counting..._
|
||||
|
||||
- [Add To The Path Via Path Array](zsh/add-to-the-path-via-path-array.md)
|
||||
- [Link A Scalar To An Array](zsh/link-a-scalar-to-an-array.md)
|
||||
- [Use A Space To Exclude Command From History](zsh/use-a-space-to-exclude-command-from-history.md)
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
33
git/override-the-global-git-ignore-file.md
Normal file
33
git/override-the-global-git-ignore-file.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Override The Global Git Ignore File
|
||||
|
||||
One of the places that `git` looks when deciding whether to pay attention to or
|
||||
ignore a file is in your global _ignore_ file. By default, `git` will look for
|
||||
this file at `$XDG_CONFIG_HOME/git/ignore` or `$HOME/.config/git/ignore`.
|
||||
|
||||
I don't have `$XDG_CONFIG_HOME` set on my machine, so it will fall back to the
|
||||
config directory under `$HOME`.
|
||||
|
||||
I may have to create the `git` directory and `ignore` file.
|
||||
|
||||
```bash
|
||||
$ mkdir $HOME/.config/git
|
||||
$ touch $HOME/.config/git/ignore
|
||||
```
|
||||
|
||||
Then I can add file and directories to exclude to that `ignore` file just like
|
||||
I would any other `.gitignore` file.
|
||||
|
||||
If I'd prefer for the global _ignore_ file to live somewhere else, I can
|
||||
specify that location and filename in my `$HOME/.gitconfig` file.
|
||||
|
||||
```
|
||||
[core]
|
||||
excludesFile = ~/.gitignore
|
||||
```
|
||||
|
||||
Setting this will override the default, meaning the default file mentioned
|
||||
above will be ignored ("now you know how it feels, ignore file!"). In this
|
||||
case, I'll need to create the `.gitignore` file in my home directory and add
|
||||
any of my ignore rules.
|
||||
|
||||
[source](https://git-scm.com/docs/gitignore)
|
||||
14
internet/exclude-ai-overview-from-google-search.md
Normal file
14
internet/exclude-ai-overview-from-google-search.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Exclude AI Overview From Google Search
|
||||
|
||||
At the top of most Google searches these days is a section of text that takes a
|
||||
moment to appear, presumably because it is being generated in the moment. This
|
||||
is Google's _AI Overview_. These are sometimes useful summaries of the article
|
||||
you are about to click on anyway. Other times the overview is no good, it takes
|
||||
up a bunch of screen real estate, and may even [10x the energy consumed by a
|
||||
regular
|
||||
search](https://www.reddit.com/r/technology/comments/1dsvefb/googles_ai_search_summaries_use_10x_more_energy/).
|
||||
|
||||
If you want to exclude the _AI Overview_, tack on a `-ai` when writing out your
|
||||
search query.
|
||||
|
||||
[source](https://www.yahoo.com/tech/turn-off-ai-overview-results-170014202.html)
|
||||
@@ -5,6 +5,8 @@ an array-like object with all of the arguments to the function. Even if not
|
||||
all of the arguments are referenced in the function signature, they can
|
||||
still be accessed via the `arguments` object.
|
||||
|
||||
> For ES6+ compatibility, the `spread` operator used via [rest parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) is preferred over the `arugments` object when accessing an abritrary number of function arguments.
|
||||
|
||||
```javascript
|
||||
function argTest(one) {
|
||||
console.log(one);
|
||||
|
||||
35
postgres/enforce-uniqueness-on-column-expression.md
Normal file
35
postgres/enforce-uniqueness-on-column-expression.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Enforce Uniqueness On Column Expression
|
||||
|
||||
When creating a table for, say `users`, where you will store `email` addresses,
|
||||
you'll likely want to enforce uniqueness on the that `email` field. And because
|
||||
people have all sorts of ways of entering their emails, in terms of casing, you
|
||||
may be tempted to try to enforce uniqueness on a lowercased version of `email`.
|
||||
|
||||
```sql
|
||||
create table users (
|
||||
id integer generated always as identity primary key,
|
||||
email text not null,
|
||||
unique ( lower(email) ) -- !! this won't work
|
||||
);
|
||||
```
|
||||
|
||||
A unique _constraint_ must be on one or more columns. You cannot include an
|
||||
_expression_ when defining the unique constraint.
|
||||
|
||||
You can however accomplish similar aims with [a _unique index_ on the
|
||||
expression](https://www.postgresql.org/docs/current/indexes-expressional.html).
|
||||
That is because the index is able to store the result of the expression in
|
||||
itself.
|
||||
|
||||
```sql
|
||||
create table users (
|
||||
id integer generated always as identity primary key,
|
||||
email text not null
|
||||
);
|
||||
|
||||
create unique index unq_lower_email on users ( lower(email) );
|
||||
```
|
||||
|
||||
This is likely what you want for this example anyway because it will probably
|
||||
be a common query to look up the user by `lower(email)` and the index will
|
||||
speed up those queries.
|
||||
30
postgres/postgres-does-not-support-unsigned-integers.md
Normal file
30
postgres/postgres-does-not-support-unsigned-integers.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Postgres Does Not Support Unsigned Integers
|
||||
|
||||
PostgreSQL has a variety of sizes of integer types, from `smallint` (2 bytes)
|
||||
to `integer` (4 bytes) to `bigint` (8 bytes), as well as [other numeric
|
||||
types](https://www.postgresql.org/docs/current/datatype-numeric.html).
|
||||
|
||||
It does _not_ however support unsigned versions of these numeric types.
|
||||
|
||||
That means, with an `integer` for instance, we can store numbers between
|
||||
`-2147483648` and `+2147483647`. That's everything that can fit into 4 bytes.
|
||||
In a system that supported 4 byte unsigned integers we'd be able to represent
|
||||
from `0` all the way up to `4294967295`.
|
||||
|
||||
In PostgreSQL, we're limited to these _signed_ numeric types.
|
||||
|
||||
That means if we were hoping that the data type could essentially enforce a
|
||||
non-negative restriction on the data in one of our columns, we're going to have
|
||||
to be more creative. The obvious choice to me is to consider adding a [check
|
||||
constraint](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS)
|
||||
(e.g. `quantity integer check (quantity > 0)`).
|
||||
|
||||
Another option, as pointed out by [this StackOverflow
|
||||
answer](https://stackoverflow.com/a/31833279/535590), is to create [a
|
||||
user-defined _domain
|
||||
type_](https://www.postgresql.org/docs/current/domains.html) that restricts
|
||||
valid values. To me, the ergonomics of using a domain type are a bit awkward
|
||||
and not worth the effort.
|
||||
|
||||
With either of these solutions, we are only approximating an unsigned integer
|
||||
and do not actually have the same range of values available.
|
||||
46
postgres/put-unique-constraint-on-generated-column.md
Normal file
46
postgres/put-unique-constraint-on-generated-column.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Put Unique Constraint On Generated Column
|
||||
|
||||
You cannot apply a _unique constraint_ to an expression over a column, e.g.
|
||||
`lower(email)`. You can, however, create a [generated
|
||||
column](https://www.postgresql.org/docs/current/ddl-generated-columns.html) for
|
||||
that expression and then apply the unique constraint to that generated column.
|
||||
|
||||
Here is what that could look like:
|
||||
|
||||
```sql
|
||||
> create table users (
|
||||
id integer generated always as identity primary key,
|
||||
name text not null,
|
||||
email text not null,
|
||||
email_lower text generated always as (lower(email)) stored,
|
||||
unique ( email_lower )
|
||||
);
|
||||
|
||||
> \d users
|
||||
+-------------+---------+-----------------------------------------------------------------+
|
||||
| Column | Type | Modifiers |
|
||||
|-------------+---------+-----------------------------------------------------------------|
|
||||
| id | integer | not null generated always as identity |
|
||||
| name | text | not null |
|
||||
| email | text | not null |
|
||||
| email_lower | text | default lower(email) generated always as (lower(email)) stored |
|
||||
+-------------+---------+-----------------------------------------------------------------+
|
||||
Indexes:
|
||||
"users_pkey" PRIMARY KEY, btree (id)
|
||||
"users_email_lower_key" UNIQUE CONSTRAINT, btree (email_lower)
|
||||
```
|
||||
|
||||
And then an demonstration of violating that constraint:
|
||||
|
||||
```sql
|
||||
|
||||
> insert into users (name, email) values ('Bob', 'bob@email.com');
|
||||
INSERT 0 1
|
||||
|
||||
> insert into users (name, email) values ('Bobby', 'BOB@email.com');
|
||||
duplicate key value violates unique constraint "users_email_lower_key"
|
||||
DETAIL: Key (email_lower)=(bob@email.com) already exists.
|
||||
```
|
||||
|
||||
The main tradeoff here is that you are doubling the amount of storage you need
|
||||
for that column. Unless it is a massive table, that is likely not an issue.
|
||||
64
workflow/send-a-message-to-a-discord-channel.md
Normal file
64
workflow/send-a-message-to-a-discord-channel.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Send A Message To A Discord Channel
|
||||
|
||||
I recently added a form to [visualmode.dev](https://www.visualmode.dev) that
|
||||
when submitted should send the details to an internal channel in my discord
|
||||
server.
|
||||
|
||||
I didn't need to set up an _App_ or a _Bot_ to do this. It is much simpler than
|
||||
that. All I needed was a valid [_webhook_
|
||||
endpoint](https://discord.com/developers/docs/resources/webhook) for my channel
|
||||
that I can `POST` to.
|
||||
|
||||
From Discord, I can select _Edit Channel_ for a specific channel, go to the
|
||||
_Integrations_ tab, go to _Webhooks_, and then create a _New Webhook_. I can
|
||||
name it, save it, and then copy the webhook URL.
|
||||
|
||||
As a demonstration, I can `POST` to that webhook URL using `cURL` like so:
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"content":"Hello from cURL!"}' <YOUR_WEBHOOK_URL>
|
||||
```
|
||||
|
||||
Similarly, in some non-public-facing code like a Next.js serverless function, I
|
||||
can `POST` to that webhook URL with the `content` that I want to appear in my
|
||||
channel.
|
||||
|
||||
```
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const data = await request.json()
|
||||
|
||||
const discordWebhookUrl = process.env.DISCORD_WEBHOOK_URL
|
||||
if (discordWebhookUrl) {
|
||||
try {
|
||||
const response = await fetch(discordWebhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: `New contact form submission:\nName: ${data.name}\nEmail: ${data.email}\nCompany: ${data.company}\nPhone: ${data.phone}\nMessage: ${data.message}`,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to send Discord message')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending to Discord:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to process form submission' },
|
||||
{ status: 500 },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Form submitted successfully' })
|
||||
}
|
||||
```
|
||||
|
||||
This [Structure of Webhook
|
||||
guide](https://birdie0.github.io/discord-webhooks-guide/discord_webhook.html)
|
||||
has more details on how to specifically structure and format a more complex
|
||||
message.
|
||||
37
zsh/use-a-space-to-exclude-command-from-history.md
Normal file
37
zsh/use-a-space-to-exclude-command-from-history.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Use A Space To Exclude Command From History
|
||||
|
||||
When using a shell like `zsh`, you get the benefit of it keeping track of the
|
||||
history of the commands you've entered into the shell. This means you can
|
||||
quickly traverse pack to a previous command that you want to run again. It also
|
||||
means [a tool like `fzf` can hook into your history
|
||||
file](https://github.com/junegunn/fzf?tab=readme-ov-file#key-bindings-for-command-line)
|
||||
so that you can fuzzy-search for a command you may have executed weeks ago.
|
||||
|
||||
The history is stored on your machine in a plaintext file. Not every command
|
||||
should be stored in a plaintext file. For instance, you don't want `zsh` to
|
||||
persist a command that includes a password.
|
||||
|
||||
With the `histignorespace` option enabled in `zsh`, we can put a leading space
|
||||
in front of our command and it will be excluded from the history file.
|
||||
|
||||
Try it yourself:
|
||||
|
||||
```bash
|
||||
$ echo 'this command will be remembered'
|
||||
this command will be remembered
|
||||
|
||||
$ echo 'this command will be forgotten'
|
||||
this command will be forgotten
|
||||
```
|
||||
|
||||
Notice the leading space in the second command. Trying pressing your _up_ arrow
|
||||
and notice only that first `echo` is remembered.
|
||||
|
||||
Make sure `histignorespace` is included in the list when you run `setopt`. If
|
||||
it isn't, then add it:
|
||||
|
||||
```bash
|
||||
$ setopt histignorespace
|
||||
```
|
||||
|
||||
[source](https://stackoverflow.com/questions/8473121/execute-a-command-without-keeping-it-in-history/49643320#49643320)
|
||||
Reference in New Issue
Block a user