mirror of
https://github.com/jbranchaud/til
synced 2026-01-15 21:18:02 +00:00
Compare commits
1 Commits
68fa39da4f
...
4db0017665
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4db0017665 |
37
README.md
37
README.md
@@ -10,7 +10,7 @@ pairing with smart people at Hashrocket.
|
||||
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||
|
||||
_1427 TILs and counting..._
|
||||
_1396 TILs and counting..._
|
||||
|
||||
---
|
||||
|
||||
@@ -41,7 +41,6 @@ _1427 TILs and counting..._
|
||||
* [jq](#jq)
|
||||
* [Kitty](#kitty)
|
||||
* [Linux](#linux)
|
||||
* [LLM](#llm)
|
||||
* [Mac](#mac)
|
||||
* [MongoDB](#mongodb)
|
||||
* [MySQL](#mysql)
|
||||
@@ -99,7 +98,6 @@ _1427 TILs and counting..._
|
||||
### Brew
|
||||
|
||||
- [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md)
|
||||
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
|
||||
|
||||
### Chrome
|
||||
|
||||
@@ -261,7 +259,6 @@ _1427 TILs and counting..._
|
||||
|
||||
- [Accessing a Lost Commit](git/accessing-a-lost-commit.md)
|
||||
- [Add A Range Of Filename To gitignore](git/add-a-range-of-filenames-to-gitignore.md)
|
||||
- [Add Only Tracked Files From A Directory](git/add-only-tracked-files-from-a-directory.md)
|
||||
- [Amend Author Of Previous Commit](git/amend-author-of-previous-commit.md)
|
||||
- [Auto-Squash Those Fixup Commits](git/auto-squash-those-fixup-commits.md)
|
||||
- [Caching Credentials](git/caching-credentials.md)
|
||||
@@ -321,7 +318,6 @@ _1427 TILs and counting..._
|
||||
- [Quicker Commit Fixes With The Fixup Flag](git/quicker-commit-fixes-with-the-fixup-flag.md)
|
||||
- [Rebase Commits With An Arbitrary Command](git/rebase-commits-with-an-arbitrary-command.md)
|
||||
- [Reference A Commit Via Commit Message Pattern Matching](git/reference-a-commit-via-commit-message-pattern-matching.md)
|
||||
- [Remove Untracked Files From A Directory](git/remove-untracked-files-from-a-directory.md)
|
||||
- [Rename A Remote](git/rename-a-remote.md)
|
||||
- [Renaming A Branch](git/renaming-a-branch.md)
|
||||
- [Resetting A Reset](git/resetting-a-reset.md)
|
||||
@@ -401,7 +397,6 @@ _1427 TILs and counting..._
|
||||
- [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.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)
|
||||
- [Make Elements Non-Interactive With Inert](html/make-elements-non-interactive-with-inert.md)
|
||||
- [Prevent Search Engines From Indexing A Page](html/prevent-search-engines-from-indexing-a-page.md)
|
||||
- [Render Text As Superscript](html/render-text-as-superscript.md)
|
||||
- [Submit A Form With A Button Outside The Form](html/submit-a-form-with-a-button-outside-the-form.md)
|
||||
@@ -412,7 +407,6 @@ _1427 TILs and counting..._
|
||||
|
||||
### Inngest
|
||||
|
||||
- [Ensure Lookup Can Be Retried](inngest/ensure-lookup-can-be-retried.md)
|
||||
- [Exit Function Early Without Retries](inngest/exit-function-early-without-retries.md)
|
||||
|
||||
### Internet
|
||||
@@ -442,7 +436,6 @@ _1427 TILs and counting..._
|
||||
- [Computed Property Names In ES6](javascript/computed-property-names-in-es6.md)
|
||||
- [Conditionally Include Pairs In An Object](javascript/conditionally-include-pairs-in-an-object.md)
|
||||
- [Configure Jest To Run A Test Setup File](javascript/configure-jest-to-run-a-test-setup-file.md)
|
||||
- [Convert Seconds To Date Object](javascript/convert-seconds-to-date-object.md)
|
||||
- [Create A Cancelable Promise With PCancelable](javascript/create-a-cancelable-promise-with-pcancelable.md)
|
||||
- [Create An Array Containing 1 To N](javascript/create-an-array-containing-1-to-n.md)
|
||||
- [Create An Object With No Properties](javascript/create-an-object-with-no-properties.md)
|
||||
@@ -559,11 +552,6 @@ _1427 TILs and counting..._
|
||||
- [Show Used And Available System Memory](linux/show-used-and-available-system-memory.md)
|
||||
- [Upgrading Ubuntu](linux/upgrading-ubuntu.md)
|
||||
|
||||
### LLM
|
||||
|
||||
- [Send cURL To Claude Text Completion API](llm/send-curl-to-claude-text-completion-api.md)
|
||||
- [Use The llm CLI With Claude Models](llm/use-the-llm-cli-with-claude-models.md)
|
||||
|
||||
### Mac
|
||||
|
||||
- [Access All Screen And Video Capture Options](mac/access-all-screen-and-video-capture-options.md)
|
||||
@@ -633,10 +621,8 @@ _1427 TILs and counting..._
|
||||
|
||||
- [Create Files And Directories For Dynamic Routes](nextjs/create-files-and-directories-for-dynamic-routes.md)
|
||||
- [Define URL Redirects In The Next Config](nextjs/define-url-redirects-in-the-next-config.md)
|
||||
- [Fetch Does Not Work In API Serverless Function](nextjs/fetch-does-not-work-in-api-serverless-function.md)
|
||||
- [Make Environment Variable Publicly Available](nextjs/make-environment-variable-publicly-available.md)
|
||||
- [Match Middleware On Groups Of Paths](nextjs/match-middleware-on-groups-of-paths.md)
|
||||
- [Precedence Of Dot Env Files](nextjs/precedence-of-dot-env-files.md)
|
||||
- [Push A Route With A URL Object](nextjs/push-a-route-with-a-url-object.md)
|
||||
- [Redirect An Unauthorized User](nextjs/redirect-an-unauthorized-user.md)
|
||||
- [Remove A Query Param From The URL](nextjs/remove-a-query-param-from-the-url.md)
|
||||
@@ -675,7 +661,6 @@ _1427 TILs and counting..._
|
||||
- [Capitalize All The Words](postgres/capitalize-all-the-words.md)
|
||||
- [Change The Current Directory For psql](postgres/change-the-current-directory-for-psql.md)
|
||||
- [Change The Owner Of A Sequence](postgres/change-the-owner-of-a-sequence.md)
|
||||
- [Check If Clusters Are Upgrade Compatible](postgres/check-if-clusters-are-upgrade-compatible.md)
|
||||
- [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)
|
||||
@@ -692,7 +677,6 @@ _1427 TILs and counting..._
|
||||
- [Count How Many Records There Are Of Each Type](postgres/count-how-many-records-there-are-of-each-type.md)
|
||||
- [Count Records By Type](postgres/count-records-by-type.md)
|
||||
- [Count The Number Of Trues In An Aggregate Query](postgres/count-the-number-of-trues-in-an-aggregate-query.md)
|
||||
- [Create A Cluster In A Specific Data Directory](postgres/create-a-cluster-in-a-specific-data-directory.md)
|
||||
- [Create A Composite Primary Key](postgres/create-a-composite-primary-key.md)
|
||||
- [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)
|
||||
@@ -740,7 +724,6 @@ _1427 TILs and counting..._
|
||||
- [Group By The Result Of A Function Call](postgres/group-by-the-result-of-a-function-call.md)
|
||||
- [Idempotent Inserts](postgres/idempotent-inserts.md)
|
||||
- [Include All Queries In The Log File](postgres/include-all-queries-in-the-log-file.md)
|
||||
- [Include Columns In A Covering Index](postgres/include-columns-in-a-covering-index.md)
|
||||
- [Include Multiple Tables In A pg_dump](postgres/include-multiple-tables-in-a-pg-dump.md)
|
||||
- [Insert A Bunch Of Records With Generate Series](postgres/insert-a-bunch-of-records-with-generate-series.md)
|
||||
- [Insert Just The Defaults](postgres/insert-just-the-defaults.md)
|
||||
@@ -833,7 +816,6 @@ _1427 TILs and counting..._
|
||||
- [Access Instance Variables](python/access-instance-variables.md)
|
||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||
- [Test A Function With Pytest](python/test-a-function-with-pytest.md)
|
||||
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
||||
|
||||
### Rails
|
||||
|
||||
@@ -894,7 +876,6 @@ _1427 TILs and counting..._
|
||||
- [Get ActiveRecord Attribute Directly From Database](rails/get-active-record-attribute-directly-from-database.md)
|
||||
- [Get An Array Of Values From The Database](rails/get-an-array-of-values-from-the-database.md)
|
||||
- [Get An Empty ActiveRecord Relation](rails/get-an-empty-activerecord-relation.md)
|
||||
- [Get Formatted UTC Offset Value](rails/get-formatted-utc-offset-value.md)
|
||||
- [Get Help With A Rails App Update](rails/get-help-with-a-rails-app-update.md)
|
||||
- [Get The Column Names For A Model](rails/get-the-column-names-for-a-model.md)
|
||||
- [Get The Current Time](rails/get-the-current-time.md)
|
||||
@@ -910,7 +891,6 @@ _1427 TILs and counting..._
|
||||
- [Load A File When Starting Rails Console](rails/load-a-file-when-starting-rails-console.md)
|
||||
- [Load Records In Batches With find_each](rails/load-records-in-batches-with-find-each.md)
|
||||
- [Log SQL Queries Executed By ActiveRecord](rails/log-sql-queries-executed-by-activerecord.md)
|
||||
- [Look Up Time Zone Info For Identifier](rails/look-up-time-zone-info-for-identifier.md)
|
||||
- [Mark A Migration As Irreversible](rails/mark-a-migration-as-irreversible.md)
|
||||
- [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)
|
||||
@@ -1107,7 +1087,6 @@ _1427 TILs and counting..._
|
||||
- [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)
|
||||
- [Block Comments](ruby/block-comments.md)
|
||||
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
||||
@@ -1131,7 +1110,6 @@ _1427 TILs and counting..._
|
||||
- [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)
|
||||
- [Define Multiline Strings With Heredocs](ruby/define-multiline-strings-with-heredocs.md)
|
||||
- [Destructure The First Item From An Array](ruby/destructure-the-first-item-from-an-array.md)
|
||||
- [Destructuring Arrays In Blocks](ruby/destructuring-arrays-in-blocks.md)
|
||||
- [Disassemble Some Codes](ruby/disassemble-some-codes.md)
|
||||
@@ -1150,12 +1128,10 @@ _1427 TILs and counting..._
|
||||
- [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md)
|
||||
- [Find The Min And Max With A Single Call](ruby/find-the-min-and-max-with-a-single-call.md)
|
||||
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
||||
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
||||
- [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 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)
|
||||
- [If You Detect None](ruby/if-you-detect-none.md)
|
||||
- [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md)
|
||||
@@ -1211,7 +1187,6 @@ _1427 TILs and counting..._
|
||||
- [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)
|
||||
- [Specify Dependencies For A Rake Task](ruby/specify-dependencies-for-a-rake-task.md)
|
||||
- [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md)
|
||||
- [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md)
|
||||
- [Squeeze Out The Extra Space](ruby/squeeze-out-the-extra-space.md)
|
||||
- [String Interpolation With Instance Variables](ruby/string-interpolation-with-instance-variables.md)
|
||||
@@ -1376,9 +1351,7 @@ _1427 TILs and counting..._
|
||||
- [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 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)
|
||||
- [Get Matching Filenames As Output From Grep](unix/get-matching-filenames-as-output-from-grep.md)
|
||||
- [Get The Unix Timestamp](unix/get-the-unix-timestamp.md)
|
||||
- [Global Substitution On The Previous Command](unix/global-substitution-on-the-previous-command.md)
|
||||
@@ -1390,7 +1363,6 @@ _1427 TILs and counting..._
|
||||
- [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)
|
||||
- [Jump To The Ends Of Your Shell History](unix/jump-to-the-ends-of-your-shell-history.md)
|
||||
@@ -1400,7 +1372,6 @@ _1427 TILs and counting..._
|
||||
- [Less With Style](unix/less-with-style.md)
|
||||
- [List All The Enabled ZSH Options](unix/list-all-the-enabled-zsh-options.md)
|
||||
- [List All Users](unix/list-all-users.md)
|
||||
- [List Files In A Single Column](unix/list-files-in-a-single-column.md)
|
||||
- [List Files Ordered By Modification Date](unix/list-files-ordered-by-modification-date.md)
|
||||
- [List Names Of Files With Matches](unix/list-names-of-files-with-matches.md)
|
||||
- [List Of Sessions To A Machine](unix/list-of-sessions-to-a-machine.md)
|
||||
@@ -1419,7 +1390,6 @@ _1427 TILs and counting..._
|
||||
- [Partial String Matching In Bash Scripts](unix/partial-string-matching-in-bash-scripts.md)
|
||||
- [PID Of The Current Shell](unix/pid-of-the-current-shell.md)
|
||||
- [Print A Range Of Lines For A File With Bat](unix/print-a-range-of-lines-for-a-file-with-bat.md)
|
||||
- [Print DateTime Represented By Unix Timestamp](unix/print-datetime-represented-by-unix-timestamp.md)
|
||||
- [Print Milliseconds In Human-Readable Format](unix/print-milliseconds-in-human-readable-format.md)
|
||||
- [Print Out Files In Reverse](unix/print-out-files-in-reverse.md)
|
||||
- [Print The Current Date In Human-Readable Format](unix/print-the-current-date-in-human-readable-format.md)
|
||||
@@ -1450,7 +1420,6 @@ _1427 TILs and counting..._
|
||||
- [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)
|
||||
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
||||
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
|
||||
- [Update Package Versions Known By asdf Plugin](unix/update-package-versions-known-by-asdf-plugin.md)
|
||||
- [Use fzf To Change Directories](unix/use-fzf-to-change-directories.md)
|
||||
- [Use Regex Pattern Matching With Grep](unix/use-regex-pattern-matching-with-grep.md)
|
||||
@@ -1633,12 +1602,10 @@ _1427 TILs and counting..._
|
||||
- [Add The VSCode CLI To Your Path](vscode/add-the-vscode-cli-to-your-path.md)
|
||||
- [Advance Through Search Results](vscode/advance-through-search-results.md)
|
||||
- [Enable Breadcrumbs For Version 1.26 Release](vscode/enable-breadcrumbs-for-version-126-release.md)
|
||||
- [Find The Location Of User Settings JSON File](vscode/find-the-location-of-user-settings-json-file.md)
|
||||
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
|
||||
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
|
||||
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
|
||||
- [Toggle Between Terminals](vscode/toggle-between-terminals.md)
|
||||
- [Turn Off Display Of Tabs For Files](vscode/turn-off-display-of-tabs-for-files.md)
|
||||
|
||||
### Webpack
|
||||
|
||||
@@ -1650,8 +1617,6 @@ _1427 TILs and counting..._
|
||||
|
||||
### Workflow
|
||||
|
||||
- [Add Subtitles To Existing Mux Video Asset](workflow/add-subtitles-to-existing-mux-video-asset.md)
|
||||
- [Access 1Password Credential From CLI](workflow/access-1password-credential-from-cli.md)
|
||||
- [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md)
|
||||
- [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)
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# List All Services Managed By Brew
|
||||
|
||||
Daemonized services, such as PostgreSQL, can be installed and managed with
|
||||
Homebrew. Under the hood `brew` uses `launchctl` on Mac to manage these
|
||||
services — i.e. starting, restarting, and stopping them.
|
||||
|
||||
Assuming you've already installed some services, you can run `brew services
|
||||
list` to see what services there are and what their current status is.
|
||||
|
||||
```bash
|
||||
$ brew services list
|
||||
Name Status User File
|
||||
mailhog none
|
||||
mysql none
|
||||
postgresql@11 started jbranchaud ~/Library/LaunchAgents/homebrew.mxcl.postgresql@11.plist
|
||||
postgresql@13 none
|
||||
postgresql@16 none
|
||||
unbound none
|
||||
```
|
||||
|
||||
This is the default behavior if you just run `brew services` without a subcommand.
|
||||
|
||||
This is helpful if you are, for instance, trying to see which PostgreSQL server
|
||||
version you are currently running and which other ones are available to run. I
|
||||
might then issue a `stop` to `postgresql@11` so that I can then `start` the
|
||||
`postgresql@16` service.
|
||||
|
||||
See `brew services --help` for more details.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Add Only Tracked Files From A Directory
|
||||
|
||||
The two extremes of staging files in a git repo are to either selectively pick
|
||||
each individual chunk of changes with `git add --patch` (my preference!) or to
|
||||
run `git add -A` to add everything.
|
||||
|
||||
Now let's say I have large directory full of files that get generated during
|
||||
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.
|
||||
|
||||
Running `git add spec/cassettes` won't do the track 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.
|
||||
|
||||
```bash
|
||||
$ git add -u spec/cassettes
|
||||
```
|
||||
|
||||
That will stage every change to any already known files in `spec/cassettes`.
|
||||
|
||||
See `man git-add` for more details.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Remove Untracked Files From A Directory
|
||||
|
||||
Let's say I have a directory (`spec/cassettes`) full of a ton of generated YAML
|
||||
files. Most of these files are tracked by git. However, I just generated a
|
||||
bunch of new ones that are untracked. For whatever reason, I don't want these
|
||||
files. I need to delete them.
|
||||
|
||||
Running `rm` on each of them is going to be too tedious. And it is tricky to
|
||||
target them for a bulk delete since there are a ton of other files in that
|
||||
directory that I want to keep.
|
||||
|
||||
One way to approach this is have `git ls-files` help out with listing all files in the
|
||||
directory that are untracked. The `--others` flag filters to untracked files.
|
||||
|
||||
```bash
|
||||
git ls-files --others --exclude-standard spec/cassettes
|
||||
```
|
||||
|
||||
From there, I can pipe it to `rm` (with `xargs` collapsing all the files into a
|
||||
single line):
|
||||
|
||||
```bash
|
||||
git ls-files --others --exclude-standard spec/cassettes | xargs rm
|
||||
```
|
||||
|
||||
See `man git-ls-files` for more details.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Make Elements Non-Interactive With Inert
|
||||
|
||||
The [`inert`
|
||||
attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert)
|
||||
global attribute is a boolean that can be applied to an element or section of
|
||||
content in an HTML document. When it is `true`, that elements and anything
|
||||
nested within it will not be interactive.
|
||||
|
||||
```html
|
||||
<div class="fancy-animation" inert>
|
||||
<!-- ... -->
|
||||
</div>
|
||||
```
|
||||
|
||||
This has a couple different implications:
|
||||
|
||||
1. Click events are not fired on these elements.
|
||||
|
||||
2. These elements will not be able to gain focus.
|
||||
|
||||
3. These elements and content are hidden from assistive technology.
|
||||
|
||||
This is useful for a variety of things. In particular, it is good for
|
||||
accessibility when a portion of the document, like a fancy animation, isn't
|
||||
meant to be traversed by assistive technology.
|
||||
@@ -1,57 +0,0 @@
|
||||
# Ensure Lookup Can Be Retried
|
||||
|
||||
A common thing to do in a workflow step is to look up a record. This might be a
|
||||
record that was created or updated around the time that the workflow was
|
||||
triggered.
|
||||
|
||||
You need to be sure the record was found before proceeding. That might end up
|
||||
looking like this:
|
||||
|
||||
```typescript
|
||||
export default inngest.createFunction(
|
||||
{ id: "record-user-purchase" },
|
||||
{ event: "app/record.purchase" },
|
||||
async ({ event, step }) => {
|
||||
const checkoutSession =
|
||||
await step.run("find checkout session", async () => {
|
||||
const cs = provider.lookupSession(event.checkoutSessionId)
|
||||
|
||||
return cs;
|
||||
});
|
||||
|
||||
if(!checkoutSession) {
|
||||
throw new Error('Checkout session not found')
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
This approach has a subtle problem which is that the error for a missing
|
||||
checkout session is raised _outside_ the step that sets `checkoutSession`. As
|
||||
inngest does a series of retries, the memorized `checkoutSession` step won't be
|
||||
rerun and the error will continue to be thrown.
|
||||
|
||||
It is better to raise the error _within_ the lookup step:
|
||||
|
||||
```typescript
|
||||
export default inngest.createFunction(
|
||||
{ id: "record-user-purchase" },
|
||||
{ event: "app/record.purchase" },
|
||||
async ({ event, step }) => {
|
||||
const checkoutSession =
|
||||
await step.run("find checkout session", async () => {
|
||||
const cs = provider.lookupSession(event.checkoutSessionId)
|
||||
|
||||
if(!cs) {
|
||||
throw new Error('Checkout session not found')
|
||||
}
|
||||
|
||||
return cs;
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
If the checkout session is missing on the first couple tries, the step will
|
||||
have a chance to retry the lookup and maybe eventually find what it is looking
|
||||
for.
|
||||
@@ -1,32 +0,0 @@
|
||||
# Convert Seconds To Date Object
|
||||
|
||||
Let's say you have an integer that represents the number of seconds since the
|
||||
unix epoch. This is a reasonably common way for systems to represent a date.
|
||||
|
||||
For example, `1713350171` is an _Expiration Date_ I just got from an API.
|
||||
|
||||
But how do we know what date that actually represents and how can we get a
|
||||
JavaScript `Date` object from that value?
|
||||
|
||||
The `new Date()` constructor can produce a date object given an integer. That
|
||||
integer is not supposed to be seconds since the unix epoch though. See what we
|
||||
get here:
|
||||
|
||||
```javascript
|
||||
> new Date(1713350171)
|
||||
1970-01-20T19:55:50.171Z
|
||||
```
|
||||
|
||||
Something is off. The integer that you pass to `new Date()` needs to be the
|
||||
_number of milliseconds_ since the unix epoch. We can get there by multiplying
|
||||
our _seconds_ value by `1000`.
|
||||
|
||||
```javascript
|
||||
> new Date(1713350171 * 1000)
|
||||
2024-04-17T10:36:11.000Z
|
||||
```
|
||||
|
||||
Not only can we, as humans, read that date, but we have a `Date` object that we
|
||||
can use within our program.
|
||||
|
||||
Note: if you execute `Date.now()`, the value you get is in milliseconds.
|
||||
@@ -1,40 +0,0 @@
|
||||
# Send cURL To Claude Text Completion API
|
||||
|
||||
Here is how we can make a `cURL` (`POST`) request to the Claude text completion
|
||||
API. It requires already having a Claude API account with (paid) credits. At
|
||||
this time, you can get $5 in free credits to try it out.
|
||||
|
||||
Assuming all that, we can grab an API key, store it somewhere safe and
|
||||
accessible like 1Password, and then start formatting a request.
|
||||
|
||||
We need to specify a couple headers as well as `POST` body parameters.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: $(op item get "Anthropic Claude API Key" --field credential)" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-d \
|
||||
'{
|
||||
"model": "claude-2.1",
|
||||
"max_tokens_to_sample": 1024,
|
||||
"prompt": "Human: Show me an example of a simple Ruby program.\n\nAssistant:"
|
||||
}' \
|
||||
https://api.anthropic.com/v1/complete
|
||||
```
|
||||
|
||||
The required headers are:
|
||||
- `"Content-Type: application/json"`
|
||||
- `x-api-key` with our API key
|
||||
- `"anthropic-version: 2023-06-01"` (the latest Anthropic API version)
|
||||
|
||||
Then, in the body, we specify:
|
||||
- the `model` (e.g. `claude-2.1`)
|
||||
- the max number of tokens you want the model to use
|
||||
- a prompt that starts with `Human:` and then prompts the `Assistant:`
|
||||
|
||||
Note: this is a legacy API and the [Messages
|
||||
API](https://docs.anthropic.com/claude/reference/messages_post) should be
|
||||
preferred.
|
||||
|
||||
[source](https://docs.anthropic.com/claude/reference/complete_post)
|
||||
@@ -1,26 +0,0 @@
|
||||
# Use The llm CLI With Claude Models
|
||||
|
||||
[Simon Willison's `llm`](https://llm.datasette.io/en/stable/index.html) can be
|
||||
used with a bunch of different models (local and API). The whole thing is
|
||||
plugin driven. To use a specific model, you'll need to install the plugin for
|
||||
it. For instance, to use the [Claude 3 family of
|
||||
models](https://www.anthropic.com/news/claude-3-family) you need to install
|
||||
`llm-claude-3`.
|
||||
|
||||
```bash
|
||||
$ llm install llm-claude-3
|
||||
```
|
||||
|
||||
Then when prompting `llm`, specify which of the Claude models you want to use —
|
||||
`claude-3-haiku`, `claude-3-sonnet`, or `claude-3-opus` — with the `-m` flag:
|
||||
|
||||
```bash
|
||||
$ llm \
|
||||
-m claude-3-haiku \
|
||||
--key $CLAUDE_API_KEY \
|
||||
'Show me the SQL query to create a cocktails table.'
|
||||
```
|
||||
|
||||
Note: instead of adding my Claude API key to the key store, I've opted to
|
||||
include it with the `--key` flag via an environment variable that I've set
|
||||
ahead of time.
|
||||
@@ -1,24 +0,0 @@
|
||||
# Fetch Does Not Work In API Serverless Function
|
||||
|
||||
Next.js ships with [its own implementation of
|
||||
`fetch`](https://nextjs.org/docs/app/api-reference/functions/fetch) that
|
||||
extends the [native `fetch`
|
||||
API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
||||
|
||||
I ran into a bug recently that was only reproducible in production. I had a
|
||||
Next.js API function that was `POST`ing to another API endpoint using `fetch`.
|
||||
I tested it several ways in development. It worked great. However, once the
|
||||
feature was in production, it was silently failing. After some `console.log`
|
||||
debugging, I discovered that the target API was getting an empty `body` in the
|
||||
`POST` request.
|
||||
|
||||
I don't know the specifics of why, but somehow the `fetch` implementation
|
||||
running in the Vercel serverless function environment apparently strips out the
|
||||
`body` of a POST request.
|
||||
|
||||
The solution, for me, was to add the
|
||||
[`node-fetch`](https://github.com/node-fetch/node-fetch) package and import
|
||||
that version of fetch in my API function. Once I made that change, my feature
|
||||
was working again.
|
||||
|
||||
[source](https://github.com/vercel/vercel/discussions/4971)
|
||||
@@ -1,41 +0,0 @@
|
||||
# Precedence Of Dot Env Files
|
||||
|
||||
_Dot Env_ files like `.env`, `.env.development`, `.env.local`, etc. are one of
|
||||
the main ways to configure your Next.js app across various environments.
|
||||
|
||||
It's not uncommon to see several different `.env*` files in production app that
|
||||
is under active development.
|
||||
|
||||
Here is an example of almost every variant in play:
|
||||
|
||||
```bash
|
||||
$ ls -a -1 .env*
|
||||
.env
|
||||
.env.development
|
||||
.env.development.local
|
||||
.env.development.local.example
|
||||
.env.local
|
||||
.env.production
|
||||
.env.test
|
||||
```
|
||||
|
||||
So, how does Next.js decide which files to load and in what order?
|
||||
|
||||
It will always attempt to load `.env` and `.env.local` (except in `test`) if
|
||||
those exist. It will also look for environment-specific files based on the
|
||||
`NODE_ENV` (which can be one of `development`, `test`, or `production`). So, in
|
||||
`development`, the `.env.development` and `.env.development.local` will be
|
||||
loaded. Something like `.env.development.local.example` isn't on the list, but
|
||||
rather is a convention for a dotenv file's template.
|
||||
|
||||
As for the order, the environment itself (your system's environment variables)
|
||||
which are present in `process.env` take the highest precedence. After that, it
|
||||
looks in any of the follow present files in this order, stopping once it finds
|
||||
what it is looking for:
|
||||
|
||||
- `.env.$(NODE_ENV).local`
|
||||
- `.env.local`
|
||||
- `.env.$(NODE_ENV)`
|
||||
- `.env`
|
||||
|
||||
[source](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order)
|
||||
@@ -1,42 +0,0 @@
|
||||
# Check If Clusters Are Upgrade Compatible
|
||||
|
||||
One of the ways to upgrade a PostgreSQL database from one server version to
|
||||
another is to use the built-in `pg_upgrade` command. This can be faster and
|
||||
require fewer manual steps than something like a `pg_dump` and `pg_restore`.
|
||||
|
||||
However, before you run the `pg_upgrade` command for real, you should check
|
||||
that the target database is compatible with the current database. To do this,
|
||||
write your `pg_update` command with all the flags you need and then tack on
|
||||
`--check` at the end. This does a dry-run reporting the results of a series of
|
||||
consistency checks.
|
||||
|
||||
Here is what a successful _check_ looks like:
|
||||
|
||||
```bash
|
||||
$ /usr/local/opt/postgresql@13/bin/pg_upgrade \
|
||||
--old-bindir $HOME/.asdf/installs/postgres/12.3/bin \
|
||||
--new-bindir /usr/local/opt/postgresql@13/bin \
|
||||
--old-datadir $HOME/.asdf/installs/postgres/12.3/data \
|
||||
--new-datadir ./postgres/data \
|
||||
--check
|
||||
|
||||
Performing Consistency Checks
|
||||
-----------------------------
|
||||
Checking cluster versions ok
|
||||
Checking database user is the install user ok
|
||||
Checking database connection settings ok
|
||||
Checking for prepared transactions ok
|
||||
Checking for system-defined composite types in user tables ok
|
||||
Checking for reg* data types in user tables ok
|
||||
Checking for contrib/isn with bigint-passing mismatch ok
|
||||
Checking for presence of required libraries ok
|
||||
Checking database user is the install user ok
|
||||
Checking for prepared transactions ok
|
||||
Checking for new cluster tablespace directories ok
|
||||
|
||||
*Clusters are compatible*
|
||||
```
|
||||
|
||||
If there is an issue, such as mismatched collation settings, the output will
|
||||
report the issue. You'll have to decide how to resolve those on a case-by-case
|
||||
basis.
|
||||
@@ -1,46 +0,0 @@
|
||||
# Create A Cluster In A Specific Data Directory
|
||||
|
||||
Let's say I want to create a PostgreSQL cluster near my app. So, I create a
|
||||
`postgres/data` directory next to my app. Then I run the `initdb` command
|
||||
pointing at that directory and specifying the UTF-8 locale.
|
||||
|
||||
Here is what that looks like:
|
||||
|
||||
```bash
|
||||
$ /usr/local/opt/postgresql@13/bin/initdb -D postgres/data --locale=en_US.UTF-8
|
||||
|
||||
The files belonging to this database system will be owned by user "jbranchaud".
|
||||
This user must also own the server process.
|
||||
|
||||
The database cluster will be initialized with locale "en_US.UTF-8".
|
||||
The default database encoding has accordingly been set to "UTF8".
|
||||
The default text search configuration will be set to "english".
|
||||
|
||||
Data page checksums are disabled.
|
||||
|
||||
fixing permissions on existing directory postgres/data ... ok
|
||||
creating subdirectories ... ok
|
||||
selecting dynamic shared memory implementation ... posix
|
||||
selecting default max_connections ... 100
|
||||
selecting default shared_buffers ... 128MB
|
||||
selecting default time zone ... America/Chicago
|
||||
creating configuration files ... ok
|
||||
running bootstrap script ... ok
|
||||
performing post-bootstrap initialization ... ok
|
||||
syncing data to disk ... ok
|
||||
|
||||
initdb: warning: enabling "trust" authentication for local connections
|
||||
You can change this by editing pg_hba.conf or using the option -A, or
|
||||
--auth-local and --auth-host, the next time you run initdb.
|
||||
|
||||
Success. You can now start the database server using:
|
||||
|
||||
'/usr/local/opt/postgresql@13/bin/pg_ctl' -D postgres/data -l logfile start
|
||||
```
|
||||
|
||||
As stated at the end of the command's output, I can run the `postgres` server
|
||||
with that data directory with:
|
||||
|
||||
```bash
|
||||
$ '/usr/local/opt/postgresql@13/bin/pg_ctl' -D postgres/data -l logfile start
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
# Include Columns In A Covering Index
|
||||
|
||||
A _covering index_ is a special type of B-Tree index that, in addition to
|
||||
indexing on a certain field, also _includes_ one or more columns as extra data
|
||||
in the leaves of the tree. When created correctly, this can speed up the
|
||||
queries it targets by achieving an _index-only scan_.
|
||||
|
||||
Let's say we have a frequently run query on a large `events` table that looks
|
||||
like this:
|
||||
|
||||
```sql
|
||||
select user_id, identifier, type
|
||||
from events
|
||||
where user_id = $1;
|
||||
```
|
||||
|
||||
Here is what it looks like to create an index for this query with the `include`
|
||||
keyword:
|
||||
|
||||
```sql
|
||||
create index user_id_on_events_idx
|
||||
on (user_id)
|
||||
include (identifier, type);
|
||||
```
|
||||
|
||||
An index on its own can already cause a significant speed up to the queries it
|
||||
targets, but may still need to retrieve some `select` attributes from the
|
||||
table. For hot-path queries with a set of specific columns always included in
|
||||
the select, there can be significant additional speed ups by having the index
|
||||
_cover_ those columns.
|
||||
|
||||
For more details, check out [A Close Look At The Index Include
|
||||
Clause](https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes).
|
||||
@@ -1,31 +0,0 @@
|
||||
# Use pipx To Install End User Apps
|
||||
|
||||
The [`pipx`](https://pipx.pypa.io/stable/) tool is an installer for the python
|
||||
ecosystem. It differs from `pip` in that it is for installing end-user
|
||||
applications and it does so in isolated environments.
|
||||
|
||||
You can install `pipx` with an OS-specific installer like Homebrew:
|
||||
|
||||
```bash
|
||||
$ brew install pipx
|
||||
```
|
||||
|
||||
Ensure `pipx`-installed apps are on your path:
|
||||
|
||||
```bash
|
||||
$ pipx ensurepath
|
||||
```
|
||||
|
||||
Then use `pipx` to install programs like
|
||||
[`cowsay`](https://pypi.org/project/cowsay/) or
|
||||
[`llm`](https://llm.datasette.io/en/stable/setup.html):
|
||||
|
||||
```bash
|
||||
$ pipx install llm
|
||||
|
||||
$ which llm
|
||||
/Users/jbranchaud/.local/bin/llm
|
||||
|
||||
$ llm --version
|
||||
llm, version 0.13.1
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
# Get Formatted UTC Offset Value
|
||||
|
||||
The UTC offset (during DST) for Chicago is `-05:00`. And for Newfoundland it is
|
||||
`-02:30`.
|
||||
|
||||
Rails has an [`ActiveSupport::TimeZone`
|
||||
module](https://api.rubyonrails.org/v7.1.3.2/classes/ActiveSupport/TimeZone.html#method-c-seconds_to_utc_offset)
|
||||
that can turn the UTC offset in seconds into this formatted value.
|
||||
|
||||
```ruby
|
||||
> chicago = TZInfo::Timezone.get('America/Chicago')
|
||||
=> #<TZInfo::DataTimezone: America/Chicago>
|
||||
> ActiveSupport::TimeZone.seconds_to_utc_offset(chicago.utc_offset)
|
||||
=> "-05:00"
|
||||
|
||||
> newfoundland = TZInfo::Timezone.get('America/St_Johns')
|
||||
=> #<TZInfo::DataTimezone: America/St_Johns>
|
||||
> ActiveSupport::TimeZone.seconds_to_utc_offset(newfoundland.utc_offset)
|
||||
=> "-02:30"
|
||||
```
|
||||
|
||||
The underlying `tzinfo` gem is DST-aware and its database even knows which time
|
||||
zone identifiers don't observe DST, so you can run this anytime of the year and
|
||||
expect reliable results.
|
||||
|
||||
Here is another way at this:
|
||||
|
||||
```ruby
|
||||
> chicago = TZInfo::Timezone.get('America/Chicago')
|
||||
=> #<TZInfo::DataTimezone: America/Chicago>
|
||||
> ActiveSupport::TimeZone[chicago.name]
|
||||
=> #<ActiveSupport::TimeZone:0x00000001099d8140 @name="America/Chicago", @tzinfo=#<TZInfo::DataTimezone: America/Chicago>, @utc_offset=nil>
|
||||
> ActiveSupport::TimeZone[chicago.name].formatted_offset
|
||||
=> "-06:00"
|
||||
> ActiveSupport::TimeZone[chicago.name].formatted_offset(false)
|
||||
=> "-0600"
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
# Look Up Time Zone Info For Identifier
|
||||
|
||||
The `ActiveSupport::TimeZone` class overrides the `#[]` method to be a lookup
|
||||
mechanism for IANA Time Zone Identifier strings. These are strings like
|
||||
`America/Chicago` (or anything else listed under `TZInfo::Timezone.all`).
|
||||
|
||||
Let's get an instance for `America/Chicago`.
|
||||
|
||||
```ruby
|
||||
> chi = ActiveSupport::TimeZone['America/Chicago']
|
||||
=> #<ActiveSupport::TimeZone:0x00000001099d8140
|
||||
@name="America/Chicago",
|
||||
@tzinfo=#<TZInfo::DataTimezone: America/Chicago>,
|
||||
@utc_offset=nil>
|
||||
```
|
||||
|
||||
Notice it has a `tzinfo` instance variable that we can access. That object
|
||||
contains all kinds of useful things.
|
||||
|
||||
```ruby
|
||||
> chi.tzinfo.name
|
||||
=> "America/Chicago"
|
||||
> chi.tzinfo.friendly_identifier
|
||||
=> "America - Chicago"
|
||||
> chi.tzinfo.abbr
|
||||
=> "CDT"
|
||||
> chi.tzinfo.utc_offset
|
||||
=> -18000
|
||||
> chi.tzinfo.dst?
|
||||
=> true
|
||||
```
|
||||
|
||||
All of these and more. Run `ls chi.tzinfo` in a `pry` session to see what else.
|
||||
@@ -1,45 +0,0 @@
|
||||
# Audit Your Ruby Project For Any CVEs
|
||||
|
||||
The [`bundler-audit` gem](https://github.com/rubysec/bundler-audit) is a handy
|
||||
tool that you can run manually or integrate into your CI workflow to warn you
|
||||
about any CVEs in your dependencies.
|
||||
|
||||
Running this tool without any arguments will perform a check of your
|
||||
`Gemfile.lock` file. It will check against the
|
||||
[`ruby-advisory-db`](https://github.com/rubysec/ruby-advisory-db) for any CVEs
|
||||
linked to your dependencies, down to the patch-level.
|
||||
|
||||
```bash
|
||||
$ bundle exec bundler-audit
|
||||
|
||||
Name: puma
|
||||
Version: 4.3.12
|
||||
CVE: CVE-2024-21647
|
||||
GHSA: GHSA-c2f4-cvqm-65w2
|
||||
Criticality: Medium
|
||||
URL: https://github.com/puma/puma/security/advisories/GHSA-c2f4-cvqm-65w2
|
||||
Title: Puma HTTP Request/Response Smuggling vulnerability
|
||||
Solution: upgrade to '~> 5.6.8', '>= 6.4.2'
|
||||
|
||||
Vulnerabilities found!
|
||||
```
|
||||
|
||||
In this example run, a vulnerability was found in the currently installed
|
||||
version of the `puma` gem.
|
||||
|
||||
I believe a standard `bundler-audit` command will make sure the advisory DB is
|
||||
up-to-date, but to be sure, you can run the `update` command.
|
||||
|
||||
```bash
|
||||
$ bundle exec bundler-audit update
|
||||
|
||||
Updating ruby-advisory-db ...
|
||||
From https://github.com/rubysec/ruby-advisory-db
|
||||
* branch master -> FETCH_HEAD
|
||||
Already up to date.
|
||||
Updated ruby-advisory-db
|
||||
ruby-advisory-db:
|
||||
advisories: 884 advisories
|
||||
last updated: 2024-03-26 16:27:16 -0700
|
||||
commit: 840f21aeeb8a06a93a3c3bf1e2a92d7167029992
|
||||
```
|
||||
@@ -1,47 +0,0 @@
|
||||
# Define Multiline Strings With Heredocs
|
||||
|
||||
[A _heredoc_ (_here document_) is a special ruby
|
||||
syntax](https://ruby-doc.org/core-2.5.0/doc/syntax/literals_rdoc.html#label-Here+Documents)
|
||||
for defining formatted multiline strings. These are useful for any situation
|
||||
where you need to define a block of text where newlines and indentation are
|
||||
preserved -- e.g. output an error, help message, or some formatted SQL.
|
||||
|
||||
The standard syntax is defined with `<<` and some uppercase identifier (e.g.
|
||||
`TXT`, `DOC`, `SQL`, etc.) to open and close the multiline string.
|
||||
|
||||
```ruby
|
||||
def lookup_team(team_id)
|
||||
query = <<SQL
|
||||
select users.id from users
|
||||
join teams
|
||||
on teams.id = users.team_id
|
||||
where teams.id = #{team_id}
|
||||
order by created_at desc
|
||||
limit 10;
|
||||
SQL
|
||||
|
||||
team_member_ids = db.execute(query)
|
||||
end
|
||||
```
|
||||
|
||||
With the SQL formatted this way, it is easier to read and we can print/log out
|
||||
this nicely formatted version to make debugging easier.
|
||||
|
||||
Notice that the terminating `SQL` identifier is fully left justified. I find
|
||||
that visually off relative to the indentation of everything else, so I like to
|
||||
use the _indented_ heredoc syntax (`<<-`).
|
||||
|
||||
```ruby
|
||||
def lookup_team(team_id)
|
||||
query = <<-SQL
|
||||
select users.id from users
|
||||
join teams
|
||||
on teams.id = users.team_id
|
||||
where teams.id = #{team_id}
|
||||
order by created_at desc
|
||||
limit 10;
|
||||
SQL
|
||||
|
||||
team_member_ids = db.execute(query)
|
||||
end
|
||||
```
|
||||
@@ -1,35 +0,0 @@
|
||||
# Format A Hash Into A String Template
|
||||
|
||||
The `%` method as defined by `String`
|
||||
([`String#%`](https://ruby-doc.org/core-3.0.0/String.html#method-i-25)) allows
|
||||
you to format (interpolate) an object or array of values into a string. That
|
||||
string needs to contain template markers for where the values should go.
|
||||
|
||||
Here is an example of folding an array of values into a string with [`%s`
|
||||
format
|
||||
specifier](https://docs.ruby-lang.org/en/master/format_specifications_rdoc.html#label-Specifier+s):
|
||||
|
||||
```ruby
|
||||
> User = Struct.new(:id)
|
||||
=> User
|
||||
> user1 = User.new(123)
|
||||
=> #<struct User id=123>
|
||||
> "%s ID: %s" % [user1.class.to_s, user1.id]
|
||||
=> "User ID: 123"
|
||||
```
|
||||
|
||||
Or perhaps more usefully for a string that acts as a template, you can used
|
||||
named specifiers that correspond to hash keys:
|
||||
|
||||
```ruby
|
||||
> template = "You paid %{formatted_price} for %{product}. Enjoy your purchase!"
|
||||
=> "You paid %{formatted_price} for %{product}. Enjoy your purchase!"
|
||||
|
||||
> data = { product: "Ruby Explained™", formatted_price: "$38.99" }
|
||||
=> {:product=>"Ruby Explained™", :formatted_price=>"$38.99"}
|
||||
|
||||
> template % data
|
||||
=> "You paid $38.99 for Ruby Explained™. Enjoy your purchase!"
|
||||
```
|
||||
|
||||
[source](https://hashrocket.com/blog/posts/using-a-hash-of-data-for-string-replacement-in-ruby)
|
||||
@@ -1,42 +0,0 @@
|
||||
# Get UTC Offset For Different Time Zones
|
||||
|
||||
The [IANA Time Zone Database](https://www.iana.org/time-zones) uses identifiers
|
||||
like `America/Chicago`, `Asia/Hong_Kong`, `Africa/Nairobi`, etc. as specifiers
|
||||
for notable locations with time zone information.
|
||||
|
||||
> Most timezones correspond to a notable location and the database records all
|
||||
> known clock transitions for that location; some timezones correspond instead
|
||||
> to a fixed UTC offset.
|
||||
—[Theory and pragmatics of the tz code and data](https://data.iana.org/time-zones/theory.html)
|
||||
|
||||
These identifiers can be used to look up time zone details with the [`tzinfo`
|
||||
gem](https://github.com/tzinfo/tzinfo).
|
||||
|
||||
Here is an example of passing one to the `#get` method and then getting the UTC
|
||||
offset in seconds.
|
||||
|
||||
```ruby
|
||||
> require 'tzinfo'
|
||||
|
||||
> mountain = TZInfo::Timezone.get('America/Denver')
|
||||
=> #<TZInfo::DataTimezone: America/Denver>
|
||||
> mountain.utc_offset
|
||||
=> -21600
|
||||
```
|
||||
|
||||
We can even get the base UTC offset that doesn't account for DST:
|
||||
|
||||
```ruby
|
||||
> moutain.base_utc_offset
|
||||
=> -25200
|
||||
```
|
||||
|
||||
Notice, this is the same as the standard offset for a time zone like Phoenix
|
||||
that doesn't observe DST.
|
||||
|
||||
```ruby
|
||||
> phoenix = TZInfo::Timezone.get('America/Phoenix')
|
||||
=> #<TZInfo::DataTimezone: America/Phoenix>
|
||||
> phoenix.utc_offset
|
||||
=> -25200
|
||||
```
|
||||
@@ -1,46 +0,0 @@
|
||||
# Specify How Random Array#sample Is
|
||||
|
||||
A typical use of the
|
||||
[`sample`](https://ruby-doc.org/core-2.4.0/Array.html#method-i-sample) method
|
||||
on [`Array`](https://ruby-doc.org/core-2.4.0/Array.html) will look something
|
||||
like this:
|
||||
|
||||
```ruby
|
||||
> chars = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map(&:to_a).flatten
|
||||
|
||||
> chars.sample(6)
|
||||
=> ["o", "Z", "X", "i", "8", "Y"]
|
||||
```
|
||||
|
||||
By default, this is using `Random` (a pseudo-random number generator) to
|
||||
produce a _random_ sampling of elements from your array.
|
||||
|
||||
The longer form of this looks like:
|
||||
|
||||
```ruby
|
||||
> chars.sample(6, random: Random)
|
||||
=> ["F", "c", "g", "I", "w", "E"]
|
||||
```
|
||||
|
||||
Or to get reproducible results, you can specify the `Random` seed:
|
||||
|
||||
```ruby
|
||||
> chars.sample(6, random: Random.new(123))
|
||||
=> ["T", "c", "D", "K", "P", "s"]
|
||||
```
|
||||
|
||||
If instead you want a cryptographically random sampling of elements from your
|
||||
array, you can specify a different random number generator. Such as
|
||||
[`SecureRandom`](https://ruby-doc.org/stdlib-2.5.1/libdoc/securerandom/rdoc/SecureRandom.html).
|
||||
|
||||
```ruby
|
||||
> require 'securerandom'
|
||||
=> true
|
||||
|
||||
> chars.sample(6, random: SecureRandom)
|
||||
=> ["3", "C", "o", "i", "K", "4"]
|
||||
```
|
||||
|
||||
The
|
||||
[`Array#shuffle`](https://ruby-doc.org/core-2.4.0/Array.html#method-i-shuffle)
|
||||
method is another example of a method that can take a `random` option.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Generate Base64 Encoding Without Newlines
|
||||
|
||||
There are a variety of tools that can generate a Base64 encoding of given text.
|
||||
Most of them that I've encountered have a number of characters at which they
|
||||
introduce a newline character. Here is `openssl` as an example:
|
||||
|
||||
```bash
|
||||
❯ echo "here is a long bit of text to base64 encode with openssl" | openssl base64
|
||||
aGVyZSBpcyBhIGxvbmcgYml0IG9mIHRleHQgdG8gYmFzZTY0IGVuY29kZSB3aXRo
|
||||
IG9wZW5zc2wK
|
||||
```
|
||||
|
||||
[The theory I've seen](https://superuser.com/a/1225139) is that this is to
|
||||
accommodate 80-character terminal screens when chunks of encoding were included
|
||||
in emails.
|
||||
|
||||
With the `openssl base64` command, there is not an option to exclude the
|
||||
newlines, but we can pipe it through `tr` to remove them.
|
||||
|
||||
```bash
|
||||
❯ echo "here is a long bit of text to base64 encode with openssl" | \
|
||||
openssl base64 | \
|
||||
tr -d '\n'
|
||||
aGVyZSBpcyBhIGxvbmcgYml0IG9mIHRleHQgdG8gYmFzZTY0IGVuY29kZSB3aXRoIG9wZW5zc2wK
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
# Get A List Of Locales On Your System
|
||||
|
||||
The `locale -a` command will list all the available locales on your system.
|
||||
|
||||
You'll see a giant list that probably includes these and many more values.
|
||||
|
||||
```bash
|
||||
$ locale -a
|
||||
|
||||
en_NZ
|
||||
nl_NL.UTF-8
|
||||
pt_BR.UTF-8
|
||||
fr_CH.ISO8859-15
|
||||
eu_ES.ISO8859-15
|
||||
en_US.US-ASCII
|
||||
af_ZA
|
||||
bg_BG
|
||||
cs_CZ.UTF-8
|
||||
fi_FI
|
||||
zh_CN.UTF-8
|
||||
eu_ES
|
||||
sk_SK.ISO8859-2
|
||||
nl_BE
|
||||
fr_BE
|
||||
sk_SK
|
||||
en_US.UTF-8
|
||||
...
|
||||
```
|
||||
|
||||
Each of these locales identifies itself by the language and the manner in which
|
||||
the language is used in a particular place. For example, `en_NZ` is _English as
|
||||
it is spoken in New Zealand_. `fr_BE` is _French as it is spoken in Belgium_.
|
||||
`en_US.UTF-8` is _English as it is spoken in the US, with a UTF-8 character set
|
||||
encoding_.
|
||||
|
||||
[source](https://www.postgresql.org/docs/current/locale.html#LOCALE-OVERVIEW)
|
||||
@@ -1,31 +0,0 @@
|
||||
# Include Ignore Files In Ripgrep Search
|
||||
|
||||
By default, [ripgrep (`rg`)](https://github.com/BurntSushi/ripgrep) will look
|
||||
for and respect _ignore_ files like `.gitignore`. Any file and directory marked
|
||||
by those ignore files will no be included in an `rg` search.
|
||||
|
||||
This is usually what you want. Sometimes, however, it can be useful to get
|
||||
results from this ignored files as well.
|
||||
|
||||
In order to ignore your ignore files, pass the `--no-ignore` flag to `rg`:
|
||||
|
||||
```bash
|
||||
$ rg --no-ignore ENV_VAR_KEY
|
||||
```
|
||||
|
||||
Something to keep in mind is that `rg` also ignores _hidden_ files and
|
||||
directories (those things that are prefixed with a `.`, such as `.env` or
|
||||
`.config/`). If some of your ignored files are also _hidden_ files, then they
|
||||
still won't show up in search. You'll need the `--hidden` flag as well.
|
||||
|
||||
```bash
|
||||
$ rg --no-ignore --hidden ENV_VAR_KEY
|
||||
```
|
||||
|
||||
A shorthand equivalent for that is `-uu`:
|
||||
|
||||
```bash
|
||||
$ rg -uu ENV_VAR_KEY
|
||||
```
|
||||
|
||||
See `man rg` for more details.
|
||||
@@ -1,34 +0,0 @@
|
||||
# List Files In A Single Column
|
||||
|
||||
The `ls` command lists out the files and directories in your current directory
|
||||
or a specified directory.
|
||||
|
||||
The standard output for an `ls` command expands the fit the width of your
|
||||
terminal screen.
|
||||
|
||||
```bash
|
||||
❯ ls *.(ts|js)
|
||||
cypress.config.ts postcss.config.js remix.config.js remix.env.d.ts server.ts tailwind.config.ts vitest.config.ts
|
||||
```
|
||||
|
||||
This might not always be the ideal way to view that output. For instance, the
|
||||
above example when shared as a code block like in this post ends up with
|
||||
horizontal scroll.
|
||||
|
||||
With the `-1` flag, we can tell `ls` to display the results in a single
|
||||
vertical column.
|
||||
|
||||
```bash
|
||||
❯ ls -1 *.(ts|js)
|
||||
cypress.config.ts
|
||||
postcss.config.js
|
||||
remix.config.js
|
||||
remix.env.d.ts
|
||||
server.ts
|
||||
tailwind.config.ts
|
||||
vitest.config.ts
|
||||
```
|
||||
|
||||
See `man ls` for more details.
|
||||
|
||||
[source](https://askubuntu.com/questions/954924/listing-files-in-directory-in-one-column)
|
||||
@@ -1,16 +0,0 @@
|
||||
# Print DateTime Represented By Unix Timestamp
|
||||
|
||||
A lot of tools and systems use a Unix timestamp to represent a point in time.
|
||||
It is the number of seconds since the Unix epoch (Jan 1, 1970). However, just
|
||||
looking at a timestamp like `1623867544` doesn't tell a human much. I can't
|
||||
mentally translate that to the date and time that it is representing.
|
||||
|
||||
The `date` utility can help. Give it the `-r` flag with the timestamp value (in
|
||||
seconds) and it will display the date and time in a human-readable format.
|
||||
|
||||
```bash
|
||||
❯ date -r '1623867544'
|
||||
Wed Jun 16 13:19:04 CDT 2021
|
||||
```
|
||||
|
||||
See `man date` for more details.
|
||||
@@ -1,33 +0,0 @@
|
||||
# Unrestrict Where ripgrep Searches
|
||||
|
||||
One of the conveniences of [`rg`
|
||||
(ripgrep)](https://github.com/BurntSushi/ripgrep) is that by default it doesn't
|
||||
search in places you probably don't want it to search. That means it ignores
|
||||
anything specified by your `.gitignore` file, it excludes hidden files and
|
||||
directories (dotfiles, e.g. `.git/` or `.env`), and it excludes binary files.
|
||||
|
||||
These restrictions can be incrementally undone as needed using the `-u` flag.
|
||||
|
||||
The `-u` flag on its own will remove the ignored files restriction. This is
|
||||
equivalent to the `--no-ignore` flag.
|
||||
|
||||
```bash
|
||||
$ rg -u pattern
|
||||
```
|
||||
|
||||
Adding an additional `u` (`-uu`) to that flag will remove both the ignored files and
|
||||
hidden files restrictions. This is a shorthand equivalent to both `--no-ignore`
|
||||
and `--hidden`.
|
||||
|
||||
```bash
|
||||
$ rg -uu pattern
|
||||
```
|
||||
|
||||
Adding one more `u` (`-uuu`) will additionally remove the binary file
|
||||
restriction. Equivalent to those other two flags plus `--text`.
|
||||
|
||||
```bash
|
||||
$ rg -uuu pattern
|
||||
```
|
||||
|
||||
[source](https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#automatic-filtering)
|
||||
@@ -1,31 +0,0 @@
|
||||
# Find The Location Of User Settings JSON File
|
||||
|
||||
There are a ton ways to customize and fine-tune VS Code. All of the settings
|
||||
have defaults which can then be customized on a workspace and user level.
|
||||
Typically you'll access these files within VS Code via the Command Palette. I
|
||||
was curious though: since it is just a JSON file, surely I can view and modify
|
||||
it with any editor.
|
||||
|
||||
On a Mac, the user-level `settings.json` file is located at
|
||||
`~/Library/Application\ Support/Code/User/settings.json`.
|
||||
|
||||
Open it up and you might see something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"window.zoomLevel": 1,
|
||||
"workbench.editor.showTabs": "single"
|
||||
}
|
||||
```
|
||||
|
||||
A handful of settings with specific overrides.
|
||||
|
||||
Feel free to edit what is in here or add other settings that you want to set.
|
||||
Just make sure you know what you're doing and that you set valid values.
|
||||
|
||||
The VS Code docs list [the locations of these files on other operating
|
||||
systems](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations).
|
||||
|
||||
[source](https://stackoverflow.com/questions/53840644/location-of-vs-code-preferences)
|
||||
@@ -1,18 +0,0 @@
|
||||
# Turn Off Display Of Tabs For Files
|
||||
|
||||
Each time a file is opened in VS Code, it appears as one of many toward the top
|
||||
of the window right above the text editor area. It shows the name of the file
|
||||
and the 'dirty' status. Over time as more and more files are opened and edited,
|
||||
the tabs accumulate and can only be viewed by horizontally scrolling left and
|
||||
right.
|
||||
|
||||
There are other ways to navigate to files besides selecting tabs of already
|
||||
opened files. So, if you'd like to reclaim some screen real estate and reduce
|
||||
the visual clutter of the tabs, you can turn them off.
|
||||
|
||||
Hit `Cmd+Shift+P`, then search for and go to _Preferences: Open Settings (UI)_.
|
||||
Search for _Show Tabs_ which will show up under _Workbench > Editor_.
|
||||
|
||||
The default is _Multiple_. You can switch it to _None_ to completely hide tabs.
|
||||
You can also set it to _Single_ which will display file name and path for the
|
||||
current file, but eliminate the clutter of all other tabs.
|
||||
@@ -1,29 +0,0 @@
|
||||
# Access 1Password Credential From CLI
|
||||
|
||||
With the `op` CLI, I can store things like API keys and secrets in my 1Password
|
||||
vault and then access them from the command line. This assumes I've already
|
||||
installed the CLI (`brew install 1password-cli`) and connected it to the
|
||||
1Password app via the _Developer_ settings.
|
||||
|
||||
The `op item get` command takes a credential name and returns all the details
|
||||
for the entry with that _Title_. Here is how I can access my _Anthropic Claude
|
||||
API Key_ details.
|
||||
|
||||
```bash
|
||||
$ op item get "Anthropic Claude API Key"
|
||||
```
|
||||
|
||||
With the `--field` flag, I can grab a specific field, such as the `credential`,
|
||||
from that entry.
|
||||
|
||||
```bash
|
||||
$ op item get "Anthropic Claude API Key" --field credential
|
||||
sk-ant-api03-abc......xyz
|
||||
```
|
||||
|
||||
A command like this can be embedded in other commands as a way of referencing
|
||||
secrets without explicitly entering them into your shell history.
|
||||
|
||||
```bash
|
||||
$ curl https://api -H "x-api-key: $(op item get "Anthropic Claude API Key" --field credential)" ...
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
# Add Subtitles To Existing Mux Video Asset
|
||||
|
||||
There is a [JavaScript/TypeScript Mux
|
||||
SDK](https://github.com/muxinc/mux-node-sdk) for interacting with Mux via their
|
||||
[API](https://docs.mux.com/api-reference).
|
||||
|
||||
With a Mux client (initialized from `MUX_ACCESS_TOKEN` and `MUX_SECRET_KEY` in
|
||||
our environment), we can destructure `Video` which will allow us to call
|
||||
`Video.Assets.createTrack`. We pass that function the ID of an existing video
|
||||
asset and then an object of data defining the subtitle track. In this case, it
|
||||
is an English subtitle track. We need to point Mux to a `url` where the WebVTT
|
||||
formatted subtitles are hosted.
|
||||
|
||||
```typescript
|
||||
// add-srt-to-specific-video.ts
|
||||
|
||||
import Mux from '@mux/mux-node'
|
||||
|
||||
require('dotenv-flow').config({
|
||||
default_node_env: 'development',
|
||||
})
|
||||
|
||||
const assetId = "mux-asset-id" // set this value
|
||||
const srtUrl = "https://public-webvtt-file" // set this value
|
||||
|
||||
// Set up Mux API Client
|
||||
const MUX_ACCESS_TOKEN = process.env.MUX_ACCESS_TOKEN as string
|
||||
const MUX_SECRET_KEY = process.env.MUX_SECRET_KEY as string
|
||||
const muxClient = new Mux(MUX_ACCESS_TOKEN, MUX_SECRET_KEY)
|
||||
const {Video} = muxClient
|
||||
|
||||
await Video.Assets.createTrack(assetId, {
|
||||
url: srtUrl,
|
||||
type: 'text',
|
||||
text_type: 'subtitles',
|
||||
closed_captions: false,
|
||||
language_code: 'en-US',
|
||||
name: 'English',
|
||||
passthrough: 'English',
|
||||
})
|
||||
```
|
||||
|
||||
We can run the above script with `ts-node` from the command-line like so:
|
||||
|
||||
```bash
|
||||
$ npx ts-node --files --skipProject add-srt-to-specific-video.ts
|
||||
```
|
||||
|
||||
We'll see the new track on the existing asset in the Mux dashboard.
|
||||
Reference in New Issue
Block a user