1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-03 07:08:01 +00:00

Compare commits

...

3 Commits

4 changed files with 126 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://visualmode.kit.com/newsletter).
_1703 TILs and counting..._
_1706 TILs and counting..._
See some of the other learning resources I work on:
@@ -1413,6 +1413,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [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)
@@ -1499,6 +1500,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
@@ -1716,6 +1718,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)

View File

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

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

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