mirror of
https://github.com/jbranchaud/til
synced 2026-01-21 16:08:03 +00:00
Compare commits
1 Commits
6984685276
...
b92e6729ff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b92e6729ff |
@@ -10,7 +10,7 @@ pairing with smart people at Hashrocket.
|
|||||||
|
|
||||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||||
|
|
||||||
_1508 TILs and counting..._
|
_1503 TILs and counting..._
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -297,7 +297,6 @@ _1508 TILs and counting..._
|
|||||||
- [Configure Global gitignore File](git/configure-global-gitignore-file.md)
|
- [Configure Global gitignore File](git/configure-global-gitignore-file.md)
|
||||||
- [Configuring The Pager](git/configuring-the-pager.md)
|
- [Configuring The Pager](git/configuring-the-pager.md)
|
||||||
- [Copy A File From Another Branch](git/copy-a-file-from-another-branch.md)
|
- [Copy A File From Another Branch](git/copy-a-file-from-another-branch.md)
|
||||||
- [Count All Files Of Specific Type Tracked By Git](git/count-all-files-of-specific-type-tracked-by-git.md)
|
|
||||||
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
|
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
|
||||||
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
||||||
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
||||||
@@ -887,7 +886,6 @@ _1508 TILs and counting..._
|
|||||||
|
|
||||||
- [Access Instance Variables](python/access-instance-variables.md)
|
- [Access Instance Variables](python/access-instance-variables.md)
|
||||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||||
- [Dunder Methods](python/dunder-methods.md)
|
|
||||||
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
||||||
- [Test A Function With Pytest](python/test-a-function-with-pytest.md)
|
- [Test A Function With Pytest](python/test-a-function-with-pytest.md)
|
||||||
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
||||||
@@ -942,7 +940,6 @@ _1508 TILs and counting..._
|
|||||||
- [Demodulize A Class Name](rails/demodulize-a-class-name.md)
|
- [Demodulize A Class Name](rails/demodulize-a-class-name.md)
|
||||||
- [Different Ways To Add A Foreign Key Reference](rails/different-ways-to-add-a-foreign-key-reference.md)
|
- [Different Ways To Add A Foreign Key Reference](rails/different-ways-to-add-a-foreign-key-reference.md)
|
||||||
- [Disambiguate Where In A Joined Relation](rails/disambiguate-where-in-a-joined-relation.md)
|
- [Disambiguate Where In A Joined Relation](rails/disambiguate-where-in-a-joined-relation.md)
|
||||||
- [Empty find_by Returns First Record](rails/empty-find-by-returns-first-record.md)
|
|
||||||
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md)
|
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md)
|
||||||
- [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
- [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
||||||
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
||||||
@@ -1211,7 +1208,6 @@ _1508 TILs and counting..._
|
|||||||
- [Enumerate A Pairing Of Every Two Sequential Items](ruby/enumerate-a-pairing-of-every-two-sequential-items.md)
|
- [Enumerate A Pairing Of Every Two Sequential Items](ruby/enumerate-a-pairing-of-every-two-sequential-items.md)
|
||||||
- [Evaluating One-Off Commands](ruby/evaluating-one-off-commands.md)
|
- [Evaluating One-Off Commands](ruby/evaluating-one-off-commands.md)
|
||||||
- [Exclude Values From An Array](ruby/exclude-values-from-an-array.md)
|
- [Exclude Values From An Array](ruby/exclude-values-from-an-array.md)
|
||||||
- [Execute Several Commands With Backtick Heredoc](ruby/execute-several-commands-with-backtick-heredoc.md)
|
|
||||||
- [Exit A Process With An Error Message](ruby/exit-a-process-with-an-error-message.md)
|
- [Exit A Process With An Error Message](ruby/exit-a-process-with-an-error-message.md)
|
||||||
- [Expect A Method To Be Called And Actually Call It](ruby/expect-a-method-to-be-called-and-actually-call-it.md)
|
- [Expect A Method To Be Called And Actually Call It](ruby/expect-a-method-to-be-called-and-actually-call-it.md)
|
||||||
- [Extract A Column Of Data From A CSV File](ruby/extract-a-column-of-data-from-a-csv-file.md)
|
- [Extract A Column Of Data From A CSV File](ruby/extract-a-column-of-data-from-a-csv-file.md)
|
||||||
@@ -1465,7 +1461,6 @@ _1508 TILs and counting..._
|
|||||||
- [Global Substitution On The Previous Command](unix/global-substitution-on-the-previous-command.md)
|
- [Global Substitution On The Previous Command](unix/global-substitution-on-the-previous-command.md)
|
||||||
- [Globbing For All Directories In Zsh](unix/globbing-for-all-directories-in-zsh.md)
|
- [Globbing For All Directories In Zsh](unix/globbing-for-all-directories-in-zsh.md)
|
||||||
- [Globbing For Filenames In Zsh](unix/globbing-for-filenames-in-zsh.md)
|
- [Globbing For Filenames In Zsh](unix/globbing-for-filenames-in-zsh.md)
|
||||||
- [Gracefully Exit A Script With Trap](unix/gracefully-exit-a-script-with-trap.md)
|
|
||||||
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
||||||
- [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
- [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# Count All Files Of Specific Type Tracked By Git
|
|
||||||
|
|
||||||
I want to get a count of all the markdown files in my [TIL
|
|
||||||
repo](https://github.com/jbranchaud/til). Since all the files I care about are
|
|
||||||
tracked by `git`, I can use `git ls-files` to get a listing of all files. That
|
|
||||||
command on its own lists all files tracked by your git repository. Though there
|
|
||||||
are many other flags we can apply, that will do for my purposes.
|
|
||||||
|
|
||||||
By giving `git ls-files` a pattern to match against, I can turn up just, for
|
|
||||||
instance, markdown files (`*.md`). I can pipe that to `wc -l` to get a count
|
|
||||||
rather than exploding my terminal with a list of file names.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git ls-files '*.md' | wc -l
|
|
||||||
1503
|
|
||||||
```
|
|
||||||
|
|
||||||
That command includes `README.md` and `CONTRIBUTING.md`, but really I only want
|
|
||||||
to count the markdown files that constitute a TIL. Those all happen to be
|
|
||||||
nested under a single directory. So I can tweak the glob pattern like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git ls-files '*/*.md' | wc -l
|
|
||||||
1501
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man git-ls-files` for more details.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Dunder Methods
|
|
||||||
|
|
||||||
Python has all kinds of special, or rather, _magic_ methods that allow for
|
|
||||||
customizing all kinds of class behavior. There is `__init__()`, `__bool__()`,
|
|
||||||
and so many others.
|
|
||||||
|
|
||||||
The thing they all have in common is that their names are wrapped in _double
|
|
||||||
underscores_. This is why they are called _dunder methods_.
|
|
||||||
|
|
||||||
Some of these are used every single day, like the `__init__()` method for
|
|
||||||
defining how a class should create an object. Others, used from time to time,
|
|
||||||
are for overriding how comparisons or conversions happen. E.g. you may want to
|
|
||||||
override `__bool__()` or `__len__()` to customize the truthiness of a custom
|
|
||||||
class.
|
|
||||||
|
|
||||||
There are so many others, ones you probably haven't even heard of. To see a
|
|
||||||
full listing, check out this [cheat sheet of every dunder
|
|
||||||
method](https://www.pythonmorsels.com/every-dunder-method/#cheat-sheet).
|
|
||||||
|
|
||||||
Note: these are not to be confused with _dunder attributes_ which are things
|
|
||||||
like `__name__`, `__file__`, and `__version__` which correspond to a value that
|
|
||||||
you can access in a specific context rather than behavior to override.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Empty find_by Returns First Record
|
|
||||||
|
|
||||||
During a RubyConf 2024 talk, a speaker mentioned that if you pass `nil` to
|
|
||||||
[ActiveRecord's `#find_by`
|
|
||||||
method](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find_by),
|
|
||||||
it will return the first record from the database. This is a bit unintuitive,
|
|
||||||
so lets look at an example and then I'll show you why.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
> Book.first
|
|
||||||
#=> #<Book:0x00000001142e4c48 id: 13, title: "The Secret History", ... >
|
|
||||||
|
|
||||||
> Book.find_by(nil)
|
|
||||||
#=> #<Book:0x00000001142ca3c0 id: 13, title: "The Secret History", ... >
|
|
||||||
```
|
|
||||||
|
|
||||||
So, that is the same object in both cases, but why?
|
|
||||||
|
|
||||||
Our first hint is in the SQL that gets constructed when making that method
|
|
||||||
call.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Book Load (2.5ms) SELECT "books".* FROM "books" LIMIT $1 [["LIMIT", 1]]
|
|
||||||
```
|
|
||||||
|
|
||||||
It's grabbing all books and limiting to _one_ result.
|
|
||||||
|
|
||||||
Lets look at the underlying implementation of the `#find_by` method.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# File activerecord/lib/active_record/relation/finder_methods.rb, line 111
|
|
||||||
def find_by(arg, *args)
|
|
||||||
where(arg, *args).take
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Sure enough, the implementation is a `#where` followed by a `#take`. Since the
|
|
||||||
`#where` is receiving `nil` as its `arg`, there are no conditions _filtering_
|
|
||||||
the query. And the `#take` corresponds to the `limit 1`.
|
|
||||||
|
|
||||||
Knowing that, we can understand that we will also get the first record from the
|
|
||||||
database if we call `#find_by` with `{}`. Again, no conditions to filter on, so
|
|
||||||
give me all books limited to one.
|
|
||||||
|
|
||||||
One small caveat: notice how there is no `order by` clause in the above SQL
|
|
||||||
output. This differs from `Books.first` which implicitly does an order on the
|
|
||||||
`id` column. Though these method are likely to return the same result, the
|
|
||||||
ordering of `#find_by` is not guaranteed to be the same without an `order by`
|
|
||||||
clause.
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Execute Several Commands With Backtick Heredoc
|
|
||||||
|
|
||||||
A fun feature of Ruby is that we can execute a command in a subprocess just by
|
|
||||||
wrapping it in backticks.
|
|
||||||
|
|
||||||
For instance, we might shell out to `git` to check if a file is tracked:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
`git ls-files --error-unmatch #{file_path} 2>/dev/null`
|
|
||||||
$?.success?
|
|
||||||
```
|
|
||||||
|
|
||||||
But what if we need to execute several commands? Perhaps they depend on one
|
|
||||||
another. We want them to run in the same subprocess.
|
|
||||||
|
|
||||||
For this, we can use the backtick version of a heredoc. That is a special
|
|
||||||
version of a heredoc where the delimiter is wrapped in backticks.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
puts <<`SHELL`
|
|
||||||
# Set up trap
|
|
||||||
trap 'echo "Cleaning up temp files"; rm -f *.tmp' EXIT
|
|
||||||
|
|
||||||
# Create temporary file
|
|
||||||
echo "test data" > work.tmp
|
|
||||||
|
|
||||||
# Do some work
|
|
||||||
cat work.tmp
|
|
||||||
|
|
||||||
# Trap will clean up on exit
|
|
||||||
SHELL
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we set up a `trap` for file cleanup on exit, then create a file, then do
|
|
||||||
something with the file, and that's it, the process exits (triggering the
|
|
||||||
trap).
|
|
||||||
|
|
||||||
[source](https://ruby-doc.org/3.3.6/syntax/literals_rdoc.html#label-Here+Document+Literals)
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Gracefully Exit A Script With Trap
|
|
||||||
|
|
||||||
With `trap` you can intercept signals that would cause your script to exit and
|
|
||||||
then inject some additional behavior. Perhaps you want to make sure the script
|
|
||||||
cleans up after itself before it exists.
|
|
||||||
|
|
||||||
During this script's execution, it creates a file in the filesystem. It would
|
|
||||||
be nice to make sure that no matter path this script ends up down that it will
|
|
||||||
clean up after itself as it exits. We set up a `trap` that looks for the `EXIT`
|
|
||||||
signal to do this.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Set up trap
|
|
||||||
trap 'echo "Cleaning up temp files"; rm -f *.tmp' EXIT
|
|
||||||
|
|
||||||
# Create temporary file
|
|
||||||
echo "test data" > work.tmp
|
|
||||||
|
|
||||||
# Do some work
|
|
||||||
cat work.tmp
|
|
||||||
|
|
||||||
# Trap will clean up on exit
|
|
||||||
```
|
|
||||||
|
|
||||||
Whatever is in quotes is what the trap will execute when it is triggered. The
|
|
||||||
following one or more signals are what the trap listens for, in this case
|
|
||||||
`EXIT`.
|
|
||||||
|
|
||||||
[source](https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html)
|
|
||||||
Reference in New Issue
Block a user