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

Compare commits

...

38 Commits

Author SHA1 Message Date
Mohammad Alyetama
1fce1af3d7 Merge bc767a0ad3 into c16d80fd94 2024-10-29 18:44:21 +08: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
jbranchaud
dfe9c002ee Add Add Unique Constraint Using Existing Index as a Postgres TIL 2024-10-26 10:03:23 -05:00
jbranchaud
3e34636d80 Add Analyze Your Website Performance as an Internet TIL 2024-10-25 12:38:46 -05:00
jbranchaud
dcef57d344 Add Make Truly Deep Clone With Structured Clone as a JavaScript TIL 2024-10-25 08:36:46 -05:00
jbranchaud
6580393b7a Add Create bigint Identity Column For Primary Key as a Drizzle TIL 2024-10-24 17:39:55 -05:00
jbranchaud
17d7f0933b Add Add Subscriber To Kit Form Via API as a Workflow TIL 2024-10-23 19:12:52 -05:00
jbranchaud
e4abc56f4c Add Add Hotkeys For Specific Raycast Extensions as a Workflow TIL 2024-10-22 14:30:59 -05:00
jbranchaud
43ea7acd74 Add Use A Space To Exclude Command Fromm History as a Zsh TIL 2024-10-21 11:21:21 -05:00
jbranchaud
d7d331b688 Add Put Unique Constraint On Generated Column as a Postgres TIL 2024-10-21 11:04:04 -05:00
jbranchaud
b743dc2ac0 Add Exclude AI Overview From Google Search as an Internet TIL 2024-10-20 19:38:08 -05:00
jbranchaud
4a72c63e42 Add Enforce Uniqueness On Column Expression as a Postgres TIL 2024-10-19 19:13:30 -05:00
jbranchaud
431507fd0e Add Send A Message To A Discord Channel as a Workflow TIL 2024-10-18 17:59:08 -05:00
jbranchaud
fb153f35bf Add Postgre Does Not Support Unsigned Integers as a PostgreSQL TIL 2024-10-18 17:43:46 -05:00
jbranchaud
f4ba6a9ef7 Add Override The Global Git Ignore File as a Git TIL 2024-10-18 15:55:40 -05:00
jbranchaud
0dee39d3c5 Add Configure Email Redirect With Cloudflare as a Workflow TIL 2024-10-17 11:09:04 -05:00
jbranchaud
5ebdd9a1a9 Add Convert JPEG To PNG With ffmpeg as a Unix TIL 2024-10-16 12:43:18 -05:00
jbranchaud
33c5cd748f Fix codeblock example in latest TIL 2024-10-15 15:07:55 -05:00
jbranchaud
ff515c8d6a Add Get URL For GitHub User Profile Photo as a Workflow TIL 2024-10-15 15:07:19 -05:00
jbranchaud
fad36e0691 Add Check How A File Is Being Ignored as a Git TIL 2024-10-15 12:19:55 -05:00
jbranchaud
4d1d8e7134 Add Switch Moving End Of Visual Selection as a Vim TIL 2024-10-14 20:20:41 -05:00
jbranchaud
567637497c Add Ensure Resources Always Get Closed as a Java TIL 2024-10-14 16:04:07 -05:00
jbranchaud
028b76ba6b Add Generate Types For A Content Collection as an Astro TIL 2024-10-13 17:03:25 -05:00
jbranchaud
1934c8f63e Add Make Direnv Less Noisy as a Unix TIL 2024-10-13 16:25:44 -05:00
jbranchaud
e5a003dbaf Add Markdown Files Are Of Type MarkdownInstance as an Astro TIL 2024-10-13 11:46:36 -05:00
jbranchaud
ab9d2b5bf6 Add Check Media Queries From JavaScript as a JavaScript TIL 2024-10-12 12:30:07 -05:00
jbranchaud
aa00c55b06 Add Jump To Problems In The Current File as a VSCode TIL 2024-10-12 10:35:52 -05:00
jbranchaud
cc858382d8 Add Temporarily Turn Off Pending Migrations Error as a Rails TIL 2024-10-11 15:05:31 -05:00
jbranchaud
dbb8c585c1 Add updated note to TIL about godoc 2024-10-11 13:57:47 -05:00
jbranchaud
f25064031f Add Export List Of Everything Installed By Brew as a Brew TIL 2024-10-11 10:57:42 -05:00
jbranchaud
cfbe640eb0 Add Keyboard Shortcuts For Interacting With Text Areas as a Mac TIL 2024-10-10 11:52:22 -05:00
jbranchaud
bf04dfcca5 Add Rollback A Couple Migrations as a Rails TIL 2024-10-09 17:44:45 -05:00
jbranchaud
24b1b02d52 Add Define The Root Path For The App as a Rails TIL 2024-10-09 13:23:09 -05:00
jbranchaud
8ef2cfdc69 Add Prevent erb_lint From Removing Opening Tags as a Ruby TIL 2024-10-09 10:53:39 -05:00
Mohammad Alyetama
bc767a0ad3 Update bew cask command 2022-11-24 17:49:13 -05:00
36 changed files with 1269 additions and 2 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).
_1455 TILs and counting..._
_1488 TILs and counting..._
---
@@ -19,6 +19,7 @@ _1455 TILs and counting..._
* [Ack](#ack)
* [Amplify](#amplify)
* [Ansible](#ansible)
* [Astro](#astro)
* [Brew](#brew)
* [Chrome](#chrome)
* [Clojure](#clojure)
@@ -26,6 +27,7 @@ _1455 TILs and counting..._
* [Deno](#deno)
* [Devops](#devops)
* [Docker](#docker)
* [Drizzle](#drizzle)
* [Elixir](#elixir)
* [Gatsby](#gatsby)
* [Git](#git)
@@ -98,9 +100,15 @@ _1455 TILs and counting..._
- [Loop Over A List Of Dictionaries](ansible/loop-over-a-list-of-dictionaries.md)
### Astro
- [Generate Types For A Content Collection](astro/generate-types-for-a-content-collection.md)
- [Markdown Files Are Of Type MarkdownInstance](astro/markdown-files-are-of-type-markdown-instance.md)
### Brew
- [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md)
- [Export List Of Everything Installed By Brew](brew/export-list-of-everything-installed-by-brew.md)
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
### Chrome
@@ -201,6 +209,11 @@ _1455 TILs and counting..._
- [List Running Docker Containers](docker/list-running-docker-containers.md)
- [Run A Basic PostgreSQL Server In Docker](docker/run-a-basic-postgresql-server-in-docker.md)
### Drizzle
- [Create bigint Identity Column For Primary Key](drizzle/create-bigint-identity-column-for-primary-key.md)
- [Get Fields For Inserted Row](drizzle/get-fields-for-inserted-row.md)
### Elixir
- [All Values For A Key In A Keyword List](elixir/all-values-for-a-key-in-a-keyword-list.md)
@@ -269,6 +282,7 @@ _1455 TILs and counting..._
- [Auto-Squash Those Fixup Commits](git/auto-squash-those-fixup-commits.md)
- [Caching Credentials](git/caching-credentials.md)
- [Change The Start Point Of A Branch](git/change-the-start-point-of-a-branch.md)
- [Check How A File Is Being Ignored](git/check-how-a-file-is-being-ignored.md)
- [Checking Commit Ancestry](git/checking-commit-ancestry.md)
- [Checkout Old Version Of A File](git/checkout-old-version-of-a-file.md)
- [Checkout Previous Branch](git/checkout-previous-branch.md)
@@ -318,6 +332,7 @@ _1455 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)
@@ -424,8 +439,10 @@ _1455 TILs and counting..._
### Internet
- [Add Emoji To GitHub Repository Description](internet/add-emoji-to-github-repository-description.md)
- [Analyze Your Website Performance](internet/analyze-your-website-performance.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)
@@ -435,6 +452,7 @@ _1455 TILs and counting..._
### Java
- [Ensure Resources Always Get Closed](java/ensure-resources-always-get-closed.md)
- [Install Java On Mac With Brew](java/install-java-on-mac-with-brew.md)
- [Run A Hello World Program In Eclipse](java/run-a-hello-world-program-in-eclipse.md)
@@ -448,6 +466,7 @@ _1455 TILs and counting..._
- [Check If A Number Is Positive Or Negative](javascript/check-if-a-number-is-positive-or-negative.md)
- [Check If File Exists Before Reading It](javascript/check-if-file-exists-before-reading-it.md)
- [Check If Something Is An Array](javascript/check-if-something-is-an-array.md)
- [Check Media Queries From JavaScript](javascript/check-media-queries-from-javascript.md)
- [Check The Password Confirmation With Yup](javascript/check-the-password-confirmation-with-yup.md)
- [Compare The Equality Of Two Date Objects](javascript/compare-the-equality-of-two-date-objects.md)
- [Computed Property Names In ES6](javascript/computed-property-names-in-es6.md)
@@ -493,6 +512,7 @@ _1455 TILs and counting..._
- [List Top-Level NPM Dependencies](javascript/list-top-level-npm-dependencies.md)
- [Load And Use Env Var In Node Script](javascript/load-and-use-env-var-in-node-script.md)
- [Make The Browser Editable With Design Mode](javascript/make-the-browser-editable-with-design-mode.md)
- [Make Truly Deep Clone With Structured Clone](javascript/make-truly-deep-clone-with-structured-clone.md)
- [Matching A Computed Property In Function Args](javascript/matching-a-computed-property-in-function-args.md)
- [Matching Multiple Values In A Switch Statement](javascript/matching-multiple-values-in-a-switch-statement.md)
- [Mock A Function With Return Values Using Jest](javascript/mock-a-function-with-return-values-using-jest.md)
@@ -590,6 +610,7 @@ _1455 TILs and counting..._
- [Find The Process Using A Specific Port](mac/find-the-process-using-a-specific-port.md)
- [Gesture For Viewing All Windows Of Current App](mac/gesture-for-viewing-all-windows-of-current-app.md)
- [Insert A Non-Breaking Space Character](mac/insert-a-non-breaking-space-character.md)
- [Keyboard Shortcuts For Interesting With Text Areas](mac/keyboard-shortcuts-for-interacting-with-text-areas.md)
- [List All The Say Voices](mac/list-all-the-say-voices.md)
- [Open Finder.app To Specific Directory](mac/open-finder-app-to-specific-directory.md)
- [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md)
@@ -683,6 +704,7 @@ _1455 TILs and counting..._
- [A Better Null Display Character](postgres/a-better-null-display-character.md)
- [Add Foreign Key Constraint Without A Full Lock](postgres/add-foreign-key-constraint-without-a-full-lock.md)
- [Add ON DELETE CASCADE To Foreign Key Constraint](postgres/add-on-delete-cascade-to-foreign-key-constraint.md)
- [Add Unique Constraint Using Existing Index](postgres/add-unique-constraint-using-existing-index.md)
- [Adding Composite Uniqueness Constraints](postgres/adding-composite-uniqueness-constraints.md)
- [Aggregate A Column Into An Array](postgres/aggregate-a-column-into-an-array.md)
- [Assumed Radius Of The Earth](postgres/assumed-radius-of-the-earth.md)
@@ -702,6 +724,7 @@ _1455 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)
@@ -732,6 +755,7 @@ _1455 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)
@@ -784,11 +808,13 @@ _1455 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)
@@ -899,6 +925,7 @@ _1455 TILs and counting..._
- [Custom Validation Message](rails/custom-validation-message.md)
- [Customize Paths And Helpers For Devise Routes](rails/customize-paths-and-helpers-for-devise-routes.md)
- [Customize The Path Of A Resource Route](rails/customize-the-path-of-a-resource-route.md)
- [Define The Root Path For The App](rails/define-the-root-path-for-the-app.md)
- [Delete Paranoid Records](rails/delete-paranoid-records.md)
- [Demodulize A Class Name](rails/demodulize-a-class-name.md)
- [Different Ways To Add A Foreign Key Reference](rails/different-ways-to-add-a-foreign-key-reference.md)
@@ -964,6 +991,7 @@ _1455 TILs and counting..._
- [Rescue From With A Separate Method](rails/rescue-from-with-a-separate-method.md)
- [Respond With JSON Regardless of Content Type](rails/respond-with-json-regardless-of-content-type.md)
- [Retrieve An Object If It Exists](rails/retrieve-an-object-if-it-exists.md)
- [Rollback A Couple Migrations](rails/rollback-a-couple-migrations.md)
- [Rollback A Specific Migration Out Of Order](rails/rollback-a-specific-migration-out-of-order.md)
- [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md)
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md)
@@ -976,6 +1004,7 @@ _1455 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)
@@ -986,6 +1015,7 @@ _1455 TILs and counting..._
- [Skip Validations When Creating A Record](rails/skip-validations-when-creating-a-record.md)
- [Specify New Attributes For #find_or_create_by](rails/specify-new-attributes-for-find-or-create-by.md)
- [Temporarily Disable strong_params](rails/temporarily-disable-strong-params.md)
- [Temporarily Turn Off Pending Migrations Error](rails/temporarily-turn-off-pending-migrations-error.md)
- [Test For A Subset Of Attributes On A Model](rails/test-for-a-subset-of-attributes-on-a-model.md)
- [Test If An Instance Variable Was Assigned](rails/test-if-an-instance-variable-was-assigned.md)
- [Test If deliver_later Is Called For A Mailer](rails/test-if-deliver-later-is-called-for-a-mailer.md)
@@ -1212,6 +1242,7 @@ _1455 TILs and counting..._
- [Pattern Match Values From A Hash](ruby/pattern-match-values-from-a-hash.md)
- [Percent Notation](ruby/percent-notation.md)
- [Precedence Of Logical Operators](ruby/precedence-of-logical-operators.md)
- [Prevent erb_lint From Removing Opening Tags](ruby/prevent-erb-lint-from-removing-opening-tags.md)
- [Print Data To Formatted Table](ruby/print-data-to-formatted-table.md)
- [Question Mark Operator](ruby/question-mark-operator.md)
- [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md)
@@ -1371,6 +1402,7 @@ _1455 TILs and counting..._
- [Command Line Length Limitations](unix/command-line-length-limitations.md)
- [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md)
- [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md)
- [Convert JPEG To PNG With ffmpeg](unix/convert-jpeg-to-png-with-ffmpeg.md)
- [Convert SVG To Favicon](unix/convert-svg-to-favicon.md)
- [Copying File Contents To System Paste Buffer](unix/copying-file-contents-to-system-paste-buffer.md)
- [Copying Nested Directories With Ditto](unix/copying-nested-directories-with-ditto.md)
@@ -1441,6 +1473,7 @@ _1455 TILs and counting..._
- [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md)
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
- [Map A Domain To localhost](unix/map-a-domain-to-localhost.md)
- [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md)
- [Occupy A Local Port With Netcat](unix/occupy-a-local-port-with-netcat.md)
@@ -1641,6 +1674,7 @@ _1455 TILs and counting..._
- [Swap Occurrences Of Two Words](vim/swap-occurrences-of-two-words.md)
- [Swapping Split Windows](vim/swapping-split-windows.md)
- [Swap The Position Of Two Split Windows](vim/swap-the-position-of-two-split-windows.md)
- [Switch Moving End Of Visual Selection](vim/switch-moving-end-of-visual-selection.md)
- [Tabs To Spaces](vim/tabs-to-spaces.md)
- [The Vim Info File](vim/the-vim-info-file.md)
- [Toggle Absolute And Relative Paths In BufExplorer](vim/toggle-absolute-and-relative-paths-in-bufexplorer.md)
@@ -1665,6 +1699,7 @@ _1455 TILs and counting..._
- [Advance Through Search Results](vscode/advance-through-search-results.md)
- [Enable Breadcrumbs For Version 1.26 Release](vscode/enable-breadcrumbs-for-version-126-release.md)
- [Find The Location Of User Settings JSON File](vscode/find-the-location-of-user-settings-json-file.md)
- [Jump To Problems In The Current File](vscode/jump-to-problems-in-the-current-file.md)
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
@@ -1681,14 +1716,18 @@ _1455 TILs and counting..._
### Workflow
- [Add Hotkeys For Specific Raycast Extensions](workflow/add-hotkeys-for-specific-raycast-extensions.md)
- [Add Subscriber To Kit Form Via API](workflow/add-subscriber-to-kit-form-via-api.md)
- [Add Subtitles To Existing Mux Video Asset](workflow/add-subtitles-to-existing-mux-video-asset.md)
- [Access 1Password Credential From CLI](workflow/access-1password-credential-from-cli.md)
- [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md)
- [Configure Email Redirect With Cloudflare](workflow/configure-email-redirect-with-cloudflare.md)
- [Convert An ePub Document To PDF On Mac](workflow/convert-an-epub-document-to-pdf-on-mac.md)
- [Create A Local Sanity Dataset Backup](workflow/create-a-local-sanity-dataset-backup.md)
- [Create A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md)
- [Enable Dev Tools For Safari](workflow/enable-dev-tools-for-safari.md)
- [Forward Stripe Events To Local Server](workflow/forward-stripe-events-to-local-server.md)
- [Get URL For GitHub User Profile Photo](workflow/get-url-for-github-user-profile-photo.md)
- [Get Your Public IP Address](workflow/get-your-public-ip-address.md)
- [Import A Github Project Into CodeSandbox](workflow/import-a-github-project-into-codesandbox.md)
- [Interactively Kill A Process With fkill](workflow/interactively-kill-a-process-with-fkill.md)
@@ -1696,6 +1735,7 @@ _1455 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)
@@ -1731,6 +1771,7 @@ _1455 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

View File

@@ -0,0 +1,56 @@
# Generate Types For A Content Collection
Let's say I'm using Astro to publish posts via markdown. One of the best ways
to do that is as a _Content Collection_. The posts will live in `src/content`
probably under a `posts` directory. Plus a config file will define the
collection and specify validations for the frontmatter.
```typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const postsCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
tags: z.array(z.string())
})
});
export const collections = {
'posts': postsCollection,
};
```
When I first add this to my project and get the collection, it won't know what
the types are.
```astro
---
import { getCollection } from "astro:content";
export async function getStaticPaths() {
const blogEntries = await getCollection("posts");
// ^^^ any
return blogEntries.map((entry) => ({
params: { slug: entry.slug },
props: { entry },
}));
}
---
```
I can tell Astro to generate a fresh set of types for things like content
collections by running the [`astro sync`
command](https://docs.astro.build/en/reference/cli-reference/#astro-sync).
```bash
$ npm run astro sync
```
This updates auto-generated files under the `.astro` directory which get pulled
in to your project's `env.d.ts` file.
All of these types will also be synced anytime I run `astro dev`, `astro
build`, or `astro check`.

View File

@@ -0,0 +1,53 @@
# Markdown Files Are Of Type MarkdownInstance
One of the things Astro excels at is rendering markdown files as HTML pages in
your site. And at some point we'll want to access a listing of those markdown
files in order to do something like display a list of them on an index page.
For that, we'll use
[`Astro.glob()`](https://docs.astro.build/en/reference/api-reference/#astroglob).
```typescript
---
const allPosts = await Astro.glob("../posts/*.md");
---
<ul>
{allPosts.map(post => {
return <Post title={post.frontmatter.title} slug={post.frontmatter.slug} />
})}
</ul>
```
This looks great, but we'll run into a type error on that first line:
`'allPosts' implicitly has type 'any'`. We need to declare the type
of these post instances that are being read-in by Astro.
These are of [type
`MarkdownInstance`](https://docs.astro.build/en/reference/api-reference/#markdown-files).
That's a generic though, so we need to tell it a bit more about the shape of a
post.
```typescript
import type { MarkdownInstance } from "astro";
export type BarePost = {
layout: string;
title: string;
slug: string;
tags: string[];
};
export type Post = MarkdownInstance<BarePost>;
```
We can then update that first line:
```typescript
const allPosts: Post[] = await Astro.glob("../posts/*.md");
```
Alternatively, you can specify the generic on `glob`:
```typescript
const allPosts = await Astro.glob<BarePost>("../posts/*.md");
```

View File

@@ -0,0 +1,48 @@
# Export List Of Everything Installed By Brew
If you're on a Mac using Homebrew to install various tools and utilities, there
may come a time when you want a listing of what is installed.
Run this command:
```bash
$ brew bundle dump
```
It may take 10 or so seconds. When it is done, you'll have a `Brewfile` in your
current directory.
Open it up and you'll see a bunch of lines like the following:
```
tap "heroku/brew"
tap "homebrew/bundle"
tap "homebrew/services"
tap "mongodb/brew"
tap "planetscale/tap"
tap "stripe/stripe-cli"
brew "asdf"
brew "bat"
brew "direnv"
brew "entr"
brew "exa"
brew "fd"
brew "ffmpeg"
brew "fx"
brew "fzf"
brew "gcc"
brew "gh"
brew "planetscale/tap/pscale"
brew "stripe/stripe-cli/stripe"
cask "1password-cli"
vscode "ms-playwright.playwright"
vscode "ms-vsliveshare.vsliveshare"
vscode "prisma.prisma"
```
Notice there are `tap`, `brew`, `cask`, and even `vscode` directives.
This is a file you could export and then run on a 'new' machine to install all
the programs you're used to having available on your current machine.
[source](https://danmunoz.com/setting-up-a-new-computer-with-homebrew/)

View File

@@ -0,0 +1,48 @@
# Create bigint Identity Column For Primary Key
Using the Drizzle ORM with Postgres, here is how we can create a table that
uses a [`bigint` data
type](https://orm.drizzle.team/docs/column-types/pg#bigint) as a primary key
[identity
column](https://www.postgresql.org/docs/current/ddl-identity-columns.html).
```typescript
import {
pgTable,
bigint,
text,
timestamp,
} from "drizzle-orm/pg-core";
// Users table
export const users = pgTable("users", {
id: bigint({ mode: 'bigint' }).primaryKey().generatedAlwaysAsIdentity(),
email: text("email").unique().notNull(),
name: text("name").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
```
There are a couple key pieces here:
1. We import `bigint` so that we can declare a column of that type.
2. We specify that it is a primary key with `.primaryKey()`.
3. We declare its default value as `generated always as identity` via
`.generatedAlwaysAsIdentity()`.
Note: you need to specify the `mode` for `bigint` or else you will see a
`TypeError: Cannot read properties of undefined (reading 'mode')` error.
If we run `npx drizzle-kit generate` the SQL migration file that gets
generated will contain something like this:
```sql
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" (
"id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "users_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
"email" text NOT NULL,
"name" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "users_email_unique" UNIQUE("email")
);
```

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,28 @@
# Check How A File Is Being Ignored
There are a few places on your machine where you can specify the files that git
should ignore. The most common is a repository's `.gitignore` file. The other
places those excludes are specified can be more obscure. Fortunately, `git
check-ignore` is a command that can show you specifically where.
For instance, let's check why my `notes.md` file is being ignored.
```bash
$ git check-ignore -v .DS_Store
.git/info/exclude:7:notes.md notes.md
```
At some point I added it to my repo's `.git/info/exclude` file. The `-v` flag
(_verbose_) when included with `check-ignore` tells me the file location.
How about these pesky `.DS_Store` directories? How are those being ignored?
```bash
$ git check-ignore -v .DS_Store
/Users/jbranchaud/.gitignore:3:.DS_Store .DS_Store
```
Ah yes, I had added it to my _global exclude file_ which I've configured in
`~/.gitconfig` to be the `~/.gitignore` file.
See `man git-check-ignore` for more details.

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

View File

@@ -15,4 +15,10 @@ $ godoc -http=:6060
and then visit `localhost:6060`.
Note: if you do not already have `godoc` installed, you can install it with:
```bash
$ go install golang.org/x/tools/cmd/godoc@latest
```
[source](http://www.andybritcliffe.com/post/44610795381/offline-go-lang-documentation)

View File

@@ -0,0 +1,21 @@
# Analyze Your Website Performance
The [PageSpeed Insights](https://pagespeed.web.dev/) tool from Google is a
great way to quickly get actionable insights about where to improve your
website and app's _Performance_, _Accessibility_, and _SEO_.
To see how your public site or app does, grab its URL and analyze it at
[PageSpeed Insights](https://pagespeed.web.dev/).
It will take a minute to run on either Mobile or Desktop (make sure to check
both) and then will output four headline numbers (out of 100) for each of the
categories.
You can then dig in to each category to see what recommendations they make for
improving your score.
This can also be run directly from Chrome devtools which is useful if you want
to see how a locally running site is doing. You can run the analysis from the
_Lighthouse_ tab of devtools. Note: if the _Performance_ score looks bad, it
might be that you are running a non-optimized dev server that isn't reflective
of how your site would do in production.

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

View File

@@ -0,0 +1,55 @@
# Ensure Resources Always Get Closed
Java has a construct known as _try-with-resource_ that allows us to always
ensure opened resources get closed. This is safer than similar cleanup in the
`finally` block which could still leave a memory leak if an error occurs in
that block.
To use the _try-with-resource_ construct, instantiate your opened resource in
parentheses with the `try`.
```java
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
// ...
}
```
The resource will be automatically closed when the try/catch block completes.
Here is a full example:
```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
String fileName = "example.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
int lineCount = 0;
while ((line = reader.readLine()) != null && lineCount < 5) {
System.out.println(line);
lineCount++;
}
} catch (IOException e) {
System.out.println("An error occurred while reading the file: " + e.getMessage());
}
}
}
```
You can even specify multiple resources in one `try`. The above does that, but
this will make it more obvious:
```java
try (FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr)) {
// ...
}
```
[source](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)

View File

@@ -0,0 +1,28 @@
# Check Media Queries From JavaScript
I'm usually thinking about and [using media
queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries)
from a CSS context. I use them to control what styles are displayed for a
variety of scenarios, such as at different screen widths, when a user prefers
reduced motion, or when the user prefers a dark color scheme.
The current value of various media queries can be checked from a JavaScript
context as well.
For instance, if we want to see if the user prefers a _dark_ color schema, we
can look for a _match_ on that media query with
[`matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia).
```javascript
> window.matchMedia('(prefers-color-scheme: dark)')
MediaQueryList {media: '(prefers-color-scheme: dark)', matches: true, onchange: null}
> window.matchMedia('(prefers-color-scheme: dark)')['matches']
true
```
This queries for the [`prefers-color-scheme` media
feature](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
The [Astro.build Blog
Tutorial](https://docs.astro.build/en/tutorial/6-islands/2/#add-client-side-interactivity)
shows an example of using this to wire up a Light/Dark mode toggle.

View File

@@ -0,0 +1,61 @@
# Make Truly Deep Clone With Structured Clone
There are a lot of ways to make a copy of an object. They are all hacks and
they all fail in certain circumstances. Using the spread trick only gives you a
shallow copy where references to nested objects and arrays can still be
updated. The `JSON.stringify` trick has to make things like dates into strings,
so it is lossy.
There is however now a dedicated method for deep copies with broad support
called
[`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone).
It is available on `window`. Let's take a look at it and see how it comparse to
the spread operator trick.
```javascript
> // some data setup
> const data = { one: 1, two: 2, rest: [3,4,5] }
> const obj = { hello: 'world', taco: 'bell', data }
> const shallowObj = { ...obj }
> const deepObj = structuredClone(obj)
> // let's modify the original `data.rest` array
> data.rest.push(6)
4
> data
{ one: 1, two: 2, rest: [ 3, 4, 5, 6 ] }
> // now let's see who was impacted by that mutation
> obj
{
hello: 'world',
taco: 'bell',
data: { one: 1, two: 2, rest: [ 3, 4, 5, 6 ] }
}
> shallowObj
{
hello: 'world',
taco: 'bell',
data: { one: 1, two: 2, rest: [ 3, 4, 5, 6 ] }
}
> deepObj
{
hello: 'world',
taco: 'bell',
data: { one: 1, two: 2, rest: [ 3, 4, 5 ] }
}
```
The `shallowObj` from the spread operator copy was mutated even though we
didn't intend for that. The `deepObj` from `structuredClone` was a true deep
copy and was unaffected.
[source](https://www.builder.io/blog/structured-clone)

View File

@@ -0,0 +1,24 @@
# Keyboard Shortcuts For Interacting With Text Areas
When interacting with a document text area on MacOS (such as in the Notes app),
there are a bunch of keyboard shortcuts made available to you via the operating
system.
A couple common ones that I'm used to from Unix environments are:
- `ctrl-a` to move the cursor to the beginning of the line
- `ctrl-e` to move the cursor to the end of the line
- `ctrl-p` to move the cursor up a line (this is a common _previous_ keybinding)
- `ctrl-n` to move the cursor down a line (this is a common _next_ keybinding)
A handy one that I wasn't aware of is `ctrl-l` which will scroll the text area
so that the cursor gets centered in the view area.
A much more niche one I wasn't aware of is `ctrl-t` which swaps the character
before the cursor with the character after the cursor. I can only imagine this
being useful for quickly fixing transposed characters in a misspelled word,
e.g. `baer`.
There are many more *Document Shortcuts* as well as well as keyboard shorcuts
for other apps and situations. The full listing is at [Mac Keyboard
Shortcuts](https://support.apple.com/en-us/102650).

View File

@@ -0,0 +1,25 @@
# Add Unique Constraint Using Existing Index
Adding a unique constraint to an existing column on a production table can
block updates. If we need to avoid this kind of locking for the duration of
index creation, then we can first create the index concurrently and then use
that existing index to back the unique constraint.
```sql
create index concurrently users_email_idx on users (email);
-- wait for that to complete
alter table users
add constraint unique_users_email unique using index users_email_idx;
```
First, we concurrently create the index. The time this takes will depend on how
large the table is. That's the blocking time we are avoiding with this
approach. Then once that completes we can apply a unique constraint using that
preexisting index.
Note: if a non-unique value exists in the table for that column, adding the
constraint will fail. You'll need to deal with that _duplicate_ value first.
[source](https://dba.stackexchange.com/questions/81627/postgresql-9-3-add-unique-constraint-using-an-existing-unique-index)

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

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

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

View File

@@ -0,0 +1,37 @@
# Define The Root Path For The App
The `root_path` helper that you might want to use in Rails controllers and
views is not available by default.
```ruby
> Rails.application.routes.url_helpers.root_path
ruby/3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.14.0/lib/irb.rb:1285:in `full_message': undefined method `root_path' for #<Module:0x0000000106d11738> (NoMethodError)
Rails.application.routes.url_helpers.root_path
^^^^^^^^^^
Did you mean? logout_path
book_path
```
It needs to be declared in the `config/routes.rb` file with the controller
action that it points to.
```ruby
# config/routes.rb
Rails.application.routes.draw do
root 'home#index'
end
```
Once this is defined the `root_path` will now be available with the rest of
your URL helpers.
```ruby
better-reads(dev)> reload!
Reloading...
better-reads(dev)> Rails.application.routes.url_helpers.root_path
=> "/"
```
[source](https://guides.rubyonrails.org/routing.html#using-root)

View File

@@ -0,0 +1,25 @@
# Rollback A Couple Migrations
Let's say we need to rollback a couple Rails migrations that have been applied
to our local environment. We run `rails db:migrate:status` and see that there
are _2_ migrations that we want to _undo_.
We can accomplish this by using the `STEP` env var with the rollback command.
```bash
$ rails db:rollback STEP=2
```
Just set `STEP` to the number of migrations that we need to rollback. If we
then rerun `rails db:migrate:status` we'll now see those latest two migrations
are `down`.
Note: by default Rails doesn't like to operate with pending migrations. If we
want to temporarily disable the pending migration check, we can alter the
migration error config in `config/development.rb`.
```diff
# Raise an error on page load if there are pending migrations.
- # config.active_record.migration_error = :page_load
+ config.active_record.migration_error = false
```

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,33 @@
# Temporarily Turn Off Pending Migrations Error
Whenever I'm working on an end-to-end feature in a Rails app, soon or later I
am going to see the _Pending Migrations_ error page. I try to visit one of the
routes in the browser and the Rails app serves this error page instead of my
actual request response.
This is typically what I want. If there are migrations just sitting there that
haven't been run yet, that's probably an issue. Maybe I just pulled down the
latest changes from my teammates. The app isn't going to work properly without
whatever schema changes are prescribed in those pending migrations.
The thing to do is run those migrations.
In some special cases though, I know what I'm doing and I would like to operate
my app locally with specific migrations not yet applied.
To skip the error, I can change this `config/environments/development.rb`
setting from:
```ruby
config.active_record.migration_error = :page_load
```
to:
```ruby
config.active_record.migration_error = false
```
I just need to make sure to switch it back when I'm done.
[source](https://til.hashrocket.com/posts/ujcixh5rwi-rails-ignore-pending-migrations)

View File

@@ -0,0 +1,45 @@
# Prevent erb_lint From Removing Opening Tags
The [`erb_lint` gem](https://github.com/Shopify/erb_lint) is a tool from
shopify for linting and auto-formatting ERB files. When I first set it up in a
Rails codebase with the base `.erb-lint.yml` recommended in the README, I ran
into a pernicious issue. The linter wanted to remove opening tags (i.e. `<%`
and `<%=`) from my ERB files.
So, for a file that looked like this:
```erb
<div>
<%= form_with(url: login_path, scope: :session) do |f| %>
<div>
<%= f.label :email %>
<%= f.email_field :email %>
</div>
</div>
```
It would get formatted to this:
```erb
<div>
form_with(url: login_path, scope: :session) do |f| %>
<div>
f.label :email %>
f.email_field :email %>
</div>
</div>
```
Yikes!
I had to disable a couple rules (under `rubocop_config:`) in the `.erb-lint.yml` file to get it to stop
doing this.
```yaml
Layout/InitialIndentation:
Enabled: false
Layout/TrailingEmptyLines:
Enabled: false
```
[source](https://github.com/Shopify/erb_lint/issues/222)

View File

@@ -0,0 +1,20 @@
# Convert JPEG To PNG With ffmpeg
The `ffmpeg` utility "is a universal media converter." That means we can use it
to convert, for instance, a JPEG file to a PNG file.
There is not a lot to a conversion like this. We use `-i` to specify the
existing input file (a JPEG) and then the other argument is the name and
extension of the output file.
```bash
$ ls
profile.jpg
$ ffmpeg -i profile.jpg profile.png
$ ls
profile.jpg profile.png
```
See `man ffmpeg` for more details.

View File

@@ -0,0 +1,32 @@
# Make Direnv Less Noisy
I've been using [`direnv`](https://direnv.net/) to manage project and folder
specific environment variables for a bit now. I've found it to be pretty
seamless. It can feel like it is littering my shell with too much output when I
change directories though.
There are two levers to control its output.
First, the direnv logs (e.g. `direnv: loading ~/.../.envrc`) can be controlled
with the `DIRENV_LOG_FORMAT` env var. Add this to the
`~/.config/direnv/direnvrc` file (add that directory and file if necessary).
You can leave it blank to altogether hide log messages or you can gray-out the
log messages like this:
```
export DIRENV_LOG_FORMAT=$'\033[2mdirenv: %s\033[0m'
```
Second, you can hide the env var diff with a separate config. This diff is not
covered under the umbrella of logs controlled by the above setting. Set
[`hide_env_diff` in the `~/.config/direnv/direnv.toml`
file](https://direnv.net/man/direnv.toml.1.html#codehideenvdiffcode):
```toml
[global]
hide_env_diff = true
```
This second config was only added as of `v2.34.0`.
[source](https://esham.io/2023/10/direnv)

View File

@@ -0,0 +1,17 @@
# Switch Moving End Of Visual Selection
When I go into character-wise visual selection mode, one end of the visual
selection is fixed while I move my cursor around to define the other end of it.
Let's say I've arranged a visual selection that encompasses several lines of my
file. And then I realize that the fixed front-end of my visual selection is off
by a bit. Maybe I've selected an entire function definition and I just want to
inner part of the function.
Instead of starting over with my visual selection. I can leave the right-end of
the visual selection where it is, hit `o` which will switch the moving end to
the other side, and then continue making adjustments from there.
I can always hit `o` again to switch it back to the original side.
See `:h v_o` for more details.

View File

@@ -0,0 +1,18 @@
# Jump To Problems In The Current File
VSCode has a system for different extensions to report problems. The editor
will visually flag those problems in the editor with a red squiggly underline.
It will also list them in the _Problems_ tab of the bottom tray (hit `Cmd+j` to
pop that tray open).
If there are some active problems in your current file, you can jump right to
them.
Hit `F8` and you'll jump to the next problem after wherever the cursor is. Hit
`F8` again and it will go to the next one.
Want to track back through the file? Hit `Shift+F8` and you'll jump to the
closest problem _behind_ the current cursor position.
Using these two, you can quickly survey the current problems in your file
before deciding how to proceed.

View File

@@ -0,0 +1,19 @@
# Add Hotkeys For Specific Raycast Extensions
One of the main things I use [Raycast](https://www.raycast.com/) for is its
built-in clipboard history extension. It's super handy when I've copied a Git
SHA or a URL or a bit of text several copies ago and I want to be able to refer
back to it. It keeps 7 days of clipboard history by default, so anything that I
remember copying is going to be there.
To get there, I have to trigger Raycast, which I usually open via Alfred (I
know, 😅). Then I search for Clipboard History (or find it under recent
suggestions).
Raycast supports arbitrary hotkey bindings for triggering different extensions
and their features. So I can get to the Clipboard History in a single step --
e.g. with `Ctrl+Shift+V`.
From Raycast _Settings_, go to the _Extensions_ tab. Find _Clipboard History_
in the list and then click the _Record Hotkey_ input for it. I then type
`Ctrl+Shift+V` and I'm all set.

View File

@@ -0,0 +1,64 @@
# Add Subscriber To Kit Form Via API
Because the Kit API is a bit sparse in words, I found it difficult to figure
out exactly what I needed to do to add a subscriber via a custom form. After
some experimenting and reading through [Course
Builder](https://github.com/badass-courses/course-builder/blob/main/packages/core/src/providers/convertkit.ts)
code, I was able to figure out that with the Kit v4 API there are two separate
calls that need to be made.
First, you need to create an `inactive` subscriber with the user's email (and
first name if given) via the [Create a subscriber
endpoint](https://developers.kit.com/v4#create-a-subscriber).
```typescript
async function createSubscriber(
data: SubscriberData,
apiKey: string,
): Promise<Response> {
return fetch('https://api.convertkit.com/v4/subscribers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Kit-Api-Key': apiKey,
},
body: JSON.stringify({
email_address: data.email,
first_name: data.name,
state: 'inactive',
}),
})
}
```
Since this subscriber is `inactive`, they won't show up in the Kit dashboard,
but a subscriber record of them has been created.
Second, you need to add the subscriber to something, like a form or a sequence.
In my case, I had already created a form via the Kit dashboard, so I grab that
_Form ID_ and stick it in my `.env`. That way I am able to [add the subscriber
to the form by email
address](https://developers.kit.com/v4#add-subscriber-to-form-by-email-address).
```typescript
async function addSubscriberToForm(
email: string,
apiKey: string,
formId: string,
): Promise<Response> {
const url = `https://api.convertkit.com/v4/forms/${formId}/subscribers`
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Kit-Api-Key': apiKey,
},
body: JSON.stringify({ email_address: email }),
})
}
```
This will send a confirmation email to the email address. Once the person has
opened the email and hit 'Confirm', their corresponding `subscriber` record
will be marked as `active` and they will be added to the list of subscribers
for that form.

View File

@@ -0,0 +1,23 @@
# Configure Email Redirect With Cloudflare
I have a domain registered with Cloudflare --
[visualmode.dev](https://www.visualmode.dev). I want to be able to sign up for
services and receive emails as `josh@visualmode.dev`. I don't want a separate
inbox that I have to check though.
The solution, for me, is to have Cloudflare redirect incoming emails to an
email address/inbox that I'm already regularly checking.
On the _Email_ dashboard there is a _Routing Rules_ tab. In the _Custom
Addresses_ section, I can click _Create Address_. There I specify the custom
address (what will appear before the `@`), the action to make ("Send to an
email"), and the destination (the existing email address for an inbox I
regularly check).
Finally, I hit _Save_. If they don't already exist, I'll be prompted to confirm
the setup of MX records for the domain. After confirming that, I should be able
to receive emails via that new address.
The Email Routing dashboard will even show me a summary of all rerouted emails.
[source](https://blog.cloudflare.com/introducing-email-routing/#cloudflare-email-routing)

View File

@@ -6,7 +6,7 @@ convert it using the `ebook-convert` binary from `Calibre`.
First, install `Calibre`:
```bash
$ brew cask install calibre
$ brew install --cask calibre
```
Then convert your ePub using `ebook-convert`:

View File

@@ -0,0 +1,27 @@
# Get URL For GitHub User Profile Photo
You can access your (or really any user's) profile photo by visiting the GitHub
profile URL with `.png` added on to the end.
So, my GitHub profile URL is:
```
https://github.com/jbranchaud
```
If I were to add `.png` on to that and visit it in my browser:
```
https://github.com/jbranchaud.png
```
I'd be redirected to the following URL:
```
https://avatars.githubusercontent.com/u/694063?v=4
```
This is the stable URL for my GitHub avatar. In the browser I will see the full
resolution image which I can download as needed.
[source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82)

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

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