diff --git a/README.md b/README.md index 504b94f..866e72f 100644 --- a/README.md +++ b/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). -_1505 TILs and counting..._ +_1506 TILs and counting..._ --- @@ -942,6 +942,7 @@ _1505 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) diff --git a/rails/empty-find-by-returns-first-record.md b/rails/empty-find-by-returns-first-record.md new file mode 100644 index 0000000..39f0f3a --- /dev/null +++ b/rails/empty-find-by-returns-first-record.md @@ -0,0 +1,43 @@ +# 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.find_by(nil) +#=> # +``` + +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.