mirror of
https://github.com/jbranchaud/til
synced 2026-01-08 01:28:02 +00:00
Compare commits
8 Commits
ab36b2e467
...
d3f211773a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3f211773a | ||
|
|
a547b9cee2 | ||
|
|
99ce5aee7b | ||
|
|
60b6aa40ad | ||
|
|
f97634a61e | ||
|
|
34ba60d313 | ||
|
|
3a178e901e | ||
|
|
460473a87f |
@@ -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).
|
||||||
|
|
||||||
_1628 TILs and counting..._
|
_1634 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,6 +114,7 @@ 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)
|
||||||
@@ -134,6 +135,7 @@ 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)
|
||||||
@@ -1035,6 +1037,7 @@ 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)
|
||||||
@@ -1539,6 +1542,7 @@ 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)
|
||||||
@@ -1586,6 +1590,7 @@ 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)
|
||||||
@@ -1696,6 +1701,7 @@ 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)
|
||||||
|
|||||||
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)
|
||||||
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.
|
||||||
@@ -15,10 +15,8 @@ const remove = (items,index) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const list = [1,2,3,4,5];
|
const list = [1,2,3,4,5];
|
||||||
remove(list, 2);
|
remove(list, 2); // [1,2,4,5]
|
||||||
// [1,2,3,4]
|
// list still [1,2,3,4,5]
|
||||||
list
|
|
||||||
// [1,2,3,4,5]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It only took a couple lines of code and immutability is baked in.
|
It only took a couple lines of code and immutability is baked in.
|
||||||
|
|||||||
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.
|
||||||
38
unix/create-a-filename-with-the-current-date.md
Normal file
38
unix/create-a-filename-with-the-current-date.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
60
unix/have-script-shellcheck-itself-when-executing.md
Normal file
60
unix/have-script-shellcheck-itself-when-executing.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# 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.
|
||||||
|
```
|
||||||
30
vim/bypass-on-save-tooling-when-writing-file.md
Normal file
30
vim/bypass-on-save-tooling-when-writing-file.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# 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