1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-03 23:28:02 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Nicholas Wilson
c525d217eb Merge 5615da920f into 4ba53dca7d 2025-04-08 15:29:56 -05:00
jbranchaud
4ba53dca7d Add Create And Execute SQL Statements With \gexec as a Postgres TIL 2025-04-07 17:52:13 -05:00
jbranchaud
571f465fe6 Fix some typos in an old git TIL 2025-04-07 17:18:05 -05:00
jbranchaud
a547b9cee2 Add Create A Filename With The Current Date as a Unix TIL 2025-04-02 09:26:38 -05:00
jbranchaud
99ce5aee7b Add Bypass On-Save Tooling When Writing File as a Vim TIL 2025-04-01 10:56:25 -05:00
jbranchaud
60b6aa40ad Add Open Current Tab In New Window With Vimium as a Chrome TIL 2025-03-31 10:18:13 -05:00
jbranchaud
f97634a61e Add Have Script ShellCheck Itself When Executing as a Unix TIL 2025-03-29 09:20:39 -05:00
jbranchaud
34ba60d313 Add List RDS Snapshots With Matching Identifier Prefix as an AWS TIL 2025-03-28 11:25:31 -05:00
jbranchaud
3a178e901e Add Filter ActiveModel Validation Errors as a Rails TIL 2025-03-27 10:41:35 -05:00
Bob Conan
5615da920f Update README.md, fix typos 2024-11-15 16:16:31 -06:00
BobConanDev
c60c63f554 Updated README.md, fix typo(s) 2024-11-15 16:42:57 -05:00
9 changed files with 285 additions and 6 deletions

View File

@@ -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)

View 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)

View 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.

View File

@@ -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.

View 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)
```

View 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.

View 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
```

View 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.
```

View 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.