mirror of
https://github.com/jbranchaud/til
synced 2026-01-17 14:08:01 +00:00
Compare commits
1 Commits
35367e3f31
...
ad0c2b9d84
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0c2b9d84 |
12
.vimrc
12
.vimrc
@@ -9,15 +9,3 @@ function! CountTILs()
|
||||
endfunction
|
||||
|
||||
nnoremap <leader>c :call CountTILs()<cr>
|
||||
|
||||
augroup DisableMarkdownFormattingForTILReadme
|
||||
autocmd!
|
||||
autocmd BufRead ~/code/til/README.md autocmd! Format
|
||||
augroup END
|
||||
|
||||
" local til_readme_group = vim.api.nvim_create_augroup('DisableMarkdownFormattingForTILReadme', { clear = true })
|
||||
" vim.api.nvim_create_autocmd('BufRead', {
|
||||
" command = 'autocmd! Format',
|
||||
" group = til_readme_group,
|
||||
" pattern = vim.fn.expand '~/code/til/README.md',
|
||||
" })
|
||||
|
||||
10
README.md
10
README.md
@@ -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).
|
||||
|
||||
_1512 TILs and counting..._
|
||||
_1504 TILs and counting..._
|
||||
|
||||
---
|
||||
|
||||
@@ -887,8 +887,6 @@ _1512 TILs and counting..._
|
||||
|
||||
- [Access Instance Variables](python/access-instance-variables.md)
|
||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||
- [Dunder Methods](python/dunder-methods.md)
|
||||
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.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)
|
||||
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
||||
@@ -943,7 +941,6 @@ _1512 TILs and counting..._
|
||||
- [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)
|
||||
- [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 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)
|
||||
@@ -1020,7 +1017,6 @@ _1512 TILs and counting..._
|
||||
- [Serialize With fast_jsonapi In A Rails App](rails/serialize-with-fast-jsonapi-in-a-rails-app.md)
|
||||
- [Set A Timestamp Field To The Current Time](rails/set-a-timestamp-field-to-the-current-time.md)
|
||||
- [Set DateTime To Include Time Zone In Migrations](rails/set-datetime-to-include-time-zone-in-migrations.md)
|
||||
- [Set Default As SQL Function In Migration](rails/set-default-as-sql-function-in-migration.md)
|
||||
- [Set default_url_options For Entire Application](rails/set-default-url-options-for-entire-application.md)
|
||||
- [Set Schema Search Path](rails/set-schema-search-path.md)
|
||||
- [Set Statement Timeout For All Postgres Connections](rails/set-statement-timeout-for-all-postgres-connections.md)
|
||||
@@ -1213,7 +1209,6 @@ _1512 TILs and counting..._
|
||||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
@@ -1223,7 +1218,6 @@ _1512 TILs and counting..._
|
||||
- [Find The Min And Max With A Single Call](ruby/find-the-min-and-max-with-a-single-call.md)
|
||||
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
||||
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
||||
- [Forward All Arguments To Another Method](ruby/forward-all-arguments-to-another-method.md)
|
||||
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
||||
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
||||
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
||||
@@ -1250,7 +1244,6 @@ _1512 TILs and counting..._
|
||||
- [Navigate Back In The Browser With Capybara](ruby/navigate-back-in-the-browser-with-capybara.md)
|
||||
- [Next And Previous Floats](ruby/next-and-previous-floats.md)
|
||||
- [Or Operator Precedence](ruby/or-operator-precedence.md)
|
||||
- [Output Bytecode For A Ruby Program](ruby/output-bytecode-for-a-ruby-program.md)
|
||||
- [Override The Initial Sequence Value](ruby/override-the-initial-sequence-value.md)
|
||||
- [Parallel Bundle Install](ruby/parallel-bundle-install.md)
|
||||
- [Parse JSON Into An OpenStruct](ruby/parse-json-into-an-open-struct.md)
|
||||
@@ -1469,7 +1462,6 @@ _1512 TILs and counting..._
|
||||
- [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 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 With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
||||
|
||||
@@ -4,9 +4,7 @@ PostgreSQL's `between` construct allows you to make a comparison _between_
|
||||
two values (numbers, timestamps, etc.).
|
||||
|
||||
```sql
|
||||
> select *
|
||||
from generate_series(1,10) as numbers(a)
|
||||
where numbers.a between 3 and 6;
|
||||
> select * from generate_series(1,10) as numbers(a) where numbers.a between 3 and 6;
|
||||
a
|
||||
---
|
||||
3
|
||||
@@ -19,9 +17,7 @@ If you supply an empty range by using the larger of the two values first, an
|
||||
empty set will result.
|
||||
|
||||
```sql
|
||||
> select *
|
||||
from generate_series(1,10) as numbers(a)
|
||||
where numbers.a between 6 and 3;
|
||||
> select * from generate_series(1,10) as numbers(a) where numbers.a between 6 and 3;
|
||||
a
|
||||
---
|
||||
```
|
||||
@@ -30,9 +26,7 @@ Tacking `symmetric` onto the `between` construct is one way to avoid this
|
||||
issue.
|
||||
|
||||
```sql
|
||||
> select *
|
||||
from generate_series(1,10) as numbers(a)
|
||||
where numbers.a between symmetric 6 and 3;
|
||||
> select * from generate_series(1,10) as numbers(a) where numbers.a between symmetric 6 and 3;
|
||||
a
|
||||
---
|
||||
3
|
||||
@@ -45,5 +39,3 @@ issue.
|
||||
> that the argument to the left of AND be less than or equal to the argument
|
||||
> on the right. If it is not, those two arguments are automatically swapped,
|
||||
> so that a nonempty range is always implied.
|
||||
|
||||
[source](https://www.postgresql.org/docs/current/functions-comparison.html#:~:text=BETWEEN%20SYMMETRIC%20is%20like%20BETWEEN,nonempty%20range%20is%20always%20implied.)
|
||||
|
||||
@@ -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,86 +0,0 @@
|
||||
# Override The Boolean Context Of A Class
|
||||
|
||||
Everything in Python has a truthiness that can be checked with `bool()`. An
|
||||
empty list (`[]`) is falsy. A non-empty list (`[1,2,3]`) is truthy. Similar
|
||||
with numbers:
|
||||
|
||||
```python
|
||||
>>> bool(0)
|
||||
False
|
||||
>>> bool(1)
|
||||
True
|
||||
```
|
||||
|
||||
Any instance of an object is going to be truthy by default. If you want to
|
||||
control in what context an instance is considered truthy or falsy, you can
|
||||
override
|
||||
[`__bool__()`](https://docs.python.org/3/reference/datamodel.html#object.__bool__).
|
||||
If that's not implemented, but
|
||||
[`__len__()`](https://docs.python.org/3/reference/datamodel.html#object.__len__)
|
||||
is, then it will fallback to that.
|
||||
|
||||
Let's look at a few example classes:
|
||||
|
||||
```python
|
||||
class CartZero:
|
||||
def __init__(self, items=[]):
|
||||
self.items = items or []
|
||||
|
||||
class CartBool:
|
||||
def __init__(self, items=[]):
|
||||
self.items = items or []
|
||||
|
||||
def __bool__(self):
|
||||
print("__bool__() override")
|
||||
return bool(self.items)
|
||||
|
||||
class CartLen:
|
||||
def __init__(self, items=[]):
|
||||
self.items = items or []
|
||||
|
||||
def __len__(self):
|
||||
print("__len__() override")
|
||||
return len(self.items)
|
||||
|
||||
class CartBoolAndLen:
|
||||
def __init__(self, items=[]):
|
||||
self.items = items or []
|
||||
|
||||
def __len__(self):
|
||||
print("__len__() override")
|
||||
return len(self.items)
|
||||
|
||||
def __bool__(self):
|
||||
print("__bool__() override")
|
||||
return bool(self.items)
|
||||
|
||||
cart1 = CartZero()
|
||||
cart2 = CartBool()
|
||||
cart3 = CartLen()
|
||||
cart4 = CartBoolAndLen()
|
||||
|
||||
print("CartZero() -> %s" %(bool(cart1)))
|
||||
print('')
|
||||
print("CartBool() -> %s" %(bool(cart2)))
|
||||
print('')
|
||||
print("CartLen() -> %s" %(bool(cart3)))
|
||||
print('')
|
||||
print("CartBoolAndLen() -> %s" %(bool(cart4)))
|
||||
```
|
||||
|
||||
An 'empty' `Cart` be default is truthy. However, we can override some
|
||||
combination of `__bool__()` or `__len__()` to give it a boolean context that
|
||||
goes `false` when "empty".
|
||||
|
||||
```
|
||||
CartZero() -> True
|
||||
|
||||
__bool__() override
|
||||
CartBool() -> False
|
||||
|
||||
__len__() override
|
||||
CartLen() -> False
|
||||
|
||||
__bool__() override
|
||||
CartBoolAndLen() -> False
|
||||
```
|
||||
@@ -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,52 +0,0 @@
|
||||
# Set Default As SQL Function In Migration
|
||||
|
||||
With static default values, like `0`, `true`, or `'pending'`, we can set them
|
||||
directly as the value of `default`.
|
||||
|
||||
```ruby
|
||||
class CreateActionsTable < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
create_table :actions do |t|
|
||||
t.string :status, default: 'pending'
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
However, if we want our default value to be a SQL function like `now()`, we
|
||||
have to use a lambda.
|
||||
|
||||
Let's extend the above example to see what that looks like:
|
||||
|
||||
```ruby
|
||||
class CreateActionsTable < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
create_table :actions do |t|
|
||||
t.string :status, default: 'pending'
|
||||
|
||||
t.column :created_at, :timestamptz, default: -> { 'now()' }, null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If we need to alter the default of an existing table's column, we can do
|
||||
something like this:
|
||||
|
||||
```ruby
|
||||
class AddDefaultTimestampsToActions < ActiveRecord::Migration[7.2]
|
||||
def up
|
||||
change_column_default :actions, :created_at, -> { "now()" }
|
||||
change_column_default :actions, :updated_at, -> { "now()" }
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_default :actions, :created_at, nil
|
||||
change_column_default :actions, :updated_at, nil
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
I believe this functionality is available to Rails 5.0 and later.
|
||||
|
||||
[source](https://github.com/rails/rails/issues/27077#issuecomment-261155826)
|
||||
@@ -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,45 +0,0 @@
|
||||
# Forward All Arguments To Another Method
|
||||
|
||||
There are three types of arguments that a Ruby method can receive. Positional
|
||||
arguments, keyword arguments, and a block argument.
|
||||
|
||||
A method that deals with all three might be defined like this:
|
||||
|
||||
```ruby
|
||||
def forwarding_method(*args, **kwargs, &block)
|
||||
# implementation
|
||||
end
|
||||
```
|
||||
|
||||
Now lets say we have some concrete method that we want to forward these
|
||||
arguments to:
|
||||
|
||||
```ruby
|
||||
def concrete_method(*args, **kwargs)
|
||||
x = args.first || 1
|
||||
key, y = kwargs.first || [:a, 2]
|
||||
|
||||
puts "Dealing with #{x} and key #{key}: #{y}"
|
||||
|
||||
yield(x, y)
|
||||
end
|
||||
```
|
||||
|
||||
We could forward arguments the longhand way like this:
|
||||
|
||||
```ruby
|
||||
def forwarding_method(*args, **kwargs, &block)
|
||||
concrete_method(*args, **kwargs, &block)
|
||||
end
|
||||
```
|
||||
|
||||
However, since Ruby 2.7 we have access to a shorthand "triple-dot" syntax for
|
||||
forwarding all arguments.
|
||||
|
||||
```ruby
|
||||
def forwarding_method(...)
|
||||
concrete_method(...)
|
||||
end
|
||||
```
|
||||
|
||||
[source](https://ruby-doc.org/3.3.6/syntax/methods_rdoc.html#label-Argument+Forwarding)
|
||||
@@ -1,42 +0,0 @@
|
||||
# Output Bytecode For A Ruby Program
|
||||
|
||||
The `ruby` CLI comes with a flag to dump the disassembled YARV bytecode for the
|
||||
given Ruby program. This can be a fun way to explore how a Ruby program is
|
||||
interpreted under the hood.
|
||||
|
||||
Aaron Patterson demoed this behavior during his RubyConf 2024 talk.
|
||||
|
||||
Pass the `--dump` flag with `insns` along with either the path to your file or
|
||||
an inline bit of Ruby.
|
||||
|
||||
Here is a really basic example:
|
||||
|
||||
```bash
|
||||
❯ ruby --dump=insns -e '2 + 3'
|
||||
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,5)> (catch: false)
|
||||
0000 putobject 2 ( 1)[Li]
|
||||
0002 putobject 3
|
||||
0004 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
|
||||
0006 leave
|
||||
```
|
||||
|
||||
And another quite basic example, but with local variables this time:
|
||||
|
||||
```bash
|
||||
❯ ruby --dump=insns -e 'x = 2; y = 3; x + y'
|
||||
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,19)> (catch: false)
|
||||
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
|
||||
[ 2] x@0 [ 1] y@1
|
||||
0000 putobject 2 ( 1)[Li]
|
||||
0002 setlocal_WC_0 x@0
|
||||
0004 putobject 3
|
||||
0006 setlocal_WC_0 y@1
|
||||
0008 getlocal_WC_0 x@0
|
||||
0010 getlocal_WC_0 y@1
|
||||
0012 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
|
||||
0014 leave
|
||||
```
|
||||
|
||||
If you want to dig in to how to read everything that is going on in these
|
||||
outputs, I'd recommend checking out this [Advent of YARV
|
||||
series](https://kddnewton.com/2022/11/30/advent-of-yarv-part-0.html)
|
||||
@@ -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