mirror of
https://github.com/jbranchaud/til
synced 2026-01-10 18:48:02 +00:00
Compare commits
171 Commits
2a1dae4757
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
712fc66aae | ||
|
|
e95477607e | ||
|
|
087766a792 | ||
|
|
4801e730f9 | ||
|
|
bd021f7eab | ||
|
|
8d8cfd56ce | ||
|
|
f4faa06258 | ||
|
|
8ccbd82320 | ||
|
|
5ea4165893 | ||
|
|
73476a8d16 | ||
|
|
c5ce81f918 | ||
|
|
32be787998 | ||
|
|
1d835d3553 | ||
|
|
0d4959046d | ||
|
|
b1198d2488 | ||
|
|
6c3805e7cd | ||
|
|
d980514bff | ||
|
|
db26fc97c6 | ||
|
|
8094448877 | ||
|
|
883b3e6ee6 | ||
|
|
57c4954d6f | ||
|
|
86a7815a9f | ||
|
|
676038e992 | ||
|
|
01fd503a92 | ||
|
|
8b718aee4f | ||
|
|
88f49de7f3 | ||
|
|
9f9fce7835 | ||
|
|
65a4d0ef3d | ||
|
|
6c8a5eb36d | ||
|
|
fed722d7fe | ||
|
|
fbebc3e5ee | ||
|
|
83d55c420e | ||
|
|
8dbbfe0eda | ||
|
|
c38d9f090e | ||
|
|
bae3527baf | ||
|
|
53a0b88eff | ||
|
|
665c8f994f | ||
|
|
5f11b1665b | ||
|
|
ce5ff038c0 | ||
|
|
486a6ef5a9 | ||
|
|
c1ce559452 | ||
|
|
07c4aa86b7 | ||
|
|
a0c2a29a96 | ||
|
|
45b269abf1 | ||
|
|
44dc6f2b1f | ||
|
|
821a7e5c67 | ||
|
|
c0f20267bb | ||
|
|
50deb6175f | ||
|
|
d1f41884ce | ||
|
|
91149fe7cc | ||
|
|
e2eb31a4a9 | ||
|
|
113b7b2dfe | ||
|
|
16074c021f | ||
|
|
9e76753540 | ||
|
|
eb4dea611e | ||
|
|
6ef998b024 | ||
|
|
6e066ec72a | ||
|
|
060ce8262d | ||
|
|
96fd138837 | ||
|
|
a51d716e45 | ||
|
|
59de2fef0d | ||
|
|
fdd2461b75 | ||
|
|
e8c2e01d6f | ||
|
|
ed9cedc870 | ||
|
|
da585ec5a4 | ||
|
|
35d1a81ea7 | ||
|
|
d69fefe9f0 | ||
|
|
1cc612294e | ||
|
|
d79264395b | ||
|
|
2d5abd9cbf | ||
|
|
2efaf27066 | ||
|
|
6b4b2c588c | ||
|
|
e473fa781d | ||
|
|
5ce5eccb0a | ||
|
|
db4961a8eb | ||
|
|
ff227a39ed | ||
|
|
0d3975eb9c | ||
|
|
d171c3784b | ||
|
|
e6d00a94f3 | ||
|
|
0e934d8dd3 | ||
|
|
c30b17dd68 | ||
|
|
757e163c2e | ||
|
|
cf037f13f7 | ||
|
|
08fb235e81 | ||
|
|
95dc00d748 | ||
|
|
ece12aac76 | ||
|
|
9a6a40bdd6 | ||
|
|
4b4bd2350f | ||
|
|
5924edf4c0 | ||
|
|
5eb21b3aa2 | ||
|
|
5b3f1536fd | ||
|
|
ec0e84664f | ||
|
|
3912276599 | ||
|
|
d166ffac0b | ||
|
|
e8b953ba6d | ||
|
|
8613c21f41 | ||
|
|
2b5df03981 | ||
|
|
aef15d53b0 | ||
|
|
0c31fb6363 | ||
|
|
cb94142042 | ||
|
|
ae2974e3b8 | ||
|
|
0ed4d84bc6 | ||
|
|
3b7e3258fe | ||
|
|
d00796b054 | ||
|
|
8fecb0e863 | ||
|
|
14942c20d7 | ||
|
|
e901ae3b77 | ||
|
|
a4fee08596 | ||
|
|
6e518763c7 | ||
|
|
bb40353512 | ||
|
|
917f9e516e | ||
|
|
d8dfcce0fc | ||
|
|
0d173ccaaf | ||
|
|
8dd9f86b80 | ||
|
|
2bb8af2880 | ||
|
|
e16c2525be | ||
|
|
a55fff68e1 | ||
|
|
162a7ceea3 | ||
|
|
f578727349 | ||
|
|
4ba53dca7d | ||
|
|
571f465fe6 | ||
|
|
a547b9cee2 | ||
|
|
99ce5aee7b | ||
|
|
60b6aa40ad | ||
|
|
f97634a61e | ||
|
|
34ba60d313 | ||
|
|
3a178e901e | ||
|
|
db07125ba9 | ||
|
|
b6cf4ba0ad | ||
|
|
e4d695e465 | ||
|
|
5c9a3888fd | ||
|
|
22541826d6 | ||
|
|
b39ee94c90 | ||
|
|
efad7da916 | ||
|
|
ca3327bda3 | ||
|
|
595ac85f17 | ||
|
|
92d732c769 | ||
|
|
d6ebe52523 | ||
|
|
93398ab950 | ||
|
|
b1b2aa8982 | ||
|
|
6cbf1cb974 | ||
|
|
79faae1047 | ||
|
|
1316eb70ec | ||
|
|
ddf1c51fd9 | ||
|
|
60020d6e0e | ||
|
|
ef9f88f3c8 | ||
|
|
c8445c45a9 | ||
|
|
497b0ff3b7 | ||
|
|
64df6d16d7 | ||
|
|
7dac057246 | ||
|
|
8961c67026 | ||
|
|
4ff24a7336 | ||
|
|
2916fbc3b5 | ||
|
|
e3fc10edd8 | ||
|
|
4fe0817b2d | ||
|
|
fc74264040 | ||
|
|
2b38e1caf8 | ||
|
|
db6d18f086 | ||
|
|
84548b7a7f | ||
|
|
f9b966a0f1 | ||
|
|
871d555e95 | ||
|
|
cea3bc05b5 | ||
|
|
ee31f5b70d | ||
|
|
5b6a88b327 | ||
|
|
49ebb8dd78 | ||
|
|
6ff8f19d08 | ||
|
|
14e6635383 | ||
|
|
0a0a509827 | ||
|
|
bb331577ca | ||
|
|
0e0dcbf2b4 | ||
|
|
fe9b62a631 |
5
.gitmodules
vendored
Normal file
5
.gitmodules
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[submodule "notes"]
|
||||
path = notes
|
||||
url = git@github.com:jbranchaud/til-notes-private.git
|
||||
branch = main
|
||||
ignore = all
|
||||
186
README.md
186
README.md
@@ -6,26 +6,32 @@ A collection of concise write-ups on small things I learn day to day across a
|
||||
variety of languages and technologies. These are things that don't really
|
||||
warrant a full blog post. These are things I've picked up by [Learning In
|
||||
Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and
|
||||
pairing with smart people at Hashrocket.
|
||||
working across different projects via [VisualMode](https://www.visualmode.dev/).
|
||||
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
|
||||
|
||||
_1589 TILs and counting..._
|
||||
_1723 TILs and counting..._
|
||||
|
||||
See some of the other learning resources I work on:
|
||||
|
||||
- [Get Started with Vimium](https://egghead.io/courses/get-started-with-vimium~3t5f7)
|
||||
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
||||
- [Vim Un-Alphabet](https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU)
|
||||
|
||||
If you've learned something here, support my efforts writing daily TILs by
|
||||
[buying me a coffee](https://buymeacoffee.com/jbranchaud) 💜
|
||||
|
||||
---
|
||||
|
||||
### Categories
|
||||
|
||||
* [Ack](#ack)
|
||||
* [Amplify](#amplify)
|
||||
* [Ansible](#ansible)
|
||||
* [Astro](#astro)
|
||||
* [AWS](#aws)
|
||||
* [Brew](#brew)
|
||||
* [Chrome](#chrome)
|
||||
* [Claude Code](#claude-code)
|
||||
* [Clojure](#clojure)
|
||||
* [CSS](#css)
|
||||
* [Deno](#deno)
|
||||
@@ -35,6 +41,7 @@ See some of the other learning resources I work on:
|
||||
* [Elixir](#elixir)
|
||||
* [Gatsby](#gatsby)
|
||||
* [Git](#git)
|
||||
* [GitHub](#github)
|
||||
* [GitHub Actions](#github-actions)
|
||||
* [Go](#go)
|
||||
* [GROQ](#groq)
|
||||
@@ -51,6 +58,7 @@ See some of the other learning resources I work on:
|
||||
* [Linux](#linux)
|
||||
* [LLM](#llm)
|
||||
* [Mac](#mac)
|
||||
* [Mise](#mise)
|
||||
* [MongoDB](#mongodb)
|
||||
* [MySQL](#mysql)
|
||||
* [Neovim](#neovim)
|
||||
@@ -76,6 +84,7 @@ See some of the other learning resources I work on:
|
||||
* [SQLite](#sqlite)
|
||||
* [Streaming](#streaming)
|
||||
* [Tailwind CSS](#tailwind-css)
|
||||
* [Taskfile](#taskfile)
|
||||
* [tmux](#tmux)
|
||||
* [TypeScript](#typescript)
|
||||
* [Unix](#unix)
|
||||
@@ -97,10 +106,6 @@ See some of the other learning resources I work on:
|
||||
- [Case-Insensitive Search](ack/case-insensitive-search.md)
|
||||
- [List Available File Types](ack/list-available-file-types.md)
|
||||
|
||||
### Amplify
|
||||
|
||||
- [Sign Up User With Email And Password](amplify/sign-up-user-with-email-and-password.md)
|
||||
|
||||
### Ansible
|
||||
|
||||
- [Loop Over A List Of Dictionaries](ansible/loop-over-a-list-of-dictionaries.md)
|
||||
@@ -110,10 +115,24 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
|
||||
### AWS
|
||||
|
||||
- [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md)
|
||||
- [Find And Follow Server Logs](aws/find-and-follow-server-logs.md)
|
||||
- [List RDS Snapshots With Matching Identifier Prefix](aws/list-rds-snapshots-with-matching-identifier-prefix.md)
|
||||
- [Output CLI Results In Different Formats](aws/output-cli-results-in-different-formats.md)
|
||||
- [Sign Up User With Email And Password](aws/sign-up-user-with-email-and-password.md)
|
||||
- [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md)
|
||||
- [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md)
|
||||
- [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md)
|
||||
|
||||
### Brew
|
||||
|
||||
- [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md)
|
||||
- [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)
|
||||
- [Install From Nonstandard Brewfile](brew/install-from-nonstandard-brewfile.md)
|
||||
- [Install Go Packages In Brewfile](brew/install-go-packages-in-brewfile.md)
|
||||
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
|
||||
|
||||
### Chrome
|
||||
@@ -124,10 +143,12 @@ See some of the other learning resources I work on:
|
||||
- [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md)
|
||||
- [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.md)
|
||||
- [Keybinding To Focus The Address Bar](chrome/keybinding-to-focus-the-address-bar.md)
|
||||
- [Open Current Tab In New Window With Vimium](chrome/open-current-tab-in-new-window-with-vimium.md)
|
||||
- [Pause JavaScript From The Source DevTools Panel](chrome/pause-javascript-from-the-source-devtools-panel.md)
|
||||
- [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md)
|
||||
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
||||
- [Reference The Selected Node](chrome/reference-the-selected-node.md)
|
||||
- [Search Tabs With The Vimium Vomnibar](chrome/search-tabs-with-the-vimium-vomnibar.md)
|
||||
- [Selecting DOM Elements Faster Than Ever](chrome/selecting-dom-elements-faster-than-ever.md)
|
||||
- [Simulating Various Connection Speeds](chrome/simulating-various-connection-speeds.md)
|
||||
- [Toggle Device Mode](chrome/toggle-device-mode.md)
|
||||
@@ -135,6 +156,12 @@ See some of the other learning resources I work on:
|
||||
- [Trigger Commands From The Devtools Command Palette](chrome/trigger-commands-from-the-devtools-command-palette.md)
|
||||
- [View Network Traffic For New Tabs](chrome/view-network-traffic-for-new-tabs.md)
|
||||
|
||||
### Claude Code
|
||||
|
||||
- [Allow Edits From The Start](claude-code/allow-edits-from-the-start.md)
|
||||
- [Monitor Usage Limits From CLI](claude-code/monitor-usage-limits-from-cli.md)
|
||||
- [Open Current Prompt In Default Editor](claude-code/open-current-prompt-in-default-editor.md)
|
||||
|
||||
### Clojure
|
||||
|
||||
- [Aggregation Using merge-with](clojure/aggregation-using-merge-with.md)
|
||||
@@ -176,16 +203,22 @@ See some of the other learning resources I work on:
|
||||
- [Define HSL Colors With Alpha Values](css/define-hsl-colors-with-alpha-values.md)
|
||||
- [Display Responsive iframe Maintaining Aspect Ratio](css/display-responsive-iframe-maintaining-aspect-ratio.md)
|
||||
- [Dry Up SCSS With Mixins](css/dry-up-scss-with-mixins.md)
|
||||
- [Filter Blur Requires Expensive Calculation](css/filter-blur-requires-expensive-calculation.md)
|
||||
- [Give Elements The Same Width With Flexbox](css/give-elements-the-same-width-with-flexbox.md)
|
||||
- [Let Pointer Events Pass Through An Element](css/let-pointer-events-pass-through-an-element.md)
|
||||
- [Lighten And Darken With CSS Brightness Filter](css/lighten-and-darken-with-css-brightness-filter.md)
|
||||
- [Lighten And Darken With SCSS](css/lighten-and-darken-with-scss.md)
|
||||
- [Make A Block Of Text Respect New Lines](css/make-a-block-of-text-respect-new-lines.md)
|
||||
- [Parameterized SCSS Mixins](css/parameterized-scss-mixins.md)
|
||||
- [Prevent Invisible Elements From Being Clicked](css/prevent-invisible-elements-from-being-clicked.md)
|
||||
- [:root Has Higher Specificity Than html](css/root-has-higher-specificity-than-html.md)
|
||||
- [Style A Background With A Linear Gradient](css/style-a-background-with-a-linear-gradient.md)
|
||||
- [Using Maps In SCSS](css/using-maps-in-scss.md)
|
||||
|
||||
### Cursor
|
||||
|
||||
- [Allow Cursor To Be Launched From CLI](cursor/allow-cursor-to-be-launched-from-cli.md)
|
||||
|
||||
### Deno
|
||||
|
||||
- [Read In The Contents Of A File](deno/read-in-the-contents-of-a-file.md)
|
||||
@@ -214,9 +247,12 @@ See some of the other learning resources I work on:
|
||||
|
||||
### Docker
|
||||
|
||||
- [Check Postgres Version Running In Docker Container](docker/check-postgres-version-running-in-docker-container.md)
|
||||
- [Configure Different Host And Container Ports](docker/configure-different-host-and-container-ports.md)
|
||||
- [List Running Docker Containers](docker/list-running-docker-containers.md)
|
||||
- [Prevent Containers From Running On Startup](docker/prevent-containers-from-running-on-startup.md)
|
||||
- [Run A Basic PostgreSQL Server In Docker](docker/run-a-basic-postgresql-server-in-docker.md)
|
||||
- [Run SQL Script Against Postgres Container](docker/run-sql-script-against-postgres-container.md)
|
||||
|
||||
### Drizzle
|
||||
|
||||
@@ -294,13 +330,17 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [Check If A File Has Changed In A Script](git/check-if-a-file-has-changed-in-a-script.md)
|
||||
- [Check If A File Is Under Version Control](git/check-if-a-file-is-under-version-control.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)
|
||||
- [Cherry Pick A Range Of Commits](git/cherry-pick-a-range-of-commits.md)
|
||||
- [Cherry Pick Multiple Commits At Once](git/cherry-pick-multiple-commits-at-once.md)
|
||||
- [Clean Out All Local Branches](git/clean-out-all-local-branches.md)
|
||||
- [Clean Out Working Copy With Patched Restore](git/clean-out-working-copy-with-patched-restore.md)
|
||||
- [Clean Up Old Remote Tracking References](git/clean-up-old-remote-tracking-references.md)
|
||||
- [Clear Entries From Git Stash](git/clear-entries-from-git-stash.md)
|
||||
- [Clone A Repo Just For The Files, Without History](git/clone-a-repo-just-for-the-files-without-history.md)
|
||||
- [Clone A Repo Locally From .git](git/clone-a-repo-locally-from-git.md)
|
||||
- [Configure Global gitignore File](git/configure-global-gitignore-file.md)
|
||||
@@ -310,13 +350,16 @@ See some of the other learning resources I work on:
|
||||
- [Count Number Of Commits On A Branch](git/count-number-of-commits-on-a-branch.md)
|
||||
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
|
||||
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
||||
- [Determine Absolute Path Of Top-Level Project Directory](git/determine-absolute-path-of-top-level-project-directory.md)
|
||||
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
||||
- [Diffing With Patience](git/diffing-with-patience.md)
|
||||
- [Dropping Commits With Git Rebase](git/dropping-commits-with-git-rebase.md)
|
||||
- [Dry Runs in Git](git/dry-runs-in-git.md)
|
||||
- [Exclude A File From A Diff Output](git/exclude-a-file-from-a-diff-output.md)
|
||||
- [Exclude A Directory During A Command](git/exclude-a-directory-during-a-command.md)
|
||||
- [Excluding Files Locally](git/excluding-files-locally.md)
|
||||
- [Extend Git With Custom Commands](git/extend-git-with-custom-commands.md)
|
||||
- [Files With Local Changes Cannot Be Removed](git/files-with-local-changes-cannot-be-removed.md)
|
||||
- [Find And Remove Files That Match A Name](git/find-and-remove-files-that-match-a-name.md)
|
||||
- [Find The Date That A File Was Added To The Repo](git/find-the-date-that-a-file-was-added-to-the-repo.md)
|
||||
- [Find The Initial Commit](git/find-the-initial-commit.md)
|
||||
@@ -328,6 +371,7 @@ See some of the other learning resources I work on:
|
||||
- [Grep For A Pattern On Another Branch](git/grep-for-a-pattern-on-another-branch.md)
|
||||
- [Grep Over Commit Messages](git/grep-over-commit-messages.md)
|
||||
- [Highlight Extra Whitespace In Diff Output](git/highlight-extra-whitespace-in-diff-output.md)
|
||||
- [Highlight Small Change On Single Line](git/highlight-small-change-on-single-line.md)
|
||||
- [Ignore Changes To A Tracked File](git/ignore-changes-to-a-tracked-file.md)
|
||||
- [Ignore Files Specific To Your Workflow](git/ignore-files-specific-to-your-workflow.md)
|
||||
- [Include A Message With Your Stashed Changes](git/include-a-message-with-your-stashed-changes.md)
|
||||
@@ -340,6 +384,7 @@ See some of the other learning resources I work on:
|
||||
- [Last Commit A File Appeared In](git/last-commit-a-file-appeared-in.md)
|
||||
- [List All Files Added During Span Of Time](git/list-all-files-added-during-span-of-time.md)
|
||||
- [List All Files Changed Between Two Branches](git/list-all-files-changed-between-two-branches.md)
|
||||
- [List All Git Aliases From gitconfig](git/list-all-git-aliases-from-gitconfig.md)
|
||||
- [List Branches That Contain A Commit](git/list-branches-that-contain-a-commit.md)
|
||||
- [List Commits On A Branch](git/list-commits-on-a-branch.md)
|
||||
- [List Different Commits Between Two Branches](git/list-different-commits-between-two-branches.md)
|
||||
@@ -362,10 +407,12 @@ See some of the other learning resources I work on:
|
||||
- [Renaming A Branch](git/renaming-a-branch.md)
|
||||
- [Resetting A Reset](git/resetting-a-reset.md)
|
||||
- [Resolve A Merge Conflict From Stash Pop](git/resolve-a-merge-conflict-from-stash-pop.md)
|
||||
- [Restore File From One Branch To The Current](git/restore-file-from-one-branch-to-the-current.md)
|
||||
- [Review Commits From Before A Certain Date](git/review-commits-from-before-a-certain-date.md)
|
||||
- [Run A Git Command From Outside The Repo](git/run-a-git-command-from-outside-the-repo.md)
|
||||
- [Set A Custom Pager For A Specific Command](git/set-a-custom-pager-for-a-specific-command.md)
|
||||
- [Set Default Branch Name For New Repos](git/set-default-branch-name-for-new-repos.md)
|
||||
- [Set Up GPG Signing Key](git/set-up-gpg-signing-key.md)
|
||||
- [Shorthand To Force Push A Branch](git/shorthand-to-force-push-a-branch.md)
|
||||
- [Show All Commits For A File Beyond Renaming](git/show-all-commits-for-a-file-beyond-renaming.md)
|
||||
- [Show Changes For Files That Match A Pattern](git/show-changes-for-files-that-match-a-pattern.md)
|
||||
@@ -373,11 +420,13 @@ See some of the other learning resources I work on:
|
||||
- [Show File Diffs When Viewing Git Log](git/show-file-diffs-when-viewing-git-log.md)
|
||||
- [Show List Of Most Recently Committed Branches](git/show-list-of-most-recently-committed-branches.md)
|
||||
- [Show Only Commits That Touch Specific Lines](git/show-only-commits-that-touch-specific-lines.md)
|
||||
- [Show Summary Stats For Current Branch](git/show-summary-stats-for-current-branch.md)
|
||||
- [Show The diffstat Summary Of A Commit](git/show-the-diffstat-summary-of-a-commit.md)
|
||||
- [Show The Good And The Bad With Git Bisect](git/show-the-good-and-the-bad-with-git-bisect.md)
|
||||
- [Show What Is In A Stash](git/show-what-is-in-a-stash.md)
|
||||
- [Single Key Presses in Interactive Mode](git/single-key-presses-in-interactive-mode.md)
|
||||
- [Skip A Bad Commit When Bisecting](git/skip-a-bad-commit-when-bisecting.md)
|
||||
- [Skip Git Hooks As Needed](git/skip-git-hooks-as-needed.md)
|
||||
- [Skip Pre-Commit Hooks](git/skip-pre-commit-hooks.md)
|
||||
- [Staging Changes Within Vim](git/staging-changes-within-vim.md)
|
||||
- [Staging Stashes Interactively](git/staging-stashes-interactively.md)
|
||||
@@ -401,6 +450,13 @@ See some of the other learning resources I work on:
|
||||
- [What Is The Current Branch?](git/what-is-the-current-branch.md)
|
||||
- [Whitespace Warnings](git/whitespace-warnings.md)
|
||||
|
||||
### GitHub
|
||||
|
||||
- [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md)
|
||||
- [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md)
|
||||
- [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md)
|
||||
- [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md)
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
- [Cache Playwright Dependencies Across Workflows](github-actions/cache-playwright-dependencies-across-workflows.md)
|
||||
@@ -448,17 +504,20 @@ See some of the other learning resources I work on:
|
||||
|
||||
### Heroku
|
||||
|
||||
- [Check Ruby Version For Production App](heroku/check-ruby-version-for-production-app.md)
|
||||
- [Connect To A Database By Color](heroku/connect-to-a-database-by-color.md)
|
||||
- [Deploy A Review App To A Different Stack](heroku/deploy-a-review-app-to-a-different-stack.md)
|
||||
- [Diagnose Problems In A Heroku Postgres Database](heroku/diagnose-problems-in-a-heroku-postgres-database.md)
|
||||
- [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md)
|
||||
- [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md)
|
||||
- [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md)
|
||||
- [Specify Default Team And App For Project](heroku/specify-default-team-and-app-for-project.md)
|
||||
- [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md)
|
||||
|
||||
### HTML
|
||||
|
||||
- [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.md)
|
||||
- [Allow Number Input To Accept Decimal Values](html/allow-number-input-to-accept-decimal-values.md)
|
||||
- [Determine Which Button Submitted The Form](html/determine-which-button-submitted-the-form.md)
|
||||
- [Disable Auto-Completion For A Form Input](html/disable-auto-completion-for-a-form-input.md)
|
||||
- [Disclose Additional Details](html/disclose-additional-details.md)
|
||||
@@ -483,12 +542,14 @@ See some of the other learning resources I work on:
|
||||
- [Analyze Your Website Performance](internet/analyze-your-website-performance.md)
|
||||
- [Check Your Public IP Address](internet/check-your-public-ip-address.md)
|
||||
- [Digraph Unicode Characters Have A Titlecase](internet/digraph-unicode-characters-have-a-titlecase.md)
|
||||
- [Download A Google Doc As Specific Format](internet/download-a-google-doc-as-specific-format.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)
|
||||
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
|
||||
- [Grab The RSS Feed For A Substack Blog](internet/grab-the-rss-feed-for-a-substack-blog.md)
|
||||
- [Search Tweets By Author](internet/search-tweets-by-author.md)
|
||||
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
|
||||
- [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md)
|
||||
@@ -535,6 +596,7 @@ See some of the other learning resources I work on:
|
||||
- [Find Where Yarn Is Installing Binaries](javascript/find-where-yarn-is-installing-binaries.md)
|
||||
- [for...in Iterates Over Object Properties](javascript/for-in-iterates-over-object-properties.md)
|
||||
- [Format A Decimal To A Fixed Number Of Digits](javascript/format-a-decimal-to-a-fixed-number-of-digits.md)
|
||||
- [Format A List Of Items By Locale](javascript/format-a-list-of-items-by-locale.md)
|
||||
- [Format Time Zone Identifier](javascript/format-time-zone-identifier.md)
|
||||
- [Formatting Values With Units For Display](javascript/formatting-values-with-units-for-display.md)
|
||||
- [Freeze An Object, Sorta](javascript/freeze-an-object-sorta.md)
|
||||
@@ -608,7 +670,9 @@ See some of the other learning resources I work on:
|
||||
### jj
|
||||
|
||||
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
|
||||
- [Describe Current Changes And Create New Change](jj/describe-current-changes-and-create-new-change.md)
|
||||
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
|
||||
- [Squash Changes Into Parent Commit Interactively](jj/squash-changes-into-parent-commit-interactively.md)
|
||||
|
||||
### jq
|
||||
|
||||
@@ -650,18 +714,24 @@ See some of the other learning resources I work on:
|
||||
- [Access All Screen And Video Capture Options](mac/access-all-screen-and-video-capture-options.md)
|
||||
- [Access System Information On OS X](mac/access-system-information-on-osx.md)
|
||||
- [Access Unsupported Screen Resolutions With RDM](mac/access-unsupported-screen-resolutions-with-rdm.md)
|
||||
- [Add A Bunch Of CLI Utilities With coreutils](mac/add-a-bunch-of-cli-utilities-with-coreutils.md)
|
||||
- [Capture Screenshot To Clipboard From CLI](mac/capture-screenshot-to-clipboard-from-cli.md)
|
||||
- [Check Network Quality Stats From The Command Line](mac/check-network-quality-stats-from-the-command-line.md)
|
||||
- [Clean Up Old Homebrew Files](mac/clean-up-old-homebrew-files.md)
|
||||
- [Convert An HEIC Image File To JPG](mac/convert-an-heic-image-file-to-jpg.md)
|
||||
- [Default Screenshot Location](mac/default-screenshot-location.md)
|
||||
- [Detect How Long A User Has Been Idle](mac/detect-how-long-a-user-has-been-idle.md)
|
||||
- [Disable Swipe Navigation For A Specific App](mac/disable-swipe-navigation-for-a-specific-app.md)
|
||||
- [Display A Message With Alfred](mac/display-a-message-with-alfred.md)
|
||||
- [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)
|
||||
- [Inspect Assertions Preventing Sleep](mac/inspect-assertions-preventing-sleep.md)
|
||||
- [Keyboard Shortcuts For Interesting With Text Areas](mac/keyboard-shortcuts-for-interacting-with-text-areas.md)
|
||||
- [Launch Some Confetti](mac/launch-some-confetti.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)
|
||||
- [Prevent Sleep With The Caffeinate Command](mac/prevent-sleep-with-the-caffeinate-command.md)
|
||||
- [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md)
|
||||
- [Require Additional JS Libraries In Postman](mac/require-additional-js-libraries-in-postman.md)
|
||||
- [Resize App Windows With AppleScript](mac/resize-app-windows-with-applescript.md)
|
||||
@@ -677,6 +747,15 @@ See some of the other learning resources I work on:
|
||||
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
||||
- [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md)
|
||||
|
||||
### Mise
|
||||
|
||||
- [Create Umbrella Task For All Test Tasks](mise/create-umbrella-task-for-all-test-tasks.md)
|
||||
- [List The Files Being Loaded By Mise](mise/list-the-files-being-loaded-by-mise.md)
|
||||
- [Look In Ruby Version Dotfile](mise/look-in-ruby-version-dotfile.md)
|
||||
- [Preserve Color Output For Task Command](mise/preserve-color-output-for-task-command.md)
|
||||
- [Read Existing Dot Env File Into Env Vars](mise/read-existing-dot-env-file-into-env-vars.md)
|
||||
- [Run A Command With Specific Tool Version](mise/run-a-command-with-specific-tool-version.md)
|
||||
|
||||
### MongoDB
|
||||
|
||||
- [Determine The Database Version](mongodb/determine-the-database-version.md)
|
||||
@@ -694,6 +773,7 @@ See some of the other learning resources I work on:
|
||||
- [Doing Date Math](mysql/doing-date-math.md)
|
||||
- [Dump A Database To A File](mysql/dump-a-database-to-a-file.md)
|
||||
- [Echo A Message From A SQL File](mysql/echo-a-message-from-a-sql-file.md)
|
||||
- [Get Idea Of What Is In A JSON Column](mysql/get-idea-of-what-is-in-a-json-column.md)
|
||||
- [Ignore Duplicates When Inserting Records](mysql/ignore-duplicates-when-inserting-records.md)
|
||||
- [List Databases And Tables](mysql/list-databases-and-tables.md)
|
||||
- [Run Statements In A Transaction](mysql/run-statements-in-a-transaction.md)
|
||||
@@ -707,7 +787,9 @@ See some of the other learning resources I work on:
|
||||
|
||||
- [Allow Neovim To Copy/Paste With System Clipboard](neovim/allow-neovim-to-copy-paste-with-system-clipboard.md)
|
||||
- [Create User Command To Open Init Config](neovim/create-user-command-to-open-init-config.md)
|
||||
- [Jump Between Changes In Current File](neovim/jump-between-changes-in-current-file.md)
|
||||
- [Run A Lua Statement From The Command Prompt](neovim/run-a-lua-statement-from-the-command-prompt.md)
|
||||
- [Run nvim With Factory Defaults](neovim/run-nvim-with-factory-defaults.md)
|
||||
- [Set Up Vim-Plug With Neovim](neovim/set-up-vim-plug-with-neovim.md)
|
||||
|
||||
### Netlify
|
||||
@@ -745,6 +827,7 @@ See some of the other learning resources I work on:
|
||||
|
||||
### Planetscale
|
||||
|
||||
- [See What Databases You Have Access To](planetscale/see-what-databases-you-have-access-to.md)
|
||||
- [Seed Production Data Into Another Branch](planetscale/seed-production-data-into-another-branch.md)
|
||||
|
||||
### pnpm
|
||||
@@ -771,6 +854,7 @@ See some of the other learning resources I work on:
|
||||
- [Check If The Local Server Is Running](postgres/check-if-the-local-server-is-running.md)
|
||||
- [Check If User Role Exists For Database](postgres/check-if-user-role-exists-for-database.md)
|
||||
- [Check Table For Any Oprhaned Records](postgres/check-table-for-any-orphaned-records.md)
|
||||
- [Check The Size Of Databases In A Cluster](postgres/check-the-size-of-databases-in-a-cluster.md)
|
||||
- [Checking Inequality](postgres/checking-inequality.md)
|
||||
- [Checking The Type Of A Value](postgres/checking-the-type-of-a-value.md)
|
||||
- [Clear The Screen In psql](postgres/clear-the-screen-in-psql.md)
|
||||
@@ -791,6 +875,7 @@ See some of the other learning resources I work on:
|
||||
- [Create A Table From The Structure Of Another](postgres/create-a-table-from-the-structure-of-another.md)
|
||||
- [Create An Index Across Two Columns](postgres/create-an-index-across-two-columns.md)
|
||||
- [Create An Index Without Locking The Table](postgres/create-an-index-without-locking-the-table.md)
|
||||
- [Create And Execute SQL Statements With \gexec](postgres/create-and-execute-sql-statements-with-gexec.md)
|
||||
- [Create Database Uses Template1](postgres/create-database-uses-template1.md)
|
||||
- [Create hstore From Two Arrays](postgres/create-hstore-from-two-arrays.md)
|
||||
- [Create Table Adds A Data Type](postgres/create-table-adds-a-data-type.md)
|
||||
@@ -815,6 +900,7 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [Extracting Nested JSON Data](postgres/extracting-nested-json-data.md)
|
||||
- [Fetch Data From An Endpoint In SQL](postgres/fetch-data-from-an-endpoint-in-sql.md)
|
||||
- [Fetch Specific Number Of Results](postgres/fetch-specific-number-of-results.md)
|
||||
- [Find Duplicate Records In Table Without Unique Id](postgres/find-duplicate-records-in-table-without-unique-id.md)
|
||||
- [Find Records That Contain Duplicate Values](postgres/find-records-that-contain-duplicate-values.md)
|
||||
@@ -873,6 +959,7 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [References Target Primary Key By Default](postgres/references-target-primary-key-by-default.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)
|
||||
@@ -883,9 +970,11 @@ See some of the other learning resources I work on:
|
||||
- [Set Inclusion With hstore](postgres/set-inclusion-with-hstore.md)
|
||||
- [Set A Seed For The Random Number Generator](postgres/set-a-seed-for-the-random-number-generator.md)
|
||||
- [Set A Statement Timeout Threshold For A Session](postgres/set-a-statement-timeout-threshold-for-a-session.md)
|
||||
- [Set Up A Project-Local Cluster With Postgres.app](postgres/set-up-a-project-local-cluster-with-postgres-app.md)
|
||||
- [Sets With The Values Command](postgres/sets-with-the-values-command.md)
|
||||
- [Shorthand Absolute Value Operator](postgres/shorthand-absolute-value-operator.md)
|
||||
- [Show All Versions Of An Operator](postgres/show-all-versions-of-an-operator.md)
|
||||
- [Show Reconstructed Constraints For A Table](postgres/show-reconstructed-constraints-for-a-table.md)
|
||||
- [Show The Hidden Queries Behind Backslash Commands](postgres/show-the-hidden-queries-behind-backslash-commands.md)
|
||||
- [Sleeping](postgres/sleeping.md)
|
||||
- [Special Math Operators](postgres/special-math-operators.md)
|
||||
@@ -938,6 +1027,7 @@ See some of the other learning resources I work on:
|
||||
### Python
|
||||
|
||||
- [Access Instance Variables](python/access-instance-variables.md)
|
||||
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
|
||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||
- [Dunder Methods](python/dunder-methods.md)
|
||||
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
|
||||
@@ -947,16 +1037,18 @@ See some of the other learning resources I work on:
|
||||
|
||||
### Rails
|
||||
|
||||
- [Access Secrets In A Rails 5.2 App](rails/access-secrets-in-a-rails-5-2-app.md)
|
||||
- [ActiveRecord Query For This Or That](rails/active-record-query-for-this-or-that.md)
|
||||
- [Add A Check Constraint To A Table](rails/add-a-check-constraint-to-a-table.md)
|
||||
- [Add A Database Index If It Does Not Already Exist](rails/add-a-database-index-if-it-does-not-already-exist.md)
|
||||
- [Add A Foreign Key Reference To A Table](rails/add-a-foreign-key-reference-to-a-table.md)
|
||||
- [Add A Generated Column To A PostgreSQL Table](rails/add-a-generated-column-to-a-postgresql-table.md)
|
||||
- [Add A Reference Column With An Index](rails/add-a-reference-column-with-an-index.md)
|
||||
- [Add ActiveRecord Error Not Tied To Any Attribute](rails/add-activerecord-error-not-tied-to-any-attribute.md)
|
||||
- [Add Color To The IRB Console Prompt](rails/add-color-to-the-irb-console-prompt.md)
|
||||
- [Add React With Webpacker To A New Rails App](rails/add-react-with-webpacker-to-a-new-rails-app.md)
|
||||
- [Add timestamptz Columns With The Migration DSL](rails/add-timestamptz-columns-with-the-migration-dsl.md)
|
||||
- [Access Secrets In A Rails 5.2 App](rails/access-secrets-in-a-rails-5-2-app.md)
|
||||
- [ActiveRecord Query For This Or That](rails/active-record-query-for-this-or-that.md)
|
||||
- [Adjust The Production Log Level](rails/adjust-the-production-log-level.md)
|
||||
- [Advance The Date](rails/advance-the-date.md)
|
||||
- [Allow Associations To Be Optional](rails/allow-associations-to-be-optional.md)
|
||||
- [Allow List Params Anywhere With Strong Params](rails/allow-list-params-anywhere-with-strong-params.md)
|
||||
@@ -984,6 +1076,7 @@ See some of the other learning resources I work on:
|
||||
- [Comparing DateTimes Down To Second Precision](rails/comparing-datetimes-down-to-second-precision.md)
|
||||
- [Conditional Class Selectors in Haml](rails/conditional-class-selectors-in-haml.md)
|
||||
- [Convert A Symbol To A Constant](rails/convert-a-symbol-to-a-constant.md)
|
||||
- [Convert JSON Field To Hash With Indifferent Access](rails/convert-json-field-to-hash-with-indifferent-access.md)
|
||||
- [Count The Number Of Records By Attribute](rails/count-the-number-of-records-by-attribute.md)
|
||||
- [Create A Custom Named References Column](rails/create-a-custom-named-references-column.md)
|
||||
- [Create A Join Table With The Migration DSL](rails/create-a-join-table-with-the-migration-dsl.md)
|
||||
@@ -991,6 +1084,7 @@ See some of the other learning resources I work on:
|
||||
- [Creating Records of Has_One Associations](rails/creating-records-of-has-one-associations.md)
|
||||
- [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 Template For New Schema Migration](rails/customize-template-for-new-schema-migration.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)
|
||||
@@ -999,12 +1093,17 @@ See some of the other learning resources I work on:
|
||||
- [Different Ways To Add A Foreign Key Reference](rails/different-ways-to-add-a-foreign-key-reference.md)
|
||||
- [Disambiguate Where In A Joined Relation](rails/disambiguate-where-in-a-joined-relation.md)
|
||||
- [Empty find_by Returns First Record](rails/empty-find-by-returns-first-record.md)
|
||||
- [Enforce Locals Passed To A Partial](rails/enforce-locals-passed-to-a-partial.md)
|
||||
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md)
|
||||
- [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
||||
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
||||
- [Filter ActiveModel Validation Errors](rails/filter-active-model-validation-errors.md)
|
||||
- [Filter ActiveStorage Blobs To Only Images](rails/filter-active-storage-blobs-to-only-images.md)
|
||||
- [Find Or Create A Record With FactoryBot](rails/find-or-create-a-record-with-factory-bot.md)
|
||||
- [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md)
|
||||
- [Force All Users To Sign Out](rails/force-all-users-to-sign-out.md)
|
||||
- [Format DateTime With Builtin Formats](rails/format-datetime-with-builtin-formats.md)
|
||||
- [Format Specific html.erb Template Files](rails/format-specific-html-erb-template-files.md)
|
||||
- [Generate A Model](rails/generate-a-model.md)
|
||||
- [Generate A Rails App From The Main Branch](rails/generate-a-rails-app-from-the-main-branch.md)
|
||||
- [Generating And Executing SQL](rails/generating-and-executing-sql.md)
|
||||
@@ -1034,6 +1133,7 @@ See some of the other learning resources I work on:
|
||||
- [Make A String Attribute Easy To Inquire About](rails/make-a-string-attribute-easy-to-inquire-about.md)
|
||||
- [Make ActionMailer Synchronous In Test](rails/make-action-mailer-synchronous-in-test.md)
|
||||
- [Make Remove Column Migration Reversible](rails/make-remove-column-migration-reversible.md)
|
||||
- [Manage Timestamps With Upsert](rails/manage-timestamps-with-upsert.md)
|
||||
- [Manually Run A Migration From Rails Console](rails/manually-run-a-migration-from-rails-console.md)
|
||||
- [Mark For Destruction](rails/mark-for-destruction.md)
|
||||
- [Mask An ActiveRecord Attribute](rails/mask-an-activerecord-attribute.md)
|
||||
@@ -1042,6 +1142,7 @@ See some of the other learning resources I work on:
|
||||
- [Mock Rails Environment With An Inquiry Instance](rails/mock-rails-environment-with-an-inquiry-instance.md)
|
||||
- [Order Matters For `rescue_from` Blocks](rails/order-matters-for-rescue-from-blocks.md)
|
||||
- [Override Text Displayed By Form Label](rails/override-text-displayed-by-form-label.md)
|
||||
- [Parameterize A String With Underscores](rails/parameterize-a-string-with-underscores.md)
|
||||
- [Params Includes Submission Button Info](rails/params-includes-submission-button-info.md)
|
||||
- [Params Is A Hash With Indifferent Access](rails/params-is-a-hash-with-indifferent-access.md)
|
||||
- [Parse Query Params From A URL](rails/parse-query-params-from-a-url.md)
|
||||
@@ -1050,7 +1151,9 @@ See some of the other learning resources I work on:
|
||||
- [Polymorphic Path Helpers](rails/polymorphic-path-helpers.md)
|
||||
- [Prefer select_all Over execute For Read Queries](rails/prefer-select-all-over-execute-for-read-queries.md)
|
||||
- [Pretend Generations](rails/pretend-generations.md)
|
||||
- [Prevent Mailer Previews From Cluttering Database](rails/prevent-mailer-previews-from-cluttering-database.md)
|
||||
- [Prevent Writes With A Sandboxed Rails Console](rails/prevent-writes-with-a-sandboxed-rails-console.md)
|
||||
- [Provide Fake Form Helper To Controllers](rails/provide-fake-form-helper-to-controllers.md)
|
||||
- [Query A Single Value From The Database](rails/query-a-single-value-from-the-database.md)
|
||||
- [Read In Environment-Specific Config Values](rails/read-in-environment-specific-config-values.md)
|
||||
- [Read-Only Models](rails/read-only-models.md)
|
||||
@@ -1063,15 +1166,19 @@ See some of the other learning resources I work on:
|
||||
- [Rescue From](rails/rescue-from.md)
|
||||
- [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)
|
||||
- [Restart Puma Server By Touching Restart File](rails/restart-puma-server-by-touching-restart-file.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)
|
||||
- [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
|
||||
- [Run Dev Processes With Overmind Instead Of Foreman](rails/run-dev-processes-with-overmind-instead-of-foreman.md)
|
||||
- [Run Rails Console With Remote Dokku App](rails/run-rails-console-with-remote-dokku-app.md)
|
||||
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md)
|
||||
- [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md)
|
||||
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
|
||||
- [Scope Records To A Lower Or Upper Bound](rails/scope-records-to-a-lower-or-upper-bound.md)
|
||||
- [Secure Passwords With Rails And Bcrypt](rails/secure-passwords-with-rails-and-bcrypt.md)
|
||||
- [Select A Select By Selector](rails/select-a-select-by-selector.md)
|
||||
- [Select A Specific Rails Version To Install](rails/select-a-specific-rails-version-to-install.md)
|
||||
@@ -1100,6 +1207,7 @@ See some of the other learning resources I work on:
|
||||
- [Update Column Versus Update Attribute](rails/update-column-versus-update-attribute.md)
|
||||
- [Upgrading Your Manifest For Sprocket's 4](rails/upgrading-your-manifest-for-sprockets-4.md)
|
||||
- [Use IRB And Ruby Flags With Rails Console](rails/use-irb-and-ruby-flags-with-rails-console.md)
|
||||
- [Use .ruby Extension For Template File](rails/use-ruby-extension-for-template-file.md)
|
||||
- [Useful ActiveSupport Constants For Durations](rails/useful-active-support-constants-for-durations.md)
|
||||
- [Validate Column Data With Check Constraints](rails/validate-column-data-with-check-constraints.md)
|
||||
- [Verify And Read A Signed Cookie Value](rails/verify-and-read-a-signed-cookie-value.md)
|
||||
@@ -1239,8 +1347,9 @@ See some of the other learning resources I work on:
|
||||
- [Add Progress Reporting To Long-Running Script](ruby/add-progress-reporting-to-long-running-script.md)
|
||||
- [Are They All True?](ruby/are-they-all-true.md)
|
||||
- [Assert About An Object's Attributes With RSpec](ruby/assert-about-an-objects-attributes-with-rspec.md)
|
||||
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
||||
- [Assoc For Hashes](ruby/assoc-for-hashes.md)
|
||||
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
||||
- [Avoid Double Negation With Minitest Refute](ruby/avoid-double-negation-with-minitest-refute.md)
|
||||
- [Block Comments](ruby/block-comments.md)
|
||||
- [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md)
|
||||
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
||||
@@ -1260,8 +1369,11 @@ See some of the other learning resources I work on:
|
||||
- [Create a CSV::Table Object](ruby/create-a-csv-table-object.md)
|
||||
- [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md)
|
||||
- [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md)
|
||||
- [Create Mock Class That Can Be Overridden](ruby/create-mock-class-that-can-be-overridden.md)
|
||||
- [Create A Module Of Utility Functions](ruby/create-a-module-of-utility-functions.md)
|
||||
- [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md)
|
||||
- [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md)
|
||||
- [Decompose Unicode Character With Diacritic Mark](ruby/decompose-unicode-character-with-diacritic-mark.md)
|
||||
- [Defaulting To Frozen String Literals](ruby/defaulting-to-frozen-string-literals.md)
|
||||
- [Define A Custom RSpec Matcher](ruby/define-a-custom-rspec-matcher.md)
|
||||
- [Define A Method On A Struct](ruby/define-a-method-on-a-struct.md)
|
||||
@@ -1293,6 +1405,8 @@ See some of the other learning resources I work on:
|
||||
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
||||
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
||||
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
||||
- [Get Specific Values From Arrays And Hashes](ruby/get-specific-values-from-hashes-and-arrays.md)
|
||||
- [Get The Names Of The Month](ruby/get-the-names-of-the-month.md)
|
||||
- [Get The Output Of Running A System Program](ruby/get-the-output-of-running-a-system-program.md)
|
||||
- [Get UTC Offset For Different Time Zones](ruby/get-utc-offset-for-different-time-zones.md)
|
||||
- [Identify Outdated Gems](ruby/identify-outdated-gems.md)
|
||||
@@ -1300,15 +1414,18 @@ See some of the other learning resources I work on:
|
||||
- [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md)
|
||||
- [Include Extra Context In A Honeybadger Notify](ruby/include-extra-context-in-a-honeybadger-notify.md)
|
||||
- [Ins And Outs Of Pry](ruby/ins-and-outs-of-pry.md)
|
||||
- [Install And Require Gems Inline Without Gemfile](ruby/install-and-require-gems-inline-without-gemfile.md)
|
||||
- [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md)
|
||||
- [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md)
|
||||
- [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md)
|
||||
- [Join URI Path Parts](ruby/join-uri-path-parts.md)
|
||||
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
|
||||
- [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md)
|
||||
- [Limit Split](ruby/limit-split.md)
|
||||
- [List The Running Ruby Version](ruby/list-the-running-ruby-version.md)
|
||||
- [Listing Local Variables](ruby/listing-local-variables.md)
|
||||
- [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md)
|
||||
- [Make Structs Easier To Use With Keyword Initialization](ruby/make-structs-easier-to-use-with-keyword-initialization.md)
|
||||
- [Map With Index Over An Array](ruby/map-with-index-over-an-array.md)
|
||||
- [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md)
|
||||
- [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md)
|
||||
@@ -1316,6 +1433,7 @@ See some of the other learning resources I work on:
|
||||
- [Named Regex Captures Are Assigned To Variables](ruby/named-regex-captures-are-assigned-to-variables.md)
|
||||
- [Navigate Back In The Browser With Capybara](ruby/navigate-back-in-the-browser-with-capybara.md)
|
||||
- [Next And Previous Floats](ruby/next-and-previous-floats.md)
|
||||
- [OpenStruct Has Bad Performance Characteristics](ruby/open-struct-has-bad-performance-characteristics.md)
|
||||
- [Or Operator Precedence](ruby/or-operator-precedence.md)
|
||||
- [Output Bytecode For A Ruby Program](ruby/output-bytecode-for-a-ruby-program.md)
|
||||
- [Override The Initial Sequence Value](ruby/override-the-initial-sequence-value.md)
|
||||
@@ -1334,6 +1452,8 @@ See some of the other learning resources I work on:
|
||||
- [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md)
|
||||
- [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md)
|
||||
- [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md)
|
||||
- [Reference Hash Key With Safe Navigation](ruby/reference-hash-key-with-safe-navigation.md)
|
||||
- [Regenerate Lock File With Newer Bundler](ruby/regenerate-lock-file-with-newer-bundler.md)
|
||||
- [Rendering ERB](ruby/rendering-erb.md)
|
||||
- [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md)
|
||||
- [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md)
|
||||
@@ -1348,9 +1468,11 @@ See some of the other learning resources I work on:
|
||||
- [Scripting With RVM](ruby/scripting-with-rvm.md)
|
||||
- [Scroll To Top Of Page With Capybara](ruby/scroll-to-top-of-page-with-capybara.md)
|
||||
- [Search For Gem Versions Available To Install](ruby/search-for-gem-versions-available-to-install.md)
|
||||
- [Set Default Tasks For Rake To Run](ruby/set-default-tasks-for-rake-to-run.md)
|
||||
- [Set RVM Default Ruby](ruby/set-rvm-default-ruby.md)
|
||||
- [Shift The Month On A Date Object](ruby/shift-the-month-on-a-date-object.md)
|
||||
- [Show Public Methods With Pry](ruby/show-public-methods-with-pry.md)
|
||||
- [Show The Bundler Location Of An Installed Gem](ruby/show-the-bundler-location-of-an-installed-gem.md)
|
||||
- [Silence The Output Of A Ruby Statement In Pry](ruby/silence-the-output-of-a-ruby-statement-in-pry.md)
|
||||
- [Single And Double Quoted String Notation](ruby/single-and-double-quoted-string-notation.md)
|
||||
- [Skip Specific CVEs When Auditing Your Bundle](ruby/skip-specific-cves-when-auditing-your-bundle.md)
|
||||
@@ -1391,6 +1513,7 @@ See some of the other learning resources I work on:
|
||||
- [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md)
|
||||
- [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md)
|
||||
- [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md)
|
||||
- [Reference The Full Match In The Replacement](sed/reference-the-full-match-in-the-replacement.md)
|
||||
- [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md)
|
||||
|
||||
### Shell
|
||||
@@ -1411,12 +1534,19 @@ See some of the other learning resources I work on:
|
||||
|
||||
- [Apply Tailwind Classes To Existing CSS Class](tailwind/apply-tailwind-classes-to-existing-css-class.md)
|
||||
- [Base Styles For Text Link](tailwind/base-styles-for-text-link.md)
|
||||
- [Disable And Enable A Button](tailwind/disable-and-enable-a-button.md)
|
||||
- [Specify Paths For Purging Unused CSS](tailwind/specify-paths-for-purging-unused-css.md)
|
||||
- [Use Tailwind Typography Prose In Dark Mode](tailwind/use-tailwind-typography-prose-in-dark-mode.md)
|
||||
|
||||
### Taskfile
|
||||
|
||||
- [Create Interactive Picker For Set Of Subtasks](taskfile/create-interactive-picker-for-set-of-subtasks.md)
|
||||
- [Run A Task If It Meets Criteria](taskfile/run-a-task-if-it-meets-criteria.md)
|
||||
|
||||
### tmux
|
||||
|
||||
- [Access Past Copy Buffer History](tmux/access-past-copy-buffer-history.md)
|
||||
- [Add Bindings To Split Panes To Current Directory](tmux/add-bindings-to-split-panes-to-current-directory.md)
|
||||
- [Adjusting Window Pane Size](tmux/adjusting-window-pane-size.md)
|
||||
- [Break Current Pane Out To Separate Window](tmux/break-current-pane-out-to-separate-window.md)
|
||||
- [Change Base Directory Of Existing Session](tmux/change-base-directory-of-existing-session.md)
|
||||
@@ -1446,6 +1576,7 @@ See some of the other learning resources I work on:
|
||||
- [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md)
|
||||
- [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md)
|
||||
- [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md)
|
||||
- [Set Up Forwarding Prefix For Nested Session](tmux/set-up-forwarding-prefix-for-nested-session.md)
|
||||
- [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md)
|
||||
- [Swap Split Panes](tmux/swap-split-panes.md)
|
||||
- [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md)
|
||||
@@ -1479,6 +1610,8 @@ See some of the other learning resources I work on:
|
||||
### Unix
|
||||
|
||||
- [All The Environment Variables](unix/all-the-environment-variables.md)
|
||||
- [Apply Successive Filters To Lines In Less](unix/apply-successive-filters-to-lines-in-less.md)
|
||||
- [Authorize A cURL Request](unix/authorize-a-curl-request.md)
|
||||
- [Cat A File With Line Numbers](unix/cat-a-file-with-line-numbers.md)
|
||||
- [Cat Files With Color Using Bat](unix/cat-files-with-color-using-bat.md)
|
||||
- [Change Default Shell For A User](unix/change-default-shell-for-a-user.md)
|
||||
@@ -1502,13 +1635,16 @@ See some of the other learning resources I work on:
|
||||
- [Count The Number Of ripgrep Pattern Matches](unix/count-the-number-of-ripgrep-pattern-matches.md)
|
||||
- [Count The Number Of Words On A Webpage](unix/count-the-number-of-words-on-a-webpage.md)
|
||||
- [Create A File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md)
|
||||
- [Create A Filename With The Current Date](unix/create-a-filename-with-the-current-date.md)
|
||||
- [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.md)
|
||||
- [Curl With Cookies](unix/curl-with-cookies.md)
|
||||
- [Curling For Headers](unix/curling-for-headers.md)
|
||||
- [Curling With Basic Auth Credentials](unix/curling-with-basic-auth-credentials.md)
|
||||
- [Determine ipv4 And ipv6 Public IP Addresses](unix/determine-ipv4-and-ipv6-public-ip-addresses.md)
|
||||
- [Different Ways To Generate A v4 UUID](unix/different-ways-to-generate-a-v4-uuid.md)
|
||||
- [Display All The Terminal Colors](unix/display-all-the-terminal-colors.md)
|
||||
- [Display Free Disk Space](unix/display-free-disk-space.md)
|
||||
- [Display Line Numbers While Using Less](unix/display-line-numbers-while-using-less.md)
|
||||
- [Display The Contents Of A Directory As A Tree](unix/display-the-contents-of-a-directory-as-a-tree.md)
|
||||
- [Do A Dry Run Of An rsync](unix/do-a-dry-run-of-an-rsync.md)
|
||||
- [Do Not Overwrite Existing Files](unix/do-not-overwrite-existing-files.md)
|
||||
@@ -1516,11 +1652,14 @@ See some of the other learning resources I work on:
|
||||
- [Enable Multi-Select Of Results With fzf](unix/enable-multi-select-of-results-with-fzf.md)
|
||||
- [Exclude A Command From The ZSH History File](unix/exclude-a-command-from-the-zsh-history-file.md)
|
||||
- [Exclude A Directory With Find](unix/exclude-a-directory-with-find.md)
|
||||
- [Exclude A Specific File From fd Results](unix/exclude-a-specific-file-from-fd-results.md)
|
||||
- [Exclude Certain Files From An rsync Run](unix/exclude-certain-files-from-an-rsync-run.md)
|
||||
- [Figure Out The Week Of The Year From The Terminal](unix/figure-out-the-week-of-the-year-from-the-terminal.md)
|
||||
- [File Type Info With File](unix/file-type-info-with-file.md)
|
||||
- [Find All Files Matching A Name With fd](unix/find-all-files-matching-a-name-with-fd.md)
|
||||
- [Find All Files With A Specific Extension With fd](unix/find-all-files-with-a-specific-extension-with-fd.md)
|
||||
- [Find All Tool Version Files Containing Postgres](unix/find-all-tool-version-files-containing-postgres.md)
|
||||
- [Find And Copy A Value From Large JSON Output](unix/find-and-copy-a-value-from-large-json-output.md)
|
||||
- [Find Any Dotfiles That Modify Path Env Var](unix/find-any-dotfiles-that-modify-path-env-var.md)
|
||||
- [Find A File Installed By Brew](unix/find-a-file-installed-by-brew.md)
|
||||
- [Find Duplicate Lines In A File](unix/find-duplicate-lines-in-a-file.md)
|
||||
@@ -1528,9 +1667,12 @@ See some of the other learning resources I work on:
|
||||
- [Find Newer Files](unix/find-newer-files.md)
|
||||
- [Find Occurrences Of Multiple Values With Ripgrep](unix/find-occurrences-of-multiple-values-with-ripgrep.md)
|
||||
- [Find Top-Level Directories Matching A Pattern](unix/find-top-level-directories-matching-a-pattern.md)
|
||||
- [Fix Previous Command With fc](unix/fix-previous-command-with-fc.md)
|
||||
- [Fix Shim Path After asdf Upgrade](unix/fix-shim-path-after-asdf-upgrade.md)
|
||||
- [Fix Unlinked Node Binaries With asdf](unix/fix-unlinked-node-binaries-with-asdf.md)
|
||||
- [Forward Multiple Ports Over SSH](unix/forward-multiple-ports-over-ssh.md)
|
||||
- [Generate A SAML Key And Certificate Pair](unix/generate-a-saml-key-and-certificate-pair.md)
|
||||
- [Generate A Sequence Of Numbered Items](unix/generate-a-sequence-of-numbered-items.md)
|
||||
- [Generate Base64 Encoding Without Newlines](unix/generate-base64-encoding-without-newlines.md)
|
||||
- [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)
|
||||
@@ -1545,12 +1687,14 @@ See some of the other learning resources I work on:
|
||||
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
||||
- [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
||||
- [Have Script ShellCheck Itself When Executing](unix/have-script-shellcheck-itself-when-executing.md)
|
||||
- [Hexdump A Compiled File](unix/hexdump-a-compiled-file.md)
|
||||
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
||||
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
||||
- [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md)
|
||||
- [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md)
|
||||
- [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md)
|
||||
- [Interpret Cron Schedule From The CLI](unix/interpret-cron-schedule-from-the-cli.md)
|
||||
- [Jump To The Ends Of Your Shell History](unix/jump-to-the-ends-of-your-shell-history.md)
|
||||
- [Kill Everything Running On A Certain Port](unix/kill-everything-running-on-a-certain-port.md)
|
||||
- [Killing A Frozen SSH Session](unix/killing-a-frozen-ssh-session.md)
|
||||
@@ -1567,11 +1711,13 @@ See some of the other learning resources I work on:
|
||||
- [List Parent pid With ps](unix/list-parent-pid-with-ps.md)
|
||||
- [List Stats For A File](unix/list-stats-for-a-file.md)
|
||||
- [List The Available JDKs](unix/list-the-available-jdks.md)
|
||||
- [List The PID And Name Of Current Shell Process](unix/list-the-pid-and-name-of-current-shell-process.md)
|
||||
- [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md)
|
||||
- [List TXT DNS Records For A Domain](unix/list-txt-dns-records-for-a-domain.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)
|
||||
- [Make Neovim The Default Way To View Man Pages](unix/make-neovim-the-default-way-to-view-man-pages.md)
|
||||
- [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.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)
|
||||
@@ -1589,6 +1735,7 @@ See some of the other learning resources I work on:
|
||||
- [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md)
|
||||
- [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md)
|
||||
- [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md)
|
||||
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
|
||||
- [Repeat Yourself](unix/repeat-yourself.md)
|
||||
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
|
||||
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
|
||||
@@ -1602,6 +1749,7 @@ See some of the other learning resources I work on:
|
||||
- [Securely Remove Files](unix/securely-remove-files.md)
|
||||
- [See Where asdf Gets Current Tool Version](unix/see-where-asdf-gets-current-tool-version.md)
|
||||
- [Set The asdf Package Version For A Single Shell](unix/set-the-asdf-package-version-for-a-single-shell.md)
|
||||
- [Shorten SSH Commands With Aliases](unix/shorten-ssh-commands-with-aliases.md)
|
||||
- [Show A File Preview When Searching With FZF](unix/show-a-file-preview-when-searching-with-fzf.md)
|
||||
- [Show Disk Usage For The Current Directory](unix/show-disk-usage-for-the-current-directory.md)
|
||||
- [Show The Size Of Everything In A Directory](unix/show-the-size-of-everything-in-a-directory.md)
|
||||
@@ -1613,6 +1761,7 @@ See some of the other learning resources I work on:
|
||||
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
|
||||
- [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md)
|
||||
- [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md)
|
||||
- [Transform Text To Lowercase](unix/transform-text-to-lowercase.md)
|
||||
- [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md)
|
||||
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
|
||||
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
||||
@@ -1654,6 +1803,7 @@ See some of the other learning resources I work on:
|
||||
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
||||
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
||||
- [Build And Install A Go Program](vim/build-and-install-a-go-program.md)
|
||||
- [Bypass On-Save Tooling When Writing File](vim/bypass-on-save-tooling-when-writing-file.md)
|
||||
- [Case-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md)
|
||||
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
||||
- [Center The Cursor](vim/center-the-cursor.md)
|
||||
@@ -1694,6 +1844,7 @@ See some of the other learning resources I work on:
|
||||
- [Generate and Edit Rails Migration](vim/generate-and-edit-rails-migration.md)
|
||||
- [Get The pid Of The Session](vim/get-the-pid-of-the-session.md)
|
||||
- [Go Back To The Previous Window](vim/go-back-to-the-previous-window.md)
|
||||
- [Go To Beginning And End Of Line](vim/go-to-beginning-and-end-of-line.md)
|
||||
- [Go To File With Line Number](vim/go-to-file-with-line-number.md)
|
||||
- [Grepping Through The Vim Help Files](vim/grepping-through-the-vim-help-files.md)
|
||||
- [Head of File Name](vim/head-of-file-name.md)
|
||||
@@ -1755,6 +1906,7 @@ See some of the other learning resources I work on:
|
||||
- [Replace A Character](vim/replace-a-character.md)
|
||||
- [Reset Target tslime Pane](vim/reset-target-tslime-pane.md)
|
||||
- [Reverse A Group Of Lines](vim/reverse-a-group-of-lines.md)
|
||||
- [Reword A Commit Message With Fugitive](vim/reword-a-commit-message-with-fugitive.md)
|
||||
- [Rotate Everything By 13 Letters](vim/rotate-everything-by-13-letters.md)
|
||||
- [Rotate The Orientation Of Split Windows](vim/rotate-the-orientation-of-split-windows.md)
|
||||
- [Running Bundle With vim-bundler](vim/running-bundle-with-vim-bundler.md)
|
||||
@@ -1803,6 +1955,7 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [Open File On Remote Like GitHub](vscode/open-file-on-remote-like-github.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)
|
||||
- [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.md)
|
||||
@@ -1831,6 +1984,8 @@ See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [Create Todo Items In Logseq](workflow/create-todo-items-in-logseq.md)
|
||||
- [Do Project Time Tracking From The CLI](workflow/do-project-time-tracking-from-the-cli.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)
|
||||
@@ -1849,6 +2004,7 @@ See some of the other learning resources I work on:
|
||||
- [Temporarily Hide CleanShot X Capture Previews](workflow/temporarily-hide-cleanshot-x-capture-previews.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)
|
||||
- [View A Nicely-Formatted CSV In Terminal](workflow/view-a-nicely-formatted-csv-in-terminal.md)
|
||||
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
|
||||
|
||||
### XState
|
||||
@@ -1879,7 +2035,9 @@ See some of the other learning resources I work on:
|
||||
|
||||
### Zsh
|
||||
|
||||
- [A Better Way To Reload ZSH Configuration](zsh/a-better-way-to-reload-zsh-configuration.md)
|
||||
- [Add To The Path Via Path Array](zsh/add-to-the-path-via-path-array.md)
|
||||
- [Create And Jump Into A Directory](zsh/create-and-jump-into-a-directory.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)
|
||||
|
||||
@@ -1891,6 +2049,10 @@ current number of TILs and display the result in the command tray.
|
||||
|
||||
## About
|
||||
|
||||
I've written more about how this repo came to be in [How I Built a Learning
|
||||
Machine](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and [A
|
||||
Decade of TILs](https://www.visualmode.dev/a-decade-of-tils).
|
||||
|
||||
I shamelessly stole this idea from
|
||||
[thoughtbot/til](https://github.com/thoughtbot/til).
|
||||
|
||||
@@ -1902,7 +2064,7 @@ I shamelessly stole this idea from
|
||||
|
||||
## License
|
||||
|
||||
© 2015-2025 Josh Branchaud
|
||||
© 2015-2026 Josh Branchaud
|
||||
|
||||
This repository is licensed under the MIT license. See `LICENSE` for
|
||||
details.
|
||||
|
||||
73
Taskfile.yml
Normal file
73
Taskfile.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
NOTES_DIR: notes
|
||||
NOTES_FILE: '{{.NOTES_DIR}}/NOTES.md'
|
||||
EDITOR: '{{.EDITOR | default "nvim"}}'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
desc: Show available commands
|
||||
cmds:
|
||||
- task --list
|
||||
|
||||
notes:
|
||||
desc: Interactive picker for notes tasks
|
||||
cmds:
|
||||
- |
|
||||
TASK=$(task --list | grep "^\* notes:" | sed 's/^\* notes://' | sed 's/\s\+/ - /' | fzf --prompt="Select notes task: " --height=40% --reverse) || true
|
||||
if [ -n "$TASK" ]; then
|
||||
TASK_NAME=$(echo "$TASK" | awk '{print $1}' | sed 's/:$//')
|
||||
task notes:$TASK_NAME
|
||||
fi
|
||||
interactive: true
|
||||
silent: true
|
||||
|
||||
notes:edit:
|
||||
desc: All-in-one edit, commit, and push notes
|
||||
cmds:
|
||||
- task notes:open
|
||||
- task notes:push
|
||||
|
||||
notes:sync:
|
||||
desc: Sync latest changes from the notes submodule
|
||||
cmds:
|
||||
- cd {{.NOTES_DIR}} && git checkout main && git pull
|
||||
silent: false
|
||||
|
||||
notes:open:
|
||||
desc: Opens NOTES.md (syncs latest changes first) in default editor
|
||||
deps: [notes:sync]
|
||||
cmds:
|
||||
- $EDITOR {{.NOTES_FILE}}
|
||||
interactive: true
|
||||
|
||||
notes:push:
|
||||
desc: Commit and push changes to notes submodule
|
||||
dir: '{{.NOTES_DIR}}'
|
||||
cmds:
|
||||
- git add NOTES.md
|
||||
- git commit -m "Update notes - $(date '+%Y-%m-%d %H:%M')"
|
||||
- git pull --rebase
|
||||
- git push
|
||||
status:
|
||||
- git diff --exit-code NOTES.md
|
||||
silent: false
|
||||
|
||||
notes:status:
|
||||
desc: Check status of notes submodule
|
||||
dir: '{{.NOTES_DIR}}'
|
||||
cmds:
|
||||
- git status
|
||||
|
||||
notes:diff:
|
||||
desc: Show uncommitted changes in notes
|
||||
dir: '{{.NOTES_DIR}}'
|
||||
cmds:
|
||||
- git diff NOTES.md
|
||||
|
||||
notes:log:
|
||||
desc: Show recent commit history for notes
|
||||
dir: '{{.NOTES_DIR}}'
|
||||
cmds:
|
||||
- git log --oneline -10
|
||||
30
aws/aws-cli-requires-groff-executable.md
Normal file
30
aws/aws-cli-requires-groff-executable.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# AWS CLI Requires Groff Executable
|
||||
|
||||
I have the AWS CLI installed on this machine, but when I went to run certain
|
||||
commands like `aws logs tail my_log_group` or even `aws logs tail help`, I'd
|
||||
get the following error:
|
||||
|
||||
```
|
||||
$ aws logs tail help
|
||||
|
||||
Could not find executable named 'groff'
|
||||
```
|
||||
|
||||
This may only be an issue on MacOS Ventura for older versions of the CLI, per
|
||||
[this PR](https://github.com/aws/aws-cli/pull/7413):
|
||||
|
||||
> The CLI's help commands are currently broken on macOS Ventura because Ventura has replaced groff with mandoc. This PR fixes the issue by falling back on mandoc if groff doesn't exist in the path.
|
||||
|
||||
There are two ways of dealing with this. One would be to install the missing
|
||||
dependency, [`groff`](https://www.gnu.org/software/groff/):
|
||||
|
||||
```bash
|
||||
$ brew install groff
|
||||
```
|
||||
|
||||
The other is to update the AWS CLI to one that falls back to `mandoc`.
|
||||
Depending on how you originally installed the AWS CLI, you can either [follow
|
||||
their official install/upgrade
|
||||
instructions](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html),
|
||||
`pip install --upgrade awscli`, or upgrade view homebrew (`brew upgrade
|
||||
awscli`).
|
||||
46
aws/find-and-follow-server-logs.md
Normal file
46
aws/find-and-follow-server-logs.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Find And Follow Server Logs
|
||||
|
||||
Let's say you are authenticated with the AWS CLI and have the appropriate
|
||||
CloudWatch permissions. You have a few services running in production with
|
||||
associated logs. One of those is a Rails server.
|
||||
|
||||
We want to run `aws logs tail`, but first we check how that command works.
|
||||
|
||||
```bash
|
||||
$ aws logs tail help
|
||||
```
|
||||
|
||||
We see a bunch of options, but the only required one is `group_name` ("The name
|
||||
of the CloudWatch Logs group."). We may also notice the `--follow` flag which
|
||||
we'll want to use as well to keep incoming logs flowing.
|
||||
|
||||
We need to determine the log group name for the Rails server. We can do that
|
||||
from the CLI as well (no need to dig into the web UI).
|
||||
|
||||
```bash
|
||||
$ aws logs describe-log-groups
|
||||
|
||||
{
|
||||
"logGroups": [
|
||||
{
|
||||
"logGroupName": "/aws/codebuild/fc-rails-app-abcefg-123456",
|
||||
"creationTime": 1739476650823,
|
||||
"metricFilterCount": 0,
|
||||
"arn": "arn:aws:logs:us-east-2:123456789:log-group:/aws/codebuild/fc-rails-app-abcefg-123456:*",
|
||||
"storedBytes": 65617,
|
||||
"logGroupClass": "STANDARD",
|
||||
"logGroupArn": "arn:aws:logs:us-east-2:123456789:log-group:/aws/codebuild/fc-rails-app-abcefg-123456"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Because the group name is descriptive enough, we can find the log group we are
|
||||
interested in: `/aws/codebuild/fc-rails-app-abcefg-123456`.
|
||||
|
||||
Now we know what we want to `tail`.
|
||||
|
||||
```bash
|
||||
$ aws logs tail /aws/codebuild/fc-rails-app-abcefg-123456 --follow
|
||||
```
|
||||
29
aws/list-rds-snapshots-with-matching-identifier-prefix.md
Normal file
29
aws/list-rds-snapshots-with-matching-identifier-prefix.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# List RDS Snapshots With Matching Identifier Prefix
|
||||
|
||||
I'm working on a script that manually creates a snapshot which it will then
|
||||
restore to a temporary database that I can scrub and dump. The snapshots that
|
||||
this script takes are _manual_ and they are named with identifiers that have a
|
||||
defining prefix (`dev-snapshot-`). Besides the few snapshots created by this
|
||||
script, there are tons of automated snapshots that RDS creates for
|
||||
backup/recovery purposes.
|
||||
|
||||
I want to list any snapshots that have been created by the script. I can do
|
||||
this with the `describe-db-snapshots` command and some filters.
|
||||
|
||||
```bash
|
||||
$ aws rds describe-db-snapshots \
|
||||
--snapshot-type manual \
|
||||
--query "DBSnapshots[?starts_with(DBSnapshotIdentifier, 'dev-snapshot-')].DBSnapshotIdentifier" \
|
||||
--no-cli-pager
|
||||
|
||||
[
|
||||
"dev-snapshot-20250327-155355"
|
||||
]
|
||||
```
|
||||
|
||||
There are two key pieces. The `--snapshot-type manual` filter excludes all
|
||||
those automated snapshots. The `--query` both filters to any snapshots whose
|
||||
identifier `?starts_with` the prefix `dev-snapshot-` and then refines the
|
||||
output to just the `DBSnapshotIdentifier` instead of the entire JSON object.
|
||||
|
||||
[source](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-snapshots.html)
|
||||
49
aws/output-cli-results-in-different-formats.md
Normal file
49
aws/output-cli-results-in-different-formats.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Output CLI Results In Different Formats
|
||||
|
||||
The AWS CLI can output the results of commands in three different formats.
|
||||
|
||||
- Text
|
||||
- JSON
|
||||
- Table
|
||||
|
||||
The _default_ output format for my AWS CLI is currently configured to `json`.
|
||||
|
||||
```bash
|
||||
$ aws configure get output
|
||||
json
|
||||
```
|
||||
|
||||
I can either accept the default or I can override it with the `--output` flag.
|
||||
|
||||
```bash
|
||||
$ aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].Endpoint' \
|
||||
--no-cli-pager
|
||||
[
|
||||
{
|
||||
"Address": "fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com",
|
||||
"Port": 5432,
|
||||
"HostedZoneId": "A1BCDE2FG345H6"
|
||||
}
|
||||
]
|
||||
|
||||
$ aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].Endpoint' \
|
||||
--no-cli-pager \
|
||||
--output table
|
||||
----------------------------------------------------------------------------------------------------
|
||||
| DescribeDBInstances |
|
||||
+-----------------------------------------------------------------------+-----------------+--------+
|
||||
| Address | HostedZoneId | Port |
|
||||
+-----------------------------------------------------------------------+-----------------+--------+
|
||||
| fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com | A1BCDE2FG345H6 | 5432 |
|
||||
+-----------------------------------------------------------------------+-----------------+--------+
|
||||
|
||||
$ aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].Endpoint' \
|
||||
--no-cli-pager \
|
||||
--output text
|
||||
fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com A1BCDE2FG345H6 5432
|
||||
```
|
||||
|
||||
[source](https://docs.aws.amazon.com/cli/v1/userguide/cli-usage-output-format.html)
|
||||
50
aws/ssh-into-an-ecs-container.md
Normal file
50
aws/ssh-into-an-ecs-container.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# SSH Into An ECS Container
|
||||
|
||||
In [Connect To Production Rails Console on AWS /
|
||||
Flightcontrol](https://www.visualmode.dev/connect-to-production-rails-console-aws-flightcontrol),
|
||||
I went into full detail about how to access `rails console` for a production
|
||||
Rails app running in an ECS container.
|
||||
|
||||
A big part of that process was establishing an SSH connection to the ECS container.
|
||||
|
||||
To do that, I need to know my region, container ID, and task ID. I can get the
|
||||
first two by listing my clusters and finding the cluster/container that houses
|
||||
the Rails app.
|
||||
|
||||
```bash
|
||||
$ aws ecs list-clusters
|
||||
{
|
||||
"clusterArns": [
|
||||
"arn:aws:ecs:us-east-2:123:cluster/rails-app-abc123"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The region then is `us-east-2` and the container ID is `rails-app-abc123`.
|
||||
|
||||
I can use that to find the task ID:
|
||||
|
||||
```bash
|
||||
$ aws ecs list-tasks --region us-east-2 --cluster rails-app-abc123
|
||||
{
|
||||
"taskArns": [
|
||||
"arn:aws:ecs:us-east-2:123:task/rails-app-abc123/8526b3191d103bb1ff90c65a655ad004"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The task ID is the final portion of the URL:
|
||||
`8526b3191d103bb1ff90c65a655ad004`.
|
||||
|
||||
Putting this all together I can SSH into the ECS container with a bash profile
|
||||
like so:
|
||||
|
||||
```bash
|
||||
$ aws ecs execute-command \
|
||||
--region us-east-2 \
|
||||
--cluster rails-app-abc123 \
|
||||
--container rails-app-abc123 \
|
||||
--task 8526b3191d103bb1ff90c65a655ad004 \
|
||||
--interactive \
|
||||
--command "/bin/bash"
|
||||
```
|
||||
38
aws/turn-off-output-pager-for-a-command.md
Normal file
38
aws/turn-off-output-pager-for-a-command.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Turn Off Output Pager For A Command
|
||||
|
||||
It is not uncommon for an AWS CLI command to return a ton of output. When that
|
||||
happens, it is nice that the results end up in pager program (like `less`)
|
||||
where you can search and review them, copy a value of interest, and then exit.
|
||||
The pager prevents that wall of output from cluttering your terminal history.
|
||||
|
||||
However, sometimes I am running a command that I know is going to return a
|
||||
small result. I'd rather have the results go to stdout where I can see them in
|
||||
the terminal history rather than to an ephemeral pager.
|
||||
|
||||
For that situation I can tack on the `--no-cli-pager` flag.
|
||||
|
||||
```bash
|
||||
$ aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].EngineVersion' \
|
||||
--output json \
|
||||
--no-cli-pager
|
||||
|
||||
[
|
||||
"13.15",
|
||||
"16.8"
|
||||
]
|
||||
```
|
||||
|
||||
Here I've asked the AWS CLI to tell me the engine versions of all my RDS
|
||||
Postgres databases. Because I know the results are only going to include a
|
||||
couple results for my couple of DBs, I'd like to skip the pager —
|
||||
`--no-cli-pager`.
|
||||
|
||||
Though I think it is better to do this on a case by case basis, it is also
|
||||
possible to turn off the pager via the CLI configuration file.
|
||||
|
||||
```bash
|
||||
$ aws configure set cli_pager ""
|
||||
```
|
||||
|
||||
[source](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-pagination.html#cli-usage-pagination-clientside)
|
||||
37
aws/use-specific-aws-profile-with-cli.md
Normal file
37
aws/use-specific-aws-profile-with-cli.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Use Specific AWS Profile With CLI
|
||||
|
||||
I have multiple AWS profiles authenticated with the AWS CLI. For some projects
|
||||
I need to use the `default` one and for others I need to use the other.
|
||||
|
||||
First, I can list the available profiles like so:
|
||||
|
||||
```bash
|
||||
$ aws configure list-profiles
|
||||
default
|
||||
dev-my-app
|
||||
```
|
||||
|
||||
For one-off commands I can specify the profile for any AWS CLI command using
|
||||
the `--profile` flag.
|
||||
|
||||
```bash
|
||||
$ aws ecs list-clusters --profile josh-visualmode
|
||||
```
|
||||
|
||||
However, I don't want to have to specify that flag every time when I'm working
|
||||
on a specific project. Instead I can specify the profile with an environment
|
||||
variable. The [`direnv`](https://direnv.net/) tool is a great way to do this on
|
||||
a per-project / per-directory basis.
|
||||
|
||||
I can create or update the `.envrc` file (assuming I have `direnv` installed)
|
||||
adding the following line (and re-allowing the changed file):
|
||||
|
||||
```
|
||||
# .envrc
|
||||
export AWS_PROFILE=dev-my-app
|
||||
```
|
||||
|
||||
Now, any AWS command I issue from that directory or its subdirectories will use
|
||||
that profile by default.
|
||||
|
||||
[source](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-using-profiles)
|
||||
40
brew/clean-up-your-brew-installations.md
Normal file
40
brew/clean-up-your-brew-installations.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Clean Up Your Brew Installations
|
||||
|
||||
Over time as you upgrade brew-installed programs and make changes to your
|
||||
`Brewfile`, your machine will have artifacts left behind that you no longer
|
||||
need.
|
||||
|
||||
Periodically, it is good to clean things up.
|
||||
|
||||
First, you can get a summary of stale and outdated files that brew has
|
||||
installed. Use the `--dry-run` flag.
|
||||
|
||||
```bash
|
||||
$ brew cleanup --dry-run
|
||||
```
|
||||
|
||||
If you feel good about what you see in the output, then give things a clean.
|
||||
|
||||
```bash
|
||||
$ brew cleanup
|
||||
```
|
||||
|
||||
Second, if you are using a `Brewfile` to manage what `brew` installs, then you
|
||||
can instruct `brew` to uninstall any dependencies that aren't specified in that
|
||||
file.
|
||||
|
||||
By default it operates as a dry run and the `--force` flag will be needed to
|
||||
actually do the cleanup. And specify the filename if it doesn't match the
|
||||
default of `Brewfile`.
|
||||
|
||||
```bash
|
||||
$ brew bundle cleanup --file=Brewfile.personal
|
||||
```
|
||||
|
||||
If the output looks good, then force the cleanup:
|
||||
|
||||
```bash
|
||||
$ brew bundle cleanup --force --file=Brewfile.personal
|
||||
```
|
||||
|
||||
See `brew cleanup --help` and `brew bundle --help` for more details.
|
||||
25
brew/install-from-nonstandard-brewfile.md
Normal file
25
brew/install-from-nonstandard-brewfile.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Install From Nonstandard Brewfile
|
||||
|
||||
When you want to install the packages listed in the `Brewfile` for your current
|
||||
project (or dotfiles), you can run:
|
||||
|
||||
```bash
|
||||
$ brew bundle
|
||||
```
|
||||
|
||||
And `brew` knows to look for and use the `Brewfile` in the current directory.
|
||||
|
||||
If, however, you are trying to run `brew bundle` for a `Brewfile` located
|
||||
somewhere besides the current directory *OR* you want to target a file with a
|
||||
non-standard name (like
|
||||
[`Brewfile.personal`](https://github.com/jbranchaud/dotfiles/blob/main/Brewfile.personal)),
|
||||
then you can use the `--file` flag.
|
||||
|
||||
```bash
|
||||
$ brew bundle --file Brewfile.personal
|
||||
```
|
||||
|
||||
This is what I do [here in my `dotfiles`
|
||||
repo](https://github.com/jbranchaud/dotfiles/blob/b053f6251cae7ed52f698fc2a2c40ba82c5881b0/installer/mac-setup.sh#L42-L48).
|
||||
|
||||
See `man brew` and find the section on `brew bundle` for more details.
|
||||
27
brew/install-go-packages-in-brewfile.md
Normal file
27
brew/install-go-packages-in-brewfile.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Install Go Packages In Brewfile
|
||||
|
||||
Typically my `Brewfile` is only full of `brew` and `cask` directives. That's
|
||||
starting to change now that `brew` supports installing Go packages listed in the
|
||||
`Brewfile`.
|
||||
|
||||
Use the `go` directive and the URL to the hosted Go package.
|
||||
|
||||
Here is an example of a `Brewfile` that includes a `cask`, `brew`, and `go`
|
||||
directive.
|
||||
|
||||
```
|
||||
# screen resolution tool
|
||||
cask "betterdisplay"
|
||||
|
||||
# Mac keychain management, gpg key
|
||||
brew "pinentry-mac"
|
||||
|
||||
# Sanitized production Postgres dumps
|
||||
go "github.com/jackc/pg_partialcopy"
|
||||
```
|
||||
|
||||
I've recently added the exact package from above to my [`dotfiles`
|
||||
repo](https://github.com/jbranchaud/dotfiles/commit/e83e9d19504f0e2f95eba33123f907f999bf865e).
|
||||
|
||||
Here is the [PR to `brew`](https://github.com/Homebrew/brew/pull/20798) where
|
||||
this functionality was added back in October of 2025.
|
||||
14
chrome/open-current-tab-in-new-window-with-vimium.md
Normal file
14
chrome/open-current-tab-in-new-window-with-vimium.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Open Current Tab In New Window With Vimium
|
||||
|
||||
Sometime I have a busy Chrome window going with a bunch of tabs open for
|
||||
various lines of work as well as a number of tabs that I've neglected to close.
|
||||
I then open a new tab, find something useful, and realize I'm at a "branching
|
||||
point". I'm about to start in on a specific chunk of work that will probably
|
||||
involve opening several more tabs and switch back and forth between some
|
||||
dashboards. I want to start all of this from a fresh slate -- or at least from
|
||||
a fresh Chrome window.
|
||||
|
||||
With [Vimium](https://github.com/philc/vimium), I can hit `W` (`Shift-w`) to
|
||||
have the current tab move from the current window to a new window. The original
|
||||
window, minus that one tab, will be left as is so that I can go back to it as
|
||||
needed.
|
||||
22
chrome/search-tabs-with-the-vimium-vomnibar.md
Normal file
22
chrome/search-tabs-with-the-vimium-vomnibar.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Search Tabs With The Vimium Vomnibar
|
||||
|
||||
If you use Chrome like I do, then you eventually end up with several windows
|
||||
with dozens if not 100+ tabs open. It can start to get tedius with that many
|
||||
tabs to find and navigate to a given tab. Someone might suggest closing a few
|
||||
dozen tabs as a solution to this predicament. However, Vimium offers a solution
|
||||
that doesn't require I [_kill my
|
||||
darlings_](https://en.wiktionary.org/wiki/kill_one%27s_darlings).
|
||||
|
||||
The Vomnibar, a Vimium-powered search bar, can be summoned with `T` to only
|
||||
search through open tabs.
|
||||
|
||||
When I hit `T`, I see a text area (for refining the search) and then a bunch of
|
||||
entries populate below that which I immediately recognize as many of those tabs
|
||||
that I'm going to get back to one of these days.
|
||||
|
||||
To narrow down to the specific thing I'm looking for, I type something into the
|
||||
input. Then I arrow to the result I'm looking for and hit enter. And I'm
|
||||
transported to that tab.
|
||||
|
||||
If I don't like where I ended up, I can also go back to the tab I had been on
|
||||
with `^`.
|
||||
35
claude-code/allow-edits-from-the-start.md
Normal file
35
claude-code/allow-edits-from-the-start.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Allow Edits From The Start
|
||||
|
||||
A common pattern for me when using Claude Code is that I start it up in a
|
||||
project, I prompt it with a question or feature spec, it either comes up with a
|
||||
plan or just starts working, and as soon as it is ready to make its first edits
|
||||
to a file, it prompts me something like:
|
||||
|
||||
```
|
||||
Do you want to make this edit to Taskfile.yml?
|
||||
❯ 1. Yes
|
||||
2. Yes, allow all edits during this session (shift+tab)
|
||||
3. Type here to tell Claude what to do differently
|
||||
```
|
||||
|
||||
That's a nice default so that I don't get surprised by Claude Code editing a
|
||||
bunch of files.
|
||||
|
||||
However, if I'm in a git-backed project and I'm going into a session intending
|
||||
to make edits, then I can skip the formalities. I can tell Claude Code when
|
||||
starting up the session that edits are allowed.
|
||||
|
||||
```sh
|
||||
$ claude --permission-mode acceptEdits
|
||||
```
|
||||
|
||||
When I do this, I'll see the following indicator below the prompt input field:
|
||||
|
||||
```
|
||||
⏵⏵ accept edits on (shift+tab to cycle)
|
||||
```
|
||||
|
||||
If I've already started `claude` but I forgot to specify that permission mode, I
|
||||
can also toggle right into _accept edits_ by hitting `Shift+Tab`.
|
||||
|
||||
[source](https://www.youtube.com/watch?v=_IK18goX4X8)
|
||||
18
claude-code/monitor-usage-limits-from-cli.md
Normal file
18
claude-code/monitor-usage-limits-from-cli.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Monitor Usage Limits From CLI
|
||||
|
||||
When I first started using Claude Code enough to push the usage limits, I would
|
||||
periodically switch over to the browser to check
|
||||
`https://claude.ai/settings/usage` to see how close I was getting. That page
|
||||
would tell me what percentage of my allotted usage I had consumed so far for the
|
||||
current 5-hour session and then how long until that 5-hour usage window resets.
|
||||
|
||||
This can also be viewed directly in Claude Code for the CLI.
|
||||
|
||||
First, run the `/status` slash command and then _tab_ over to the _Usage_
|
||||
section. There you will see the same details as in the web view.
|
||||
|
||||
I'm also learned, as I write this, that you can go directly to the _Usage_
|
||||
section by typing the `/usage` slash command.
|
||||
|
||||
See [the docs](https://code.claude.com/docs/en/slash-commands) for a listing of
|
||||
all slash commands.
|
||||
15
claude-code/open-current-prompt-in-default-editor.md
Normal file
15
claude-code/open-current-prompt-in-default-editor.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Open Current Prompt In Default Editor
|
||||
|
||||
[Claude Code](https://www.claude.com/product/claude-code) gives you a single
|
||||
line to write a prompt. You can write and write as much as you want, but it will
|
||||
all be on that single line. And avoid accidentally hitting 'Enter' before you're
|
||||
done.
|
||||
|
||||
I found myself wanting to space out my thoughts, create a code block as part of
|
||||
a prompt, and generally have a scratch pad instead of just a text box. By
|
||||
hitting `ctrl-g`, I can move the current prompt into my default editor (in my
|
||||
case, `nvim`). From there I can continue to write, edit, and format with all the
|
||||
affordances of an editor.
|
||||
|
||||
Once I'm done crafting the prompt, I can save (e.g. `:wq`) and Claude Code will
|
||||
be primed with that text. I can then hit 'Enter' to let `claude` do its thing.
|
||||
29
css/filter-blur-requires-expensive-calculation.md
Normal file
29
css/filter-blur-requires-expensive-calculation.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Filter Blur Requires Expensive Calculation
|
||||
|
||||
I had [a
|
||||
page](https://www.visualmode.dev/connect-to-production-rails-console-aws-flightcontrol)
|
||||
on my blog that was experiencing some odd rendering behavior. The issue was
|
||||
manifesting a couple ways.
|
||||
|
||||
- Resizing and scrolling were janky and causing entire page layers to re-render
|
||||
causing the page to flash in and out.
|
||||
- Sometimes entire layer chunks would fail to paint leaving a white block
|
||||
missing from the page.
|
||||
|
||||
The issue was occurring with and without JavaScript turned on for a
|
||||
statically-built page. I suspected that some aspect of the CSS was at fault.
|
||||
|
||||
I was going back and forth with Dillon Hafer about what the issue could be and
|
||||
he wondered, "could it be the backdrop-blur class from tailwind?". I tried
|
||||
removing that class and the responsiveness of the page immediately improved.
|
||||
|
||||
The [`filter:
|
||||
blur`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/blur)
|
||||
and [`backdrop-filter:
|
||||
blur`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter) both
|
||||
use an expensive [Gaussian blur](https://en.wikipedia.org/wiki/Gaussian_blur)
|
||||
calculation. One of these on a modern machine and browser probably won't have a
|
||||
noticable impact. However, a bunch of them, as in the case of my page with a
|
||||
recurring component, can have quite the performance hit.
|
||||
|
||||
[source](https://github.com/tailwindlabs/tailwindcss/issues/15256)
|
||||
29
css/prevent-invisible-elements-from-being-clicked.md
Normal file
29
css/prevent-invisible-elements-from-being-clicked.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Prevent Invisible Elements From Being Clicked
|
||||
|
||||
I have a nav element that when clicked reveals a custom drop-down menu. It
|
||||
reveals it using CSS transitions and transformations (`opacity` and `scale`).
|
||||
When the nav element is clicked again, the reverse of these transformations is
|
||||
applied to "hide" the menu. This gives a nice visual effect.
|
||||
|
||||
It only makes the menu invisible and doesn't actually make it go away. That
|
||||
means that menu could be invisible, but hovering over the top of a button on
|
||||
the screen. The button cannot be clicked now because the menu is intercepting
|
||||
that [_pointer
|
||||
event_](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events).
|
||||
|
||||
The fix is to apply CSS (or a class) when the drop-down menu is closed that
|
||||
tells it to ignore _pointer events_.
|
||||
|
||||
```css
|
||||
.pointer-events-none {
|
||||
pointer-events: none;
|
||||
}
|
||||
```
|
||||
|
||||
This is more of less what [the `pointer-events-none` TailwindCSS
|
||||
utility](https://tailwindcss.com/docs/pointer-events) looks like.
|
||||
|
||||
This class is applied by default to the drop-down menu. Then when the nav item
|
||||
is clicked, some JavaScript removes that class at the same moment that the menu
|
||||
is visually appearing. When a menu item is selected or the menu otherwise
|
||||
closed, it transitions away and the `pointer-events-none` class is reapplied.
|
||||
32
cursor/allow-cursor-to-be-launched-from-cli.md
Normal file
32
cursor/allow-cursor-to-be-launched-from-cli.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Allow Cursor To Be Launched From CLI
|
||||
|
||||
It is nice to be able to open Cursor for a specific project directly from the
|
||||
terminal like so:
|
||||
|
||||
```bash
|
||||
$ cd ~/dev/my/project
|
||||
|
||||
$ cursor .
|
||||
```
|
||||
|
||||
For the `cursor` launcher binary to be available like that, we have to find it
|
||||
and add it to the path.
|
||||
|
||||
It is probably located in the `/Applications` folder and within that nested down
|
||||
a couple directories is a `bin` directory that contains the binary we're looking
|
||||
for.
|
||||
|
||||
```bash
|
||||
ls /Applications/Cursor.app/Contents/Resources/app/bin
|
||||
bin/
|
||||
├── code*
|
||||
├── cursor*
|
||||
└── cursor-tunnel*
|
||||
```
|
||||
|
||||
The `cursor` binary is what we want, so let's add that to our path. In my case,
|
||||
I'll add this to my `~/.zshrc` file.
|
||||
|
||||
```bash
|
||||
export PATH="/Applications/Cursor.app/Contents/Resources/app/bin:$PATH"
|
||||
```
|
||||
28
docker/check-postgres-version-running-in-docker-container.md
Normal file
28
docker/check-postgres-version-running-in-docker-container.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Check Postgres Version Running In Docker Container
|
||||
|
||||
I have a docker container that I'm using to run a PostgreSQL development
|
||||
database on my local machine. It was a while ago when I set it up, so I can't
|
||||
remember specifically which major version of PostgreSQL I am using.
|
||||
|
||||
I use `docker ps` to list the names of each container.
|
||||
|
||||
```bash
|
||||
$ docker ps --format "{{.Names}}"
|
||||
still-postgres-1
|
||||
better_reads-postgres-1
|
||||
```
|
||||
|
||||
I grab the one I am interested in. In this case, that is `still-postgres-1`.
|
||||
|
||||
Then I can execute a `select version()` statement with `psql` against the
|
||||
container with that name like so:
|
||||
|
||||
```bash
|
||||
$ docker exec still-postgres-1 psql -U postgres -c "select version()";
|
||||
version
|
||||
---------------------------------------------------------------------------------------------------------------------
|
||||
PostgreSQL 16.2 (Debian 16.2-1.pgdg120+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
|
||||
(1 row)
|
||||
```
|
||||
|
||||
And there I have it. I'm running Postgres v16 in this container.
|
||||
53
docker/prevent-containers-from-running-on-startup.md
Normal file
53
docker/prevent-containers-from-running-on-startup.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Prevent Containers From Running On Startup
|
||||
|
||||
I have a bunch of docker containers managed by Docker Desktop. Some are related
|
||||
to projects I'm actively working on. Whereas many others are inactive projects.
|
||||
|
||||
When I restart my machine, regardless of which containers I had running or
|
||||
turned off, several of them are booted into a running state on startup. This is
|
||||
becaue their restart policy is set to `always`. That's fine for the project I'm
|
||||
actively working on, but the others I would like to be _off_ by default.
|
||||
|
||||
I need to update each of their restart policies from `always` to `no`.
|
||||
|
||||
First, I need to figure out their container IDs:
|
||||
|
||||
```bash
|
||||
$ docker ps --all
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
eb7b40aeba2d postgres:latest "docker-entrypoint.s…" 3 months ago Up 11 minutes 0.0.0.0:9875->5432/tcp still-postgres-1
|
||||
eb9ab2213f2b postgres:latest "docker-entrypoint.s…" 3 months ago Exited (0) 11 minutes ago next-drizzle-migration-repro-app-postgres-1
|
||||
ba792e185734 postgres:latest "docker-entrypoint.s…" 4 months ago Up 11 minutes 0.0.0.0:9876->5432/tcp better_reads-postgres-1
|
||||
3139f9beae76 postgres:latest "docker-entrypoint.s…" 9 months ago Exited (128) 7 months ago basic-next-prisma-postgres-1
|
||||
```
|
||||
|
||||
Referencing the `CONTAINER ID` and `NAMES` columns, I'm able to then inspect
|
||||
each container and see the current `RestartPolicy`:
|
||||
|
||||
```bash
|
||||
$ docker inspect eb9ab2213f2b | grep -A3 RestartPolicy
|
||||
"RestartPolicy": {
|
||||
"Name": "always",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
```
|
||||
|
||||
I can then update the `RestartPolicy` to be `no`:
|
||||
|
||||
```bash
|
||||
$ docker update --restart no eb9ab2213f2b
|
||||
```
|
||||
|
||||
Inpsecting that container again, I can see the updated policy:
|
||||
|
||||
```bash
|
||||
$ docker inspect eb9ab2213f2b | grep -A3 RestartPolicy
|
||||
"RestartPolicy": {
|
||||
"Name": "no",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
```
|
||||
|
||||
Rinse and repeat for each of the offending containers.
|
||||
|
||||
[source](https://stackoverflow.com/questions/45423334/stopping-docker-containers-from-being-there-on-startup)
|
||||
42
docker/run-sql-script-against-postgres-container.md
Normal file
42
docker/run-sql-script-against-postgres-container.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Run SQL Script Against Postgres Container
|
||||
|
||||
I've been using dockerized Postgres for local development with several projects
|
||||
lately. This is typically with framework tooling (like Rails) where schema
|
||||
migrations and query execution are handled by the tooling using the specified
|
||||
connection parameters.
|
||||
|
||||
However, I was experimenting with and iterating on some Postgres functions
|
||||
outside of any framework tooling. I needed a way to run the SQL script that
|
||||
(re)creates the function via `psql` on the docker container.
|
||||
|
||||
With a local, non-containerized Postgres instance, I'd redirect the file to
|
||||
`psql` like so:
|
||||
|
||||
```bash
|
||||
$ psql -U postgres -d postgres < experimental-functions.sql
|
||||
```
|
||||
|
||||
When I tried doing this with `docker exec` though, it was silently failing /
|
||||
doing nothing. As far as I can tell, there was a mismatch with redirection
|
||||
handling across the bounds of the container.
|
||||
|
||||
To get around this, I first copy the file into the `/tmp` directory on the
|
||||
container:
|
||||
|
||||
```bash
|
||||
$ docker cp experimental-functions.sql still-postgres-1:/tmp/experimental-functions.sql
|
||||
```
|
||||
|
||||
Then the `psql` command that docker executes can be pointed directly at a
|
||||
local-to-it SQL file.
|
||||
|
||||
```bash
|
||||
$ docker exec still-postgres-1 psql \
|
||||
-U postgres \
|
||||
-d postgres \
|
||||
-f /tmp/experimental-functions.sql
|
||||
```
|
||||
|
||||
There are probably other ways to handle this, but I got into a nice rhythm with
|
||||
this file full of `create or replace function ...` definitions where I could
|
||||
modify, copy over, execute, run some SQL to verify, and repeat.
|
||||
4
dprint.json
Normal file
4
dprint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"excludes": ["README.md"],
|
||||
"plugins": ["https://plugins.dprint.dev/markdown-0.16.0.wasm"]
|
||||
}
|
||||
@@ -9,10 +9,10 @@ test runs. Most of these files are tracked (already checked in to the
|
||||
repository). There are also many new files generated as part of the most recent
|
||||
test run.
|
||||
|
||||
I want to staging the changes to files that are already tracked, but hold off
|
||||
on doing anything with the new files.
|
||||
I want to stage the changes to files that are already tracked, but hold off on
|
||||
doing anything with the new files.
|
||||
|
||||
Running `git add spec/cassettes` won't do the track because that will pull in
|
||||
Running `git add spec/cassettes` won't do the trick because that will pull in
|
||||
everything. Running `git add --patch spec/cassettes` will take long and be
|
||||
tedious. Instead what I want is the `-u` flag. It's short for _update_ which
|
||||
means it will only stage already tracked files.
|
||||
|
||||
38
git/check-if-a-file-has-changed-in-a-script.md
Normal file
38
git/check-if-a-file-has-changed-in-a-script.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Check If A File Has Changed In A Script
|
||||
|
||||
If I'm at the command line and I want to check if a file has changed, I can run
|
||||
`git diff` and see what has changed. If I want to be more specific, I can run
|
||||
`git diff README.md` to see if there are changes to that specific file.
|
||||
|
||||
If I'm trying to do this check in a script though, I want the command to clearly
|
||||
tell the script _Yes_ or _No_. Usually a script looks for an exit code to
|
||||
determine what path to take. But as long as `git diff` runs successfully,
|
||||
regardless of whether or not their are changes, it is going to have an
|
||||
affirmative exit code of `0`.
|
||||
|
||||
This is why `git diff` offers the `--exit-code` flag.
|
||||
|
||||
> Make the program exit with codes similar to diff(1). That is, it exits with 1
|
||||
> if there were differences and 0 means no differences.
|
||||
|
||||
With that in mind, we can wire up a script with `git diff` that takes different
|
||||
paths depending on whether or not there are changes.
|
||||
|
||||
```bash
|
||||
if ! git diff --exit-code README.md; then
|
||||
echo "README.md has changes"
|
||||
else
|
||||
echo "README.md is clean"
|
||||
fi
|
||||
```
|
||||
|
||||
We can take this a step further and instead use the `--quiet` flag.
|
||||
|
||||
> Disable all output of the program. Implies --exit-code. Disables execution of
|
||||
> external diff helpers whose exit code is not trusted
|
||||
|
||||
This exhibits the same behavior as `--exit-code` and goes the additional step of
|
||||
silencing diff output and disabling execution of external diff helpers like
|
||||
`delta`.
|
||||
|
||||
See `man git-diff` for more details.
|
||||
36
git/check-if-a-file-is-under-version-control.md
Normal file
36
git/check-if-a-file-is-under-version-control.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Check If A File Is Under Version Control
|
||||
|
||||
The `git ls-files` command can be used with the `--error-unmatch` flag to check
|
||||
if a file is under version control. It does this by checking if any of the
|
||||
listed files appears on the _index_. If any does not, it is treated as an error.
|
||||
|
||||
In a project, I have a `README.md` that is under version control. And I have
|
||||
`node_modules` that shouldn't be under version control (which is why they are
|
||||
listed in my `.gitignore` file). I can check the README and a file somewhere in
|
||||
`node_modules`.
|
||||
|
||||
```bash
|
||||
❯ git ls-files --error-unmatch README.md
|
||||
README.md
|
||||
|
||||
❯ git ls-files --error-unmatch node_modules/@ai-sdk/anthropic/CHANGELOG.md
|
||||
error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git
|
||||
Did you forget to 'git add'?
|
||||
```
|
||||
|
||||
Notice the second command results in an error because of the untracked
|
||||
`CHANGELOG.md` file in `node_modules`.
|
||||
|
||||
Here is another example of this at work while specifying multiple files:
|
||||
|
||||
```bash
|
||||
❯ git ls-files --error-unmatch README.md node_modules/@ai-sdk/anthropic/CHANGELOG.md package.json
|
||||
README.md
|
||||
package.json
|
||||
error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git
|
||||
Did you forget to 'git add'?
|
||||
```
|
||||
|
||||
Each tracked file gets listed and then the untracked file results in an error.
|
||||
|
||||
See `man git-ls-files` for more details.
|
||||
35
git/cherry-pick-multiple-commits-at-once.md
Normal file
35
git/cherry-pick-multiple-commits-at-once.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Cherry Pick Multiple Commits At Once
|
||||
|
||||
I've always thought of `git cherry-pick` as being a command that you can run
|
||||
against a single commit by specifying the SHA of that commit. That's how I've
|
||||
always used it.
|
||||
|
||||
The man page for `git-cherry-pick` plainly states:
|
||||
|
||||
> Given one or more existing commits, apply the change each one introduces,
|
||||
> recording a new commit for each.
|
||||
|
||||
We can cherry pick multiple commits at once in a single command. They will be
|
||||
applied one at a time in the order listed.
|
||||
|
||||
Here we can see an example of applying two commits to the current branch and
|
||||
the accompanying output as they are auto-merged.
|
||||
|
||||
```bash
|
||||
$ git cherry-pick 5206af5 6362f41
|
||||
Auto-merging test/services/event_test.rb
|
||||
[jb/my-feature-branch 961f3deb] Use the other testing syntax
|
||||
Date: Fri May 2 10:50:14 2025 -0500
|
||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
||||
Auto-merging test/services/event_test.rb
|
||||
[jb/my-feature-branch b15835d0] Make other changes to the test
|
||||
Date: Fri May 2 10:54:48 2025 -0500
|
||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
||||
```
|
||||
|
||||
If the commits cannot be cleanly merged, then you may need to do some manual
|
||||
resolution as they are applied. Or maybe you want to try including the
|
||||
`-Xpatience` merge strategy.
|
||||
|
||||
See `man git-cherry-pick` for more details. Make sure to look at the _Examples_
|
||||
section which contains much more advanced examples beyond what is shown above.
|
||||
26
git/clear-entries-from-git-stash.md
Normal file
26
git/clear-entries-from-git-stash.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Clear Entries From Git Stash
|
||||
|
||||
I often stash changes as I'm moving between branches, rebasing, or pulling in
|
||||
changes from the remote. Usually these are changes that I will want to restore
|
||||
with a `git stash pop` in a few moments.
|
||||
|
||||
However, sometimes these stashed changes are abandoned to time.
|
||||
|
||||
When I run `git stash list` on an active project, I see that there are nine
|
||||
entries in the list. When I do `git show stash@{0}` and `git show stash@{1}` to
|
||||
see the changes that comprise the latest two entries, I don't see anything I
|
||||
care about.
|
||||
|
||||
I can get rid of those individual entries with, say, `git stash drop
|
||||
stash@{0}`.
|
||||
|
||||
But I'm pretty confident that I don't care about any of the nine entries in my
|
||||
stash list, so I want to _clear_ out all of them. I can do that with:
|
||||
|
||||
```bash
|
||||
$ git stash clear
|
||||
```
|
||||
|
||||
Now when I run `git stash list`, I see nothing.
|
||||
|
||||
See `man git-stash` for more details.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Determine Absolute Path Of Top-Level Project Directory
|
||||
|
||||
The `git rev-parse` command is a git plumbing command for parsing different
|
||||
kinds of things in git into a canonical form that can be used in a deterministic
|
||||
way by scripts. I would typically think of using it to work with branch names,
|
||||
tags, and other kinds of refs.
|
||||
|
||||
There is a handy, sorta off-label use for it in determining the absolute path of
|
||||
the root directory for the current git repository. Use the `--show-toplevel`
|
||||
flag with no other arguments.
|
||||
|
||||
```bash
|
||||
❯ git rev-parse --show-toplevel
|
||||
/Users/lastword/dev/jbranchaud/til
|
||||
```
|
||||
|
||||
Here, I am in the local copy of [my TIL repo](https://github.com/jbranchaud/til). This command gives me the absolute
|
||||
path of the top-level directory where that `.git` directory resides.
|
||||
|
||||
This is useful for scripts that need to orient themselves to the current
|
||||
project's top-level directory regardless of what directory they are being
|
||||
executed from. This is useful for things like a git hook script or monorepos
|
||||
with scripts located in a specific sub-project directory.
|
||||
|
||||
Also worth mentioning is the `--show-superproject-working-tree` flag. In my TIL
|
||||
repo, I have a private repository included as a submodule. Within that directory
|
||||
`--show-toplevel` will produce the absolute path to the submodule. If I instead
|
||||
want the absolute path of the _super project_ (in this case TIL), then I can use
|
||||
this other flag.
|
||||
|
||||
```bash
|
||||
❯ git rev-parse --show-toplevel
|
||||
/Users/lastword/dev/jbranchaud/til/notes
|
||||
|
||||
❯ git rev-parse --show-superproject-working-tree
|
||||
/Users/lastword/dev/jbranchaud/til
|
||||
```
|
||||
|
||||
See `man git-rev-parse` for more details.
|
||||
32
git/exclude-a-directory-during-a-command.md
Normal file
32
git/exclude-a-directory-during-a-command.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Exclude A Directory During A Command
|
||||
|
||||
Many of the git commands we use, such as `git add`, `git restore`, etc., target
|
||||
files and paths relative to the current directory. This is typically exactly
|
||||
what we want, to stage and unstage and so forth the files and directories in
|
||||
front of us.
|
||||
|
||||
I recently ran into a situation where I needed to restore a small subset of
|
||||
changes. At the same time, I had a massive number of auto-generated files
|
||||
recording HTTP interactions (hundreds of files, modified on the working tree).
|
||||
I wanted to run a `git restore`, but wading through all those HTTP recording
|
||||
files was not feasible.
|
||||
|
||||
I needed to exclude those files. They all belonged to a `spec/cassettes`
|
||||
directory. I could exclude them with a _pathspec_ magic signature pattern which
|
||||
is used to alter and limit the paths in a git command.
|
||||
|
||||
A _pathspec_ magic signature is a special pattern made up of a `:` followed by
|
||||
some signature declaring what the pattern means.
|
||||
|
||||
The `(exclude)`, `!`, and `^` magic signatures all mean the same thing —
|
||||
exclude. So, we can exclude a directory from a `git restore` command like so:
|
||||
|
||||
```bash
|
||||
$ git restore --patch -- . ':!spec/cassettes'
|
||||
```
|
||||
|
||||
We've employed two pathspec patterns here. The first, `.`, scopes everything to
|
||||
the current directory. The second, `':!spec/cassettes'` excludes everything in
|
||||
the `spec/cassettes` directory.
|
||||
|
||||
See `man gitglossary` for more on _pathspecs_.
|
||||
26
git/files-with-local-changes-cannot-be-removed.md
Normal file
26
git/files-with-local-changes-cannot-be-removed.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Files With Local Changes Cannot Be Removed
|
||||
|
||||
This is a nice quality-of-life feature in `git` that should help you avoid
|
||||
accidentally discarding changes that won't be retrievable.
|
||||
|
||||
```bash
|
||||
❯ git rm .tool-versions
|
||||
error: the following file has local modifications:
|
||||
.tool-versions
|
||||
(use --cached to keep the file, or -f to force removal)
|
||||
```
|
||||
|
||||
My `.tool-versions` file has some local changes. I don't realize that and I go
|
||||
to issue a `git rm` command on that file. Instead of quietly wiping out my
|
||||
changes, `git` lets me know I'm doing something destructive (these local
|
||||
changes won't be in the diff or the reflog).
|
||||
|
||||
I can force the removal if I know what I'm doing with the `-f` flag. Or I can
|
||||
take the two step approach of calling `git restore` on that file and then `git
|
||||
rm`.
|
||||
|
||||
The `--cached` flag is also interesting because it doesn't actually delete the
|
||||
file from my file system, but it does stage the file deletion with `git`. That
|
||||
means the file now shows up as one of my untracked files.
|
||||
|
||||
See `man git-rm` for more details.
|
||||
44
git/highlight-small-change-on-single-line.md
Normal file
44
git/highlight-small-change-on-single-line.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Highlight Small Change On Single Line
|
||||
|
||||
Sometimes a change gets made on a single, long line of text in a Git tracked
|
||||
file. If it is a small, subtle change, then it can be hard to pick out when
|
||||
looking at the diff. A standard diff will show a green line of text stacked on
|
||||
a red line of text with no more granular information.
|
||||
|
||||
There are two ways we can improve the diff output in these situations.
|
||||
|
||||
The first is built-in to git. It is the `--word-diff` flag which will visually
|
||||
isolate the portions of the line that have changed.
|
||||
|
||||
```bash
|
||||
git diff --word-diff README.md
|
||||
```
|
||||
|
||||
Which will produce something like this:
|
||||
|
||||
```diff
|
||||
A collection of concise write-ups on small things I learn [-day to day-]{+day-to-day+} across a
|
||||
```
|
||||
|
||||
The outgoing part is wrapped in `[-...-]` and the incoming part is wrapped in
|
||||
`{+...+}`.
|
||||
|
||||
The second (and my preference) is to use
|
||||
[`delta`](https://github.com/dandavison/delta) as an external differ and pager
|
||||
for git.
|
||||
|
||||
```bash
|
||||
git -c core.pager=delta diff README.md
|
||||
```
|
||||
|
||||
I cannot visually demonstrate the difference in a standard code block. So I'll
|
||||
describe it. We see a red and green line stacked on each other, but with muted
|
||||
background colors. Then the specific characters that are different stand out
|
||||
because they are highlighted with brighter red and green. I [posted a visual
|
||||
here](https://bsky.app/profile/jbranchaud.bsky.social/post/3ln245orlxs2j).
|
||||
|
||||
`delta` can also be added as a standard part of your config like I demonstrate
|
||||
in [Better Diffs With Delta](git/better-diffs-with-delta.md).
|
||||
|
||||
h/t to [Dillon Hafer's post on
|
||||
`--word-diff`](https://til.hashrocket.com/posts/t994rwt3fg-finds-diffs-in-long-line)
|
||||
28
git/list-all-git-aliases-from-gitconfig.md
Normal file
28
git/list-all-git-aliases-from-gitconfig.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# List All Git Aliases From gitconfig
|
||||
|
||||
Running the `git config --list` command will show all of the configuration
|
||||
settings you have for `git` relative to your current location. Though most of
|
||||
these setting probably live in `~/.gitconfig`, you may also have some locally
|
||||
specified ones in `.git/config`. This will grab them all including any `alias`
|
||||
entries.
|
||||
|
||||
We can narrow things down to just `alias` entries using the `--get-regexp` flag.
|
||||
|
||||
```bash
|
||||
$ git config --get-regexp '^alias\.'
|
||||
|
||||
alias.ap add --patch
|
||||
alias.authors shortlog -s -n -e
|
||||
alias.co checkout
|
||||
alias.st status
|
||||
alias.put push origin HEAD
|
||||
alias.fixup commit --fixup
|
||||
alias.squash commit --squash
|
||||
alias.doff reset HEAD^
|
||||
alias.add-untracked !git status --porcelain | awk '/\?\?/{ print $2 }' | xargs git add
|
||||
alias.reset-authors commit --amend --reset-author -CHEAD
|
||||
```
|
||||
|
||||
I use `git doff` all the time on feature branches to "pop" the latest commmit
|
||||
onto the working copy. I was trying to remember exactly what the `git doff`
|
||||
command is and this was an easy way to check.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
While preparing some stats for a recent blog post on [A Decade of
|
||||
TILs](https://www.visualmode.dev/a-decade-of-tils), I ran into an issue
|
||||
referencing chuncks of time further back than 2020.
|
||||
referencing chunks of time further back than 2020.
|
||||
|
||||
```bash
|
||||
❯ git diff --diff-filter=A --name-only HEAD@{2016-02-06}..HEAD@{2017-02-06} -- "*.md"
|
||||
|
||||
20
git/restore-file-from-one-branch-to-the-current.md
Normal file
20
git/restore-file-from-one-branch-to-the-current.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Restore File From One Branch To The Current
|
||||
|
||||
On one feature branch I have created some files and made changes to some
|
||||
existing files as part of spiking a feature. Now I'm on a different branch
|
||||
taking another shot at it. I want changes from one or two of the files. In the
|
||||
past I've used `git-checkout` for this task. However, I believe this is one of
|
||||
the use cases they had in mind when they added `git-restore`.
|
||||
|
||||
What I want to do is _restore_ the state of a file as it appears on some source
|
||||
branch to my current branch. Here is what that looks like:
|
||||
|
||||
```bash
|
||||
$ git restore --source=some-feature-branch app/models/contact.rb
|
||||
```
|
||||
|
||||
Now when I check `git status` I'll see the state of that file on the
|
||||
`some-feature-branch` branch overlayed on my current working copy. If the file
|
||||
doesn't exist, it will be created.
|
||||
|
||||
See `man git-restore` for more details.
|
||||
56
git/set-up-gpg-signing-key.md
Normal file
56
git/set-up-gpg-signing-key.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Set Up GPG Signing Key
|
||||
|
||||
I wanted to have that "Verified" icon start showing up next to my commits in
|
||||
GitHub. To do that, I need to generate a GPG key, configure the secret key in
|
||||
GitHub, and then configure the public signing key with my git config.
|
||||
|
||||
```bash
|
||||
# generate a gpg key
|
||||
$ gpg --full-generate-key
|
||||
|
||||
# Pick the following options when prompted
|
||||
# - Choose "RSA and RSA" (Options 1)
|
||||
# - Max out key size at 4096
|
||||
# - Choose expiration date (e.g. 0 for no expiration)
|
||||
# - Enter "Real name" and "Email"
|
||||
(I matched those to what is in my global git config)
|
||||
# - Set passphrase (I had 1password generate a 4-word passphrase)
|
||||
```
|
||||
|
||||
It may take a few seconds to create.
|
||||
|
||||
I can see it was created by listing my GPG keys.
|
||||
|
||||
```bash
|
||||
$ gpg --list-secret-keys --keyid-format=long
|
||||
[keyboxd]
|
||||
---------
|
||||
sec rsa4096/1A8656918A8D016B 2025-10-19 [SC]
|
||||
...
|
||||
```
|
||||
|
||||
I'll need the `1A8656918A8D016B` portion of that response for the next command
|
||||
and it is what I set as my public signing key in my git config.
|
||||
|
||||
First, though, I add the full key block to my GitHub profile which I can copy
|
||||
like so:
|
||||
|
||||
```bash
|
||||
$ gpg --armor --export 1A8656918A8D016B | pbcopy
|
||||
```
|
||||
|
||||
And then I paste that as a new GPG Key on GitHub under _Settings_ -> _SSH and
|
||||
GPG Keys_.
|
||||
|
||||
Last, I update my global git config with the signing key and the preference to
|
||||
sign commits:
|
||||
|
||||
```bash
|
||||
git config --global user.signingkey 1A8656918A8D016B
|
||||
git config --global commit.gpgsign true
|
||||
```
|
||||
|
||||
Without `commit.gpgsign`, I would have to specify the `-S` flag every time I
|
||||
want to create a signed commit.
|
||||
|
||||
[source](https://git-scm.com/book/ms/v2/Git-Tools-Signing-Your-Work)
|
||||
26
git/show-summary-stats-for-current-branch.md
Normal file
26
git/show-summary-stats-for-current-branch.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Show Summary Stats For Current Branch
|
||||
|
||||
When I push a branch up to GitHub as a PR, there is a part of the UI that shows
|
||||
you how many lines you've added and removed for this branch. It bases that off
|
||||
the target branch which is typically your `main` branch.
|
||||
|
||||
The `git diff` command can provide those same stats right in the terminal. The
|
||||
key is to specify the `--shortstat` flag which tells `git` to exclude other diff
|
||||
output and only show:
|
||||
|
||||
- Number of files changed
|
||||
- Number of insertions
|
||||
- Number of deletions
|
||||
|
||||
Here is the summary stats for a branch I'm working on:
|
||||
|
||||
```bash
|
||||
❯ git diff --shortstat main
|
||||
8 files changed, 773 insertions(+), 25 deletions(-)
|
||||
```
|
||||
|
||||
We have to be on our feature branch and then we point to the branch (or whatever
|
||||
ref) we want to diff against. Since I want to know how my feature branch
|
||||
compares to `main`, I specify that.
|
||||
|
||||
See `man git-diff` for more details.
|
||||
33
git/skip-git-hooks-as-needed.md
Normal file
33
git/skip-git-hooks-as-needed.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Skip Git Hooks As Needed
|
||||
|
||||
Projects have Git hooks configured for all sorts of reasons. Most common are
|
||||
`pre-commit` hooks which verify certain aspects of the contents of a commit.
|
||||
A `pre-commit` hook could check that the tests all pass, that the changes don't
|
||||
include any debugging statements, and so forth. There are all kinds of hooks
|
||||
though, like `pre-rebase` and `post-checkout`.
|
||||
|
||||
These hooks can sometimes get in the way and we may need to skip or disable them
|
||||
on a one-off basis.
|
||||
|
||||
Several Git commands offer a `--no-verify` flag which can skip running the hook
|
||||
associated with that command.
|
||||
|
||||
- `git commit --no-verify` (skips `pre-commit` and `commit-msg` hooks)
|
||||
- `git push --no-verify` (skips `pre-push` hook)
|
||||
- `git merge --no-verify` (skips `pre-merge-commit` hook)
|
||||
- `git am --no-verify` (skips `applypatch-msg` and `pre-applypatch` hooks)
|
||||
|
||||
If you look in the `.git/hooks` directory, there are several other hooks not
|
||||
covered by the above. So, what if I am doing an action like `git checkout` and I
|
||||
want to skip the `post-checkout` hook?
|
||||
|
||||
I can override the `hooksPath` config for that one command with the `-c` flag.
|
||||
|
||||
```sh
|
||||
$ git -c core.hooksPath=/dev/null checkout ...
|
||||
```
|
||||
|
||||
By setting it to `/dev/null`, it will find *no* hooks available, so none will be
|
||||
executed for this command.
|
||||
|
||||
See `man git-config` for more details on `core.hooksPath`.
|
||||
25
github/access-your-github-profile-photo.md
Normal file
25
github/access-your-github-profile-photo.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Access Your GitHub Profile Photo
|
||||
|
||||
Let's say I have my [GitHub profile](https://github.com/jbranchaud) pulled up in
|
||||
the browser.
|
||||
|
||||
```
|
||||
https://github.com/jbranchaud
|
||||
```
|
||||
|
||||
If I then add `.png` to the end of that in the URL bar:
|
||||
|
||||
```
|
||||
https://github.com/jbranchaud.png
|
||||
```
|
||||
|
||||
I'll be redirected to the URL where the full image file lives. In my case:
|
||||
|
||||
```
|
||||
https://avatars.githubusercontent.com/u/694063?v=4
|
||||
```
|
||||
|
||||
You can pull up yours `https://github.com/<username>.png` to access your profile
|
||||
image.
|
||||
|
||||
[source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82)
|
||||
19
github/open-a-pr-to-an-unforked-repo.md
Normal file
19
github/open-a-pr-to-an-unforked-repo.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Open A PR To An Unforked Repo
|
||||
|
||||
Sometimes I will clone a repo to explore the source code or to look into a
|
||||
potential bug. If my curiosity takes me far enough to make some changes, then I
|
||||
jump through the hoops of creating a fork, reconfiguring branches, pushing to my
|
||||
fork, and then opening the branch as a PR against the original repo.
|
||||
|
||||
The `gh` CLI allows me to avoid all that hoop-jumping. Directly from the cloned
|
||||
repo I can use `gh` to create a new PR. It will prompt me to creat a fork. If I
|
||||
accept, it will seamlessly create it and then open a PR from my fork to the
|
||||
original.
|
||||
|
||||
```bash
|
||||
$ gh pr create
|
||||
```
|
||||
|
||||
This allows me to create the PR with a few prompts from the CLI. If you prefer,
|
||||
you can include the `--web` flag to open the PR creation screen directly in the
|
||||
browser.
|
||||
20
github/target-another-repo-when-creating-a-pr.md
Normal file
20
github/target-another-repo-when-creating-a-pr.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Target Another Repo When Creating A PR
|
||||
|
||||
I have a [`dotfiles` repo](https://github.com/jbranchaud/dotfiles) that I forked
|
||||
from [`dkarter/dotfiles`](https://github.com/dkarter/dotfiles). I'm adding a
|
||||
bunch of my own customizations on a `main` branch while continually pulling in
|
||||
and merging upstream changes.
|
||||
|
||||
The primary remote according to `gh` is `jbranchaud/dotfiles`. 98% of the time
|
||||
that is what I want. However, I occasionally want to share some changes upstream
|
||||
via a PR. Running `gh pr create` as is will create a PR against my fork. To
|
||||
override this on a one-off basis, I can use the `--repo` flag.
|
||||
|
||||
```bash
|
||||
$ gh pr create --repo dkarter/dotfiles
|
||||
```
|
||||
|
||||
This will create a PR against `dkarter:master` from my branch (e.g.
|
||||
[`jbranchaud:jb/fix-hardcoded-paths`](https://github.com/dkarter/dotfiles/pull/373)).
|
||||
|
||||
See `man gh-pr-create` for more details.
|
||||
38
github/tell-gh-what-the-default-repo-is.md
Normal file
38
github/tell-gh-what-the-default-repo-is.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Tell gh What The Default Repo Is
|
||||
|
||||
I recently forked [dkarter/dotfiles](https://github.com/dkarter/dotfiles) as a
|
||||
way of bootstrapping a robust dotfile config for a new machine that I could
|
||||
start making customizations to. I'm maintaining a `my-dotfiles` branch and keep
|
||||
things in sync with the original upstream repo.
|
||||
|
||||
When trying to go to *my* fork of the repo
|
||||
([jbranchaud/dotfiles](https://github.com/jbranchaud/dotfiles)) in the web with
|
||||
the `gh` CLI tool, I ran into a weird issue. It was instead opening up to
|
||||
`dkarter/dotfiles`.
|
||||
|
||||
`gh` was under the wrong impression which repo should be considered the default.
|
||||
To clarify things for `gh`, there is a command to set the default repo.
|
||||
|
||||
```bash
|
||||
$ gh repo set-default jbranchaud/dotfiles
|
||||
✓ Set jbranchaud/dotfiles as the default repository for the current directory
|
||||
```
|
||||
|
||||
Now when I run `gh repo view --web`, it opens the browser to my fork of the
|
||||
dotfiles.
|
||||
|
||||
But where does this setting live?
|
||||
|
||||
Opening this repo's `.git/config` file I can see a section for the `origin`
|
||||
remote that includes a new line for `gh-resolved`. This being set to `base`
|
||||
tells `gh` that this remote is the one to treat as the default repo.
|
||||
|
||||
```
|
||||
[remote "origin"]
|
||||
url = git@github.com:jbranchaud/dotfiles.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
gh-resolved = base
|
||||
|
||||
```
|
||||
|
||||
See `gh repo set-default --help` for more details.
|
||||
32
heroku/check-ruby-version-for-production-app.md
Normal file
32
heroku/check-ruby-version-for-production-app.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Check Ruby Version For Production App
|
||||
|
||||
While deploying a fresh Rails app to Heroku recently, I ran into an issue. The
|
||||
`it` block argument wasn't working despite being on Ruby 4.0. Or so I thought.
|
||||
|
||||
Running the following command reported the Ruby version of that Heroku server
|
||||
instance:
|
||||
|
||||
```bash
|
||||
❯ heroku run -- ruby --version
|
||||
Running ruby --version on ⬢ my-app... up, run.3090
|
||||
ruby 3.3.9 (2025-07-24 revision f5c772fc7c) [x86_64-linux]
|
||||
```
|
||||
|
||||
I was on `3.3.9` which must have been the fallback default at the time.
|
||||
|
||||
Though I had set the Ruby version in my `.ruby-version` file, I had neglected to
|
||||
specify it in the `Gemfile` as well. Once I added it to the `Gemfile` and
|
||||
redeployed, my Heroku server instance was running the expected version of Ruby.
|
||||
|
||||
```bash
|
||||
❯ heroku run -- ruby --version
|
||||
Running ruby --version on ⬢ my-app... up, run.5353
|
||||
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
|
||||
```
|
||||
|
||||
Note: because [I have set `HEROKU_ORGANIZATION` and
|
||||
`HEROKU_APP`](set-default-team-and-app-for-project.md) in my environment
|
||||
(`.envrc`) for the local copy of the app, I don't need to specify those when
|
||||
running the `heroku run` command above.
|
||||
|
||||
See `heroku run --help` for more details.
|
||||
34
heroku/specify-default-team-and-app-for-project.md
Normal file
34
heroku/specify-default-team-and-app-for-project.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Specify Default Team And App For Project
|
||||
|
||||
Typically when you run commands with the Heroku CLI you'll need to specify the
|
||||
name of the app on Heroku you're targeting with the `--app` flag. However, to
|
||||
first see the names of the apps you may want to run `heroku apps` (or `heroku
|
||||
list`). That will list the apps for your default team.
|
||||
|
||||
If you need to see apps for a different team (i.e. organization), you'll need to
|
||||
specify that team either with the `--team` flag or by setting that as an
|
||||
environment variable.
|
||||
|
||||
Here I do the latter in an `.envrc` file:
|
||||
|
||||
```
|
||||
# Heroku
|
||||
export HEROKU_ORGANIZATION=visualmode
|
||||
```
|
||||
|
||||
Once that is set and the environment reloaded, running `heroku apps` will show
|
||||
the apps specific to that team on Heroku.
|
||||
|
||||
Similarly, if you want to set a default app for your project so that you don't
|
||||
have to always specify the `--app` flag, you can update your `.envrc`
|
||||
accordingly.
|
||||
|
||||
```
|
||||
# Heroku
|
||||
export HEROKU_ORGANIZATION=visualmode
|
||||
export HEROKU_APP=my-app
|
||||
```
|
||||
|
||||
I had a hard time finding official documentation for this which is why I'm
|
||||
writing this up here. I've manually verified this works with my own team and
|
||||
app.
|
||||
39
html/allow-number-input-to-accept-decimal-values.md
Normal file
39
html/allow-number-input-to-accept-decimal-values.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Allow Number Input To Accept Decimal Values
|
||||
|
||||
Here is a number input element:
|
||||
|
||||
```html
|
||||
<input type="number" id="amount" required class="border" />
|
||||
```
|
||||
|
||||
This renders an empty number input box with up and down arrows which will, by
|
||||
default, increment or decrement the value by **1**.
|
||||
|
||||
Of course, I can manually edit the input typing in a value like `1.25`.
|
||||
|
||||
However, when I submit that via an HTML form, the submission will be prevented
|
||||
and the browser will display a validation error.
|
||||
|
||||
> Please enter a valid value. The two nearest valid values are 1 and 2.
|
||||
|
||||
If I want to be able to input a decimal value like this, I need to change the
|
||||
`step` value. It defaults to `1`, but I could change it to `2`, `10`, or in
|
||||
this case to `0.01`.
|
||||
|
||||
```html
|
||||
<input type="number" step="0.01" id="amount" required class="border" />
|
||||
```
|
||||
|
||||
Notice now that as you click the up and down arrows, the value is incremented
|
||||
and decremented by **0.01** at a time.
|
||||
|
||||
If I want to maintain the step value of `1` while allowing decimal values, I
|
||||
can instead set the `step` value to be `any`.
|
||||
|
||||
```html
|
||||
<input type="number" step="any" id="amount" required class="border" />
|
||||
```
|
||||
|
||||
See the [MDN docs on number
|
||||
inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/number)
|
||||
for more details.
|
||||
34
internet/download-a-google-doc-as-specific-format.md
Normal file
34
internet/download-a-google-doc-as-specific-format.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Download A Google Doc As Specific Format
|
||||
|
||||
I was recently given a public Google Doc URL and I was curious if I could
|
||||
download it from the command line. I didn't want to have to install special CLI
|
||||
though. I was hoping to use something like `curl`.
|
||||
|
||||
A brief chat with Claude and I learned that not only can I use `curl`, but I
|
||||
can specify the format in the _export_ URL.
|
||||
|
||||
```bash
|
||||
$ export GOOGLE_DOC_URL="https://docs.google.com/document/d/157rMgHeBf76T9TZnUjtrUyyS2XPwG0tObr-OjYNfMaI"
|
||||
|
||||
$ echo $GOOGLE_DOC_URL
|
||||
https://docs.google.com/document/d/157rMgHeBf76T9TZnUjtrUyyS2XPwG0tObr-OjYNfMaI
|
||||
|
||||
$ curl -L "$GOOGLE_DOC_URL/export?format=pdf" -o doc.pdf
|
||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||
Dload Upload Total Spent Left Speed
|
||||
100 414 0 414 0 0 2763 0 --:--:-- --:--:-- --:--:-- 2895
|
||||
100 16588 0 16588 0 0 56214 0 --:--:-- --:--:-- --:--:-- 167k
|
||||
|
||||
$ ls doc.pdf
|
||||
doc.pdf
|
||||
```
|
||||
|
||||
I append `/export` and then include the `?format=pdf` query param to specify
|
||||
that I want the document to be exported in PDF format. With the `-o` flag I can
|
||||
specify the name and extension of the output file.
|
||||
|
||||
This is a handy on its own, but noticing that Google Docs supports other export
|
||||
formats, I thought it would be useful to go back-and-forth with Claude to
|
||||
sketch out a script that can do this and prompt me (with `fzf`) for the file
|
||||
type -- [here is the gist for
|
||||
`gdoc-download`](https://gist.github.com/jbranchaud/cf3d2028107a1bd8484eed7cca0fcdab).
|
||||
27
internet/grab-the-rss-feed-for-a-substack-blog.md
Normal file
27
internet/grab-the-rss-feed-for-a-substack-blog.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Grab The RSS Feed For A Substack Blog
|
||||
|
||||
I've been attempting to put more energy into finding and reading blog posts via
|
||||
an RSS feed reader. This as opposed to scrolling and scrolling and hoping that
|
||||
the algorithm turns up an interesting article or two.
|
||||
|
||||
A lot of people who have been blogging for a while have a handy RSS feed link
|
||||
prominently displayed on their site. We love to see it!
|
||||
|
||||
There are a few people whose writing I really enjoy that distribute their words
|
||||
via Substack. I couldn't find a prominent or not prominent RSS feed link
|
||||
anywhere on someone's Substack. What I did learn, after some searching, is that
|
||||
you can tack `/feed` onto the end of someone's Substack URL and that will give
|
||||
you the XML feed.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
Substack blog landing page URL:
|
||||
https://registerspill.thorstenball.com
|
||||
|
||||
Substack blog RSS feed URL:
|
||||
https://registerspill.thorstenball.com/feed
|
||||
```
|
||||
|
||||
Grab that feed URL and paste it into your feed reader and you should start
|
||||
seeing their stuff show up.
|
||||
42
javascript/format-a-list-of-items-by-locale.md
Normal file
42
javascript/format-a-list-of-items-by-locale.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Format A List Of Items By Locale
|
||||
|
||||
The `Intl` module includes a [`ListFormat`
|
||||
object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat)
|
||||
which can be used to format a list of items in a consistent way across locales.
|
||||
|
||||
I've reinvented the wheel of writing a helper function numerous times across
|
||||
projects for formatting a list of items that accounts for formatting based on
|
||||
how many items there are. This built-in function handles that with the added
|
||||
benefit of working across locales.
|
||||
|
||||
Here are lists of three, two, and one items formatted in the `long` styles for
|
||||
US english.
|
||||
|
||||
```javascript
|
||||
> const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
|
||||
undefined
|
||||
|
||||
> formatter.format(['Alice', 'Bob', 'Carla'])
|
||||
'Alice, Bob, and Carla'
|
||||
|
||||
> formatter.format(['Coffee', 'Tea'])
|
||||
'Coffee and Tea'
|
||||
|
||||
> formatter.format(['Taco'])
|
||||
'Taco'
|
||||
```
|
||||
|
||||
The difference between `long` and `short` style for a `conjunction` is _and_
|
||||
versus _&_. In addition to the type`conjunction`, you could also use
|
||||
`disjunction` which will do an _or_ instead of an _and_. I'm not sure what
|
||||
you'd use the `unit` type for.
|
||||
|
||||
You could use another locale, such as French, as well:
|
||||
|
||||
```javascript
|
||||
> const formatter = new Intl.ListFormat('fr', { style: 'long', type: 'conjunction' });
|
||||
undefined
|
||||
|
||||
> formatter.format(['café', 'thé'])
|
||||
'café et thé'
|
||||
```
|
||||
28
jj/describe-current-changes-and-create-new-change.md
Normal file
28
jj/describe-current-changes-and-create-new-change.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Describe Current Changes And Create New Change
|
||||
|
||||
One of the first patterns I learned with `jj` was a pair of commands to
|
||||
essentially "commit" the working copy and start a fresh, new change. So if I am
|
||||
done making some changes, I can add a description to the `(no description)`
|
||||
working copy and then start a new working copy _change_.
|
||||
|
||||
```bash
|
||||
$ jj describe -m "Add status subcommand to show current status"
|
||||
$ jj new
|
||||
```
|
||||
|
||||
I learned from [Steve](https://steveklabnik.com/) in the [jj
|
||||
discord](https://discord.gg/dkmfj3aGQN) that a shorthand for this pattern is to
|
||||
use the `jj commit` command directly.
|
||||
|
||||
> When called without path arguments or `--interactive`, `jj commit` is
|
||||
> equivalent to `jj describe` followed by `jj new`.
|
||||
|
||||
That means, instead of the above pair of commands, I could have done:
|
||||
|
||||
```bash
|
||||
$ jj commit -m "Add status subcommand to show current status"
|
||||
```
|
||||
|
||||
That would have had the same result in my case. However, notice the caveats
|
||||
mentioned in the quote above and check out `man jj-commit` for more details on
|
||||
that.
|
||||
31
jj/squash-changes-into-parent-commit-interactively.md
Normal file
31
jj/squash-changes-into-parent-commit-interactively.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Squash Changes Into Parent Commit Interactively
|
||||
|
||||
While I have some changes in progress as part of the working copy, I can squash
|
||||
them into the previous / parent commit with the `jj squash` command. Running
|
||||
that command as is will apply all the working copy changes to the parent leaving
|
||||
the current revision empty.
|
||||
|
||||
I can also interactively squash those changes similar in spirit to how I might
|
||||
use `git add --patch` to stage and then amend specific changes into the previous
|
||||
commit with `git`. This can be done with [`jj`](https://github.com/jj-vcs/jj)
|
||||
using `squash` with the `-i` flag.
|
||||
|
||||
```bash
|
||||
jj squash -i # or --interactive
|
||||
```
|
||||
|
||||
This will open up a TUI where I can click around or use keys. Each file in the
|
||||
source revision (in my case, the working copy) will be listed. I can move the
|
||||
cursor between them hitting _space_ to toggle them in or out of the squash
|
||||
selection.
|
||||
|
||||
I can also hit `f` over a given file to toggle _folding_. When folding is on, a
|
||||
diff of the file will be disclosed with checkboxes for toggling individual
|
||||
hunks and lines.
|
||||
|
||||
Once I'm satisfied with my interactive selection, I can hit `c` to confirm and
|
||||
only the selected files and changes will be squashed into the parent.
|
||||
|
||||
See `man jj-squash` for more details.
|
||||
|
||||
[source](https://steveklabnik.github.io/jujutsu-tutorial/real-world-workflows/the-squash-workflow.html)
|
||||
51
mac/add-a-bunch-of-cli-utilities-with-coreutils.md
Normal file
51
mac/add-a-bunch-of-cli-utilities-with-coreutils.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Add A Bunch Of CLI Utilities With coreutils
|
||||
|
||||
The [`coreutils`](https://www.gnu.org/software/coreutils/) project is a
|
||||
collection of useful utilities that every operating system ought to have.
|
||||
|
||||
> The GNU Core Utilities are the basic file, shell and text manipulation
|
||||
> utilities of the GNU operating system. These are the core utilities which are
|
||||
> expected to exist on every operating system.
|
||||
|
||||
While many of these utilities are redundant with BSD utilities that MacOS
|
||||
chooses to ship with, there are some differences in the overlapping ons and then
|
||||
many additions from `coreutils`.
|
||||
|
||||
They can be installed with Homebrew:
|
||||
|
||||
```bash
|
||||
$ brew install coreutils
|
||||
```
|
||||
|
||||
And then you should have some new things available on your path. Take `shuf`, for
|
||||
instance. This utility can shuffle and select items from a file or incoming
|
||||
lines from another command. Here I use it to randomly grab a number between 1
|
||||
and 5 (with the help of `seq`):
|
||||
|
||||
```bash
|
||||
❯ seq 1 5 | shuf -n 1
|
||||
3
|
||||
|
||||
❯ seq 1 5 | shuf -n 1
|
||||
2
|
||||
|
||||
❯ seq 1 5 | shuf -n 1
|
||||
5
|
||||
```
|
||||
|
||||
Or how about some utilities for manipulating file names? Among others there is
|
||||
`realpath`, `basename`, and `dirname`.
|
||||
|
||||
```bash
|
||||
❯ realpath README.md
|
||||
/Users/lastword/dev/jbranchaud/til/README.md
|
||||
|
||||
❯ realpath README.md | xargs basename
|
||||
README.md
|
||||
|
||||
❯ realpath README.md | xargs dirname
|
||||
/Users/lastword/dev/jbranchaud/til
|
||||
```
|
||||
|
||||
See the [manual](https://www.gnu.org/software/coreutils/manual/coreutils.html)
|
||||
for many more details.
|
||||
27
mac/capture-screenshot-to-clipboard-from-cli.md
Normal file
27
mac/capture-screenshot-to-clipboard-from-cli.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Capture Screenshoot To Clipboard From CLI
|
||||
|
||||
MacOS comes with a `screencapture` utility that you can run from the terminal
|
||||
to activate the built-in screenshot functionality on Mac.
|
||||
|
||||
Usually when I am taking a screenshot, I want to do something with it right
|
||||
away. Such as paste it into an application or group chat. The `-c` flag forces
|
||||
the screen capture to go the clipboard.
|
||||
|
||||
I also generally want to capture a specific area of the screen so that the
|
||||
captured image includes the right amount of context and nothing more. The `-i`
|
||||
flag puts you in interactive screen capture mode. That means your cursor will
|
||||
turn into a crosshair that you can use to make a drag selection of the capture
|
||||
area.
|
||||
|
||||
```bash
|
||||
$ screencapture -ic
|
||||
```
|
||||
|
||||
Select an area to capture, it's now on your clipboard, paste it where you need
|
||||
it.
|
||||
|
||||
Note: The first time you run this command, your terminal program (e.g. iTerm2)
|
||||
may prompt you for the necessary OS permissions in order to capture images of
|
||||
your screen. You'll need to grant those permissions and then rerun the command.
|
||||
|
||||
See `man screencapture` for more details.
|
||||
39
mac/detect-how-long-a-user-has-been-idle.md
Normal file
39
mac/detect-how-long-a-user-has-been-idle.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Detect How Long A User Has Been Idle
|
||||
|
||||
The `ioreg` utility on MacOS dumps the I/O Kit registry tree. This lets us look
|
||||
at the state of all hardware devices and drivers registered with I/O Kit.
|
||||
Looking specifically at the Human Interface Device subsystem (`IOHIDSystem`), we
|
||||
can find a handful of properties including the `HIDIdleTime`.
|
||||
|
||||
```bash
|
||||
$ ioreg -c IOHIDSystem | awk '/HIDIdleTime/'
|
||||
| | | "HIDIdleTime" = 91831000
|
||||
```
|
||||
|
||||
That value is the number of nanoseconds since a human input device was last
|
||||
interacted with. That is the amount of time the user (me) has been idle.
|
||||
|
||||
I can convert this to seconds, which is the small amount of time between me
|
||||
hitting enter in the terminal and the command finding the idle time.
|
||||
|
||||
```bash
|
||||
$ ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf "%.2f seconds\n", $NF/1000000000}'
|
||||
0.13 seconds
|
||||
```
|
||||
|
||||
I can run this in `watch` to see the elapsed idle time increment.
|
||||
|
||||
```bash
|
||||
watch -n 1 "echo -n 'Idle time: '; ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf \"%.1f seconds\\n\", \$NF/1000000000}'"
|
||||
```
|
||||
|
||||
After watching the _idle time_ increment for a bit, I can move the mouse and
|
||||
watch it reset on the next `watch` loop.
|
||||
|
||||
This could be used as part of a script that takes certain actions after the user
|
||||
has been idle for a while, like putting the display to sleep or stopping a time
|
||||
tracker app.
|
||||
|
||||
There is a _lot_ going on in the `ioreg` output and it's hard to make sense of
|
||||
hardly any of it. I found running `ioreg -c IOHIDSystem | less`, searching for
|
||||
`IdleTime`, and browsing from there to be a good starting point.
|
||||
41
mac/inspect-assertions-preventing-sleep.md
Normal file
41
mac/inspect-assertions-preventing-sleep.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Inspect Assertions Preventing Sleep
|
||||
|
||||
The `pmset` command is for inspecting and manipulating _Power Management
|
||||
Settings_ on MacOS. The `-g` flag is for _getting_ details. We can get a summary
|
||||
of power assertions with `-g assertions`. These assertions are ways that the
|
||||
system and display are prevented from sleeping.
|
||||
|
||||
A common assertion preventing sleep is the user being active. Another example of
|
||||
an assertion is a program like `caffeinate` that sets a timeout preventing sleep
|
||||
for a fixed period of time.
|
||||
|
||||
Here I activate a 30 minute (1600 second) `caffeinate` session and then I
|
||||
inspect the power management assertions which shows the details of that
|
||||
assertion as well as two others.
|
||||
|
||||
```bash
|
||||
❯ caffeinate -t 1600 &
|
||||
[1] 98217
|
||||
|
||||
❯ pmset -g assertions
|
||||
2025-11-02 13:20:57 -0600
|
||||
Assertion status system-wide:
|
||||
BackgroundTask 0
|
||||
ApplePushServiceTask 0
|
||||
UserIsActive 1
|
||||
PreventUserIdleDisplaySleep 0
|
||||
PreventSystemSleep 0
|
||||
ExternalMedia 0
|
||||
PreventUserIdleSystemSleep 1
|
||||
NetworkClientActive 0
|
||||
Listed by owning process:
|
||||
pid 98217(caffeinate): [0x00045477000194b3] 00:00:03 PreventUserIdleSystemSleep named: "caffeinate command-line tool"
|
||||
Details: caffeinate asserting for 1600 secs
|
||||
Localized=THE CAFFEINATE TOOL IS PREVENTING SLEEP.
|
||||
Timeout will fire in 1597 secs Action=TimeoutActionRelease
|
||||
pid 145(WindowServer): [0x00044f2f00099212] 00:00:00 UserIsActive named: "com.apple.iohideventsystem.queue.tickle serviceID:10009be9e service:AppleUserHIDEventService product:CTRL Keyboard eventType:3"
|
||||
Timeout will fire in 600 secs Action=TimeoutActionRelease
|
||||
pid 80(powerd): [0x00044f2f00019216] 00:22:34 PreventUserIdleSystemSleep named: "Powerd - Prevent sleep while display is on"
|
||||
```
|
||||
|
||||
See `man pmset` and `man caffeinate` for more details.
|
||||
36
mac/launch-some-confetti.md
Normal file
36
mac/launch-some-confetti.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Launch Some Confetti
|
||||
|
||||
If you have [Raycast](https://www.raycast.com/) installed on your machine, then
|
||||
you have quick access to some confetti via their quick command palette. Trigger
|
||||
the command palette to open, start typing `confetti` until it appears as the
|
||||
focused option, and then hit enter.
|
||||
|
||||
🎉
|
||||
|
||||
We can launch confetti other ways, including programmatically from scripts.
|
||||
|
||||
To do this, we need to first find the _deeplink_ for the Raycast _confetti_
|
||||
program. Trigger the command palette and type out `confetti` again. However,
|
||||
this time instead of hitting enter, hit `Cmd+k` to open other actions. Find the
|
||||
_Copy Deeplink_ option.
|
||||
|
||||
You should now have this on your clipboard:
|
||||
|
||||
```
|
||||
raycast://extensions/raycast/raycast/confetti
|
||||
```
|
||||
|
||||
With this deeplink in hand, we can now trigger confetti other places. The
|
||||
easiest way to do this is to open a terminal and pass that deep link as an
|
||||
argument to `open`.
|
||||
|
||||
```bash
|
||||
$ open raycast://extensions/raycast/raycast/confetti
|
||||
```
|
||||
|
||||
Now you can wrap that up in any old bash script or even just tack it on to the
|
||||
end of a run of your test suite:
|
||||
|
||||
```bash
|
||||
$ rails test && open raycast://extensions/raycast/raycast/confetti
|
||||
```
|
||||
30
mac/prevent-sleep-with-the-caffeinate-command.md
Normal file
30
mac/prevent-sleep-with-the-caffeinate-command.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Prevent Sleep With The Caffeinate Command
|
||||
|
||||
MacOS has a built-in utility `caffeinate` that can programatically prevent your
|
||||
machine from sleeping. There are two kinds of sleep that it can prevent via
|
||||
_assertions_.
|
||||
|
||||
> caffeinate creates assertions to alter system sleep behavior.
|
||||
|
||||
The two kinds of sleep behavior are _display sleep_ and _system idle sleep_. An
|
||||
assertion to prevent display sleep can be created with `-d` and system idle
|
||||
sleep with `-i`.
|
||||
|
||||
We can combine those to prevent both and then specify a duration (_timeout_)
|
||||
with `-t` (with a value in seconds).
|
||||
|
||||
```bash
|
||||
caffeinate -d -i -t 600
|
||||
```
|
||||
|
||||
This creates assertions with 10 minute timeouts for both display and system idle
|
||||
sleep.
|
||||
|
||||
The `caffeinate` command is blocking, so if you want to start it in the
|
||||
background, you can do that like so:
|
||||
|
||||
```bash
|
||||
caffeinate -d -i -t 600 &
|
||||
```
|
||||
|
||||
See `man caffeinate` for more details.
|
||||
53
mise/create-umbrella-task-for-all-test-tasks.md
Normal file
53
mise/create-umbrella-task-for-all-test-tasks.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Create Umbrella Task For All Test Tasks
|
||||
|
||||
When I was first sketching out the [`mise`
|
||||
tasks](https://mise.jdx.dev/tasks/running-tasks.html) for a Rails app, I added
|
||||
the following two tasks. One is for running all the `rspec` tests. The Other is
|
||||
for running all the `vitest` (JavaScript) tests.
|
||||
|
||||
```toml
|
||||
[tasks."test:rspec"]
|
||||
run = "unbuffer bundle exec rspec"
|
||||
description = "Run RSpec tests"
|
||||
depends = ["bundle-install"]
|
||||
|
||||
[tasks."test:vitest"]
|
||||
run = "unbuffer yarn test run"
|
||||
description = "Run Vitest tests"
|
||||
depends = ["node-install"]
|
||||
```
|
||||
|
||||
I didn't want to have to invoked both of this individually every time I wanted
|
||||
to run the full suite. So I added a `test:all` task to do it all.
|
||||
|
||||
```toml
|
||||
[tasks."test:all"]
|
||||
description = "Run all tests (RSpec and Vitest)"
|
||||
run = [
|
||||
"unbuffer bundle exec rspec",
|
||||
"unbuffer yarn test run",
|
||||
]
|
||||
description = "Run RSpec tests"
|
||||
depends = ["bundle-install", "node-install"]
|
||||
```
|
||||
|
||||
This worked (for now). But it ate at me, for a couple reasons. I had to
|
||||
duplicate everything about the existing `test:rspec` and `test:vitest` tasks.
|
||||
And this didn't account for a new kind of test task being added (e.g.
|
||||
`test:e2e`).
|
||||
|
||||
Instead, I can rely on `depends` and wildcards to achieve this without the
|
||||
duplication which makes it more future-proof.
|
||||
|
||||
```toml
|
||||
[tasks."test:all"]
|
||||
description = "Run all tests (RSpec and Vitest)"
|
||||
depends = ["test:*"]
|
||||
```
|
||||
|
||||
Running `mise run test:all` won't execute its own command, but because it
|
||||
depends on all other `test:*` tasks, the tests will get run through those
|
||||
dependencies.
|
||||
|
||||
This task naming pattern also allows for calling all tests with `mise run
|
||||
"test:**"`.
|
||||
29
mise/list-the-files-being-loaded-by-mise.md
Normal file
29
mise/list-the-files-being-loaded-by-mise.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# List The Files Being Loaded By Mise
|
||||
|
||||
While running `mise` for the first time, after adding a `mise.toml` file to a
|
||||
project, I noticed something strange. Instead of invoking the command I had
|
||||
specified (`mise run dev`), several parellel tool downloads were kicked off. In
|
||||
addition to Ruby, it was installing an older version of Postgres, and lua. What
|
||||
gives?
|
||||
|
||||
By running `mise cfg`, I can list all the files being loaded by `mise` and get
|
||||
to the bottom of this.
|
||||
|
||||
```bash
|
||||
mise cfg
|
||||
|
||||
Path Tools
|
||||
~/.tool-versions node, ruby, postgres, lua
|
||||
~/code/still/.ruby-version ruby
|
||||
~/code/still/Gemfile (none)
|
||||
~/code/still/.tool-versions ruby
|
||||
~/code/still/mise.toml (none)
|
||||
```
|
||||
|
||||
I was only thinking about the files local to my project and I forgot that I
|
||||
have a system-wide `.tool-versions` file. As we can see from the output, that
|
||||
file specifies `postgres` and `lua` as well. Mise wanted to ensure that it had
|
||||
downloaded the specified versions of each of those tools before running my
|
||||
task.
|
||||
|
||||
[source](https://mise.jdx.dev/configuration.html)
|
||||
27
mise/look-in-ruby-version-dotfile.md
Normal file
27
mise/look-in-ruby-version-dotfile.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Look In Ruby Version Dotfile
|
||||
|
||||
Newer versions of [`mise`](https://mise.jdx.dev/dev-tools/) specifically only
|
||||
look for tool versions in `mise.toml` as well as the asdf `.tool-versions` file.
|
||||
A lot of Ruby projects use the `.ruby-version` file to indicate the Ruby version
|
||||
of a project. To continue to use the `.ruby-version` file instead of migrating
|
||||
to `mise.toml`, you need to tell `mise` that you prefer to use the idiomatic
|
||||
version file.
|
||||
|
||||
I added the following line to my
|
||||
[`~/.config/mise/config.toml`](https://github.com/jbranchaud/dotfiles/commit/8edeb7a9c53500e89e88b4079cbd1859ebebcbda)
|
||||
file:
|
||||
|
||||
```toml
|
||||
idiomatic_version_file_enable_tools = ["ruby"]
|
||||
```
|
||||
|
||||
Now, whenever `mise` is looking for the specified Ruby version of a project, it
|
||||
will also look for `.ruby-version`.
|
||||
|
||||
Here is a [full list of idomatic version files supported by
|
||||
`mise`](https://mise.jdx.dev/configuration.html#idiomatic-version-files).
|
||||
|
||||
See
|
||||
[`idiomatic_version_file_enable_tools`](https://mise.jdx.dev/configuration/settings.html#idiomatic_version_file_enable_tools)
|
||||
as well as the [Ruby-specific documentation](https://mise.jdx.dev/lang/ruby.html#ruby-version-and-gemfile-support)
|
||||
for more details.
|
||||
45
mise/preserve-color-output-for-task-command.md
Normal file
45
mise/preserve-color-output-for-task-command.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Preserve Color Output For Task Command
|
||||
|
||||
I decided to wrap a couple test running commands for a project into a single
|
||||
`test:all` mise task. It looked something like this:
|
||||
|
||||
```toml
|
||||
[tasks."test:all"]
|
||||
run = """
|
||||
bundle exec rspec
|
||||
yarn test run
|
||||
"""
|
||||
description = "Run all tests (RSpec and Vitest)"
|
||||
depends = ["bundle-install", "node-install"]
|
||||
```
|
||||
|
||||
I can run this with `mise run test:all` and it works. However, there is a
|
||||
glaring issue that immediately juts out. All of the test runner output is
|
||||
uncolored text. I'm used to and strongly prefer greens (passes), reds (fails),
|
||||
and yellows (skips) of test runner output.
|
||||
|
||||
The test runners lose the text coloring when run through `mise` because they
|
||||
believe they are not running in _interactive_ mode.
|
||||
|
||||
The [`expect`](https://linux.die.net/man/1/expect) tools (`brew install
|
||||
expect`) install with another binary called
|
||||
[`unbuffer`](https://linux.die.net/man/1/unbuffer). `unbuffer` can coerce a
|
||||
command to run in interactive mode. Prepending these test runner commands with
|
||||
`unbuffer` will preserve the colors as the results are output to the terminal.
|
||||
|
||||
Here is the update `test:all` task:
|
||||
|
||||
```toml
|
||||
[tasks."test:all"]
|
||||
run = """
|
||||
unbuffer bundle exec rspec
|
||||
unbuffer yarn test run
|
||||
"""
|
||||
description = "Run all tests (RSpec and Vitest)"
|
||||
depends = ["bundle-install", "node-install"]
|
||||
```
|
||||
|
||||
For some commands, it seems able to stream out (rather than _buffer_) the
|
||||
results (e.g. with `vitest`). Whereas with `rspec`, the test suite runs to
|
||||
completion and is then output to the terminal. I'm still investigating
|
||||
streaming the `rspec` results.
|
||||
28
mise/read-existing-dot-env-file-into-env-vars.md
Normal file
28
mise/read-existing-dot-env-file-into-env-vars.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Read Existing Dot Env File Into Env Vars
|
||||
|
||||
Just about any web app that I've worked on has had a `.env` file as a way of
|
||||
configuring aspects of the app specific to that environment. These typically
|
||||
are read into the environment with a language-specific
|
||||
[dotenv](https://github.com/bkeepers/dotenv) tool.
|
||||
|
||||
Mise supports this convention. In addition to specifying individual non-secret
|
||||
env vars, you can also instruct `mise` to read-in a `.env` file like so:
|
||||
|
||||
```toml
|
||||
[env]
|
||||
PORT=3344
|
||||
_.file = ".env"
|
||||
```
|
||||
|
||||
The `_.file` line tells `mise` that there is a file `.env` with key-value pairs
|
||||
that it should read in. It can even handle `.env.json` and `.env.toml` file
|
||||
formats.
|
||||
|
||||
To ensure that `mise` is picking up the values from the `.env` file, you can
|
||||
run the following command and make sure they show up in the output:
|
||||
|
||||
```bash
|
||||
$ mise env
|
||||
```
|
||||
|
||||
[source](https://mise.jdx.dev/environments/secrets.html)
|
||||
39
mise/run-a-command-with-specific-tool-version.md
Normal file
39
mise/run-a-command-with-specific-tool-version.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Run A Command With Specific Tool Version
|
||||
|
||||
Because I'm using `mise` to manage the versions of tools like Node, I can
|
||||
execute commands in the context of specific versions. Behind the scenes `mise`
|
||||
makes sure I have the necessary tool(s) installed at the desired version(s).
|
||||
|
||||
So, [`mise exec` command](https://mise.jdx.dev/cli/exec.html) will default to
|
||||
using the latest version of a tool if I haven't been more specific. At the time
|
||||
of this writing, for Node, that is v23.
|
||||
|
||||
```bash
|
||||
$ mise exec node -- node --version
|
||||
v23.9.0
|
||||
```
|
||||
|
||||
To be specific I could specify the major version with `node@23` like so:
|
||||
|
||||
```bash
|
||||
mise exec node@23 -- npx repomix
|
||||
Need to install the following packages:
|
||||
repomix@0.2.39
|
||||
Ok to proceed? (y) y
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
Or if I wanted to use a different, older version of Node, I could specify that
|
||||
as well. We can see it will first install that and then execute the command:
|
||||
|
||||
```bash
|
||||
$ mise exec node@22 -- npx repomix
|
||||
gpg: Signature made Tue Feb 11 04:44:53 2025 CST
|
||||
gpg: using RSA key C0D6248439F1D5604AAFFB4021D900FFDB233756
|
||||
gpg: Good signature from "Antoine du Hamel <duhamelantoine1995@gmail.com>" [unknown]
|
||||
|
||||
📦 Repomix v0.2.39
|
||||
|
||||
...
|
||||
```
|
||||
40
mysql/get-idea-of-what-is-in-a-json-column.md
Normal file
40
mysql/get-idea-of-what-is-in-a-json-column.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Get Idea Of What Is In A JSON Column
|
||||
|
||||
While digging through some data trying to reacquaint myself with the overall
|
||||
schema and data model, I ran into an issue selecting rows from this
|
||||
`content_resource` table. There was so much text packed in to the `"body"`,
|
||||
`"summary"`, and `"description"` key-value pairs of `fields` JSON column that a
|
||||
simple `select * ... limit 3;` was overwhelming the screen with text and table
|
||||
formatting characters (i.e. `+------+-------`).
|
||||
|
||||
I figured the `fields` JSON followed a reliable structure, at least for records
|
||||
of the same `type`. So, let's start by only grabbing the
|
||||
[`json_keys`](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#function_json-keys)
|
||||
so that I can get a sense of the shape of the JSON.
|
||||
|
||||
```sql
|
||||
select id, json_keys(fields)
|
||||
from content_resource
|
||||
where type = 'post'
|
||||
limit 3;
|
||||
|
||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
||||
| id | json_keys(`fields`) |
|
||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
||||
| 1 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
||||
| 2 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
||||
| 3 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
For the `post` type, I see the same keys for this sampling of rows. Now I have
|
||||
an idea what keys are present and can start digging in further.
|
||||
|
||||
My next query might look something like this:
|
||||
|
||||
```sql
|
||||
select id, fields->'$.slug', fields->'$.title', fields->'$.state'
|
||||
from content_resource
|
||||
where type = 'post'
|
||||
limit 3;
|
||||
```
|
||||
46
neovim/jump-between-changes-in-current-file.md
Normal file
46
neovim/jump-between-changes-in-current-file.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Jump Between Changes In Current File
|
||||
|
||||
With the [gitsigns.nvim plugin](https://github.com/lewis6991/gitsigns.nvim) for
|
||||
Neovim, I get some handy Git-related capabilities like gutter highlighting of
|
||||
additions, deletions, and changes to lines in the current file. These contiguous
|
||||
sections of modification to the versioned state of a file are called hunks.
|
||||
|
||||
Here are two mappings (in Lua) for gitsigns that allow me to jump to the next
|
||||
(`]h`) or previous (`[h`) hunk in the current file.
|
||||
|
||||
```lua
|
||||
---@type LazyKeysSpec[]
|
||||
M.gitsigns_mappings = {
|
||||
|
||||
-- Navigation
|
||||
{
|
||||
']h',
|
||||
function()
|
||||
if vim.wo.diff then
|
||||
vim.cmd.normal { ']c', bang = true }
|
||||
else
|
||||
require('gitsigns').nav_hunk 'next'
|
||||
end
|
||||
end,
|
||||
desc = 'Next Hunk',
|
||||
},
|
||||
|
||||
{
|
||||
'[h',
|
||||
function()
|
||||
if vim.wo.diff then
|
||||
vim.cmd.normal { '[c', bang = true }
|
||||
else
|
||||
require('gitsigns').nav_hunk 'prev'
|
||||
end
|
||||
end,
|
||||
desc = 'Prev Hunk',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This is particularly useful when I've just opened a big file and I want to jump
|
||||
directly to active changes in that file.
|
||||
|
||||
I got this mapping directly from [Dorian's
|
||||
dotfiles](https://github.com/dkarter/dotfiles).
|
||||
23
neovim/run-nvim-with-factory-defaults.md
Normal file
23
neovim/run-nvim-with-factory-defaults.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Run nvim With Factory Defaults
|
||||
|
||||
Most of the fun of using Neovim is tailoring it to your exact needs with custom
|
||||
configurations. Your configuration can be made up of environment variables,
|
||||
`init.lua`/`init.vim`, and user directories on the `runtimepath`.
|
||||
|
||||
Perhaps though, you want to load neovim with its "factory defaults". You want
|
||||
to ignore all your custom config and your _shada_ (shared data) file. I wanted
|
||||
to do just that recently to verify that neovim has the `ft-manpage` plugin
|
||||
enabled by default (as opposed to enabled somewhere in the labryinth of my
|
||||
config files).
|
||||
|
||||
The `--clean` flag does just this. It loads built-in plugins, but none of the
|
||||
user defined config.
|
||||
|
||||
```bash
|
||||
$ nvim --clean
|
||||
```
|
||||
|
||||
This is different than `nvim -u NONE` which excludes all plugins, including
|
||||
built-in ones.
|
||||
|
||||
See `man nvim` and `:help --clean` for more details.
|
||||
1
notes
Submodule
1
notes
Submodule
Submodule notes added at 897184eb02
23
planetscale/see-what-databases-you-have-access-to.md
Normal file
23
planetscale/see-what-databases-you-have-access-to.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# See What Databases You Have Access To
|
||||
|
||||
Assuming you have the `pscale` CLI installed and you've authenticated with it,
|
||||
you can run the following to view available databases.
|
||||
|
||||
```bash
|
||||
$ pscale database list
|
||||
NAME KIND CREATED AT UPDATED AT
|
||||
----------- ------- ------------- -------------
|
||||
bookshelf mysql 3 years ago 3 years ago
|
||||
```
|
||||
|
||||
I'm not very active on my personal account. Planetscale is a multi-tenant SaaS
|
||||
though. I can switch from my personal `org` to another team I have access to.
|
||||
|
||||
```bash
|
||||
$ pscale org switch another-team
|
||||
```
|
||||
|
||||
And then from there I can run `pscale database list` again to see what databases
|
||||
I have access to from this other organization.
|
||||
|
||||
See `pscale database help` and `pscale org help` for more details.
|
||||
39
postgres/check-the-size-of-databases-in-a-cluster.md
Normal file
39
postgres/check-the-size-of-databases-in-a-cluster.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Check The Size Of Databases In A Cluster
|
||||
|
||||
The `\l` command in `psql` will list all the databases for the server. The
|
||||
field surfaced by this meta-command are:
|
||||
|
||||
- Name
|
||||
- Owner
|
||||
- Encoding
|
||||
- Locale Provider
|
||||
- Collate
|
||||
- Ctype
|
||||
- ICU Locale
|
||||
- ICU Rules
|
||||
- Access privileges
|
||||
|
||||
If we add a `+`, issuing instead `\l+`, we get three additional fields:
|
||||
|
||||
- Size
|
||||
- Tablespace
|
||||
- Description
|
||||
|
||||
The _Size_ column is the human-formatted size of each database.
|
||||
|
||||
Another way to do this is with some SQL querying the underlying record keeping
|
||||
of the server's database.
|
||||
|
||||
```sql
|
||||
select
|
||||
db.datname as db_name,
|
||||
pg_size_pretty(pg_database_size(db.datname)) as db_size
|
||||
from pg_database db
|
||||
order by pg_database_size(db.datname) desc;
|
||||
```
|
||||
|
||||
Credit to [this StackOverflow
|
||||
answer](https://stackoverflow.com/a/18907188/535590) for how to do this with a
|
||||
SQL query.
|
||||
|
||||
[source](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-LIST)
|
||||
58
postgres/create-and-execute-sql-statements-with-gexec.md
Normal file
58
postgres/create-and-execute-sql-statements-with-gexec.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Create And Execute SQL Statements With \gexec
|
||||
|
||||
The [`\gexec`
|
||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-GEXEC)
|
||||
is a variation of the [`\g`
|
||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-G),
|
||||
both of which can be used in a `psql` session. Whereas the `\g` command sends
|
||||
the current query in the buffer to the PostgreSQL server for execution, the
|
||||
`\gexec` command first sends the query to the server for execution and then
|
||||
executes each row of the result as its own SQL statement.
|
||||
|
||||
This is both a bit absurd and powerful. And a bit unnecessary considering all
|
||||
of the scripting capabilities with anything from bash to any language with a
|
||||
SQL client library.
|
||||
|
||||
Nevertheless, let's take a look at a contrived example of how it works. Here,
|
||||
we have a SQL statement that does some string concatenation based off values in
|
||||
an array. This results in three separate `create schema` statements.
|
||||
|
||||
```sql
|
||||
> select
|
||||
'create schema if not exists schema_' || letter || ';'
|
||||
from unnest(array['a', 'b', 'c']) as letter
|
||||
\gexec
|
||||
|
||||
CREATE SCHEMA
|
||||
CREATE SCHEMA
|
||||
CREATE SCHEMA
|
||||
|
||||
> \dn
|
||||
List of schemas
|
||||
Name | Owner
|
||||
----------+-------------------
|
||||
public | pg_database_owner
|
||||
schema_a | postgres
|
||||
schema_b | postgres
|
||||
schema_c | postgres
|
||||
(4 rows)
|
||||
```
|
||||
|
||||
Three new schemas get created which we can inspect with `\dn`.
|
||||
|
||||
Notice, if we simply execute the primary statement, we can see the intermediate
|
||||
result that `\gexec` will subsequently execute.
|
||||
|
||||
```sql
|
||||
> select
|
||||
'create schema if not exists schema_' || letter || ';'
|
||||
from unnest(array['a', 'b', 'c']) as letter
|
||||
\g
|
||||
|
||||
?column?
|
||||
---------------------------------------
|
||||
create schema if not exists schema_a;
|
||||
create schema if not exists schema_b;
|
||||
create schema if not exists schema_c;
|
||||
(3 rows)
|
||||
```
|
||||
49
postgres/fetch-data-from-an-endpoint-in-sql.md
Normal file
49
postgres/fetch-data-from-an-endpoint-in-sql.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Fetch Data From An Endpoint In SQL
|
||||
|
||||
The [`pgsql-http` extension](https://github.com/pramsey/pgsql-http) provides a
|
||||
variety of functions for allowing PostgreSQL to act as an HTTP client. This is
|
||||
a bit unorthodox and may not be a good idea in production systems. That said,
|
||||
it is cool that it is possible. Let's look at an example of it.
|
||||
|
||||
First, I've installed the extension on the Docker container running my local
|
||||
Postgres server.
|
||||
|
||||
```bash
|
||||
$ docker exec -it still-postgres-1 bash
|
||||
|
||||
$ apt-get update
|
||||
|
||||
$ apt-get install postgres-16-http # I'm running Postgres v16
|
||||
|
||||
$ exit
|
||||
```
|
||||
|
||||
Then I'll connect to a `psql` session in that container for the `postgres` database.
|
||||
|
||||
```bash
|
||||
$ docker exec still-postgres-1 psql -U postgres -d postgres
|
||||
```
|
||||
|
||||
Then I enable the extension.
|
||||
|
||||
```sql
|
||||
> create extension if not exists http;
|
||||
CREATE EXTENSION
|
||||
```
|
||||
|
||||
Now I can point a PostgreSQL statement at a live endpoint like
|
||||
[https://httpbun.com/ip](https://httpbun.com/ip) which will respond with a
|
||||
chunk of JSON including the IP address for that project's server. I do this
|
||||
using `http_get` which makes a `GET` request to the given endpoint. The body is
|
||||
included in the result set.
|
||||
|
||||
```bash
|
||||
> select content from http_get('http://httpbun.com/ip');
|
||||
content
|
||||
-----------------------------
|
||||
{ +
|
||||
"origin": "73.75.236.101"+
|
||||
} +
|
||||
|
||||
(1 row)
|
||||
```
|
||||
37
postgres/references-target-primary-key-by-default.md
Normal file
37
postgres/references-target-primary-key-by-default.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# References Target Primary Key By Default
|
||||
|
||||
Typically when I am creating a table or adding a column that involves a foreign
|
||||
key constraint, I explicitly name the reference column.
|
||||
|
||||
```sql
|
||||
create table contacts (
|
||||
id int generated always as identity primary key,
|
||||
user_id int references users(id);
|
||||
);
|
||||
```
|
||||
|
||||
The [Create Table PostgreSQL
|
||||
Docs](https://www.postgresql.org/docs/17/sql-createtable.html) point out that
|
||||
specifying the reference column isn't strictly necessary.
|
||||
|
||||
> These clauses specify a foreign key constraint, which requires that a group
|
||||
> of one or more columns of the new table must only contain values that match
|
||||
> values in the referenced column(s) of some row of the referenced table. If
|
||||
> the refcolumn list is omitted, the primary key of the reftable is used.
|
||||
|
||||
If we're using the primary key as the reference column, then we can choose to
|
||||
omit the reference column.
|
||||
|
||||
```sql
|
||||
create table contacts (
|
||||
id int generated always as identity primary key,
|
||||
user_id int references users;
|
||||
);
|
||||
```
|
||||
|
||||
In the same way we can do this when adding a column.
|
||||
|
||||
```sql
|
||||
alter table contacts
|
||||
add column account_id int references account;
|
||||
```
|
||||
35
postgres/set-up-a-project-local-cluster-with-postgres-app.md
Normal file
35
postgres/set-up-a-project-local-cluster-with-postgres-app.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Set Up A Project-Local Cluster With Postgres.app
|
||||
|
||||
I want to set up a PostgreSQL cluster in my project directory. This helps
|
||||
provide some separation and clarity that this cluster and its databases are just
|
||||
for this project.
|
||||
|
||||
This can be done with `Postgres.app` (on Mac) hitting the `+` button in the
|
||||
bottom left corner of the app. This will pop open a "Create new server" modal.
|
||||
|
||||
From there, you'll want to give the server a name that you can identify within
|
||||
`Postgres.app`. E.g. "<App Name> Cluster"
|
||||
|
||||
Then select the Postgres version. My existing project is still on 17, so I'll
|
||||
select that.
|
||||
|
||||
The not so intuitive part is the _Data Directory_. Use the "Choose..." file
|
||||
picker to find the root directory of your project. Select that. Then click into
|
||||
the text input for the data directory and append the name of the data directory
|
||||
_to be created_ to that path. If I want it to all go in `postgres-data`, then my
|
||||
path will look like:
|
||||
|
||||
```
|
||||
/Users/me/dev/my-app/postgres-data
|
||||
```
|
||||
|
||||
The `postgres-data` directory doesn't exist yet. But it will in a moment.
|
||||
|
||||
You probably want the default port, so leave that at `5432` unless you know
|
||||
otherwise.
|
||||
|
||||
Click `Create server`, though that won't actually create the server yet. Now
|
||||
with that server selected in `Postgres.app` click the `Initialize` button. That
|
||||
will create the `postgres-data` directory and then run `initdb` under the hood
|
||||
which will add everything your server needs. It will now be running at that
|
||||
port, ready to connect.
|
||||
35
postgres/show-reconstructed-constraints-for-a-table.md
Normal file
35
postgres/show-reconstructed-constraints-for-a-table.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Show Reconstructed Constraints For A Table
|
||||
|
||||
The [`pg_get_constraintdef`
|
||||
function](https://pgpedia.info/p/pg_get_constraintdef.html) can be used to
|
||||
reconstruct the command for creating a given constraint. This isn't necessarily
|
||||
the command (or commands) that originally created the constraint, but rather a
|
||||
reconstruction.
|
||||
|
||||
We have to pass it an `oid` that corresponds to the constraint which we can get
|
||||
from the `pg_constraint` table. These results can be further narrowed down by
|
||||
the `conname` (constraint name) and `conrelid` (table name).
|
||||
|
||||
Here is an example of listing the constraints on a `reading_statuses` table.
|
||||
|
||||
```sql
|
||||
> select
|
||||
conname,
|
||||
pg_get_constraintdef(oid)
|
||||
from pg_constraint
|
||||
where conrelid = 'reading_statuses'::regclass;
|
||||
|
||||
conname | pg_get_constraintdef
|
||||
-------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
reading_statuses_pkey | PRIMARY KEY (id)
|
||||
fk_rails_17ee7cb2c4 | FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
fk_rails_0d3729339f | FOREIGN KEY (book_id) REFERENCES books(id)
|
||||
reading_statuses_valid_status_check | CHECK (((status)::text = ANY ((ARRAY['started'::character varying, 'completed'::character varying, 'abandoned'::character varying, 'already_read'::character varying])::text[])))
|
||||
(4 rows)
|
||||
```
|
||||
|
||||
I came across this while experimenting with [an idea for a fail-fast Rails
|
||||
initializer
|
||||
check](https://gist.github.com/jbranchaud/12813a0558f9cd06bcc24b7d8706550c)
|
||||
that verifies the values of the `reading_statuses_valid_status_check` stay in
|
||||
sync with the Rails version of those values that live in a constant.
|
||||
35
python/break-debugger-on-first-line-of-program.md
Normal file
35
python/break-debugger-on-first-line-of-program.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Break Debugger On First Line Of Program
|
||||
|
||||
One of the things I appreciate about how
|
||||
[Delve](https://github.com/go-delve/delve) (the debugger for Go) works by
|
||||
default is that when you start it up, it immediately breaks on the first line
|
||||
of the program. This is as good a starting point as any regardless of whether
|
||||
you have other breakpoints set.
|
||||
|
||||
As I was reading through the VS Code Python Debugger configuration docs, I
|
||||
noticed [an option called
|
||||
`stopOnEntry`](https://code.visualstudio.com/docs/python/debugging#_stoponentry).
|
||||
It is turned off by default, but if you turn it on, then you get the same
|
||||
behavior as I described for Delve. Nice!
|
||||
|
||||
This can be configured in a `.vscode/launch.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python Debugger: Current File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"stopOnEntry": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Now, when running this debugger configuration profile, you'll break the
|
||||
debugger on the first line of the program. From there add more breakpoints,
|
||||
start stepping through, etc.
|
||||
62
rails/add-color-to-the-irb-console-prompt.md
Normal file
62
rails/add-color-to-the-irb-console-prompt.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Add Color To The IRB Console Prompt
|
||||
|
||||
IRB has a little-known [`Color`
|
||||
module](https://docs.ruby-lang.org/en/3.2/IRB/Color.html) with some helpers for
|
||||
adding a splash of color to the IRB prompt. I like to clearly differentiate the
|
||||
environment I'm in when connecting to the `rails console`, so I have a
|
||||
customize the prompt to display and colorize the current environment.
|
||||
|
||||
I can wrap any string in ANSI escape codes that instruct the terminal to style
|
||||
the text with color. For instance, here is how I can style the word `DEV` to be
|
||||
inverted against a blue background.
|
||||
|
||||
```ruby
|
||||
IRB::Color.colorize("DEV", [:BLUE, :BOLD, :REVERSE])
|
||||
```
|
||||
|
||||
which will clearly stand out from `PROD` against a red background:
|
||||
|
||||
```ruby
|
||||
IRB::Color.colorize("PROD", [:RED, :BOLD, :REVERSE])
|
||||
```
|
||||
|
||||
Here is a full example of customizing the prompt from the
|
||||
`config/application.rb` file.
|
||||
|
||||
```ruby
|
||||
module MyApp
|
||||
class Application < Rails::Application
|
||||
# ...
|
||||
|
||||
console do
|
||||
# Get the application module name and convert to kebab-case
|
||||
app_name = Rails.application.class.module_parent.name
|
||||
kebab_name = app_name.underscore.dasherize
|
||||
|
||||
# Environment color coding
|
||||
env_colors = {
|
||||
"development" => IRB::Color.colorize("DEV", [:BLUE, :BOLD, :REVERSE]),
|
||||
"production" => IRB::Color.colorize("PROD", [:RED, :BOLD, :REVERSE]),
|
||||
"test" => IRB::Color.colorize("TEST", [:YELLOW, :BOLD, :REVERSE]),
|
||||
}
|
||||
|
||||
colored_env = "(#{env_colors[Rails.env]})"
|
||||
|
||||
# Docs: https://docs.ruby-lang.org/en/3.2/IRB.html#module-IRB-label-Customizing+the+IRB+Prompt
|
||||
IRB.conf[:PROMPT][:RAILS_APP] = {
|
||||
PROMPT_I: "#{kebab_name}#{colored_env}> ",
|
||||
PROMPT_N: "#{kebab_name}#{colored_env}* ",
|
||||
PROMPT_S: "#{kebab_name}#{colored_env}% ",
|
||||
PROMPT_C: "#{kebab_name}#{colored_env}? ",
|
||||
RETURN: "=> %s\n"
|
||||
}
|
||||
|
||||
# Set it as the current prompt
|
||||
IRB.conf[:PROMPT_MODE] = :RAILS_APP
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The Ruby docs have more about [IRB Prompt
|
||||
Customization](https://docs.ruby-lang.org/en/3.2/IRB.html#module-IRB-label-Customizing+the+IRB+Prompt).
|
||||
33
rails/adjust-the-production-log-level.md
Normal file
33
rails/adjust-the-production-log-level.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Adjust The Production Log Level
|
||||
|
||||
A Rails app by default takes on the `debug` log level. This is great for
|
||||
development because it spits out a lot of information as you build and debug.
|
||||
|
||||
That's going to typically be a bit too noisy for a production environment
|
||||
though. That is why Rails ships with the `config/environments/production.rb`
|
||||
file configured to the `info` log level.
|
||||
|
||||
```ruby
|
||||
# config/environments/production.rb
|
||||
Rails.application.configure do
|
||||
|
||||
# ...
|
||||
|
||||
# "info" includes generic and useful information about system operation, but avoids logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII). If you
|
||||
# want to log everything, set the level to "debug".
|
||||
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
|
||||
|
||||
# ...
|
||||
end
|
||||
```
|
||||
|
||||
Sometimes, like when we're trying to track down some buggy behavior, we may
|
||||
want to switch Rails from one log level to another. That's why it is configured
|
||||
by the `RAILS_LOG_LEVEL` env var and otherwise falls back to `info`.
|
||||
|
||||
To, for example, switch production over to the `debug` log level, we'd first
|
||||
change the `RAILS_LOG_LEVEL` env var to `debug`. Then we'd need to make sure
|
||||
our Rails app is restarted so that the config change is picked up. Heroku's
|
||||
`heroku config:set` will do that automatically. Depending on your setup, you
|
||||
may need to manually restart your web server (e.g. Puma).
|
||||
36
rails/convert-json-field-to-hash-with-indifferent-access.md
Normal file
36
rails/convert-json-field-to-hash-with-indifferent-access.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Convert JSON Field To Hash With Indifferent Access
|
||||
|
||||
Let's say we have an `Event` model whose backing table includes a `JSONB` (or
|
||||
`JSON`) field called `details`.
|
||||
|
||||
When we access `details` in a Rails context, digging into that nested data we
|
||||
have to use string keys throughout. However, we may have existing related code
|
||||
that is dealing with this shape of data using symbol keys. This might put us in
|
||||
a position where we have to rework a bunch of existing code or do defensive
|
||||
coding like `details[:user] || details["user"]`.
|
||||
|
||||
To avoid that, we can instead have the `Event` model override `details`
|
||||
converting that underlying data to `HashWithIndifferentAccess` before returning
|
||||
it.
|
||||
|
||||
```ruby
|
||||
class Event < ApplicationRecord
|
||||
def details
|
||||
data = super
|
||||
return data if data.nil?
|
||||
|
||||
case data
|
||||
when Array
|
||||
data.map { |item| item.is_a?(Hash) ? item.with_indifferent_access : item }
|
||||
when Hash
|
||||
data.with_indifferent_access
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
With this in place, anywhere in the codebase where we access `details` on an
|
||||
instance of `Event` we will be able to use string or symbol keys
|
||||
interchangeably.
|
||||
41
rails/customize-template-for-new-schema-migration.md
Normal file
41
rails/customize-template-for-new-schema-migration.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Customize Template For New Schema Migration
|
||||
|
||||
Rails has a set of generator functionality that we can use to scaffold entire
|
||||
slices of an app all the way down to generating a single migration file.
|
||||
|
||||
```bash
|
||||
$ rails generate migration MakeUserStatusColumnNotNull
|
||||
```
|
||||
|
||||
When we run a migration generator command like that, Rails reaches for the
|
||||
[baked-in migration
|
||||
template](https://github.com/rails/rails/blob/92be9af152f721588b7414119c931ea92930947b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt)
|
||||
and creates a new migration file based on the given name and any other local
|
||||
variables that get set internally.
|
||||
|
||||
That's the standard behavior. However, we can override the migration template
|
||||
by defining our own template in `lib/templates/migration.rb.tt` of our Rails
|
||||
app. We'll need to follow the basic structure, but then we can alter it to our
|
||||
needs.
|
||||
|
||||
For instance, I typically like to use the `#up` and `#down` methods and write
|
||||
raw SQL for my migrations. To help with that this template provides a good
|
||||
starting point.
|
||||
|
||||
```ruby
|
||||
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
||||
def up
|
||||
execute <<~SQL
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
execute <<~SQL
|
||||
SQL
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
We can see in
|
||||
[`migration_generator.rb`](https://github.com/rails/rails/blob/92be9af152f721588b7414119c931ea92930947b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb#L26-L43)
|
||||
how locals get set and what template gets chosen.
|
||||
42
rails/enforce-locals-passed-to-a-partial.md
Normal file
42
rails/enforce-locals-passed-to-a-partial.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Enforce Locals Passed To A Partial
|
||||
|
||||
I have a big form partial (`_form.html.erb`) that is rendered by several
|
||||
different _new_ and _edit_ views. It's hard to tell at a glance, but the
|
||||
partial requires that `blogmark` and `cancel_path` are included as locals when
|
||||
it is rendered.
|
||||
|
||||
As of [Rails 7.1](https://github.com/rails/rails/pull/45602), we now have a
|
||||
built-in way to enforce locals passed to a partial. We can add a magic comment
|
||||
at the top of the partial that specifies the locals:
|
||||
|
||||
```ruby
|
||||
<%# locals: (blogmark:, cancel_path:) -%>
|
||||
<%= form_with(model: blogmark, local: true, class: "w-full max-w-3xl mb-8") do |form| %>
|
||||
<% ... %>
|
||||
<% end %>
|
||||
```
|
||||
|
||||
This particular ERB magic comment declares that
|
||||
[`blogmark`](https://still.visualmode.dev/blogmarks) and `cancel_path` are
|
||||
required locals.
|
||||
|
||||
So, what happens if I have a `new.html.erb` view that looks like this:
|
||||
|
||||
```ruby
|
||||
<h1>New Blogmark</h1>
|
||||
|
||||
<%= render 'blogmarks/form', blogmark: @blogmark %>
|
||||
```
|
||||
|
||||
When I try to view the page, an error is raised:
|
||||
|
||||
```
|
||||
Showing /Some/path/app/views/blogmarks/_form.html.erb where line # raised:
|
||||
|
||||
missing local: :cancel_path for app/views/blogmarks/_form.html.erb
|
||||
```
|
||||
|
||||
Updating the `render` statement to include a `cancel_path` local fixes the
|
||||
issue.
|
||||
|
||||
[source](https://gorails.com/episodes/template-locals-in-rails-7-1)
|
||||
43
rails/filter-active-model-validation-errors.md
Normal file
43
rails/filter-active-model-validation-errors.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Filter ActiveModel Validation Errors
|
||||
|
||||
Now that `ActiveModel` has a custom `Errors` class (as of Rails 6.1) instead of
|
||||
a hash, we get some useful functionality. Namely, we get a [`#where`
|
||||
method](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-where)
|
||||
that allows us to filter errors based on the attribute name, type of
|
||||
validation, and even properties of that validation.
|
||||
|
||||
Here I have created a new `Book` without any attributes. All of its validations
|
||||
are going to fail and we are going to have an `ActiveModel::Errors` object
|
||||
attached to it with several errors.
|
||||
|
||||
```ruby
|
||||
> book = Book.new
|
||||
=>
|
||||
#<Book:0x00000001110397a8
|
||||
...
|
||||
> book.valid?
|
||||
=> false
|
||||
> book.errors
|
||||
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=added_by, type=blank, options={:message=>:required, :if=>#<Proc:0x0000000110096260 /Users/jbranchaud/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/activerecord-7.2.1/lib/active_record/associations/builder/belongs_to.rb:130 (lambda)>}>, #<ActiveModel::Error attribute=title, type=blank, options={}>, #<ActiveModel::Error attribute=title, type=too_short, options={:count=>3}>, #<ActiveModel::Error attribute=author, type=blank, options={}>, #<ActiveModel::Error attribute=publication_date, type=blank, options={}>]>
|
||||
```
|
||||
|
||||
Let's say I want to check for a specific validation error. I can use `#where`
|
||||
to filter down by attribute name (e.g. `:title`). I can filter even further by
|
||||
including the validation type as well (e.g. `:too_short`).
|
||||
|
||||
```ruby
|
||||
> book.errors.where(:title)
|
||||
=>
|
||||
[#<ActiveModel::Error attribute=title, type=blank, options={}>,
|
||||
#<ActiveModel::Error attribute=title, type=too_short, options={:count=>3}>]
|
||||
> book.errors.where(:title, :too_short)
|
||||
=> [#<ActiveModel::Error attribute=title, type=too_short, options={:count=>3}>]
|
||||
> book.errors.where(:title, :too_short).first.message
|
||||
=> "is too short (minimum is 3 characters)"
|
||||
> book.errors.where(:title, :too_short).first.full_message
|
||||
=> "Title is too short (minimum is 3 characters)"
|
||||
```
|
||||
|
||||
This filtering could be used as part of conditional checks for what flash
|
||||
message gets displayed to the user or even what route/view gets rendered in
|
||||
response to the error.
|
||||
30
rails/filter-active-storage-blobs-to-only-images.md
Normal file
30
rails/filter-active-storage-blobs-to-only-images.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Filter ActiveStorage Blobs To Only Images
|
||||
|
||||
If your Rails app is using `ActiveStorage` for both images and ActionMailbox
|
||||
emails, then you're going to have a mix of both in the `active_storage_blobs`
|
||||
table.
|
||||
|
||||
```sql
|
||||
> select id, filename, content_type from active_storage_blobs limit 2;
|
||||
|
||||
| id | filename | content_type |
|
||||
|----|--------------------|----------------|
|
||||
| 1 | shirt-brothers.jpg | image/jpeg |
|
||||
| 2 | message.eml | message/rfc822 |
|
||||
```
|
||||
|
||||
In that case, you are going to want to make sure that any part of your system
|
||||
that only cares to deal with images filters down to only blobs where the
|
||||
`content_type` is one that you care about.
|
||||
|
||||
I expect that there might be a couple different image `content_type` values
|
||||
that my system handles, so I filter my `active_storage_blobs` like so:
|
||||
|
||||
|
||||
```ruby
|
||||
@images =
|
||||
ActiveStorage::Blob
|
||||
.where(content_type: %w[image/jpeg image/png image/gif image/webp])
|
||||
.order(created_at: :desc)
|
||||
.first(10)
|
||||
```
|
||||
49
rails/format-datetime-with-builtin-formats.md
Normal file
49
rails/format-datetime-with-builtin-formats.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Format DateTime With Builtin Formats
|
||||
|
||||
The Rails [`Date`](https://api.rubyonrails.org/classes/Date.html)/`DateTime`
|
||||
and [`Time`](https://api.rubyonrails.org/classes/Time.html) classes each come
|
||||
with a `DATE_FORMATS` constant that is a hash of symbol names to format
|
||||
strings.
|
||||
|
||||
```ruby
|
||||
> DateTime::DATE_FORMATS
|
||||
=>
|
||||
{:short=>"%d %b",
|
||||
:long=>"%B %d, %Y",
|
||||
:db=>"%Y-%m-%d",
|
||||
:inspect=>"%Y-%m-%d",
|
||||
:number=>"%Y%m%d",
|
||||
:long_ordinal=>
|
||||
#<Proc:0x0000000105b2cef0 /Users/jbranchaud/.local/share/mise/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/activesupport-8.0.1/lib/active_support/core_ext/date/conversions.rb:15 (lambda)>,
|
||||
:rfc822=>"%d %b %Y",
|
||||
:rfc2822=>"%d %b %Y",
|
||||
:iso8601=>
|
||||
#<Proc:0x0000000105b2cec8 /Users/jbranchaud/.local/share/mise/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/activesupport-8.0.1/lib/active_support/core_ext/date/conversions.rb:21 (lambda)>}
|
||||
```
|
||||
|
||||
These can be used as a standardized, ready-to-use, named formats when turning
|
||||
`DateTime` objects into strings.
|
||||
|
||||
Here are a few examples
|
||||
|
||||
```ruby
|
||||
> now = DateTime.now
|
||||
=> Wed, 30 Apr 2025 23:08:08 -0500
|
||||
|
||||
> now.to_fs(:long)
|
||||
=> "April 30, 2025 23:08"
|
||||
|
||||
> now.to_fs(:long_ordinal)
|
||||
=> "April 30th, 2025 23:08"
|
||||
|
||||
> now.to_fs(:iso8601)
|
||||
=> "2025-04-30T23:08:08-05:00"
|
||||
```
|
||||
|
||||
If an unrecognized key is passed to `#to_fs`, then it falls back to the
|
||||
`:iso8601` format.
|
||||
|
||||
```ruby
|
||||
> now.to_fs(:taco_bell)
|
||||
=> "2025-04-30T23:08:08-05:00"
|
||||
```
|
||||
39
rails/format-specific-html-erb-template-files.md
Normal file
39
rails/format-specific-html-erb-template-files.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Format Specific html.erb Template Files
|
||||
|
||||
There are a few tools out there that can do formatting of `*.html.erb` template
|
||||
files. One that I like is
|
||||
[nebulab/erb-formatter](https://github.com/nebulab/erb-formatter#readme)
|
||||
because it is ready to use in many different editors. That means that it is
|
||||
easier to adopt on a team of developers with different editor preferences.
|
||||
|
||||
That said, there are projects where I don't necessarily want it wired up to run
|
||||
on save because the formatting changesets will be too agressive. Instead, I
|
||||
want to run it manually on specific files as I see fit.
|
||||
|
||||
To do this, I install the formatter tool:
|
||||
|
||||
```bash
|
||||
$ gem install erb-formatter
|
||||
```
|
||||
|
||||
And now it is available as a CLI tool (try `which erb-format`).
|
||||
|
||||
As their docs recommend, I can run it against all files like so:
|
||||
|
||||
```
|
||||
$ erb-format app/views/**/*.html.erb --write
|
||||
```
|
||||
|
||||
If that is too aggressive though, I find it useful to either run against a
|
||||
specific file:
|
||||
|
||||
```
|
||||
$ erb-format app/views/some/model/index.html.erb --write
|
||||
```
|
||||
|
||||
Or when I'm wrapping up changes on a branch, I like to run it against all the
|
||||
view files that were touched on this branch:
|
||||
|
||||
```
|
||||
$ git diff --name-only master...HEAD -- app/views | xargs erb-format --write
|
||||
```
|
||||
55
rails/manage-timestamps-with-upsert.md
Normal file
55
rails/manage-timestamps-with-upsert.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Manage Timestamps With Upsert
|
||||
|
||||
Modern versions of Rails and ActiveRecord have [an `upsert`
|
||||
method](https://api.rubyonrails.org/v8.0.2/classes/ActiveRecord/Relation.html#method-i-upsert)
|
||||
which will, if available, use your database's upsert capability to either
|
||||
insert a new row or update an existing row based on the unique identifier.
|
||||
|
||||
The docs have the following disclaimer:
|
||||
|
||||
> It does not instantiate any models nor does it trigger Active Record
|
||||
> callbacks or validations. Though passed values go through Active Record’s
|
||||
> type casting and serialization.
|
||||
|
||||
It's a bit different to work with than other ActiveRecord methods. It left me
|
||||
wondering if it would handle timestamp management or if I would have to do that
|
||||
myself.
|
||||
|
||||
Let's upsert a new record into `books`:
|
||||
|
||||
```ruby
|
||||
> Book.upsert({title: "Shogun", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id)
|
||||
=> #<ActiveRecord::Result:0x00000001141c3df0 ...
|
||||
|
||||
> Book.select(:id, :title, :created_at, :updated_at).last
|
||||
=> #<Book:0x0000000113bae898 id: 12, title: "Shogun", created_at: "2025-06-26 14:08:26.035633000 +0000", updated_at: "2025-06-26 14:08:26.035633000 +0000">
|
||||
```
|
||||
|
||||
Notice that the `created_at` and `updated_at` timestamps get set.
|
||||
|
||||
Now, let's upsert the record (notice we're passing in the `id`) to update the title with an `ō`.
|
||||
|
||||
```ruby
|
||||
> Book.upsert({id: 12, title: "Shōgun", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id)
|
||||
=> #<ActiveRecord::Result:0x0000000113cace98 ...
|
||||
|
||||
> Book.select(:id, :title, :created_at, :updated_at).last
|
||||
=> #<Book:0x00000001143aadd0 id: 12, title: "Shōgun", created_at: "2025-06-26 14:08:26.035633000 +0000", updated_at: "2025-06-26 14:10:46.280480000 +0000">
|
||||
```
|
||||
|
||||
Notice that the `updated_at` gets set to a time about 2 minutes later.
|
||||
|
||||
Lastly let's look at the `record_timestamps` option. This is `nil` by default
|
||||
which means the underlying methods default kicks in which _is_ to record the
|
||||
timestamps. We can override that behavior by passing in `false`.
|
||||
|
||||
```ruby
|
||||
> Book.upsert({id: 12, title: "Shōgun, Part 2", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id, record_timestamps: false)
|
||||
=> #<ActiveRecord::Result:0x0000000113989428 ...
|
||||
|
||||
> Book.select(:id, :title, :created_at, :updated_at).last
|
||||
=> #<Book:0x0000000114fe1b80 id: 12, title: "Shōgun, Part 2", created_at: "2025-06-26 14:08:26.035633000 +0000", updated_at: "2025-06-26 14:10:46.280480000 +0000">
|
||||
```
|
||||
|
||||
Notice that the `updated_at` value doesn't change between this upsert and the
|
||||
previous upsert.
|
||||
40
rails/parameterize-a-string-with-underscores.md
Normal file
40
rails/parameterize-a-string-with-underscores.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Parameterize A String With Underscores
|
||||
|
||||
I have human-readable status strings that I'm working with like `In progress`,
|
||||
`Pending approval`, and `Completed`. I need to deterministically turn those
|
||||
into parameterized values that I can compare. That is, I want them lowercased
|
||||
and separated by underscores instead of spaces.
|
||||
|
||||
The `ActiveSupport` `#parameterize` method, as is, gets me pretty close.
|
||||
|
||||
```ruby
|
||||
> statuses = [
|
||||
"In progress",
|
||||
"Pending approval",
|
||||
"Completed"
|
||||
]
|
||||
|
||||
> statuses.map(&:parameterize)
|
||||
=> [
|
||||
"in-progress",
|
||||
"pending-approval",
|
||||
"completed"
|
||||
]
|
||||
```
|
||||
|
||||
Those are separated by dashes though. Fortunately, `parameterize` takes a
|
||||
`separator` option that we can use to verride what character is used to
|
||||
separate words. Let's use an underscore (`_`).
|
||||
|
||||
```ruby
|
||||
> statuses.map { |str| str.parameterize(separator: '_') }
|
||||
=> [
|
||||
"in_progress",
|
||||
"pending_approval",
|
||||
"completed"
|
||||
]
|
||||
```
|
||||
|
||||
See the [`#paramterize`
|
||||
docs](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize)
|
||||
for more details.
|
||||
50
rails/prevent-mailer-previews-from-cluttering-database.md
Normal file
50
rails/prevent-mailer-previews-from-cluttering-database.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Prevent Mailer Previews From Cluttering Database
|
||||
|
||||
ActionMailer Previews give you a way to view email templates that your system
|
||||
sends. This is how I check that it is styled properly and that the logic and
|
||||
data of the template are able to run and render.
|
||||
|
||||
Data for a preview typically means we need ActiveRecord objects and even their
|
||||
associations. If we start creating one-off records in our previews either with
|
||||
`#create` or with something like `FactoryBot`, those records will get left
|
||||
behind in our development database. Every view and refresh of a preview will
|
||||
generate more of these records.
|
||||
|
||||
One way to get around that is to use `#new` and `#build`. I've found this
|
||||
cumbersome and it often leaves assocations missing or inaccessible.
|
||||
|
||||
What if instead the preview could clean up after itself? That sounds like a
|
||||
great job for a database transaction.
|
||||
|
||||
Let's create a `test/mailers/previews/base_preview.rb` as a base class for all
|
||||
our preview classes.
|
||||
|
||||
```ruby
|
||||
class BasePreview < ActionMailer::Preview
|
||||
def self.call(...)
|
||||
message = nil
|
||||
ActiveRecord::Base.transaction do
|
||||
message = super(...)
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
message
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This wraps the existing `self.call` functionality in a transaction that
|
||||
collects the resulting message from the preview and then rolls back the
|
||||
database changes.
|
||||
|
||||
Now, instead of our individual preview classes inheriting directly from
|
||||
`ActionMailer::Preview`, they can inherit from `BasePreview`.
|
||||
|
||||
```ruby
|
||||
class UserMailer < BasePreview
|
||||
|
||||
# ...
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
[source](https://stackoverflow.com/a/31289295)
|
||||
40
rails/provide-fake-form-helper-to-controllers.md
Normal file
40
rails/provide-fake-form-helper-to-controllers.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Provide Fake Form Helper To Controllers
|
||||
|
||||
I'm rendering a partial from a turbo stream. The partial is meant to be
|
||||
rendered within a Rails form object because it contains an input element that
|
||||
needs to reference the form object. The problem is that from the controller
|
||||
that is streaming the partial, there is no
|
||||
[FormBuilder](https://api.rubyonrails.org/v6.1.0/classes/ActionView/Helpers/FormBuilder.html)
|
||||
object.
|
||||
|
||||
One way to get around this that I've borrowed from [Justin
|
||||
Searls](https://justin.searls.co/posts/instantiate-a-custom-rails-formbuilder-without-using-form_with/)
|
||||
is with a `FauxFormHelper`.
|
||||
|
||||
```ruby
|
||||
module FauxFormHelper
|
||||
FauxFormObject = Struct.new do
|
||||
def errors
|
||||
end
|
||||
|
||||
def method_missing(...)
|
||||
end
|
||||
|
||||
def respond_to_missing?(...)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def faux_form
|
||||
@faux_form ||= ActionView::Helpers::FormBuilder.new(
|
||||
nil,
|
||||
FauxFormObject.new,
|
||||
self,
|
||||
{}
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This module defines and exposes a `faux_form` object that controllers and views
|
||||
can access. Then my partial can recieve that form object as a parameter.
|
||||
28
rails/restart-puma-server-by-touching-restart-file.md
Normal file
28
rails/restart-puma-server-by-touching-restart-file.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Restart Puma Server By Touching Restart File
|
||||
|
||||
Puma includes a plugin that allows us to restart the web server by touching the
|
||||
`tmp/restart.txt` file.
|
||||
|
||||
In one terminal pane I have my Rails server running. In another terminal pane
|
||||
from the Rails directory, where there exists a `tmp` folder, I run the
|
||||
following command.
|
||||
|
||||
```bash
|
||||
$ touch tmp/restart.txt
|
||||
```
|
||||
|
||||
Then in the pane running the Rails server, I see the following after a second:
|
||||
|
||||
```
|
||||
* Restarting...
|
||||
=> Booting Puma
|
||||
=> Rails 8.0.1 application starting in development
|
||||
...
|
||||
```
|
||||
|
||||
What is happening is that `touch` updates the modified time of that file, which
|
||||
already exists in the `temp` directory. When the plugin notices (it checks
|
||||
every 2 seconds) that the modified time is now fresher than the original
|
||||
modified time when the plugin started, then it calls `launcher.restart`.
|
||||
|
||||
[source](https://github.com/puma/puma/blob/ca201ef69757f8830b636251b0af7a51270eb68a/lib/puma/plugin/tmp_restart.rb)
|
||||
46
rails/run-dev-processes-with-overmind-instead-of-foreman.md
Normal file
46
rails/run-dev-processes-with-overmind-instead-of-foreman.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Run Dev Processes With Overmind Instead Of Foreman
|
||||
|
||||
Most Rails projects that I have worked on have used
|
||||
[`foreman`](https://github.com/ddollar/foreman) as a development dependency for
|
||||
running all the processes declared in your Procfile (`Procfile.dev`). As far as
|
||||
having a single command to run everything (Rails server, asset building,
|
||||
worker(s), etc.), it does the job.
|
||||
|
||||
`foreman` has some serious points of friction though. The one that really stands
|
||||
out to me is that when I try to debug the development Rails server with
|
||||
`binding.irb` or `binding.pry`, the other processes tend to interfere.
|
||||
|
||||
The alternative to `foreman` that I've been trying out recently is
|
||||
[`overmind`](https://github.com/DarthSim/overmind). A specific selling point of
|
||||
`overmind` is that it runs all the development processes in a `tmux` session.
|
||||
That means you can individually connect to, inspect, and restart each process.
|
||||
|
||||
Once you've installed `overmind` (`brew install overmind`), then you can easily
|
||||
swap it in for `foreman` like so:
|
||||
|
||||
```bash
|
||||
$ overmind start -f Procfile.dev
|
||||
```
|
||||
|
||||
You can connect to any of those processes directly:
|
||||
|
||||
```bash
|
||||
$ overmind connect sidekiq
|
||||
```
|
||||
|
||||
When you want to `binding.irb` the Rails server, you can specifically connect to
|
||||
the `web` process to do that.
|
||||
|
||||
```bash
|
||||
$ overmind connect web
|
||||
```
|
||||
|
||||
If you need to stop all the process, you can run the `kill` subcommand.
|
||||
|
||||
```bash
|
||||
$ overmind kill
|
||||
```
|
||||
|
||||
Lastly, if you have a `bin/dev` script in your project, it is probably using
|
||||
`foreman`. If you and your team prefer `overmind`, then update that script
|
||||
accordingly and you can simply run `bin/dev` going forward.
|
||||
49
rails/run-rails-console-with-remote-dokku-app.md
Normal file
49
rails/run-rails-console-with-remote-dokku-app.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Run Rails Console With Remote Dokku App
|
||||
|
||||
Whenever I want to `rails console` into the _staging_ server of an app I'm
|
||||
working on, I first have to `ssh` into server and then I have to come up with
|
||||
the [`dokku`](https://dokku.com/) command to run `rails console` against the app
|
||||
on that server.
|
||||
|
||||
```bash
|
||||
local> ssh app-staging # app-staging is an SSH alias
|
||||
staging> dokku run my-app rails console
|
||||
```
|
||||
|
||||
I figured out how to reduce the friction of this by collapsing it into a single
|
||||
command that I can run locally. I can remotely run the `dokku` command with
|
||||
`ssh` using an interactive session (`-t`).
|
||||
|
||||
```bash
|
||||
local> ssh -t app-staging dokku run my-app rails console
|
||||
```
|
||||
|
||||
That will open up a `rails console` session directly in the current shell
|
||||
session via a remote SSH connection. The `-t` flag is important because that
|
||||
makes the session interactive so that I can interact with the REPL.
|
||||
|
||||
I've even packaged this up into a bin script (`bin/staging-console`) with a
|
||||
couple checks to enhance the DX. I won't put the whole thing here, but the gist
|
||||
of it is:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$DOKKU_STAGING_SSH_ALIAS" ]; then
|
||||
echo "Error: DOKKU_STAGING_SSH_ALIAS environment variable is not set."
|
||||
echo ""
|
||||
# echo more help details here ...
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if SSH alias exists
|
||||
# ...
|
||||
|
||||
# Check if we can reach the server
|
||||
# ...
|
||||
|
||||
# Run the console
|
||||
ssh -t "$DOKKU_STAGING_SSH_ALIAS" dokku run my-app rails console "$@"
|
||||
```
|
||||
55
rails/scope-records-to-a-lower-or-upper-bound.md
Normal file
55
rails/scope-records-to-a-lower-or-upper-bound.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Scope Records To A Lower Or Upper Bound
|
||||
|
||||
Typically when we use
|
||||
[`#where`](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-where)
|
||||
to scope queries against ActiveRecord models, we are looking to do a direct
|
||||
"equals" comparison.
|
||||
|
||||
Such as `auth_codes.user_id = 1` in the example below.
|
||||
|
||||
```ruby
|
||||
> AuthCode.where(user_id: 1)
|
||||
AuthCode Load (0.4ms) SELECT "auth_codes".* FROM "auth_codes" WHERE "auth_codes"."user_id" = 1 /* loading for pp */ LIMIT 11
|
||||
```
|
||||
|
||||
We can do more powerful things with `#where` (assuming your database supports
|
||||
it, in my case PostgreSQL), such as comparing over ranges of dates. Ruby's
|
||||
range syntax gives us an elegant way to express ranges.
|
||||
|
||||
```ruby
|
||||
> 2..10 # range with lower bound of 2 and upper bound of 10
|
||||
|
||||
> 2.. # 'end'less range
|
||||
|
||||
> ..10 # 'begin'less range
|
||||
```
|
||||
|
||||
These latter two examples are ranges that are unbounded on one side or the
|
||||
other. We can use these in ActiveRecord `#where` queries to do "greater than or
|
||||
equal to" and "less than or equal to" conditionals.
|
||||
|
||||
And we can do the same with ranges of dates like in the following queries.
|
||||
|
||||
|
||||
```ruby
|
||||
> AuthCode.where(created_at: 10.days.ago..).count
|
||||
AuthCode Count (97.1ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" >= '2025-09-24 00:35:46.937715'
|
||||
|
||||
> AuthCode.where(created_at: 10.days.ago..5.days.ago).count
|
||||
AuthCode Count (0.6ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" BETWEEN '2025-09-24 00:35:59.901441' AND '2025-09-29 00:35:59.901512'
|
||||
|
||||
> AuthCode.where(created_at: ..5.days.ago).count
|
||||
AuthCode Count (0.3ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" <= '2025-09-29 00:36:09.731444'
|
||||
```
|
||||
|
||||
Notice in the generated SQL how the simple `#where` method gets transformed
|
||||
into a `>=`, a `<=`, or a `between` clause.
|
||||
|
||||
And while dates are a powerful example of this, there is nothing to stop us
|
||||
from querying against other kinds of ranges like numeric ones.
|
||||
|
||||
```ruby
|
||||
# Orders under $10
|
||||
ten_dollars_in_cents = 10 * 100
|
||||
Order.where.not(fulfilled_at: nil).where(amount: ..ten_dollars_in_cents)
|
||||
```
|
||||
42
rails/use-ruby-extension-for-template-file.md
Normal file
42
rails/use-ruby-extension-for-template-file.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Use .ruby Extension For Template File
|
||||
|
||||
An interesting feature of Rails that I can't seem to find documented anywhere is
|
||||
that you can write a template file with plain Ruby by using the `.ruby`
|
||||
extension. For instance, you might want to render some JSON from a template.
|
||||
Instead of using `jbuilder` or `erb`, you can have a `show.json.ruby` file. This
|
||||
is also popular with Turbo Stream files -- e.g. `update.turbo_stream.ruby`.
|
||||
|
||||
How this works is that the entire file is evaluated as if it were a `.rb` file.
|
||||
Then the return value of the final statement is what is returned and rendered by
|
||||
Rails.
|
||||
|
||||
```ruby
|
||||
author_byline = @book.authors.map(&:name).to_sentence
|
||||
|
||||
data = {
|
||||
id: @book.id,
|
||||
title: @book.title,
|
||||
author: author_byline,
|
||||
status: @book.published_at > Time.current ? 'Coming Soon' : 'Published',
|
||||
publication_year: @book.published_at.year
|
||||
}
|
||||
|
||||
data.to_json
|
||||
```
|
||||
|
||||
That final line converts the hash of data that we've built up into a JSON string
|
||||
that can then be rendered by the controller action that corresponds to this view
|
||||
template.
|
||||
|
||||
Similarly, you can have a Turbo Stream template `show.turbo_stream.ruby` that
|
||||
looks something like this:
|
||||
|
||||
```ruby
|
||||
[
|
||||
turbo_stream.prepend("posts", @post),
|
||||
turbo_stream.update("form", partial: "form", locals: { post: Post.new })
|
||||
].join
|
||||
```
|
||||
|
||||
This template file is made up of a single statement which is an array of turbo
|
||||
stream results that get joined together.
|
||||
39
ruby/avoid-double-negation-with-minitest-refute.md
Normal file
39
ruby/avoid-double-negation-with-minitest-refute.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Avoid Double Negation With Minitest Refute
|
||||
|
||||
As I'm writing some tests for a recent feature, I end up with various test
|
||||
cases that make a variety of assertions.
|
||||
|
||||
```ruby
|
||||
assert_equal resp_body["id"], expected_id
|
||||
|
||||
# ...
|
||||
|
||||
assert_not resp_body["items"].empty?
|
||||
```
|
||||
|
||||
The first assertion reads pretty naturally. The second one requires some extra
|
||||
mental parsing because of the `_not` and then the "negative" check of
|
||||
`#empty?`.
|
||||
|
||||
Another way to express this that might read more naturally is with
|
||||
[`#refute`](https://ruby-doc.org/stdlib-3.0.2/libdoc/minitest/rdoc/Minitest/Assertions.html#method-i-refute).
|
||||
|
||||
```ruby
|
||||
refute resp_body["items"].empty?
|
||||
```
|
||||
|
||||
This says that we _refute_ that items is empty, so the assertion should fail if
|
||||
empty.
|
||||
|
||||
Ruby is flexible in other ways. We may also prefer to write it as:
|
||||
|
||||
```ruby
|
||||
assert resp_body["items"].present?
|
||||
```
|
||||
|
||||
Or we could even take advantage of a more specific variant of refute with
|
||||
[`#refute_empty`](https://ruby-doc.org/stdlib-3.0.2/libdoc/minitest/rdoc/Minitest/Assertions.html#method-i-refute_empty):
|
||||
|
||||
```ruby
|
||||
refute_empty resp_body["items"]
|
||||
```
|
||||
59
ruby/create-a-module-of-utility-functions.md
Normal file
59
ruby/create-a-module-of-utility-functions.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Create A Module Of Utility Functions
|
||||
|
||||
In my [latest blog post](https://www.visualmode.dev/create-a-module-of-utility-functions-in-ruby),
|
||||
I went into full detail about how the [`Module#module_function` method](https://ruby-doc.org/3.4.1/Module.html#method-i-module_function) works.
|
||||
It creates both a module of utility functions that we can access directly on
|
||||
that module like we would with `self` methods. It can also be included in a
|
||||
class as a way of sharing copies of those utility functions with the class. A
|
||||
key point to them being copies is that they can then be overridden by the
|
||||
including class.
|
||||
|
||||
Here is the example I used in the blog post:
|
||||
|
||||
```ruby
|
||||
module MarkdownHelpers
|
||||
module_function
|
||||
|
||||
def heading(text, level = 1)
|
||||
("#" * level) + " #{text}"
|
||||
end
|
||||
|
||||
def link(text, href)
|
||||
"[#{text}](#{href})"
|
||||
end
|
||||
|
||||
def image(alt_text, href)
|
||||
"!#{link(alt_text, href)}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
I won't cover everything that the blog post covers, but what I found really nice
|
||||
about this pattern is that I can call those utility functions directly with the
|
||||
module as the receiver:
|
||||
|
||||
```bash
|
||||
$ ruby -r ./markdown_helpers.rb -e 'puts MarkdownHelpers.link("Click here", "https://example.com")'
|
||||
[Click here](https://example.com)
|
||||
```
|
||||
|
||||
The alternative to this generally looks like:
|
||||
|
||||
```ruby
|
||||
module MarkdownHelpers
|
||||
def self.heading(text, level = 1)
|
||||
("#" * level) + " #{text}"
|
||||
end
|
||||
|
||||
def self.link(text, href)
|
||||
"[#{text}](#{href})"
|
||||
end
|
||||
|
||||
def self.image(alt_text, href)
|
||||
"!#{link(alt_text, href)}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
That would be fine, but we completely lose out on the ability to include it as a
|
||||
mix-in with other classes.
|
||||
85
ruby/create-mock-class-that-can-be-overridden.md
Normal file
85
ruby/create-mock-class-that-can-be-overridden.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Create Mock Class That Can Be Overridden
|
||||
|
||||
Let's say I've defined a `MockTwilioClient` class for my system tests so that
|
||||
they don't have to actually make calls out to the Twilio API.
|
||||
|
||||
```ruby
|
||||
class MockTwilioClient
|
||||
def send_sms_message
|
||||
"MSG_SID_123"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Now, any test that is exercising behavior that uses those parts of the
|
||||
`TwilioClient` can mock those calls in a predictable way.
|
||||
|
||||
The above class always is successful when `send_sms_message` is called. What if
|
||||
we want to simulate an error response? We need a way to override the client for
|
||||
specific testing scenarios.
|
||||
|
||||
Let's create a helper that can create `MockTwilioClient` instances, either as is
|
||||
or with overrides.
|
||||
|
||||
```ruby
|
||||
def create_mock_twilio_client(&block)
|
||||
if block_given?
|
||||
Class.new(MockTwilioClient, &block).new
|
||||
else
|
||||
MockTwilioClient.new
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If we pass it a block with specific method overrides, it will create a one-off
|
||||
anonymous subclass of `MockTwilioClient` with that block which effectively
|
||||
overrides the parent class's methods.
|
||||
|
||||
We can put this to use like so:
|
||||
|
||||
```ruby
|
||||
require 'test_helper'
|
||||
|
||||
class SomeSystemTest < SystemTestCase
|
||||
class MockTwilioClient
|
||||
def send_sms_message
|
||||
"MSG_SID_123"
|
||||
end
|
||||
end
|
||||
|
||||
def create_mock_twilio_client(&block)
|
||||
if block_given?
|
||||
Class.new(MockTwilioClient, &block).new
|
||||
else
|
||||
MockTwilioClient.new
|
||||
end
|
||||
end
|
||||
|
||||
test "send message to customer" do
|
||||
mock_client = create_mock_twilio_client
|
||||
|
||||
TwilioClient.stub(:new, mock_client) do
|
||||
# some action that uses `send_sms_message`
|
||||
|
||||
# some assertions ...
|
||||
end
|
||||
end
|
||||
|
||||
test "fail to send message to customer" do
|
||||
mock_client = create_mock_twilio_client do
|
||||
def send_sms_message
|
||||
raise "Failed to send message"
|
||||
end
|
||||
end
|
||||
|
||||
TwilioClient.stub(:new, mock_client) do
|
||||
# some action that uses `send_sms_message`
|
||||
|
||||
# some assertions ...
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
In the second test case, I override the _success path_ with a version of
|
||||
`send_sms_message` that raises an error.
|
||||
55
ruby/decompose-unicode-character-with-diacritic-mark.md
Normal file
55
ruby/decompose-unicode-character-with-diacritic-mark.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Decompose Unicode Character With Diacritic Mark
|
||||
|
||||
A character like the `ñ` is typically represented by the unicode codepoint of
|
||||
`U+00F1`. However, it is also possible to represent it with two unicode
|
||||
codepoints -- the `n` (`U+006E`) and the combining diacritical mark `˜`
|
||||
(`U+0303`).
|
||||
|
||||
We can see that by comparing a typed `ñ` with one where we split it apart into
|
||||
the separate codepoints. We can do that with
|
||||
[`#unicode_normalize`](https://apidock.com/ruby/v2_5_5/String/unicode_normalize)
|
||||
and the `:nfd` argument which stands for _Normalized Form Decomposed_.
|
||||
|
||||
```ruby
|
||||
> "ñ" == "ñ".unicode_normalize(:nfd)
|
||||
=> false
|
||||
> "ñ".unicode_normalize(:nfd).length
|
||||
=> 2
|
||||
> "ñ".length
|
||||
=> 1
|
||||
```
|
||||
|
||||
We can inspect the exact codepoints by iterating over each character and
|
||||
printing out the codepoint value.
|
||||
|
||||
```ruby
|
||||
"ñ".each_char.with_index do |char, i|
|
||||
puts "#{i}: '#{char}' -> U+#{char.ord.to_s(16).upcase.rjust(4, '0')}"
|
||||
end
|
||||
# 0: 'ñ' -> U+00F1
|
||||
# => "ñ"
|
||||
|
||||
"ñ".unicode_normalize(:nfd).each_char.with_index do |char, i|
|
||||
puts "#{i}: '#{char}' -> U+#{char.ord.to_s(16).upcase.rjust(4, '0')}"
|
||||
end
|
||||
# 0: 'n' -> U+006E
|
||||
# 1: '̃' -> U+0303
|
||||
#=> "ñ"
|
||||
```
|
||||
|
||||
Notice the difference after the character has been decomposed such that the
|
||||
diacritic is separated from the character.
|
||||
|
||||
This can be done with other characters containing diacritics.
|
||||
|
||||
And here we go the other direction with
|
||||
[`#pack`](https://ruby-doc.org/core-3.0.1/Array.html#method-i-pack).
|
||||
|
||||
```ruby
|
||||
> [0x006E, 0x0303].pack("U*")
|
||||
=> "ñ"
|
||||
> [0x00F1].pack("U*")
|
||||
=> "ñ"
|
||||
> [0x006E, 0x0303].pack("U*") == [0x00F1].pack("U*")
|
||||
=> false
|
||||
```
|
||||
44
ruby/get-specific-values-from-hashes-and-arrays.md
Normal file
44
ruby/get-specific-values-from-hashes-and-arrays.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Get Specific Values From Hashes And Arrays
|
||||
|
||||
Ruby defines a `#values_at` method on both `Hash` and `Array` that can be used
|
||||
to grab multiple values from an instance of either of those structures.
|
||||
|
||||
Here is an example of grabbing values by key (if they exist) from a hash.
|
||||
|
||||
```ruby
|
||||
> hash = {one: :two, hello: "world", four: 4}
|
||||
=> {one: :two, hello: "world", four: 4}
|
||||
> hash.values_at(:one, :four, :three)
|
||||
=> [:two, 4, nil]
|
||||
```
|
||||
|
||||
And here is an example of grabbing values at specific indexes from an array, if
|
||||
those indexes exist.
|
||||
|
||||
```ruby
|
||||
> arr = [:a, :b, :c, :d, :e]
|
||||
=> [:a, :b, :c, :d, :e]
|
||||
> arr.values_at(0, 3, 6)
|
||||
=> [:a, :d, nil]
|
||||
```
|
||||
|
||||
Notice that in both cases, `nil` is returned for a key or index that doesn't
|
||||
exist.
|
||||
|
||||
What I like about this method is that in a single call I can grab multiple
|
||||
named (or indexed) values and get a single array result with those values.
|
||||
|
||||
One way I might use this with a JSON response from an API request could look
|
||||
like this:
|
||||
|
||||
```ruby
|
||||
resp = client.getSomeData(id: 123)
|
||||
|
||||
[status, body] = resp.values_at("status", "body")
|
||||
|
||||
if status == 200
|
||||
puts body
|
||||
end
|
||||
```
|
||||
|
||||
[source](https://docs.ruby-lang.org/en/3.4/Hash.html#method-i-values_at)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user