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

Compare commits

...

35 Commits

Author SHA1 Message Date
Marcus Wyatt
ae2c7aa028 Merge cff6592c4e into 5592d4266d 2025-01-09 12:22:59 -05:00
jbranchaud
5592d4266d Add Use A Different Font With iTerm2 as a Mac TIL 2025-01-09 11:13:17 -06:00
jbranchaud
daf448c1a5 Add Rebuild Tailwind Bundle For Dev Server as a Rails TIL 2025-01-08 19:39:41 -06:00
jbranchaud
aaddc35fcd Add Disclose Additional Details as an HTML TIL 2025-01-07 13:31:31 -06:00
jbranchaud
b575534d4e Add Difference Between Slice And Pointer To Slice as a Go TIL 2025-01-06 16:43:30 -06:00
jbranchaud
ae3ecbf72c Add Start Amphetamine Session With AppleScript as a Mac TIL 2025-01-05 19:03:26 -06:00
jbranchaud
1cf67b8f1a Add Configure Max String Print Length For Delve as a Go TIL 2025-01-04 13:07:13 -06:00
jbranchaud
f9c0a566eb Add See Where asdf Gets Current Tool Version as a Unix TIL 2025-01-03 12:11:02 -06:00
jbranchaud
527038ca23 Fix TIL count, it was off by 1 2025-01-02 13:58:26 -06:00
jbranchaud
b972673008 Add Simon Willison's TIL to list of other TILs 2025-01-02 13:54:50 -06:00
jbranchaud
cc31aae25a Update copyright date to 2025, time flies 2025-01-02 13:52:57 -06:00
jbranchaud
26f30c3225 Update README with a few learning resource links 2025-01-02 13:52:24 -06:00
jbranchaud
e14da2f207 Add Basic Delve Debugging Session as a Go TIL 2025-01-02 13:45:16 -06:00
jbranchaud
b7d4a62ecb Add Refer To Implicit Block Argument With It as a Ruby TIL 2025-01-01 12:16:53 -06:00
jbranchaud
1ad41b9776 Add Connect To A SQLite Database as a Go TIL 2024-12-31 10:48:01 -06:00
jbranchaud
11716a8fb5 Add Install Latest Version Of Ruby With asdf as a Ruby TIL 2024-12-30 19:20:33 -07:00
jbranchaud
5e19d53382 Add Pass A Struct To A Function as a Go TIL 2024-12-29 10:26:55 -07:00
jbranchaud
c8aa6ee506 Add Break Justfile Into Separate Hidden Steps as a Workflow TIL 2024-12-28 09:20:00 -07:00
jbranchaud
9c0c9222f9 Add Create A Slice From An Array as a Go TIL 2024-12-26 09:47:12 -07:00
jbranchaud
855251e478 Add Clamp To An Endless Range as a Ruby TIL 2024-12-25 22:07:58 -07:00
jbranchaud
4e5ba0ce4c Add Write A Custom Scan Function For File IO as a Go TIL 2024-12-24 11:12:32 -06:00
jbranchaud
63a92cbc29 Add Use External Diff Tool Like Difftastic as a Git TIL 2024-12-23 15:41:11 -06:00
jbranchaud
8438025005 Add Deterministically Seed A Random Number Generator as a Go TIL 2024-12-22 15:04:31 -06:00
jbranchaud
a3be570a32 Add Show Linting Errors In Zed as a Workflow TIL 2024-12-21 19:14:48 -06:00
jbranchaud
464a2af6db Add Produce The Zero Value Of A Generic Type as a Go TIL 2024-12-20 15:19:10 -06:00
jbranchaud
8801f39df0 Add Manually Pass Two Git Files To Delta as a Unix TIL 2024-12-19 18:24:27 -06:00
jbranchaud
aeb55efc3c Add Detect If Stdin Comes From A Redirect as a Go TIL 2024-12-18 11:05:29 -06:00
jbranchaud
a92af09fea Add Explore The Database Schema as a SQLite TIL 2024-12-17 12:28:13 -06:00
jbranchaud
43e6433fd6 Add Check If Cobra Flag Was Set as a Go TIL 2024-12-17 01:08:50 -06:00
jbranchaud
88e675b9a3 Add Better Diffs With Delta as a Git TIL 2024-12-15 18:31:25 -06:00
jbranchaud
f5286c1f41 Add Format Date And Time With Time Constants as a Go TIL 2024-12-14 10:52:36 -06:00
jbranchaud
8787e43458 Add Fix Whitespace Errors Throughout Branch Commits as a Git TIL 2024-12-13 14:32:11 -06:00
jbranchaud
f658a31435 Add Redirect File To Stdin During Delve Debug as a Go TIL 2024-12-12 18:40:23 -06:00
jbranchaud
db00ec69c2 Add Highlight Extra Whitespace In Diff Output as a Git TIL 2024-12-11 18:43:08 -06:00
Marcus Wyatt
cff6592c4e docs: add call to minmax in example 2022-06-29 05:05:10 -07:00
31 changed files with 1208 additions and 3 deletions

View File

@@ -10,7 +10,11 @@ pairing with smart people at Hashrocket.
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
_1534 TILs and counting..._
_1562 TILs and counting..._
See some of the other learning resources I work on:
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
- [Vim Un-Alphabet](https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU)
---
@@ -283,6 +287,7 @@ _1534 TILs and counting..._
- [Add Only Tracked Files From A Directory](git/add-only-tracked-files-from-a-directory.md)
- [Amend Author Of Previous Commit](git/amend-author-of-previous-commit.md)
- [Auto-Squash Those Fixup Commits](git/auto-squash-those-fixup-commits.md)
- [Better Diffs With Delta](git/better-diffs-with-delta.md)
- [Caching Credentials](git/caching-credentials.md)
- [Change The Start Point Of A Branch](git/change-the-start-point-of-a-branch.md)
- [Check How A File Is Being Ignored](git/check-how-a-file-is-being-ignored.md)
@@ -311,12 +316,14 @@ _1534 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)
- [Fix Whitespace Errors Throughout Branch Commits](git/fix-whitespace-errors-throughout-branch-commits.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)
- [Grep For A Pattern On Another Branch](git/grep-for-a-pattern-on-another-branch.md)
- [Grep Over Commit Messages](git/grep-over-commit-messages.md)
- [Highlight Extra Whitespace In Diff Output](git/highlight-extra-whitespace-in-diff-output.md)
- [Ignore Changes To A Tracked File](git/ignore-changes-to-a-tracked-file.md)
- [Ignore Files Specific To Your Workflow](git/ignore-files-specific-to-your-workflow.md)
- [Include A Message With Your Stashed Changes](git/include-a-message-with-your-stashed-changes.md)
@@ -380,6 +387,7 @@ _1534 TILs and counting..._
- [Untrack A Directory Of Files Without Deleting](git/untrack-a-directory-of-files-without-deleting.md)
- [Untrack A File Without Deleting It](git/untrack-a-file-without-deleting-it.md)
- [Update The URL Of A Remote](git/update-the-url-of-a-remote.md)
- [Use External Diff Tool Like Difftastic](git/use-external-diff-tool-like-difftastic.md)
- [Using Commands With A Relative Date Format](git/using-commands-with-a-relative-date-format.md)
- [Verbose Commit Message](git/verbose-commit-message.md)
- [Viewing A File On Another Branch](git/viewing-a-file-on-another-branch.md)
@@ -399,17 +407,30 @@ _1534 TILs and counting..._
- [Access Go Docs Offline](go/access-go-docs-offline.md)
- [Add A Method To A Struct](go/add-a-method-to-a-struct.md)
- [Basic Delve Debugging Session](go/basic-delve-debugging-session.md)
- [Build For A Specific OS And Architecture](go/build-for-a-specific-os-and-architecture.md)
- [Check If Cobra Flag Was Set](go/check-if-cobra-flag-was-set.md)
- [Combine Two Slices](go/combine-two-slices.md)
- [Configure Max String Print Length For Delve](go/configure-max-string-print-length-for-delve.md)
- [Connect To A SQLite Database](go/connect-to-a-sqlite-database.md)
- [Create A Slice From An Array](go/create-a-slice-from-an-array.md)
- [Detect If Stdin Comes From A Redirect](go/detect-if-stdin-comes-from-a-redirect.md)
- [Deterministically Seed A Random Number Generator](go/deterministically-seed-a-random-number-generator.md)
- [Difference Between Slice And Pointer To Slice](go/difference-between-slice-and-pointer-to-slice.md)
- [Do Something N Times](go/do-something-n-times.md)
- [Find Executables Installed By Go](go/find-executables-installed-by-go.md)
- [Format Date And Time With Time Constants](go/format-date-and-time-with-time-constants.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)
- [Pass A Struct To A Function](go/pass-a-struct-to-a-function.md)
- [Produce The Zero Value Of A Generic Type](go/produce-the-zero-value-of-a-generic-type.md)
- [Redirect File To Stdin During Delve Debug](go/redirect-file-to-stdin-during-delve-debug.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)
- [Sort Slice In Ascending Or Descending Order](go/sort-slice-in-ascending-or-descending-order.md)
- [Upgrading From An Older Version On Mac](go/upgrading-from-an-older-version-on-mac.md)
- [Write A Custom Scan Function For File IO](go/write-a-custom-scan-function-for-file-io.md)
### GROQ
@@ -433,6 +454,7 @@ _1534 TILs and counting..._
- [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.md)
- [Determine Which Button Submitted The Form](html/determine-which-button-submitted-the-form.md)
- [Disable Auto-Completion For A Form Input](html/disable-auto-completion-for-a-form-input.md)
- [Disclose Additional Details](html/disclose-additional-details.md)
- [Make Elements Non-Interactive With Inert](html/make-elements-non-interactive-with-inert.md)
- [Prevent Search Engines From Indexing A Page](html/prevent-search-engines-from-indexing-a-page.md)
- [Render Text As Superscript](html/render-text-as-superscript.md)
@@ -641,6 +663,8 @@ _1534 TILs and counting..._
- [Run AppleScript Commands Inline In The Terminal](mac/run-applescript-commands-inline-in-the-terminal.md)
- [Set A Window To Its Default Zoom Level](mac/set-a-window-to-its-default-zoom-level.md)
- [Specify App When Opening From Command Line](mac/specify-app-when-opening-from-command-line.md)
- [Start Amphetamine Session With AppleScript](mac/start-amphetamine-session-with-applescript.md)
- [Use A Different Font With iTerm2](mac/use-a-different-font-with-iterm2.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)
@@ -1014,6 +1038,7 @@ _1534 TILs and counting..._
- [Query A Single Value From The Database](rails/query-a-single-value-from-the-database.md)
- [Read In Environment-Specific Config Values](rails/read-in-environment-specific-config-values.md)
- [Read-Only Models](rails/read-only-models.md)
- [Rebuild Tailwind Bundle For Dev Server](rails/rebuild-tailwind-bundle-for-dev-server.md)
- [Remove A Database Column From A Table](rails/remove-a-database-column-from-a-table.md)
- [Remove The Default Value On A Column](rails/remove-the-default-value-on-a-column.md)
- [Render An Alternative ActionMailer Template](rails/render-an-alternative-action-mailer-template.md)
@@ -1206,6 +1231,7 @@ _1534 TILs and counting..._
- [Check If A URL Resolves To 200](ruby/check-if-a-url-resolves-to-200.md)
- [Check If An Object Includes A Module](ruby/check-if-an-object-includes-a-module.md)
- [Check Return Status Of Running A Shell Command](ruby/check-return-status-of-running-a-shell-command.md)
- [Clamp To An Endless Range](ruby/clamp-to-an-endless-range.md)
- [Click On Text With Capybara](ruby/click-on-text-with-capybara.md)
- [Colorful Output With MiniTest](ruby/colorful-output-with-minitest.md)
- [Comparing Class Hierarchy Relationships](ruby/comparing-class-hierarchy-relationships.md)
@@ -1255,6 +1281,7 @@ _1534 TILs and counting..._
- [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md)
- [Include Extra Context In A Honeybadger Notify](ruby/include-extra-context-in-a-honeybadger-notify.md)
- [Ins And Outs Of Pry](ruby/ins-and-outs-of-pry.md)
- [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md)
- [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md)
- [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md)
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
@@ -1287,6 +1314,7 @@ _1534 TILs and counting..._
- [Question Mark Operator](ruby/question-mark-operator.md)
- [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md)
- [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md)
- [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md)
- [Rendering ERB](ruby/rendering-erb.md)
- [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md)
- [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md)
@@ -1354,6 +1382,7 @@ _1534 TILs and counting..._
### SQLite
- [Display Results In Readable Column Format](sqlite/display-results-in-readable-column-format.md)
- [Explore The Database Schema](sqlite/explore-the-database-schema.md)
### Streaming
@@ -1520,6 +1549,7 @@ _1534 TILs and counting..._
- [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)
- [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.md)
- [Map A Domain To localhost](unix/map-a-domain-to-localhost.md)
- [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md)
- [Occupy A Local Port With Netcat](unix/occupy-a-local-port-with-netcat.md)
@@ -1547,6 +1577,7 @@ _1534 TILs and counting..._
- [Search History](unix/search-history.md)
- [Search Man Page Descriptions](unix/search-man-page-descriptions.md)
- [Securely Remove Files](unix/securely-remove-files.md)
- [See Where asdf Gets Current Tool Version](unix/see-where-asdf-gets-current-tool-version.md)
- [Set The asdf Package Version For A Single Shell](unix/set-the-asdf-package-version-for-a-single-shell.md)
- [Show A File Preview When Searching With FZF](unix/show-a-file-preview-when-searching-with-fzf.md)
- [Show Disk Usage For The Current Directory](unix/show-disk-usage-for-the-current-directory.md)
@@ -1770,6 +1801,7 @@ _1534 TILs and counting..._
- [Add Subtitles To Existing Mux Video Asset](workflow/add-subtitles-to-existing-mux-video-asset.md)
- [Access 1Password Credential From CLI](workflow/access-1password-credential-from-cli.md)
- [Allow Key-Repeating With Cursor](workflow/allow-key-repeating-with-cursor.md)
- [Break Justfile Into Separate Hidden Steps](workflow/break-justfile-into-separate-hidden-steps.md)
- [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md)
- [Configure Email Redirect With Cloudflare](workflow/configure-email-redirect-with-cloudflare.md)
- [Convert An ePub Document To PDF On Mac](workflow/convert-an-epub-document-to-pdf-on-mac.md)
@@ -1787,6 +1819,7 @@ _1534 TILs and counting..._
- [See Overlaps For A Set Of Time Zones](workflow/see-overlaps-for-a-set-of-time-zones.md)
- [Send A Message To A Discord Channel](workflow/send-a-message-to-a-discord-channel.md)
- [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md)
- [Show Linting Errors In Zed](workflow/show-linting-errors-in-zed.md)
- [Toggle Between Stories In Storybook](workflow/toggle-between-stories-in-storybook.md)
- [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md)
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
@@ -1838,11 +1871,11 @@ I shamelessly stole this idea from
* [Today I Learned by Hashrocket](https://til.hashrocket.com)
* [jwworth/til](https://github.com/jwworth/til)
* [thoughtbot/til](https://github.com/thoughtbot/til)
* [til.simonwillison.net](https://til.simonwillison.net/)
## License
© 2015-2022 Josh Branchaud
© 2015-2025 Josh Branchaud
This repository is licensed under the MIT license. See `LICENSE` for
details.

View File

@@ -0,0 +1,43 @@
# Better Diffs With Delta
A `git diff` from the command line is relatively bare bones. It shows you
removed lines and added lines that make up a changeset with the former text in
red and the later text in green. All other contextual text is in white. I've
found this to be good enough for most of the life of my git usage. I've been
missing out though.
By using [`delta`](https://github.com/dandavison/delta) as the pager and diff
filter for `git`, I get a bunch of nice visual improvements.
- Removals and additions are red and green shaded backgrounds
- Syntax highlighting for most languages
- Highlight specific part of a line that has changed
- Visual spacing and layout is clearer
To get all of this, all I had to do was install `delta`:
```bash
$ brew install delta
```
And then add `delta` as both the _core_ pager and `diffFilter` in my global git
config file:
```
[core]
pager = delta
[interactive]
singleKey = true # unrelated, but nice to have
diffFilter = delta --color-only
```
It's also recommended that you use `zdiff3` for your merge conflict style,
which I already had:
```
[merge]
conflictstyle = zdiff3
```
Once you have ths all configred, try a `git diff` or `git add --patch` and see
how much more visual info you get.

View File

@@ -0,0 +1,39 @@
# Fix Whitespace Errors Throughout Branch Commits
Let's say we've been working on some changes to our repository on a branch.
We've made several commits. We are close to putting up a PR, but we want to
make sure everything is tidied up.
We run a check and see that there are some whitespace errors that should be
fixed.
```bash
$ git diff main --check
README.md:1: trailing whitespace.
+# git-playground
script.sh:9: trailing whitespace.
+
```
This post isn't able to show the highlighted whitespace errors, but we can see
the warnings above.
Rather than cluttering things with an additional commit that fixes these errors
or manually cleaning up each commit, we can ask `git` to fix it for us.
```bash
$ git rebase --whitespace=fix main
```
That will do a manual rebase of each commit addressing the whitespace errors.
We can run the error check again and see no output, which means we are good to
go.
```bash
$ git diff main --check
```
See the section on `--whitespace` in `man git-apply` for more details.
[source](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration)

View File

@@ -0,0 +1,30 @@
# Highlight Extra Whitespace In Diff Output
When running a `git diff` (or `git add --patch`) I'll sometimes come across
lines that don't have any visible changes. This is usually because some
whitespace characters were either added (on accident) or removed (often by a
autoformatter).
Depending on the `core.whitespace` config, you'll probably see at least some of
the whitespace errors that git provides. By default, git only highlights
whitespace errors on added (`new`) lines. However if some extra whitespace was
originally committed and is now being removed, it won't be highlighted on the
`old` line in the diff.
We can have git always highlight whitespace errors by setting
`wsErrorHighlight` to `all` in the global git config.
```bash
$ git config --global diff.wsErrorHighlight all
```
Which updates the global gitconfig file with the following line:
```
[diff]
wsErrorHighlight = all
```
The `all` option is a shorthand for `old,new,context`.
See `man git-diff` for more details.

View File

@@ -0,0 +1,23 @@
# Use External Diff Tool Like Difftastic
Assuming we already have a tool like `difft`
([difftastic](https://difftastic.wilfred.me.uk/introduction.html)) available on
our machine, we can use it as a diff viewer for the various `git` commands that
display a diff.
This requires a manual override which involve two pieces — an inline
configuration of `diff.external` specifying the binary of the external differ
and the `--ext-diff` flag which tells these commands to use the external diff
binary.
Here is what `git show` looks like with `difft`:
```bash
$ git -c diff.external=difft show --ext-diff
```
Without the `--ext-diff` flag, it will fallback to the default differ despite
`diff.external` being set.
See `man git-diff` and friends for the `--ext-diff` flag. See `man git-config`
for `diff.external`.

View File

@@ -0,0 +1,63 @@
# Basic Delve Debugging Session
When using [delve](https://github.com/go-delve/delve) to debug a Go program,
these are the series of things I usually find myself doing.
First, I start running the program with `dlv` including any arguments after a `--` (in my case, the `solve` subcommand and a filename).
```bash
$ dlv debug . -- solve samples/001.txt
```
`dlv` starts up and is ready to run my program from the beginning. I'll need to
set a couple breakpoints before continuing. I do this with the `break` command,
specifying the filename and line number.
```
(dlv) break main.go:528
Breakpoint 1 set at 0x10c1a5bea for main.traversePuzzleIterative() ./main.go:528
(dlv) break main.go:599
Breakpoint 2 set at 0x10c1a6dcc for main.traversePuzzleIterative() ./main.go:599
```
Now I can continue which will run the program until hitting a breakpoint.
```
(dlv) continue
> [Breakpoint 2] main.traversePuzzleIterative() ./main.go:599 (hits goroutine(1):1 total:1) (PC: 0x10c1a6dcc)
594: }
595: }
596:
597: topStackFrame := stack[len(stack)-1]
598: // if the current stack frame has more values, try the next
=> 599: if len(topStackFrame.PossibleValues) > 0 {
600: nextValue := topStackFrame.PossibleValues[0]
601: topStackFrame.PossibleValues = topStackFrame.PossibleValues[1:]
602: topStackFrame.CurrValue = nextValue
603:
604: // Undo the last placement and make a new one
```
I can see the context around the line we've stopped on. From here I can dig
into the current state of the program by looking at local variables (`locals`)
or printing out a specific value (`print someVar`). I can continue to step
through the program line by line with `next` or eventually run `continue` to
proceed to the next breakpoint.
```
(dlv) locals
diagnostics = main.Diagnostics {BacktrackCount: 0, NodeVisitCount: 1, ValidityCheckCount: 2,...+2 more}
stack = []main.StackData len: 1, cap: 1, [...]
emptyCellPositions = [][]int len: 3, cap: 4, [...]
emptyCellIndex = 1
status = "Invalid"
topStackFrame = main.StackData {RowIndex: 1, ColumnIndex: 7, PossibleValues: []int len: 8, cap: 8, [...],...+1 more}
(dlv) print topStackFrame
main.StackData {
RowIndex: 1,
ColumnIndex: 7,
PossibleValues: []int len: 8, cap: 8, [2,3,4,5,6,7,8,9],
CurrValue: 1,}
(dlv) next
> main.traversePuzzleIterative() ./main.go:600 (PC: 0x10c1a6dea)
```

View File

@@ -0,0 +1,41 @@
# Check If Cobra Flag Was Set
When using [Cobra](https://github.com/spf13/cobra) to define a CLI, we can
specify a flag for a command like so:
```go
var Seed int64
myCmd.PersistentFlags().Int64VarP(&Seed, "seed", "", -1, "set a seed")
```
This `--seed` flag has a _default_ of `-1`. If the flag isn't specified, then
when we access that flag's value, we'll get `-1`.
But how do we differentiate between the _default_ `-1` and someone passing `-1`
to the `--seed` flag when running the program?
In the command definition, we can look at the flags and see, by name, if
specific ones were changed by user input rather than being the defaults.
```go
myCommand := &cobra.Command{
// coommand setup ...
Run: func(cmd *cobra.Command, args []string) {
if cmd.Flags().Changed("seed") {
seed, err := cmd.Flags().GetInt64("seed")
if err != nil {
fmt.Println("Seed flag is missing from `cmdFlags()`")
os.Exit(1)
}
fmt.Printf("Seed was set to %d\n", seed)
} else {
fmt.Println("Seed was not set")
}
}
}
```
If we don't want to rely on the default and instead want to specify some other
behavior when the flag is not manually set by the user, we can detect that
scenario like this.

View File

@@ -0,0 +1,29 @@
# Configure Max String Print Length For Delve
During a [Delve](https://github.com/go-delve/delve) debugging session, we can
print out the value of a given variable with the `print` command. Similarly, we
can see the values of all local variables with the `locals` command.
Whenever Delve is printing out strings and slices, it will truncate what it
displays to 64 characters (or items) by default.
```go
(dlv) print diagnostics.Solutions[0]
"295743861\n431865972\n876192543\n387459216\n612387495\n549216738\n7635...+25 more"
```
This can be overridden by [changing the `config` of
`max-string-len`](https://github.com/derekparker/delve/blob/237c5026f40e38d2dd6f62a7362de7b25b00c1c7/Documentation/cli/expr.md?plain=1#L59)
to something longer. In my case here, all I need are about 90 characters to
display my full string, so run `config max-string-len 90` from the `dlv`
session.
```go
(dlv) config max-string-len 90
(dlv) print diagnostics.Solutions[0]
"295743861\n431865972\n876192543\n387459216\n612387495\n549216738\n763524189\n928671354\n154938627"
```
Now I can see the entire string instead of the truncated version.
[source](https://stackoverflow.com/a/52416264/535590)

View File

@@ -0,0 +1,50 @@
# Connect To A SQLite Database
Using the `database/sql` module and the `github.com/mattn/go-sqlite3` package,
we can connect to a SQLite database and run some queries. In my case, I have a
SQLite connection string exported to my environment, so I can access that with
`os.Getenv`. It's a local SQLite file, `./test.db`.
Calling `sql.Open`, I'm able to connect with a SQLite3 driver to the database
at that connection string. The `setupDatabase` function returns that database
connection pointer. Things like `Exec` and `QueryRow` can be called on `db`. I
also need to make sure I close the connection to the database with a `defer`.
Here is a full example of connecting to a local SQLite database and inserting a
record:
```go
package main
import (
"database/sql"
"fmt"
"os"
_ "github.com/mattn/go-sqlite3"
)
func setupDatabase() *sql.DB {
databaseString := os.Getenv("GOOSE_DBSTRING")
if len(databaseString) == 0 {
fmt.Println("Error retrieving `GOOSE_DBSTRING` from env")
os.Exit(1)
}
db, err := sql.Open("sqlite3", databaseString)
if err != nil {
fmt.Printf("Error opening database: %v\n", err)
os.Exit(1)
}
return db
}
func main() {
db := setupDatabase()
defer db.Close()
sql := `insert into users (name) values (?);`
db.Exec(sql, "Josh")
}
```

View File

@@ -0,0 +1,44 @@
# Create A Slice From An Array
Slices in Go are a flexible abstraction over arrays. We can create a slice from
an array with the `[n:m]` _slicing_ syntax. We specify the left and right
(exclusive) bounds of the array that we want to create the slice relative to.
We can exclude the lower bound which translates to the `0` index of the array.
We can exclude the left bound which translates to the end of the array. We can
even exclude both ends of the _slicing_ syntax which means creating a slice of
the entire array.
Here is an example of each of those:
```go
package main
import "fmt"
func main() {
arr := [...]string{
"taco",
"burrito",
"torta",
"enchilada",
"quesadilla",
"pozole",
}
firstTwo := arr[:2]
lastTwo := arr[len(arr)-2:]
all := arr[:]
fmt.Println("First two:", firstTwo)
// First two: [taco burrito]
fmt.Println("Last two:", lastTwo)
// Last two: [quesadilla pozole]
fmt.Println("All:", all)
// All: [taco burrito torta enchilada quesadilla pozole
}
```
[source](https://go.dev/blog/slices-intro#slices)

View File

@@ -0,0 +1,59 @@
# Detect If Stdin Comes From A Redirect
Reading lines of input from `stdin` is flexible. And we may need our program to
behave differently depending on where that input is coming from. For instance,
if data is redirected or piped to our program, we scan and process it directly.
Otherwise, we need to prompt the user to enter in specific info and go from
there.
We can detect whether [`os.Stdin`](https://pkg.go.dev/os#pkg-variables) is
being piped to, redirected to, or whether we should prompt the user by looking
at the file mode descriptor of
[`os.Stdin.Stat()`](https://pkg.go.dev/os#File.Stat).
```go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Stdin.Stat()
if err != nil {
fmt.Printf("Error checking stdin: %v\n", err)
os.Exit(1)
}
fromTerminal := (file.Mode() & os.ModeCharDevice) != 0
fromAPipe := (file.Mode() & os.ModeNamedPipe) != 0
if fromTerminal {
fmt.Println("This is Char Device mode, let's prompt user for input")
termScanner := bufio.NewScanner(os.Stdin)
for termScanner.Scan() {
fmt.Printf("- %s\n", termScanner.Text())
break;
}
} else if fromAPipe {
fmt.Println("This is Named Pipe mode, contents piped in")
pipeScanner := bufio.NewScanner(os.Stdin)
for pipeScanner.Scan() {
fmt.Printf("- %s\n", pipeScanner.Text())
}
} else {
fmt.Println("This means the input was redirected")
redirectScanner := bufio.NewScanner(os.Stdin)
for redirectScanner.Scan() {
fmt.Printf("- %s\n", redirectScanner.Text())
}
}
}
```
If `os.ModeCharDevice` then we are connected to a character device, like the
terminal. We can see if input is being piped in by checking against
`os.ModeNamedPipe`. Otherwise, there are a variety of file modes and I'm
willing to assume we're dealing with a regular file redirect at that point.

View File

@@ -0,0 +1,49 @@
# Deterministically Seed A Random Number Generator
If you need a random number in Go, you can always reach for the various
functions in the `rand` package.
```go
package main
import (
"fmt"
"math/rand"
)
func main() {
for range 5 {
roll := rand.Intn(6) + 1
fmt.Printf("- %d\n", roll)
}
}
```
Each time I run that, I get a random set of values. Often in programming, we
want some control over the randomness. We want to _seed_ the randomness so that
it is deterministic. We want random, but the kind of random where we know how
we got there.
```go
package main
import (
"fmt"
"math/rand"
)
func main() {
seed := int64(123)
src := rand.NewSource(seed)
rng := rand.New(src)
for range 5 {
roll := rng.Intn(6) + 1
fmt.Printf("- %d\n", roll)
}
}
```
In this second snippet, we create a `Source` with a specific seed value that we
can use with a custom `Rand` struct. We can then deterministically get random
numbers from it.

View File

@@ -0,0 +1,55 @@
# Difference Between Slice And Pointer To Slice
Though a slice can be thought of and used as a flexible, variable-length
array-like data structure, it is important to understand that it is also a
special kind of pointer to an underlying array.
This matters when we a function receives a slice versus a pointer to a slice as
an argument, depending on what it is doing with that slice.
If the function is access or updating elements in the slice, there is no
difference. There is no meaningful difference between these two functions and
we might as well use the former.
```go
func replaceAtIndex(slice []string, index int, value string) {
slice[index] = value
}
func replaceAtIndexPtr(slice *[]string, index int, value string) {
(*slice)[index] = value
}
```
On the other hand, if the receiving function needs to append to or replace the
slice, then we need to pass a pointer to the slice. A direct slice argument
will result in only the function-local copy getting replaced.
```go
package main
import (
"fmt"
)
func main() {
s1 := []int{8, 6, 7, 9}
s2 := []int{8, 6, 7, 9}
addItem(s1, 11)
fmt.Printf("s1: %v\n", s1) //=> s1: [8 6 7 9]
addItemPtr(&s2, 11)
fmt.Printf("s2: %v\n", s2) //=> s2: [8 6 7 9 11]
}
func addItem(slice []int, value int) {
slice = append(slice, value)
}
func addItemPtr(slice *[]int, value int) {
(*slice) = append(*slice, value)
}
```
[source](https://go.dev/tour/moretypes/8)

View File

@@ -0,0 +1,51 @@
# Format Date And Time With Time Constants
The Go [`time` package](https://pkg.go.dev/time) has a [`Format`
function](https://pkg.go.dev/time#Time.Format) for displaying the parts of a
date and time in standard and custom ways. It works a bit different than you
might be used to from other languages. Rather than using `strftime` identifiers
like in this string `"%B %d, %Y"`, there is a canonical date that is used as a
reference point.
That canonical date is from Janary 2nd, 2006. That was a Monday. It was at 5
seconds after 3:04PM. The Unix format of it looks like `"Mon Jan _2 15:04:05
MST 2006"`.
```
package main
import (
"fmt"
"time"
)
func main() {
// This specific time pulled from `time.Format` docs
t, _ := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015")
// Reference date and time:
// "Mon Jan _2 15:04:05 MST 2006"
strf1 := t.Format("|2006|02|01|03:04:05|Day: Mon|")
fmt.Println("strf1:", strf1)
// strf1: |2015|25|02|11:06:39|Day: Wed|
strf2 := t.Format(time.DateTime)
strf3 := t.Format(time.RubyDate)
strf4 := t.Format(time.Kitchen)
fmt.Println("DateTime:", strf2) // DateTime: 2015-02-25 11:06:39
fmt.Println("RubyDate:", strf3) // RubyDate: Wed Feb 25 11:06:39 +0000 2015
fmt.Println("Kitchen:", strf4) // Kitchen: 11:06AM
}
```
Though there are a [variety of useful formatting
constants](https://pkg.go.dev/time#pkg-constants) already available like
`DateTime`, `RubyDate`, `Kitchen`, etc., we can also define our own formatting
string by using the reference values for each part of a date and time.
If you want to reference the year, whether as `YYYY` or `YY`, it is always
going to be a form of `2006`, so `2006` or `06` respectively. Even though the
above time variable is in February, our format strings will always need to use
one of `Jan`, `January`, `01` or `1`.

View File

@@ -0,0 +1,65 @@
# Pass A Struct To A Function
Go operates as _pass-by-value_ which means that when we pass a struct to a
function, the receiving function gets a copy of the struct. Two things worth
noticing about that are 1) an extra memory allocation happens when calling the
function and 2) altering the struct does not affect the original in the calling
context.
On the other hand, we can have a function that takes a pointer to a struct.
When we call that function, we have a reference to the memory location of the
struct instead of a copy of the struct. That means no additional allocation and
modifications to the dereferenced struct are modifications to the original in
the calling context.
Here is an example that demonstrates both of these. Notice the printed output
that is included in comments at the end which shows memory locations and
contents of the struct at various points.
```go
package main
import "fmt"
type Order struct {
Item string
Quantity int
DineIn bool
}
func main() {
order := Order{Item: "taco", Quantity: 3, DineIn: true}
fmt.Println("Order:", order)
fmt.Printf("main - Loc: %p\n", &order)
doubledOrder := doubleOrder(order)
fmt.Println("Double Order:", doubledOrder)
fmt.Println("Original Order:", order)
doubleOrderPtr(&order)
fmt.Println("Double Order Ptr:", order)
}
func doubleOrder(order Order) Order {
fmt.Printf("doubleOrder - Loc: %p\n", &order)
order.Quantity *= 2
return order
}
func doubleOrderPtr(order *Order) {
fmt.Printf("doubleOrderPtr - Loc: %p\n", order)
(*order).Quantity *= 2
}
// Order: {taco 3 true}
// main - Loc: 0xc0000b4000
// doubleOrder - Loc: 0xc0000b4040
// Double Order: {taco 6 true}
// Original Order: {taco 3 true}
// doubleOrderPtr - Loc: 0xc0000b4000
// Double Order Ptr: {taco 6 true}
```

View File

@@ -0,0 +1,32 @@
# Produce The Zero Value For A Generic Type
While writing a _pop_ function that would work with slices of a generic type, I
ran into the issue of needing to produce a zero value of type `T` when
returning early for an empty slice.
The way to arbitrarily get the zero value of a generic in Go is with `*new(T)`.
I was able to use this in my `Pop` function like so:
```go
func Pop[T any](slice []T) (T, error) {
if len(slice) == 0 {
return *new(T), fmt.Errorf("cannot pop an empty slice")
}
lastItem := slice[len(slice)-1]
slice = slice[:len(slice)-1]
return lastItem, nil
}
```
If this is happening in multiple functions and we want a more self-documenting
approach, we can pull it out into a function `zero`:
```go
func zero[T any]() T {
return *new(T)
}
```

View File

@@ -0,0 +1,39 @@
# Redirect File To Stdin During Delve Debug
I have a go program that accepts input from stdin. The way I've been running
the program as I develop it is to redirect the output of some sample files to
the program.
```bash
$ go run . < sample/001.txt
```
When I then go to debug this program with
[Delve](https://github.com/go-delve/delve), I'd still like to be able to
redirect a file into the program to reproduce the exact behavior I'm seeing.
The following won't work:
```bash
$ dlv debug . < samples/001.txt
Stdin is not a terminal, use '-r' to specify redirects for the target process or --allow-non-terminal-interactive=true if you really want to specify a redirect for Delve
```
Fortunately, `dlv` sees what I'm trying to do and makes a recommendation. The
`-r` flag can be used to specify redirects for the target process. The [`dlv`
redirect
docs](https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_redirect.md)
explain that `-r` can be passed a `source:destination`. The `source` is `stdin`
by default, but can also be `stdout` and `stderr`.
I can redirect my file into the debugging session of my program like so:
```bash
$ dlv debug . -r stdin:samples/001.txt
```
Or even more succinctly:
```bash
$ dlv debug . -r samples/001.txt
```

View File

@@ -0,0 +1,58 @@
# Write A Custom Scan Function For File IO
By default a [`bufio.Scanner`](https://pkg.go.dev/bufio#Scanner) will scan
input line-by-line. In other words, splitting on newlines such that each
iteration will emit everything up to the next newline character.
We can write our own `SplitFunc` and override the default one by calling
`scanner.Split` with it. Our custom scan function needs to match the type
signature of [`SplitFunc`](https://pkg.go.dev/bufio#SplitFunc).
Here is a custom one that emits each individual character but omits the
newlines.
```go
func ScanChar(data []byte, atEOF bool) (int, []byte, error) {
if atEOF || len(data) == 0 {
return 0, nil, nil
}
start := 0
for start < len(data) {
if !utf8.FullRune(data[start:]) {
return 0, nil, nil
}
r, size := utf8.DecodeRune(data[start:])
if r == utf8.RuneError {
return 0, nil, fmt.Errorf("invalid UTF-8 encoding")
}
if r != '\n' {
return start + size, data[start:start+size], nil
}
// found a \n, advance the start position
start += size
}
return start, nil, nil
}
```
We can then use thi `ScanChar` function with a `bufio.Scanner` like so:
```go
func ReadFileByCharacter(file io.Reader) {
scanner := bufio.NewScanner(file)
// override default SplitFunc
scanner.Split(scanChar)
for scanner.Scan() {
char := scanner.Text()
fmt.Printf("- %s\n", char)
}
}
```

View File

@@ -0,0 +1,28 @@
# Disclose Additional Details
You can add extra details to an HTML page that are only disclosed if the user
chooses to disclose them. To do that, we use the `<details>` tag. This tag
needs to have a `<summary>` tag nested within it. Anything else nested within
`<details>` will be what is disclosed when it is toggled open. The `<summary>`
is what is displayed when it is not open.
Here is a `<detail>` block I recently added to [Ruby Operator
Lookup](https://www.visualmode.dev/ruby-operators).
```html
<details className="pt-2 pb-6">
<summary>What is this thing?</summary>
<p className="pl-3 pt-2 text-gray-700 text-sm">
Ruby is an expressive, versatile, and flexible dynamic programming language. That means there are all kinds of syntax features, operators, and symbols we can encounter that might look unfamiliar and are hard to look up. Ruby Operator Lookup is a directory of all these language features.
</p>
<p className="pl-3 pt-2 text-gray-700 text-sm">
Use the search bar to narrow down the results. Then click on a button for the operator or symbol you want to explore further.
</p>
</details>
```
On page load, the only thing we see is "What is this thing?" with a triangle
symbol next to it. If we click the summary, then the entire details block
(those two `<p>` tags) are disclosed.
[source](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)

View File

@@ -0,0 +1,37 @@
# Start Amphetamine Session With AppleScript
I use the _Amphetamine_ app on Mac to keep my computer from going to sleep
during the day. It is a menu bar app that can be used to start a _Session_ of
time where it will keep your computer from going to sleep. At the start of my
day, I'll typically start an 8 hour _Session_. This is useful if I have to step
away fo 10 minutes or if I'm doing some writing in my notebook, my computer
won't go to sleep on me.
Though these sessions can be controlled from the menu bar app, I was excited to
learn that I can also programatically start a session with AppleScript.
Here is how to start a _Session_ (overriding an existing session) with options
that specify it is 8 hours long and the display should not be allowed to sleep.
```bash
$ osascript -e 'tell application "Amphetamine" to start new session with options {duration:8, interval:hours, displaySleepAllowed:false}'
```
The `interval` could also be `minutes` and then I could change the duration to
an amount of time that makes sense in minutes, e.g. `90` for 1.5 hours.
Note: the `with options {...}` segement is all or nothing. All three need to be included or don't include the clause at all.
Additionally, a session of indefinite duration can be started by including no options:
```bash
$ osascript -e 'tell application "Amphetamine" to start new session'
```
And any existing session can be ended with:
```bash
$ osascript -e 'tell application "Amphetamine" to end session'
```
[source](https://iffy.freshdesk.com/support/solutions/articles/48000078223-applescript-documentation)

View File

@@ -0,0 +1,25 @@
# Use A Different Font With iTerm2
I wanted to give [`gh-dash`](https://github.com/dlvhdr/gh-dash) a try, but
after installing and opening it up, I was seeing a bunch of `?` characters
where specialized font icons were missing. Their README recommended installing
a [`Nerd Font`](https://github.com/ryanoasis/nerd-fonts) that includes those
icons, such as [`Fira Code`](https://github.com/tonsky/FiraCode).
I was able to install `font-fira-code-nerd-font` with homebrew:
```bash
$ brew install font-fira-code-nerd-font
```
Then to get iTerm2 to start using that font, I had to change the font setting
for my current profile.
Under the _iTerm2_ menu is _Settings..._. From there, I clicked the _Profiles_
section. For the _Default_ profile, I went to the _Text_ tab and under _Font_ I
selected _FireCode Nerd Font Mono_ from the dropdown.
That won't take effect on any current iTerm2 windows. Since I have everything
running through `tmux`, I could close my current window, open a new one
(`Cmd+N`), and reconnect to my existing `tmux` session. Now when I run `gh
dash`, I see all the font icons that were missing before.

View File

@@ -0,0 +1,29 @@
# Rebuild Tailwind Bundle For Dev Server
If you're using the TailwindCSS gem in your Rails app:
```ruby
# Use Tailwind CSS [https://github.com/rails/tailwindcss-rails]
gem "tailwindcss-rails"
```
you may find that as you add and adjust styles in your views, refreshing the
page doesn't take any styling effects. That is because the tailwind bundle gets
built with just the style rules that were used at the time it was generated.
In development, as we're working, we expect the styles used by our app to
actively changed. And we don't mind a little performance hit to have the bundle
rebuilt. In that case, we can instruct `puma` to _Live Rebuild_ in
`development` with the `tailwindcss` plugin.
```ruby
# config/puma.rb
# Enable TailwindCSS rebuild in development
plugin :tailwindcss if ENV.fetch("RAILS_ENV", "development") == "development"
```
This has `rails server` run a watch process in the background that live
rebuilds the bundle.
[source](https://github.com/rails/tailwindcss-rails?tab=readme-ov-file#puma-plugin)

View File

@@ -0,0 +1,22 @@
# Clamp To An Endless Range
The
[`Comparable#clamp`](https://ruby-doc.org/3.3.6/Comparable.html#method-i-clamp)
method allows us to specify the bounds of a value we want. If the target value
is between the bounds, then we get that value. Otherwise, we gets the nearest
end of the bounds.
We can even pass a range to `#clamp` instead of separate lower and upper bound
values. Because Ruby has beginless and endless ranges, this gives us the
ergonomics to, say, clamp to any non-negative value with a `0..` endless range.
Here is what that looks like:
```ruby
> 22.clamp(0..)
=> 22
> (-33).clamp(0..)
=> 0
> 0.clamp(0..)
=> 0
```

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,54 @@
# Install Latest Version Of Ruby With asdf
When I check the `asdf` Ruby plugin for known versions of Ruby:
```bash
$ asdf list-all ruby | fzf
```
I don't find the latest (`3.4`).
I need to update the plugin. A newer version of the plugin will know about
newer Ruby versions.
```bash
$ asdf plugin-update ruby
```
Now, if I run the `list-all` command again, I'll find the version I'm looking
for — `3.4.1`.
Now that `asdf` and I both know about the version to be installed, I can tell
`asdf` to install it:
```bash
$ asdf install ruby 3.4.1
```
Now, if I check the current Ruby version, I'll see that it is still set to some
other version.
```bash
$ ruby --version
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin22]
```
I need to tell `asdf` to start using this newly installed version instead,
either globally or locally.
```bash
$ # globally
$ asdf global ruby 3.4.1
$ # or locally
$ asdf local ruby 3.4.1
```
And now I'm all set:
```bash
$ asdf current ruby
ruby 3.4.1 /Users/jbranchaud/.tool-versions
$ ruby --version
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-darwin22]
```

View File

@@ -0,0 +1,43 @@
# Refer To Implicit Block Argument With It
One of the key features of the Ruby 3.4 release is the `it` implicit block
argument.
The vast majority of inline blocks defined in Ruby code receive a single block
argument. Typically we name and reference a block argument explictly like so:
```ruby
items.map { |item| item * item }
```
Ruby likes to cut away excess syntax when possible. To that end, the implicit
`it` block argument has been added. This is an identifier we can reference in
the context of a block and its value is the current
```ruby
items = [1,2,3,4,5]
squares = items.map { it * it }
pp squares
#=> [1, 4, 9, 16, 25]
```
Note: we cannot mix numbered parameters (`_1`, `_2`) with the `it` parameter.
If we do, we'll get the following error:
```ruby
def method_using_block(a, b)
yield(a, b) if block_given?
end
puts method_using_block(4,5) { _2 ** _1 } #=> 625
puts method_using_block(4,5) { _2 ** it }
# it_block.rb:12: syntax error found (SyntaxError)
# 10 |
# 11 | puts method_using_block(4,5) { _2 ** _1 }
# > 12 | ... it }
# | ^~ `it` is not allowed when a numbered parameter is already used
```
[source](https://docs.ruby-lang.org/en/3.4/NEWS_md.html)

View File

@@ -0,0 +1,31 @@
# Explore The Database Schema
The first thing I like to do when connecting to a database is get a quick lay
of the land. What are the tables and what do they look like?
I can list all tables with the `.tables` dot-command.
```sql
sqlite> .tables
ingredient_amounts ingredients recipes
```
I can then look at the `create table` statement for specific tables to see what
their schema looks like:
```sql
sqlite> .schema recipes
CREATE TABLE recipes (
id integer primary key,
name varchar not null,
description text not null,
instructions text not null
);
```
The `.schema` dot-command can also be used without any argument and it will
display the schema for all tables of all connected databases.
Run `.help` from the `sqlite3` prompt for more dot-command options.
[source](https://www.sqlite.org/cli.html#querying_the_database_schema)

View File

@@ -0,0 +1,32 @@
# Manually Pass Two Git Files To Delta
I recently [wired up `delta` as my default pager and differ for
`git`](git/better-diffs-with-delta.md). However, when I installed `delta`, I
first wanted to see what its diff output looked like.
How can I pass two versions of the same file from `git` to `delta`?
I can show the current contents of a file with `git show` referencing the
`HEAD` commit.
```bash
$ git show HEAD:main.go
```
Similiarly, I can show the contents of that file _one_ commit ago with `HEAD~`.
```bash
$ git show HEAD~:main.go
```
I can then pass each of those commands as virtual files to `delta` using the
`<()` syntax. The older file goes first and the newer second.
```bash
$ delta <(git show HEAD~:main.go) <(git show HEAD:main.go)
```
That works and comes in handy if you need to compare two things that aren't
necessarily files or aren't necessarily under version control. However, in
hindsight, I'd say it is easier to add delta as the pager and differ and try it
out directly.

View File

@@ -0,0 +1,29 @@
# See Where asdf Gets Current Tool Version
The other day I [installed the latest version of
Ruby](ruby/install-latest-version-of-ruby-with-asdf.md) with `asdf`. I then set
that version (`3.4.1`) as the global default. However, when I then ran `ruby
--version`, I was getting a `3.2.x` version. I checked my current project's
directory and there was no `.tool-versions` file, so it wasn't being set by my
current directory.
`asdf` looks up the current chain of directories until it encounters a
`.tool-versions` file, so it must have been finding one somewhere up there, but
before it was getting to the _global_ `.tool-versions` file. But where?
The `asdf current` command can tell us for a specific tool what the current
version it is set to and what file is giving that directive.
```bash
asdf current ruby
ruby 3.2.2 /Users/jbranchaud/code/.tool-versions
```
As it turns out, I had a `.tool-versions` file in `$HOME/code` that was setting
that `3.2.x` Ruby version.
I didn't want that directory controlling the Ruby version, so I removed `ruby`
from that file. `asdf` was then able to traverse up to `$HOME/.tool-versions`
for the global setting.
See `asdf help` for more details.

View File

@@ -0,0 +1,48 @@
# Break Justfile Into Separate Hidden Steps
With `just` and a project's `justfile`, I can get a summary of the commands
available to run against my project by running `just --list`. If I try to
breakdown a complex, multi-step command into separate `just` commands, it will
be nice for organization, but it will clutter the list output. I can mark
specific commands as hidden or internal by preceding them with an underscore
(`_`).
Here is a `justfile` from one of my projects that only lists a single command
`setup` which itself is supported by three internal commands: `_check-brew`,
`_install-deps`, and `_install-go-tools`.
```justfile
# Install all required development dependencies
setup: _check-brew _install-deps _install-go-tools
# Check if brew is installed
_check-brew:
#!/usr/bin/env bash
if ! command -v brew &> /dev/null; then
echo "Error: Homebrew is not installed"
echo "Please install from https://brew.sh"
exit 1
fi
brew_deps := '''
go
sqlite3
'''
# Install brew dependencies
_install-deps:
#!/usr/bin/env bash
deps=$(echo '{{brew_deps}}' | tr -s '[:space:]' ' ' | xargs)
for pkg in $deps; do
if ! brew list $pkg &>/dev/null; then
echo "Installing $pkg..."
brew install $pkg
else
echo "✓ $pkg already installed"
fi
done
# Install Go development tools
_install-go-tools:
go install github.com/pressly/goose/v3/cmd/goose@latest
```

View File

@@ -0,0 +1,22 @@
# Show Linting Errors In Zed
When working in a language like TypeScript or Go, the language server tooling
in [Zed](https://zed.dev/) can draw my attention to errors in my code. This
could be an unrecognized function or variable, a type error, or a syntax error.
When these linting errors are detected, the editor underlines them with a red
squiggly. I can hover over offending token or statement and see what the error
is.
There are also a few mouse-free ways to do this.
First, I can hit `F8` to jump to the next one of these errors in the current
file. That will move my cursor to that location and display a small overlay
with the error details.
Second, assuming Vim mode, I can navigate my cursor over a specific highlighted
token and then hit `Shift+k`. That will pop open the same small overlay to
display the error details.
Third, I can hit `Cmd+Shift+M` to open the _Project Diagnostics_ tab which
displays a series of file buffer results with the offending lines and the error
description.