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

Compare commits

..

1 Commits

Author SHA1 Message Date
Mohammad Alyetama
ad0c2b9d84 Merge bc767a0ad3 into 1c4e37ed8a 2024-11-12 19:07:50 +09:00
11 changed files with 4 additions and 395 deletions

12
.vimrc
View File

@@ -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',
" })

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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