1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-15 21:18:02 +00:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Marcus Wyatt
e996a2fcfb Merge cff6592c4e into 77f3c6a43d 2024-12-08 14:19:56 -05:00
jbranchaud
77f3c6a43d Add Synchronize Vim Clipboard With System Clipboad as a VSCode TIL 2024-12-07 10:18:29 -06:00
jbranchaud
bf6a10e2cd Add Combine Two Slices as a Go TIL 2024-12-06 14:45:33 -06:00
jbranchaud
5083c8e9f1 Add Output Explain Query Plan In Different Formats as a Postgres TIL 2024-12-05 20:50:35 -06:00
jbranchaud
a4c67c33a3 Add List TXT DNS Records For A Domain as a Unix TIL 2024-12-05 18:01:18 -06:00
jbranchaud
dce54bd689 Fix codeblock in recent TIL 2024-12-04 17:20:38 -06:00
jbranchaud
f1cc33fe40 Add Parse Flags From CLI Arguments as a Go TIL 2024-12-04 15:38:02 -06:00
jbranchaud
9dcd9daf0a Add Verify Site Ownership With DNS Record as internet TIL 2024-12-03 12:46:02 -06:00
jbranchaud
9afe6503ec Add Fetch Specific Number Of Results as a Postgres TIL 2024-12-03 08:52:38 -06:00
jbranchaud
b376f32a67 Add Parse A String Into Individual Fields as a Go TIL 2024-12-02 18:49:14 -06:00
jbranchaud
3abfa92b64 Add Write System Clipboard To A File as a Mac TIL 2024-12-01 09:58:15 -06:00
jbranchaud
d086d3b943 Add Find System-wide Config File For User as a jj TIL 2024-11-30 14:38:52 -06:00
jbranchaud
f64257f02c Add Organize Pages In Route Groups as a Next.js TIL 2024-11-29 12:21:50 -06:00
jbranchaud
b329d36888 Add Block Syntaxes Have Different Precedence as a Ruby TIL 2024-11-28 14:06:07 -06:00
jbranchaud
e0db60f6ce Add Colocate jj And git Directories For Project as a jj TIL 2024-11-27 08:41:23 -06:00
jbranchaud
5c81ddc151 Add Type Fewer Paths With Brace Expansion as a Unix TIL 2024-11-26 17:55:12 -06:00
jbranchaud
6af86bd407 Add Stack Heredocs In A Method Call as a Ruby TIL 2024-11-25 16:00:16 -06:00
jbranchaud
98d8249cf1 Add Get Latest Commit Timestamp For A File as a Git TIL 2024-11-24 23:37:16 -06:00
jbranchaud
7f1c243310 Add Gather Positional Arguments In Method Definition as a Ruby TIL 2024-11-23 10:11:49 -06:00
Marcus Wyatt
cff6592c4e docs: add call to minmax in example 2022-06-29 05:05:10 -07:00
19 changed files with 715 additions and 1 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).
_1514 TILs and counting..._
_1531 TILs and counting..._
---
@@ -41,6 +41,7 @@ _1514 TILs and counting..._
* [Internet](#internet)
* [Java](#java)
* [JavaScript](#javascript)
* [jj](#jj)
* [jq](#jq)
* [Kitty](#kitty)
* [Linux](#linux)
@@ -310,6 +311,7 @@ _1514 TILs and counting..._
- [Find And Remove Files That Match A Name](git/find-and-remove-files-that-match-a-name.md)
- [Find The Date That A File Was Added To The Repo](git/find-the-date-that-a-file-was-added-to-the-repo.md)
- [Find The Initial Commit](git/find-the-initial-commit.md)
- [Get Latest Commit Timestamp For A File](git/get-latest-commit-timestamp-for-a-file.md)
- [Get The Name Of The Current Branch](git/get-the-name-of-the-current-branch.md)
- [Get The Short Version Of The Latest Commit](git/get-the-short-version-of-the-latest-commit.md)
- [Grab A Single File From A Stash](git/grab-a-single-file-from-a-stash.md)
@@ -397,9 +399,12 @@ _1514 TILs and counting..._
- [Access Go Docs Offline](go/access-go-docs-offline.md)
- [Build For A Specific OS And Architecture](go/build-for-a-specific-os-and-architecture.md)
- [Combine Two Slices](go/combine-two-slices.md)
- [Do Something N Times](go/do-something-n-times.md)
- [Find Executables Installed By Go](go/find-executables-installed-by-go.md)
- [Not So Random](go/not-so-random.md)
- [Parse A String Into Individual Fields](go/parse-a-string-into-individual-fields.md)
- [Parse Flags From CLI Arguments](go/parse-flags-from-cli-arguments.md)
- [Replace The Current Process With An External Command](go/replace-the-current-process-with-an-external-command.md)
- [Sleep For A Duration](go/sleep-for-a-duration.md)
- [Upgrading From An Older Version On Mac](go/upgrading-from-an-older-version-on-mac.md)
@@ -455,6 +460,7 @@ _1514 TILs and counting..._
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
- [Search Tweets By Author](internet/search-tweets-by-author.md)
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
- [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md)
### Java
@@ -568,6 +574,11 @@ _1514 TILs and counting..._
- [Yarn Commands Without The Emojis](javascript/yarn-commands-without-the-emojis.md)
- [Yup Schemas Are Validated Asynchronously](javascript/yup-schemas-are-validated-asynchronously.md)
### jj
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
### jq
- [Combine An Array Of Objects Into A Single Object](jq/combine-an-array-of-objects-into-a-single-object.md)
@@ -630,6 +641,7 @@ _1514 TILs and counting..._
- [Specify App When Opening From Command Line](mac/specify-app-when-opening-from-command-line.md)
- [Use Default Screenshot Shortcuts With CleanShot X](mac/use-default-screenshot-shortcuts-with-cleanshot-x.md)
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
- [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md)
### MongoDB
@@ -680,6 +692,7 @@ _1514 TILs and counting..._
- [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)
- [Organize Pages In Route Groups](nextjs/organize-pages-in-route-groups.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)
@@ -767,6 +780,7 @@ _1514 TILs and counting..._
- [Escaping String Literals With Dollar Quoting](postgres/escaping-string-literals-with-dollar-quoting.md)
- [Export Query Results To A CSV](postgres/export-query-results-to-a-csv.md)
- [Extracting Nested JSON Data](postgres/extracting-nested-json-data.md)
- [Fetch Specific Number Of Results](postgres/fetch-specific-number-of-results.md)
- [Find Duplicate Records In Table Without Unique Id](postgres/find-duplicate-records-in-table-without-unique-id.md)
- [Find Records That Contain Duplicate Values](postgres/find-records-that-contain-duplicate-values.md)
- [Find Records That Have Multiple Associated Records](postgres/find-records-that-have-multiple-associated-records.md)
@@ -815,6 +829,7 @@ _1514 TILs and counting..._
- [Manage Major Versions With Brew And Direnv](postgres/manage-major-versions-with-brew-and-direnv.md)
- [Max Identifier Length Is 63 Bytes](postgres/max-identifier-length-is-63-bytes.md)
- [Open Heroku Database In Postico From Terminal](postgres/open-heroku-database-in-postico-from-terminal.md)
- [Output Explain Query Plan In Different Formats](postgres/output-explain-query-plan-in-different-formats.md)
- [pg Prefix Is Reserved For System Schemas](postgres/pg-prefix-is-reserved-for-system-schemas.md)
- [Postgres Does Not Support Unsigned Integers](postgres/postgres-does-not-support-unsigned-integers.md)
- [Prepare, Execute, And Deallocate Statements](postgres/prepare-execute-and-deallocate-statements.md)
@@ -1182,6 +1197,7 @@ _1514 TILs and counting..._
- [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)
- [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md)
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
- [Chaining Multiple RSpec Change Matchers](ruby/chaining-multiple-rspec-change-matchers.md)
- [Check For Any Overlaps In List Of Ranges](ruby/check-for-any-overlaps-in-list-of-ranges.md)
@@ -1225,6 +1241,7 @@ _1514 TILs and counting..._
- [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)
- [Forward All Arguments To Another Method](ruby/forward-all-arguments-to-another-method.md)
- [Gather Positional Arguments In Method Definition](ruby/gather-positional-arguments-in-method-definition.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)
@@ -1292,6 +1309,7 @@ _1514 TILs and counting..._
- [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)
- [Stack Heredocs In A Method Call](ruby/stack-heredocs-in-a-method-call.md)
- [String Interpolation With Instance Variables](ruby/string-interpolation-with-instance-variables.md)
- [Summing Collections](ruby/summing-collections.md)
- [Triple Equals: The Case Equality Operator](ruby/triple-equals-the-case-equality-operator.md)
@@ -1495,6 +1513,7 @@ _1514 TILs and counting..._
- [List Stats For A File](unix/list-stats-for-a-file.md)
- [List The Available JDKs](unix/list-the-available-jdks.md)
- [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md)
- [List TXT DNS Records For A Domain](unix/list-txt-dns-records-for-a-domain.md)
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
@@ -1537,6 +1556,7 @@ _1514 TILs and counting..._
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
- [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)
- [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md)
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
@@ -1728,6 +1748,7 @@ _1514 TILs and counting..._
- [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)
- [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.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)

View File

@@ -0,0 +1,25 @@
# Get Latest Commit Timestamp For A File
The `git log` command can tell you all the commits that touched a file. That
can be narrowed down to the latest commit for that file with the `-1` flag. The
commit that it reports can then be further formatted to with the `--format`
flag.
The `%ai` format pattern gives the date the commit was authored in an ISO
8601-like format. The `%aI` (capital `I`) gives the date the commit was
authored strictly in the ISO 8601 format.
Here are examples of both side by side:
```bash
git log -1 --format=%ai -- README.md
2024-10-15 13:59:09 -0500
git log -1 --format=%aI -- README.md
2024-10-15T13:59:09-05:00
```
I made use of this in a script where I needed to get an idea of when various
files were most recently modified.
See `man git-log` and the `PRETTY FORMATS` section for more details.

51
go/combine-two-slices.md Normal file
View File

@@ -0,0 +1,51 @@
# Combine Two Slices
The `append` function can be used to create a new slice with the contents of
the given slice and one or more items added to the end.
We can add one or more items like so:
```go
s1 := []int{1, 2, 3, 4}
s2 := append(s1, 5)
s3 := append(s2, 6, 7, 8)
fmt.Println(s1) //=> [1 2 3 4]
fmt.Println(s2) //=> [1 2 3 4 5]
fmt.Println(s3) //=> [1 2 3 4 5 6 7 8]
```
But what if we have a second slice instead of individual items? We could import
`slices` and use its `Concat` function. Or we can stick with `append` and
unpack that slice as a series of arguments into the second part of `append`
using `slice...`.
```go
s4 := append(s2, s1...)
fmt.Println(s4) //=> [1 2 3 4 5 1 2 3 4]
```
Here is the full example:
```go
package main
import (
"fmt"
)
func main() {
s1 := []int{1, 2, 3, 4}
s2 := append(s1, 5)
s3 := append(s2, 6, 7, 8)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
s4 := append(s2, s1...)
fmt.Println(s4)
}
```
[source](https://pkg.go.dev/builtin#append)

View File

@@ -0,0 +1,39 @@
# Parse A String Into Individual Fields
Let's say you're reading in data from a file or otherwise dealing with an
arbitrary string of data. If that string has a series of values separated by
whitespace, you can parse it into individual fields with
[`strings.Fields`](https://pkg.go.dev/strings#Fields).
```go
import (
"fmt"
"strings"
)
func main() {
data := "3 5 2 6 7 1 9"
fields := strings.Fields(data)
fmt.Printf("Fields: %v", fields)
// [3 5 2 6 7 1 9]
}
```
Here is another example where we can see that `strings.Fields` deals with
multiple whitespace and surrounding whitespace:
```go
import (
"fmt"
"strings"
)
func main() {
data := " go java c++ rust "
fields := strings.Fields(data)
fmt.Printf("%v", fields)
// [go java c++ rust]
}
```

View File

@@ -0,0 +1,65 @@
# Parse Flags From CLI Arguments
Though we can grab the arguments to a Go program from `os.Args`, it requires
some manual parsing. With the built-in `flag` package, we can declare specific
flags our program accepts, by type. When we parse them, they will be separated
out from the rest of the positional arguments.
Here is an example of the program that accepts a boolean `debug` flag. This
will work with either `-debug` or `--debug`.
```go
package main
import (
"flag"
"fmt"
"os"
)
func main() {
var debug bool
flag.BoolVar(&debug, "debug", false, "turns on debug mode, extra logging")
flag.Parse()
positionalArgs := flag.Args()
if len(positionalArgs) < 1 {
fmt.Println("Please specify which part to run: 1 or 2")
os.Exit(1)
}
if debug {
fmt.Println("We are in debug mode...")
fmt.Println("Received the following argument:", positionalArgs[0])
}
// ...
}
```
We can run the program in debug mode like so:
```bash
$ go run . --debug 123
We are in debug mode...
Received the following argument: 123
```
We can also take advantage of the `help` flag that we get for free:
```bash
$ go run . --help
Usage of /var/folders/62/lx9pcjbs1zbd83zg6twwym2r0000gn/T/go-build3212087168/b001/exe/test:
-debug
turns on debug mode, extra logging
```
Note: any recognized flags need to come before any of the position arguments.
The `debug` flag won't be picked up if we run the program like this:
```bash
$ go run . 123 --debug
```
[source](https://pkg.go.dev/flag)

View File

@@ -0,0 +1,28 @@
# Verify Site Ownership With DNS Record
To run your site through Google Search Console and get detailed reports, you
need to verify that you own the site. There are several manual ways of doing
this that involve sticking a value unique to your URL in a file or header tag.
There is a better way though.
By adding a TXT DNS record wherever you domain's DNS is managed, you can prove
to Google that you own the domain. That verification applies to all paths and
subdomains of that domain.
Some providers like Cloudflare have a mostly-automated process for this that
Google can hook into as long as you grant permission via OAuth.
You can also manually create the TXT record if necessary.
Either way, it will look something like:
```bash
$ dig -t TXT visualmode.dev
;; ANSWER SECTION:
visualmode.dev. 377 IN TXT "google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-f
vblwtk"
```
More details are provided in the [Google Search Console
docs](https://support.google.com/webmasters/answer/9008080?hl=en#domain_name_verification).

View File

@@ -0,0 +1,59 @@
# Colocate jj And git Directories For Project
When doing a standard clone of a git repository with `jj`, you'll get a copy of
the project with a `.jj` directory containing the version control information.
```bash
$ jj git clone git@github.com:jbranchaud/my-repo
Fetching into new repo in "/path/of/local/repo"
...
$ exa --tree --all -L 1
.
├── .gitignore
├── .jj
├── Cargo.lock
├── Cargo.toml
└── src
```
This is fine if I'm completely familiar with using
[jujutsu](https://martinvonz.github.io/jj/latest/). However, if I'm coming from
`git` and still learning, then it would be nice to be able to fallback to
familiar `git` commands when needed.
But without a `.git` directory, I get this:
```bash
$ git log
fatal: not a git repository (or any of the parent directories): .git
```
When cloning a git repo with `jj`, I can instruct it to _colocate_ which means
that it will create both the `.jj` and the `.git` data directories in the
project.
```bash
$ jj git clone --colocate git@github.com:jbranchaud/my-repo
Fetching into new repo in "/path/of/local/repo"
...
$ exa --tree --all -L 1
.
├── .git
├── .gitignore
├── .jj
├── Cargo.lock
├── Cargo.toml
└── src
```
Now I can run `jj` commands or `git` commands:
```bash
$ git log
commit 0c72abbb83657096677f9a3d5ddc7bce20839165 (HEAD, origin/trunk, trunk)
...
```
[source](https://martinvonz.github.io/jj/latest/git-compatibility/#co-located-jujutsugit-repos)

View File

@@ -0,0 +1,25 @@
# Find System-wide Config File For User
The `jj` CLI can be configured in a couple different places. When I recently
ran a `jj config` command, I was curious where specifically it was getting set.
Those changes didn't appear in the repo's config (`./.jj/repo/config.toml`).
That makes sense since it would only apply to that repo. So, where is the
system-wide config file?
The following commond shows where on your machine it is located.
```bash
$ jj config path --user
/Users/jbranchaud/Library/Application Support/jj/config.toml
```
Now, the next time I set a config like this:
```bash
$ jj config set --user ui.paginate never
```
or want to check what other config options are set to, I can visit that path
and take a look.
[source](https://github.com/martinvonz/jj/blob/main/docs/config.md)

View File

@@ -0,0 +1,19 @@
# Write System Clipboard To A File
MacOS has two CLI utilities `pbcopy` and `pbpaste` which, respectively, copy
_to_ and paste _from_ the system clipboard via the CLI.
Let's say I've just copied a large block of text from somewhere onto my system
clipboard. I now want to paste that into a new file. Instead of creating a new
file, opening it up in my preferred editor, pasting all that text, and saving
the file, I can run one small command from the CLI.
```bash
$ pbpaste > data.txt
```
This redirects the contents of `pbpaste` (which is the system clipboard) into
the file `data.txt`. If that file doesn't already exist, then it will be
created before the data is written to it.
See `man pbpaste` for more details.

View File

@@ -0,0 +1,41 @@
# Organize Pages In Route Groups
With the Next.js App Router we can organize pages without affecting the URL
path structure by nesting those directories and pages within a _Route Group_. A
Route Group is directory where the name is surrounded by parentheses, e.g.
`/(symbols)`.
For instance, in my [Ruby Operator
Lookup](https://www.visualmode.dev/ruby-operators) project, I have the
following structure:
```bash
$ exa --true src/app/ruby-operators
src/app/ruby-operators
├── (symbols)
│ ├── ampersand
│ │ └── page.mdx
│ ├── arbitrary-keyword-arguments
│ │ └── page.mdx
│ ├── asterisk
│ │ └── page.mdx
│ ├── at-symbol
│ │ └── page.mdx
│ ├── backtick
│ │ └── page.mdx
│ ├── ...
│ └── underscore
│ └── page.mdx
├── client-layout.tsx
├── layout.tsx
├── page.tsx
└── wrapper.ts
```
I'm able to organize all the different symbols and operators under a separate
directory `/(symbol)/`. That makes development easier. However, the end result
routing still has each symbol located directly under `/ruby-operators/`, e.g.
`/ruby-operators/ampersand`.
[source](https://nextjs.org/docs/app/getting-started/project-structure#route-groups)

View File

@@ -0,0 +1,42 @@
# Fetch Specific Number Of Results
If you pull up just about any intro to PostgreSQL (or even SQL), one of the
first things they are going to teach you is the `limit` clause. This is taught
as _the_ way for limiting the result set to a specific number of rows.
```sql
> select title from books limit 4;
+-----------------------+
| title |
|-----------------------|
| The Secret History |
| A Gentleman in Moscow |
| Exhalation: Stores |
| Annihilation |
+-----------------------+
SELECT 4
```
You might be as surprised as I was to learn that `limit` is not part of the SQL
standard. It is extremely common for this use case, but the SQL standard
defines `fetch first N rows only` as the way to fetch a specific number of
rows. As we can see, [it works identically to `limit
N`](https://www.postgresql.org/docs/current/sql-select.html#SQL-LIMIT).
```sql
> select title from books fetch first 4 rows only;
+-----------------------+
| title |
|-----------------------|
| The Secret History |
| A Gentleman in Moscow |
| Exhalation: Stores |
| Annihilation |
+-----------------------+
SELECT 4
```
The `rows` and `row` keywords are interchangeable which makes statements more
readable if, for instance, you're doing `... fetch first 1 row only`.
[source](https://www.cybertec-postgresql.com/en/postgresql-limit-vs-fetch-first-rows-with-ties/)

View File

@@ -0,0 +1,57 @@
# Output Explain Query Plan In Different Formats
The output of an [`explain` (or `explain analyze`) query
plan](https://www.postgresql.org/docs/current/sql-explain.html) for a given
query defaults to a `TEXT` format that is meant to be read by a person.
```sql
> explain (analyze) select title from books where created_at > now() - '1 year'::interval;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..1.28 rows=5 width=32) (actual time=0.011..0.017 rows=22 loops=1)
Filter: (created_at > (now() - '1 year'::interval))
Planning Time: 0.052 ms
Execution Time: 0.027 ms
(4 rows)
```
If we instead want the query plan in a standardized format that is parseable
and readable by a program, we can specify an alternate format like `JSON`,
`YAML`, or `XML`.
Here is the same plan with `format json`:
```sql
> explain (analyze, format json) select title from books where created_at > now() - '1 year'::interval;
QUERY PLAN
----------------------------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Seq Scan", +
"Parallel Aware": false, +
"Async Capable": false, +
"Relation Name": "books", +
"Alias": "books", +
"Startup Cost": 0.00, +
"Total Cost": 1.28, +
"Plan Rows": 5, +
"Plan Width": 32, +
"Actual Startup Time": 0.008, +
"Actual Total Time": 0.014, +
"Actual Rows": 22, +
"Actual Loops": 1, +
"Filter": "(created_at > (now() - '1 year'::interval))",+
"Rows Removed by Filter": 0 +
}, +
"Planning Time": 0.050, +
"Triggers": [ +
], +
"Execution Time": 0.023 +
} +
]
(1 row)
```
I present all four formats for a complex query plan [in this
Gist](https://gist.github.com/jbranchaud/731b1a68f5cc70c4f7a9e1f5ef570836).

View File

@@ -0,0 +1,29 @@
# Block Syntaxes Have Different Precedence
There are two syntaxes for defining a block in Ruby. The semantically shorthand
syntax uses the curly braces (`{}`). The semantically multi-line syntax uses
`do` and `end`. For nearly all intents and purposes they are interchangable.
It is, however, worth noting that the `do`/`end` version has a lower precedence
than the already low precedence of `{}`. That said, you have to write some
weird code for this to become an issue.
Let's say we have two methods, `method_one` and `method_two`. They are both
called on the same line like below and then followed by a block argument. Which
method receives the block argument?
```ruby
method_one method_two { |n|
puts "Executing a block: #{n}"
}
method_one method_two do |n|
puts "Executing a block: #{n}"
end
```
In the first case, with the curly braces, `method_two` receives the block as an
argument. In the second case, with the `do`/`end`, `method_one` receives the
block as an argument.
[source](http://localhost:3131/ruby-operators/curly-braces#block-shorthand)

View File

@@ -21,5 +21,7 @@ at the same time when you call
```ruby
list = [3,7,4,15,9,1,2]
list.minmax
#=> [1,15]
```

View File

@@ -0,0 +1,55 @@
# Gather Positional Arguments In Method Definition
The `*` symbol can be used in Ruby in a method definition to gather up an
arbitrary number of positional arguments.
For instance, we can gather all positional arguments with this method
definition:
```ruby
def gather_all(*args)
puts args
end
```
Or we can isolate the first positional arg and then gather the rest:
```ruby
def first_and_rest(first, *rest)
puts "First: #{first}, Rest: #{rest}"
end
```
We can even do something a bit more interesting like isolating the first and
last arguments while gathering up everything else in the middle:
```ruby
def pop_parens(left, *middle, right)
if left != '(' || right != ')'
raise "Uh oh!"
else
if middle.size == 1
puts "Found: #{middle.first}"
else
pop_parens(*middle)
end
end
end
```
Here is what it looks like if we splat some different sets of arguments into
that method call:
```ruby
> tokens1 = "((((4))))".split('')
=> ["(", "(", "(", "(", "4", ")", ")", ")", ")"]
> tokens2 = "((4))))".split('')
=> ["(", "(", "4", ")", ")", ")", ")"]
> pop_parens(*tokens1)
Found: 4
=> nil
> pop_parens(*tokens2)
(irb):87:in `pop_parens': Uh oh! (RuntimeError)
```
[source](https://ruby-doc.org/3.3.6/syntax/methods_rdoc.html#label-Array-2FHash+Argument)

View File

@@ -0,0 +1,37 @@
# Stack Heredocs In A Method Call
When you put a heredoc directly in a method call as an argument, it is only the
opening identifier that goes in the argument list.
That looks like this:
```ruby
execute_in_transaction(<<~SQL)
update reading_statuses
set status = 'abandoned'
where started_at < (now() - '2 years'::interval)
and finished_at is null;
SQL
```
You might imagine then that we can put multiple heredocs in a method call. That
leads to [_stacked
heredocs_](https://www.visualmode.dev/ruby-operators/heredoc#stacked-heredocs).
```ruby
execute_in_transaction(<<~SQL1, <<~SQL2, <<~SQL3)
update reading_statuses
set status = 'abandoned'
where started_at < (now() - '2 years'::interval)
and finished_at is null;
SQL1
insert into activity_log (name, description)
values ('abandon_books', 'Mark unread books as abandoned');
SQL2
delete from background_jobs
where id = #{job_id}; -- better to sanitize values like this
SQL3
```
Notice we terminate the body of each heredoc with its closing identifier and
immediately begin the body of the next one.

View File

@@ -0,0 +1,43 @@
# List TXT DNS Records For A Domain
The `dig` command can be used to list specifically the `TXT` DNS records for a
domain using the `-t TXT` flag like so:
```bash
$ dig -t TXT visualmode.dev
; <<>> DiG 9.10.6 <<>> -t TXT visualmode.dev
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41226
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;visualmode.dev. IN TXT
;; ANSWER SECTION:
visualmode.dev. 377 IN TXT "v=spf1 include:_spf.mx.cloudflare.net ~all"
visualmode.dev. 377 IN TXT "google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-f
vblwtk"
;; Query time: 103 msec
;; SERVER: fe80::7c4b:26ff:fe85:e164%6#53(fe80::7c4b:26ff:fe85:e164%6)
;; WHEN: Tue Dec 03 12:49:38 CST 2024
;; MSG SIZE rcvd: 179
```
This is still rather verbose though. With the `+short` option we can pare down
the output to the values of any TXT records and nothing else.
```bash
$ dig -t TXT visualmode.dev +short
"v=spf1 include:_spf.mx.cloudflare.net ~all"
"google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-fvblwtk"
```
Neat! Now I can see that [my domain is correctly identifying itself with Google
Search Console](internet/verify-site-ownership-with-dns-record.md).
[source](https://serverfault.com/a/148724)

View File

@@ -0,0 +1,49 @@
# Type Fewer Paths With Brace Expansion
Bash has a feature called _brace expansion_ that allows us to do a kind of
shorthand when writing out file paths. We can specify multiple variants
comma-separated between curly braces and they'll each be expanded into separate
arguments.
It's easier to understand this by seeing it. If we type the following (don't
hit `Enter` yet):
```bash
$ mkdir src/{one,two,three}
```
And then hit _Tab_:
```bash
$ mkdir src/one src/two src/three
```
Bash uses the portion in braces to expand into separate arguments. The part
outside the braces gets reused for each. That's where we get some savings from
typing out the same path each time.
Here is another example where we use `mv` to rename a file deeply nested in our
project:
```bash
$ mv projects/project1/src/app/utils/{names,constants}.js
```
We don't even have to _Tab_ it out. We can hit _Enter_ directly and `mv` gets
both arguments.
Similarly, how about we change the extension of our renamed file:
```bash
$ mv projects/project1/src/app/utils/constants.{js,ts}
```
I've always found this feature most useful with paths and filenames, but you
can do brace expansion with any arguments.
```bash
$ echo 1{3,1,6,4,9,2,7,5}
13 11 16 14 19 12 17 15
```
[source](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html)

View File

@@ -0,0 +1,27 @@
# Synchronize Vim Clipboard With System Clipboard
When I use Vim-mode in VSCode, I _yank_ text onto the Vim clipboard by visually
selecting some text and hitting `y`. Then I can move the cursor somewhere else
in the file (or another file in VSCode) and _paste_ it by hitting `p`.
But what if I want the thing I yanked from a file to be pasted into another
program, like Chrome? Or if I've copied some text from another program and I
want to paste it into a file in VSCode?
This cross-program copy and pasting is what the _system clipboard_ on your
operating system is for. By default, the Vim clipboard is separate from the
system clipboard. I personally prefer for them to be one and the same.
To achieve this, I added the following line to my VSCode config in
`settings.json`:
```json
{
"vim.useSystemClipboard": true
}
```
It takes a little getting used to having them integrated, but I've done it for
so long that it is muscle memory. It's hard to not have them integrated now.
It's even better when I have a clipboard history tool like Raycast available
for accessing past clipboard values.