mirror of
https://github.com/jbranchaud/til
synced 2026-01-03 15:18:01 +00:00
Compare commits
18 Commits
fbebc3e5ee
...
copilot/up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c58bfbb37f | ||
|
|
9b5af6a535 | ||
|
|
62d194f492 | ||
|
|
7a7a0faf94 | ||
|
|
d980514bff | ||
|
|
db26fc97c6 | ||
|
|
8094448877 | ||
|
|
883b3e6ee6 | ||
|
|
57c4954d6f | ||
|
|
86a7815a9f | ||
|
|
676038e992 | ||
|
|
01fd503a92 | ||
|
|
8b718aee4f | ||
|
|
88f49de7f3 | ||
|
|
9f9fce7835 | ||
|
|
65a4d0ef3d | ||
|
|
6c8a5eb36d | ||
|
|
fed722d7fe |
17
README.md
17
README.md
@@ -8,9 +8,9 @@ warrant a full blog post. These are things I've picked up by [Learning In
|
||||
Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and
|
||||
pairing with smart people at Hashrocket.
|
||||
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
|
||||
|
||||
_1702 TILs and counting..._
|
||||
_1715 TILs and counting..._
|
||||
|
||||
See some of the other learning resources I work on:
|
||||
|
||||
@@ -448,6 +448,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
|
||||
### GitHub
|
||||
|
||||
- [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md)
|
||||
- [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md)
|
||||
- [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md)
|
||||
- [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md)
|
||||
@@ -505,6 +506,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md)
|
||||
- [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md)
|
||||
- [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md)
|
||||
- [Specify Default Team And App For Project](heroku/specify-default-team-and-app-for-project.md)
|
||||
- [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md)
|
||||
|
||||
### HTML
|
||||
@@ -663,6 +665,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
### jj
|
||||
|
||||
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
|
||||
- [Describe Current Changes And Create New Change](jj/describe-current-changes-and-create-new-change.md)
|
||||
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
|
||||
- [Squash Changes Into Parent Commit Interactively](jj/squash-changes-into-parent-commit-interactively.md)
|
||||
|
||||
@@ -1164,6 +1167,8 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md)
|
||||
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md)
|
||||
- [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
|
||||
- [Run Dev Processes With Overmind Instead Of Foreman](rails/run-dev-processes-with-overmind-instead-of-foreman.md)
|
||||
- [Run Rails Console With Remote Dokku App](rails/run-rails-console-with-remote-dokku-app.md)
|
||||
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md)
|
||||
- [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md)
|
||||
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
|
||||
@@ -1359,6 +1364,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md)
|
||||
- [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md)
|
||||
- [Create Mock Class That Can Be Overridden](ruby/create-mock-class-that-can-be-overridden.md)
|
||||
- [Create A Module Of Utility Functions](ruby/create-a-module-of-utility-functions.md)
|
||||
- [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md)
|
||||
- [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md)
|
||||
- [Decompose Unicode Character With Diacritic Mark](ruby/decompose-unicode-character-with-diacritic-mark.md)
|
||||
@@ -1406,12 +1412,14 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [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)
|
||||
- [Join URI Path Parts](ruby/join-uri-path-parts.md)
|
||||
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
|
||||
- [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md)
|
||||
- [Limit Split](ruby/limit-split.md)
|
||||
- [List The Running Ruby Version](ruby/list-the-running-ruby-version.md)
|
||||
- [Listing Local Variables](ruby/listing-local-variables.md)
|
||||
- [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md)
|
||||
- [Make Structs Easier To Use With Keyword Initialization](ruby/make-structs-easier-to-use-with-keyword-initialization.md)
|
||||
- [Map With Index Over An Array](ruby/map-with-index-over-an-array.md)
|
||||
- [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md)
|
||||
- [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md)
|
||||
@@ -1439,6 +1447,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [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)
|
||||
- [Reference Hash Key With Safe Navigation](ruby/reference-hash-key-with-safe-navigation.md)
|
||||
- [Regenerate Lock File With Newer Bundler](ruby/regenerate-lock-file-with-newer-bundler.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)
|
||||
@@ -1498,6 +1507,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md)
|
||||
- [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md)
|
||||
- [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md)
|
||||
- [Reference The Full Match In The Replacement](sed/reference-the-full-match-in-the-replacement.md)
|
||||
- [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md)
|
||||
|
||||
### Shell
|
||||
@@ -1524,6 +1534,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
|
||||
### Taskfile
|
||||
|
||||
- [Create Interactive Picker For Set Of Subtasks](taskfile/create-interactive-picker-for-set-of-subtasks.md)
|
||||
- [Run A Task If It Meets Criteria](taskfile/run-a-task-if-it-meets-criteria.md)
|
||||
|
||||
### tmux
|
||||
@@ -1559,6 +1570,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md)
|
||||
- [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md)
|
||||
- [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md)
|
||||
- [Set Up Forwarding Prefix For Nested Session](tmux/set-up-forwarding-prefix-for-nested-session.md)
|
||||
- [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md)
|
||||
- [Swap Split Panes](tmux/swap-split-panes.md)
|
||||
- [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md)
|
||||
@@ -1715,6 +1727,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
||||
- [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md)
|
||||
- [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md)
|
||||
- [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md)
|
||||
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
|
||||
- [Repeat Yourself](unix/repeat-yourself.md)
|
||||
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
|
||||
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
|
||||
|
||||
@@ -49,6 +49,7 @@ tasks:
|
||||
cmds:
|
||||
- git add NOTES.md
|
||||
- git commit -m "Update notes - $(date '+%Y-%m-%d %H:%M')"
|
||||
- git pull --rebase
|
||||
- git push
|
||||
status:
|
||||
- git diff --exit-code NOTES.md
|
||||
|
||||
25
github/access-your-github-profile-photo.md
Normal file
25
github/access-your-github-profile-photo.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Access Your GitHub Profile Photo
|
||||
|
||||
Let's say I have my [GitHub profile](https://github.com/jbranchaud) pulled up in
|
||||
the browser.
|
||||
|
||||
```
|
||||
https://github.com/jbranchaud
|
||||
```
|
||||
|
||||
If I then add `.png` to the end of that in the URL bar:
|
||||
|
||||
```
|
||||
https://github.com/jbranchaud.png
|
||||
```
|
||||
|
||||
I'll be redirected to the URL where the full image file lives. In my case:
|
||||
|
||||
```
|
||||
https://avatars.githubusercontent.com/u/694063?v=4
|
||||
```
|
||||
|
||||
You can pull up yours `https://github.com/<username>.png` to access your profile
|
||||
image.
|
||||
|
||||
[source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82)
|
||||
34
heroku/specify-default-team-and-app-for-project.md
Normal file
34
heroku/specify-default-team-and-app-for-project.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Specify Default Team And App For Project
|
||||
|
||||
Typically when you run commands with the Heroku CLI you'll need to specify the
|
||||
name of the app on Heroku you're targeting with the `--app` flag. However, to
|
||||
first see the names of the apps you may want to run `heroku apps` (or `heroku
|
||||
list`). That will list the apps for your default team.
|
||||
|
||||
If you need to see apps for a different team (i.e. organization), you'll need to
|
||||
specify that team either with the `--team` flag or by setting that as an
|
||||
environment variable.
|
||||
|
||||
Here I do the latter in an `.envrc` file:
|
||||
|
||||
```
|
||||
# Heroku
|
||||
export HEROKU_ORGANIZATION=visualmode
|
||||
```
|
||||
|
||||
Once that is set and the environment reloaded, running `heroku apps` will show
|
||||
the apps specific to that team on Heroku.
|
||||
|
||||
Similarly, if you want to set a default app for your project so that you don't
|
||||
have to always specify the `--app` flag, you can update your `.envrc`
|
||||
accordingly.
|
||||
|
||||
```
|
||||
# Heroku
|
||||
export HEROKU_ORGANIZATION=visualmode
|
||||
export HEROKU_APP=my-app
|
||||
```
|
||||
|
||||
I had a hard time finding official documentation for this which is why I'm
|
||||
writing this up here. I've manually verified this works with my own team and
|
||||
app.
|
||||
28
jj/describe-current-changes-and-create-new-change.md
Normal file
28
jj/describe-current-changes-and-create-new-change.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Describe Current Changes And Create New Change
|
||||
|
||||
One of the first patterns I learned with `jj` was a pair of commands to
|
||||
essentially "commit" the working copy and start a fresh, new change. So if I am
|
||||
done making some changes, I can add a description to the `(no description)`
|
||||
working copy and then start a new working copy _change_.
|
||||
|
||||
```bash
|
||||
$ jj describe -m "Add status subcommand to show current status"
|
||||
$ jj new
|
||||
```
|
||||
|
||||
I learned from [Steve](https://steveklabnik.com/) in the [jj
|
||||
discord](https://discord.gg/dkmfj3aGQN) that a shorthand for this pattern is to
|
||||
use the `jj commit` command directly.
|
||||
|
||||
> When called without path arguments or `--interactive`, `jj commit` is
|
||||
> equivalent to `jj describe` followed by `jj new`.
|
||||
|
||||
That means, instead of the above pair of commands, I could have done:
|
||||
|
||||
```bash
|
||||
$ jj commit -m "Add status subcommand to show current status"
|
||||
```
|
||||
|
||||
That would have had the same result in my case. However, notice the caveats
|
||||
mentioned in the quote above and check out `man jj-commit` for more details on
|
||||
that.
|
||||
46
rails/run-dev-processes-with-overmind-instead-of-foreman.md
Normal file
46
rails/run-dev-processes-with-overmind-instead-of-foreman.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Run Dev Processes With Overmind Instead Of Foreman
|
||||
|
||||
Most Rails projects that I have worked on have used
|
||||
[`foreman`](https://github.com/ddollar/foreman) as a development dependency for
|
||||
running all the processes declared in your Procfile (`Procfile.dev`). As far as
|
||||
having a single command to run everything (Rails server, asset building,
|
||||
worker(s), etc.), it does the job.
|
||||
|
||||
`foreman` has some serious points of friction though. The one that really stands
|
||||
out to me is that when I try to debug the development Rails server with
|
||||
`binding.irb` or `binding.pry`, the other processes tend to interfere.
|
||||
|
||||
The alternative to `foreman` that I've been trying out recently is
|
||||
[`overmind`](https://github.com/DarthSim/overmind). A specific selling point of
|
||||
`overmind` is that it runs all the development processes in a `tmux` session.
|
||||
That means you can individually connect to, inspect, and restart each process.
|
||||
|
||||
Once you've installed `overmind` (`brew install overmind`), then you can easily
|
||||
swap it in for `foreman` like so:
|
||||
|
||||
```bash
|
||||
$ overmind start -f Procfile.dev
|
||||
```
|
||||
|
||||
You can connect to any of those processes directly:
|
||||
|
||||
```bash
|
||||
$ overmind connect sidekiq
|
||||
```
|
||||
|
||||
When you want to `binding.irb` the Rails server, you can specifically connect to
|
||||
the `web` process to do that.
|
||||
|
||||
```bash
|
||||
$ overmind connect web
|
||||
```
|
||||
|
||||
If you need to stop all the process, you can run the `kill` subcommand.
|
||||
|
||||
```bash
|
||||
$ overmind kill
|
||||
```
|
||||
|
||||
Lastly, if you have a `bin/dev` script in your project, it is probably using
|
||||
`foreman`. If you and your team prefer `overmind`, then update that script
|
||||
accordingly and you can simply run `bin/dev` going forward.
|
||||
49
rails/run-rails-console-with-remote-dokku-app.md
Normal file
49
rails/run-rails-console-with-remote-dokku-app.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Run Rails Console With Remote Dokku App
|
||||
|
||||
Whenever I want to `rails console` into the _staging_ server of an app I'm
|
||||
working on, I first have to `ssh` into server and then I have to come up with
|
||||
the [`dokku`](https://dokku.com/) command to run `rails console` against the app
|
||||
on that server.
|
||||
|
||||
```bash
|
||||
local> ssh app-staging # app-staging is an SSH alias
|
||||
staging> dokku run my-app rails console
|
||||
```
|
||||
|
||||
I figured out how to reduce the friction of this by collapsing it into a single
|
||||
command that I can run locally. I can remotely run the `dokku` command with
|
||||
`ssh` using an interactive session (`-t`).
|
||||
|
||||
```bash
|
||||
local> ssh -t app-staging dokku run my-app rails console
|
||||
```
|
||||
|
||||
That will open up a `rails console` session directly in the current shell
|
||||
session via a remote SSH connection. The `-t` flag is important because that
|
||||
makes the session interactive so that I can interact with the REPL.
|
||||
|
||||
I've even packaged this up into a bin script (`bin/staging-console`) with a
|
||||
couple checks to enhance the DX. I won't put the whole thing here, but the gist
|
||||
of it is:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$DOKKU_STAGING_SSH_ALIAS" ]; then
|
||||
echo "Error: DOKKU_STAGING_SSH_ALIAS environment variable is not set."
|
||||
echo ""
|
||||
# echo more help details here ...
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if SSH alias exists
|
||||
# ...
|
||||
|
||||
# Check if we can reach the server
|
||||
# ...
|
||||
|
||||
# Run the console
|
||||
ssh -t "$DOKKU_STAGING_SSH_ALIAS" dokku run my-app rails console "$@"
|
||||
```
|
||||
59
ruby/create-a-module-of-utility-functions.md
Normal file
59
ruby/create-a-module-of-utility-functions.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Create A Module Of Utility Functions
|
||||
|
||||
In my [latest blog post](https://www.visualmode.dev/create-a-module-of-utility-functions-in-ruby),
|
||||
I went into full detail about how the [`Module#module_function` method](https://ruby-doc.org/3.4.1/Module.html#method-i-module_function) works.
|
||||
It creates both a module of utility functions that we can access directly on
|
||||
that module like we would with `self` methods. It can also be included in a
|
||||
class as a way of sharing copies of those utility functions with the class. A
|
||||
key point to them being copies is that they can then be overridden by the
|
||||
including class.
|
||||
|
||||
Here is the example I used in the blog post:
|
||||
|
||||
```ruby
|
||||
module MarkdownHelpers
|
||||
module_function
|
||||
|
||||
def heading(text, level = 1)
|
||||
("#" * level) + " #{text}"
|
||||
end
|
||||
|
||||
def link(text, href)
|
||||
"[#{text}](#{href})"
|
||||
end
|
||||
|
||||
def image(alt_text, href)
|
||||
"!#{link(alt_text, href)}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
I won't cover everything that the blog post covers, but what I found really nice
|
||||
about this pattern is that I can call those utility functions directly with the
|
||||
module as the receiver:
|
||||
|
||||
```bash
|
||||
$ ruby -r ./markdown_helpers.rb -e 'puts MarkdownHelpers.link("Click here", "https://example.com")'
|
||||
[Click here](https://example.com)
|
||||
```
|
||||
|
||||
The alternative to this generally looks like:
|
||||
|
||||
```ruby
|
||||
module MarkdownHelpers
|
||||
def self.heading(text, level = 1)
|
||||
("#" * level) + " #{text}"
|
||||
end
|
||||
|
||||
def self.link(text, href)
|
||||
"[#{text}](#{href})"
|
||||
end
|
||||
|
||||
def self.image(alt_text, href)
|
||||
"!#{link(alt_text, href)}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
That would be fine, but we completely lose out on the ability to include it as a
|
||||
mix-in with other classes.
|
||||
43
ruby/join-uri-path-parts.md
Normal file
43
ruby/join-uri-path-parts.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Join URI Path Parts
|
||||
|
||||
The
|
||||
[`URI.join`](https://ruby-doc.org/stdlib-2.5.1/libdoc/uri/rdoc/URI.html#method-c-join)
|
||||
method seems like a handy way to combine a base URL with some subpath. However,
|
||||
there are some subtle gotchas depending on where forward slashes appear in the
|
||||
two arguments.
|
||||
|
||||
Let's first look at the, in my opinion, desired behavior:
|
||||
|
||||
```ruby
|
||||
> URI.join("https://example.com/api/v1/", "users")
|
||||
=> #<URI::HTTPS https://example.com/api/v1/users>
|
||||
```
|
||||
|
||||
The base URL has a trailing slash and the path that I want to join to it has no
|
||||
leading slash. The result is a path where `users` is joined to the end of the
|
||||
base URL. That's what I'm looking for.
|
||||
|
||||
Now, let's see some variations on the above approach that give results that I
|
||||
wasn't expecting and don't want.
|
||||
|
||||
```ruby
|
||||
> URI.join("https://example.com/api/v1", "/users") # 1
|
||||
=> #<URI::HTTPS https://example.com/users>
|
||||
> URI.join("https://example.com/api/v1", "users") # 2
|
||||
=> #<URI::HTTPS https://example.com/api/users>
|
||||
> URI.join("https://example.com/api/v1/", "/users") # 3
|
||||
=> #<URI::HTTPS https://example.com/users>
|
||||
```
|
||||
|
||||
1. No trailing slash on the base URL. Leading slash on the path to join. The
|
||||
path portion of the base URL is wiped out and `/users` is joined in.
|
||||
2. No trailing slash on the base URL. No leading slash on the path to join. The
|
||||
`users` path replaces the last part of the path in the base URL.
|
||||
3. Both a trailing slash in the base URL and a leading slash in the path to
|
||||
join. Same behavior as 1.
|
||||
|
||||
I have two takeaways from this:
|
||||
- Use with caution. If I'm going to use `URI.join` for this purpose, I need to
|
||||
be careful to only use the form in the first code block.
|
||||
- The `URI.join` method is probably meant to be primarily used to join a domain
|
||||
(e.g. `http://example.com`) that has no path with some path segment.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Make Structs Easier To Use With Keyword Initialization
|
||||
|
||||
Typically a [`Struct`](https://ruby-doc.org/3.4.1/Struct.html#method-c-new) in
|
||||
Ruby is defined and initialized like so:
|
||||
|
||||
```ruby
|
||||
> Subscriber = Struct.new(:email, :first_name, :status, :tags)
|
||||
=> Subscriber
|
||||
> s1 = Subscriber.new('bob.burgers@example.com', 'Bob', :active, [:food, :family])
|
||||
=> #<struct Subscriber email="bob.burgers@example.com", first_name="Bob", status=:active, tags=[:food, :family]>
|
||||
> s1.email
|
||||
=> "bob.burgers@example.com"
|
||||
```
|
||||
|
||||
That's a nice way to structure light-weight objects.
|
||||
|
||||
A potential challenge with multi-argument `Struct` definitions like this,
|
||||
especially when they aren't colocated with initialization, is that it can be
|
||||
hard to remember or distinguish the argument order when initializing an instance
|
||||
of one.
|
||||
|
||||
Ruby 2.5 added the `keyword_init` option to help with this exact issue. When
|
||||
that option is set to `true` for a `Struct` definition, then we get to
|
||||
initialize it with keyword arguments rather than positional arguments.
|
||||
|
||||
```ruby
|
||||
> Subscriber = Struct.new(:email, :first_name, :status, :tags, keyword_init: true)
|
||||
=> Subscriber(keyword_init: true)
|
||||
* s1 = Subscriber.new(
|
||||
* first_name: 'Bob',
|
||||
* email: 'bob.burgers@example.com',
|
||||
* tags: [:food, :family],
|
||||
* status: :active
|
||||
> )
|
||||
=> #<struct Subscriber email="bob.burgers@example.com", first_name="Bob", status=:active, tags=[:food, :family]>
|
||||
> s1.email
|
||||
=> "bob.burgers@example.com"
|
||||
```
|
||||
|
||||
Notice I have to use keyword arguments now and that because of that I can
|
||||
organize them in whatever order makes sense. Coming back to view this line of
|
||||
code later, it is easy to see attribute each value corresponds to.
|
||||
|
||||
[source](https://www.bigbinary.com/blog/ruby-2-5-allows-creating-structs-with-keyword-arguments)
|
||||
34
ruby/regenerate-lock-file-with-newer-bundler.md
Normal file
34
ruby/regenerate-lock-file-with-newer-bundler.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Regenerate Lock File With Newer Bundler
|
||||
|
||||
While upgrading to the latest Ruby version (4.0.0), I also wanted to upgrade the
|
||||
version of `bundler` that my project uses. This shows up at the bottom of the
|
||||
`Gemfile.lock` file as the `BUNDLED WITH` line. Despite installing the latest
|
||||
version of `bundler`, I get the following message when I try to install
|
||||
dependencies.
|
||||
|
||||
```bash
|
||||
$ bundle install
|
||||
|
||||
Bundler 4.0.3 is running, but your lockfile was generated with 2.6.2.
|
||||
Installing Bundler 2.6.2 and restarting using that version.
|
||||
...
|
||||
```
|
||||
|
||||
Instead, what we need to tell `bundle` to update the locked version of `bundler`
|
||||
in the `Gemfile.lock`.
|
||||
|
||||
```bash
|
||||
$ bundle update --bundler
|
||||
|
||||
Fetching gem metadata from https://rubygems.org/.........
|
||||
Resolving dependencies...
|
||||
Bundle updated!
|
||||
```
|
||||
|
||||
The `--bundler` flag for `bundle-update` says the following:
|
||||
|
||||
> Update the locked version of bundler to the invoked bundler version.
|
||||
|
||||
So we could pass a specific `bundler` version to that flag, but in this case I
|
||||
want to use the version I'm invoking it with which is the latest that I just
|
||||
installed.
|
||||
27
sed/reference-the-full-match-in-the-replacement.md
Normal file
27
sed/reference-the-full-match-in-the-replacement.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Reference The Full Match In The Replacement
|
||||
|
||||
The `&` can be used in the replacement part of a `sed` expression as reference
|
||||
to the string match for this iteration of the expression. The occurrence of `&`
|
||||
will be replaced with that entire match.
|
||||
|
||||
As the `sed` man page puts it:
|
||||
|
||||
> An ampersand (“&”) appearing in the replacement is replaced by the string
|
||||
> matching the RE.
|
||||
|
||||
I made use of this recently with [a `sed` expression that was evaluating a list
|
||||
of filenames that I wanted to construct into a sequence of `mv`
|
||||
commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md). I needed
|
||||
the filename that I was matching on to appear as the first argument of the `mv`
|
||||
command I was constructing.
|
||||
|
||||
Here is what that looks like:
|
||||
|
||||
```bash
|
||||
$ ls *.pdf |
|
||||
sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/'
|
||||
```
|
||||
|
||||
Notice right after `mv` in literal quotes is the `&`. That will be replaced in
|
||||
the resulting replacement with the full matching string of the regular
|
||||
expression in the first part of the sed statement (`s/<RE>/`).
|
||||
74
taskfile/create-interactive-picker-for-set-of-subtasks.md
Normal file
74
taskfile/create-interactive-picker-for-set-of-subtasks.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Create Interactive Picker For Set Of Subtasks
|
||||
|
||||
For [my TIL repo](https://github.com/jbranchaud/til), I have a `Taskfile.yml`
|
||||
that defines a set of `notes:*` tasks for interacting with a `NOTES.md` file
|
||||
that lives in a private Git submodule.
|
||||
|
||||
I wanted to make it easier on myself to not have to remember all the different
|
||||
`notes` subtasks, so I created a helper task to make it easy to see the options
|
||||
and run one.
|
||||
|
||||
A summary of the Taskfile is shown below including the entirety of the `notes`
|
||||
task. That task will parse a listing of the available tasks (via `task --list`
|
||||
and some `sed` commands) and pass those to `fzf` to provide an interactive
|
||||
picker of the available subtasks.
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
notes:
|
||||
desc: Interactive picker for notes tasks
|
||||
cmds:
|
||||
- |
|
||||
TASK=$(task --list | grep "^\* notes:" | sed 's/^\* notes://' | sed 's/\s\+/ - /' | fzf --prompt="Select notes task: " --height=40% --reverse) || true
|
||||
if [ -n "$TASK" ]; then
|
||||
TASK_NAME=$(echo "$TASK" | awk '{print $1}' | sed 's/:$//')
|
||||
task notes:$TASK_NAME
|
||||
fi
|
||||
interactive: true
|
||||
silent: true
|
||||
|
||||
notes:edit:
|
||||
...
|
||||
|
||||
notes:sync:
|
||||
...
|
||||
|
||||
notes:open:
|
||||
...
|
||||
|
||||
notes:push:
|
||||
...
|
||||
|
||||
notes:status:
|
||||
...
|
||||
|
||||
notes:pull:
|
||||
...
|
||||
|
||||
notes:diff:
|
||||
...
|
||||
|
||||
notes:log:
|
||||
...
|
||||
```
|
||||
|
||||
Now I can run the `notes` task to get a summary and interactive picker that
|
||||
looks like the following:
|
||||
|
||||
```sh
|
||||
❯ task notes
|
||||
Select notes task:
|
||||
9/9
|
||||
> │ Interactive picker for notes tasks
|
||||
diff: Show uncommitted changes in notes
|
||||
edit: All-in-one edit, commit, and push notes
|
||||
log: Show recent commit history for notes
|
||||
open: Opens NOTES.md (syncs latest changes first) in default editor
|
||||
pull: Pull latest changes (alias for sync)
|
||||
push: Commit and push changes to notes submodule
|
||||
status: Check status of notes submodule
|
||||
sync: Sync latest changes from the notes submodule
|
||||
```
|
||||
|
||||
It pulls in the subtask name and description. I can then use `fzf`'s navigation
|
||||
and filtering to narrow down and select the task I want to run.
|
||||
24
tmux/set-up-forwarding-prefix-for-nested-session.md
Normal file
24
tmux/set-up-forwarding-prefix-for-nested-session.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Set Up Forwarding Prefix For Nested Session
|
||||
|
||||
I use
|
||||
[`ctrl-z`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L57)
|
||||
(instead of `ctrl-b`) for my tmux prefix key. I also have [`ctrl-z
|
||||
ctrl-z`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L138-L139)
|
||||
configured to toggle back and forth between the previously visited window.
|
||||
|
||||
With that in mind, I needed to set up a specific keybinding to send the prefix
|
||||
key to an inner (nested) tmux session. That's because sometimes, like with a
|
||||
tool such as `overmind`, you can end up connected to a tmux session while
|
||||
already within a tmux session.
|
||||
|
||||
So, I have `Ctrl-z a` send the prefix key to the inner tmux session. This is
|
||||
what I added to my
|
||||
[`tmux.conf`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L167-L168):
|
||||
|
||||
```
|
||||
# send prefix to inner tmux session (C-z a)
|
||||
bind-key a send-prefix
|
||||
```
|
||||
|
||||
Simply doing `Ctrl-z d` will detach me from the outer tmux session. If I want to
|
||||
detach from an inner tmux session, I can use my new keybinding -- `Ctrl-z a d`.
|
||||
51
unix/rename-a-bunch-of-files-by-constructing-mv-commands.md
Normal file
51
unix/rename-a-bunch-of-files-by-constructing-mv-commands.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Rename A Bunch Of Files By Constructing mv Commands
|
||||
|
||||
I downloaded a bunch of bank statements as PDFs. On the upside they all were
|
||||
consistently named. On the downside they used an unhelpful date format. With a
|
||||
date format that puts year before month before day, the files easily sort
|
||||
alphanumerically. However, these filenames used a date format that put month
|
||||
before day before year.
|
||||
|
||||
Here is a subset of the files
|
||||
|
||||
```bash
|
||||
$ ls *.pdf
|
||||
'012524 Statement.pdf'
|
||||
'012725 Statement.pdf'
|
||||
'022624 Statement.pdf'
|
||||
'022625 Statement.pdf'
|
||||
'032524 Statement.pdf'
|
||||
'032525 Statement.pdf'
|
||||
```
|
||||
|
||||
Notice they are named with `MMDDYY Statement.pdf`. I would instead like for them
|
||||
to be named as `YYYY-MM-DD-statement.pdf`.
|
||||
|
||||
I can generate a series of `mv` statements that then get piped to `sh` which
|
||||
_evaluates_ them. But first, let's do a dry run of a `sed` statement that
|
||||
rearranges the date parts.
|
||||
|
||||
```bash
|
||||
$ ls *.pdf | sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/'
|
||||
mv "012524 Statement.pdf" "2024-01-25-statement.pdf"
|
||||
mv "012725 Statement.pdf" "2025-01-27-statement.pdf"
|
||||
mv "022624 Statement.pdf" "2024-02-26-statement.pdf"
|
||||
mv "022625 Statement.pdf" "2025-02-26-statement.pdf"
|
||||
mv "032524 Statement.pdf" "2024-03-25-statement.pdf"
|
||||
mv "032525 Statement.pdf" "2025-03-25-statement.pdf"
|
||||
```
|
||||
|
||||
The way this works is that all the `pdf` files in the current directly get
|
||||
listed out. That gets piped to a `sed` statement that matches on capture groups
|
||||
against the first three pairs of characters (the date parts) in the filenames.
|
||||
It matches on the rest of the filename (` Statement.pdf`). This is then replaced
|
||||
by a `mv `, the full match of the original filename (`&`), and then the new
|
||||
filename made up of the rearranged date parts.
|
||||
|
||||
I can then pipe it to `sh` to run those `mv` commands.
|
||||
|
||||
```bash
|
||||
$ ls *.pdf |
|
||||
sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/' |
|
||||
sh
|
||||
```
|
||||
Reference in New Issue
Block a user