mirror of
https://github.com/jbranchaud/til
synced 2026-01-20 15:38:02 +00:00
Compare commits
1 Commits
d3f211773a
...
ab36b2e467
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab36b2e467 |
@@ -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).
|
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||||
|
|
||||||
_1634 TILs and counting..._
|
_1628 TILs and counting..._
|
||||||
|
|
||||||
See some of the other learning resources I work on:
|
See some of the other learning resources I work on:
|
||||||
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
||||||
@@ -114,7 +114,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
- [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md)
|
- [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md)
|
||||||
- [Find And Follow Server Logs](aws/find-and-follow-server-logs.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)
|
- [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)
|
- [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)
|
- [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md)
|
||||||
@@ -135,7 +134,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md)
|
- [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md)
|
||||||
- [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.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)
|
- [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)
|
- [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)
|
- [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md)
|
||||||
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
||||||
@@ -1037,7 +1035,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.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 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)
|
- [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)
|
- [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 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)
|
- [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md)
|
||||||
@@ -1542,7 +1539,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Count The Number Of ripgrep Pattern Matches](unix/count-the-number-of-ripgrep-pattern-matches.md)
|
- [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)
|
- [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 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)
|
- [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)
|
- [Curl With Cookies](unix/curl-with-cookies.md)
|
||||||
- [Curling For Headers](unix/curling-for-headers.md)
|
- [Curling For Headers](unix/curling-for-headers.md)
|
||||||
@@ -1590,7 +1586,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
- [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 Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.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)
|
- [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 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)
|
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
||||||
@@ -1701,7 +1696,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
||||||
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
||||||
- [Build And Install A Go Program](vim/build-and-install-a-go-program.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-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md)
|
||||||
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
||||||
- [Center The Cursor](vim/center-the-cursor.md)
|
- [Center The Cursor](vim/center-the-cursor.md)
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Create A Filename With The Current Date
|
|
||||||
|
|
||||||
I was recently working on a script to pull a scrubbed database dump using the
|
|
||||||
`pg_dump` Postgres utility. Ultimately, the script does something like this to
|
|
||||||
dump a remote database to a local file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_dump \
|
|
||||||
-h host.region.rds.amazonaws.com \
|
|
||||||
-U db_username \
|
|
||||||
-d db_name \
|
|
||||||
-F c \
|
|
||||||
-f scrubbed-database-$(date +%Y-%m-%d).dump
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice the last part of that command where we define the name of the dump file.
|
|
||||||
It has a `$(...)` that is used to run and interpolate a command as part of the
|
|
||||||
filename.
|
|
||||||
|
|
||||||
Here is that `date` command run on its own:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ date +%Y-%m-%d
|
|
||||||
2025-04-02
|
|
||||||
```
|
|
||||||
|
|
||||||
In the above command, that would mean if I were to run it today, I'd get
|
|
||||||
`scrubbed-database-2025-04-02.dump`.
|
|
||||||
|
|
||||||
This approach can be used with any command where you are producing a file that
|
|
||||||
you want to be dated or timestamped.
|
|
||||||
|
|
||||||
Here is another example that incorporates the time as well:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ touch $(date +%Y%m%d_%H%M%S)-migration.sql
|
|
||||||
# => 20250402_092442-migration.sql
|
|
||||||
```
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# Have Script ShellCheck Itself When Executing
|
|
||||||
|
|
||||||
The [ShellCheck](https://www.shellcheck.net/) utility can be run against bash
|
|
||||||
scripts to check if there are any warnings or errors we should fix. It works
|
|
||||||
great as long as we remember to run it.
|
|
||||||
|
|
||||||
I wondered if I could make it easier on myself by not having to remember to run
|
|
||||||
it. What if my bash script were to `shellcheck` itself?
|
|
||||||
|
|
||||||
Here is an example script where at the beginning it looks for and runs the
|
|
||||||
`shellcheck` utility against `$0` (the path of the script). This is kind of
|
|
||||||
meta. As the script is executing, it has an external program run against the
|
|
||||||
entire contents of itself. If there are any `shellcheck` issues, they get
|
|
||||||
displayed and the program exits early.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Exit immediately if any command fails
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Self-validation using ShellCheck
|
|
||||||
if command -v shellcheck &> /dev/null; then
|
|
||||||
echo "Validating script with ShellCheck..."
|
|
||||||
|
|
||||||
# $0 refers to the script itself
|
|
||||||
if ! shellcheck "$0"; then
|
|
||||||
echo "ShellCheck found issues in the script. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Script validation passed."
|
|
||||||
else
|
|
||||||
echo "Warning: ShellCheck not found. Skipping validation."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Script execution continuing..."
|
|
||||||
|
|
||||||
# shellcheck warning here
|
|
||||||
read -p "Continue with current operation? (yes/no): " CONTINUE_WITH_EXISTING
|
|
||||||
if [[ ! "$CONTINUE_WITH_EXISTING" =~ ^[Yy][Ee][Ss]$ ]]; then
|
|
||||||
echo "Operation cancelled."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
This last bit of the script with the `read` command will trigger a warning from
|
|
||||||
`shellcheck`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ./check.sh
|
|
||||||
Validating script with ShellCheck...
|
|
||||||
|
|
||||||
In ./check.sh line 23:
|
|
||||||
read -p "Continue with current operation? (yes/no): " CONTINUE_WITH_EXISTING
|
|
||||||
^--^ SC2162 (info): read without -r will mangle backslashes.
|
|
||||||
|
|
||||||
For more information:
|
|
||||||
https://www.shellcheck.net/wiki/SC2162 -- read without -r will mangle backs...
|
|
||||||
ShellCheck found issues in the script. Exiting.
|
|
||||||
```
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Bypass On-Save Tooling When Writing File
|
|
||||||
|
|
||||||
Every once in a while I run into an issue where my code formatters or linters
|
|
||||||
are misconfigured for a project. I try to save a file and it applies formatting
|
|
||||||
that I don't want. Or in an extreme case, the error ouput of the tool is what
|
|
||||||
overwrites the file.
|
|
||||||
|
|
||||||
I need to troubleshoot my dev tooling eventually, but I don't want to get
|
|
||||||
sidetracked at the moment. I just want to save the file. What can I do?
|
|
||||||
|
|
||||||
Tools like linters and code formatters are typically hooked up to Vim via
|
|
||||||
autocommands on certain actions like `FileWrite*` or `BufWrite*`. We can
|
|
||||||
execute a Vim command like writing a file (`w`) while disregarding autocommands
|
|
||||||
like so:
|
|
||||||
|
|
||||||
```vim
|
|
||||||
:noautocmd w
|
|
||||||
```
|
|
||||||
|
|
||||||
or, write and quit:
|
|
||||||
|
|
||||||
```vim
|
|
||||||
:noautocmd wq
|
|
||||||
```
|
|
||||||
|
|
||||||
This disables all autocommands for this one command. The file gets saved and
|
|
||||||
the misconfigured formatters and linters don't clobber the changes you
|
|
||||||
intended.
|
|
||||||
|
|
||||||
See `:h noautocmd` for more details.
|
|
||||||
Reference in New Issue
Block a user