mirror of
https://github.com/jbranchaud/til
synced 2026-01-03 23:28:02 +00:00
Compare commits
11 Commits
65b39fa25a
...
c525d217eb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c525d217eb | ||
|
|
4ba53dca7d | ||
|
|
571f465fe6 | ||
|
|
a547b9cee2 | ||
|
|
99ce5aee7b | ||
|
|
60b6aa40ad | ||
|
|
f97634a61e | ||
|
|
34ba60d313 | ||
|
|
3a178e901e | ||
|
|
5615da920f | ||
|
|
c60c63f554 |
13
README.md
13
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).
|
||||
|
||||
_1628 TILs and counting..._
|
||||
_1635 TILs and counting..._
|
||||
|
||||
See some of the other learning resources I work on:
|
||||
- [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)
|
||||
- [Find And Follow Server Logs](aws/find-and-follow-server-logs.md)
|
||||
- [List RDS Snapshots With Matching Identifier Prefix](aws/list-rds-snapshots-with-matching-identifier-prefix.md)
|
||||
- [Output CLI Results In Different Formats](aws/output-cli-results-in-different-formats.md)
|
||||
- [Sign Up User With Email And Password](aws/sign-up-user-with-email-and-password.md)
|
||||
- [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md)
|
||||
@@ -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)
|
||||
- [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.md)
|
||||
- [Keybinding To Focus The Address Bar](chrome/keybinding-to-focus-the-address-bar.md)
|
||||
- [Open Current Tab In New Window With Vimium](chrome/open-current-tab-in-new-window-with-vimium.md)
|
||||
- [Pause JavaScript From The Source DevTools Panel](chrome/pause-javascript-from-the-source-devtools-panel.md)
|
||||
- [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md)
|
||||
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
||||
@@ -208,7 +210,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Aliasing An Ansible Host](devops/aliasing-an-ansible-host.md)
|
||||
- [Allow Cross-Origin Requests To Include Cookies](devops/allow-cross-origin-requests-to-include-cookies.md)
|
||||
- [Allow HTTPS Through Your UFW Firewall](devops/allow-https-through-your-ufw-firewall.md)
|
||||
- [Check For Cached Site Assocation File For iOS](devops/check-for-cached-site-association-file-for-ios.md)
|
||||
- [Check For Cached Site Association File For iOS](devops/check-for-cached-site-association-file-for-ios.md)
|
||||
- [Check The Status of All Services](devops/check-the-status-of-all-services.md)
|
||||
- [Check The Syntax Of nginx Files](devops/check-the-syntax-of-nginx-files.md)
|
||||
- [Connect To An RDS PostgreSQL Database](devops/connect-to-an-rds-postgresql-database.md)
|
||||
@@ -797,7 +799,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [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)
|
||||
- [Check Table For Any Orphaned Records](postgres/check-table-for-any-orphaned-records.md)
|
||||
- [Checking Inequality](postgres/checking-inequality.md)
|
||||
- [Checking The Type Of A Value](postgres/checking-the-type-of-a-value.md)
|
||||
- [Clear The Screen In psql](postgres/clear-the-screen-in-psql.md)
|
||||
@@ -818,6 +820,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Create A Table From The Structure Of Another](postgres/create-a-table-from-the-structure-of-another.md)
|
||||
- [Create An Index Across Two Columns](postgres/create-an-index-across-two-columns.md)
|
||||
- [Create An Index Without Locking The Table](postgres/create-an-index-without-locking-the-table.md)
|
||||
- [Create And Execute SQL Statements With \gexec](postgres/create-and-execute-sql-statements-with-gexec.md)
|
||||
- [Create Database Uses Template1](postgres/create-database-uses-template1.md)
|
||||
- [Create hstore From Two Arrays](postgres/create-hstore-from-two-arrays.md)
|
||||
- [Create Table Adds A Data Type](postgres/create-table-adds-a-data-type.md)
|
||||
@@ -1035,6 +1038,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 Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
||||
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
||||
- [Filter ActiveModel Validation Errors](rails/filter-active-model-validation-errors.md)
|
||||
- [Filter ActiveStorage Blobs To Only Images](rails/filter-active-storage-blobs-to-only-images.md)
|
||||
- [Find Or Create A Record With FactoryBot](rails/find-or-create-a-record-with-factory-bot.md)
|
||||
- [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md)
|
||||
@@ -1539,6 +1543,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 Words On A Webpage](unix/count-the-number-of-words-on-a-webpage.md)
|
||||
- [Create A File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md)
|
||||
- [Create A Filename With The Current Date](unix/create-a-filename-with-the-current-date.md)
|
||||
- [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.md)
|
||||
- [Curl With Cookies](unix/curl-with-cookies.md)
|
||||
- [Curling For Headers](unix/curling-for-headers.md)
|
||||
@@ -1586,6 +1591,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 With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
||||
- [Have Script ShellCheck Itself When Executing](unix/have-script-shellcheck-itself-when-executing.md)
|
||||
- [Hexdump A Compiled File](unix/hexdump-a-compiled-file.md)
|
||||
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
||||
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
||||
@@ -1696,6 +1702,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
||||
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
||||
- [Build And Install A Go Program](vim/build-and-install-a-go-program.md)
|
||||
- [Bypass On-Save Tooling When Writing File](vim/bypass-on-save-tooling-when-writing-file.md)
|
||||
- [Case-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md)
|
||||
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
||||
- [Center The Cursor](vim/center-the-cursor.md)
|
||||
|
||||
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.
|
||||
@@ -9,10 +9,10 @@ test runs. Most of these files are tracked (already checked in to the
|
||||
repository). There are also many new files generated as part of the most recent
|
||||
test run.
|
||||
|
||||
I want to staging the changes to files that are already tracked, but hold off
|
||||
on doing anything with the new files.
|
||||
I want to stage the changes to files that are already tracked, but hold off on
|
||||
doing anything with the new files.
|
||||
|
||||
Running `git add spec/cassettes` won't do the track because that will pull in
|
||||
Running `git add spec/cassettes` won't do the trick because that will pull in
|
||||
everything. Running `git add --patch spec/cassettes` will take long and be
|
||||
tedious. Instead what I want is the `-u` flag. It's short for _update_ which
|
||||
means it will only stage already tracked files.
|
||||
|
||||
58
postgres/create-and-execute-sql-statements-with-gexec.md
Normal file
58
postgres/create-and-execute-sql-statements-with-gexec.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Create And Execute SQL Statements With \gexec
|
||||
|
||||
The [`\gexec`
|
||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-GEXEC)
|
||||
is a variation of the [`\g`
|
||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-G),
|
||||
both of which can be used in a `psql` session. Whereas the `\g` command sends
|
||||
the current query in the buffer to the PostgreSQL server for execution, the
|
||||
`\gexec` command first sends the query to the server for execution and then
|
||||
executes each row of the result as its own SQL statement.
|
||||
|
||||
This is both a bit absurd and powerful. And a bit unnecessary considering all
|
||||
of the scripting capabilities with anything from bash to any language with a
|
||||
SQL client library.
|
||||
|
||||
Nevertheless, let's take a look at a contrived example of how it works. Here,
|
||||
we have a SQL statement that does some string concatenation based off values in
|
||||
an array. This results in three separate `create schema` statements.
|
||||
|
||||
```sql
|
||||
> select
|
||||
'create schema if not exists schema_' || letter || ';'
|
||||
from unnest(array['a', 'b', 'c']) as letter
|
||||
\gexec
|
||||
|
||||
CREATE SCHEMA
|
||||
CREATE SCHEMA
|
||||
CREATE SCHEMA
|
||||
|
||||
> \dn
|
||||
List of schemas
|
||||
Name | Owner
|
||||
----------+-------------------
|
||||
public | pg_database_owner
|
||||
schema_a | postgres
|
||||
schema_b | postgres
|
||||
schema_c | postgres
|
||||
(4 rows)
|
||||
```
|
||||
|
||||
Three new schemas get created which we can inspect with `\dn`.
|
||||
|
||||
Notice, if we simply execute the primary statement, we can see the intermediate
|
||||
result that `\gexec` will subsequently execute.
|
||||
|
||||
```sql
|
||||
> select
|
||||
'create schema if not exists schema_' || letter || ';'
|
||||
from unnest(array['a', 'b', 'c']) as letter
|
||||
\g
|
||||
|
||||
?column?
|
||||
---------------------------------------
|
||||
create schema if not exists schema_a;
|
||||
create schema if not exists schema_b;
|
||||
create schema if not exists schema_c;
|
||||
(3 rows)
|
||||
```
|
||||
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