Tips: spacer_template, find and find_by, ruby pattern matching
Sharing tips taken directly from my day-to-day development work. These are about rendering a collection with partials in Action View, reminder about Active Record query return values, and about pattern matching in Ruby.
Tip 1 - Action View spacer_template
option
Do you know about the spacer_template
option when rendering a collection using a partials? For example, if you render a collection of @books
like this:
<%= render partial: @books, spacer_template: "fancy_line" %>
Adding the spacer_template
option renders whatever partial you specify in between each pair of books. For the above example, we’d have a _fancy_line.html.erb
in our app/views/books
directory with whatever we want to render.
Note that we need to explicitly specify the key in render partial: @books
. Otherwise it doesn’t work.
In general, we can skip specifying the keys partial
and locals
and list the values only. Like this:
<%= render "book", book: @book %>
<%# which is the same as this %>
<%= render partial: "book", locals: { book: @book } %>
Rails can infer the partial file name and location and pass in a matching local variable using conventions. But this is the case when parital
and locals
are the only options we need. We added a third spacer_template
option above, so we need to specify partial
as the key.
I came across this while reading the Action View Rails guide.
Tip 2 - Active Record find_by vs. find
With Rails Active Record, we think of Book.find(42)
as a shorthand for Book.find_by(id: 42)
. But they are not exactly the same.
It’s good to keep in mind that:
find_by
returnsnil
if no record is foundfind
throws anRecordNotFound
exception if no matching book is found.
>> Book.find_by(id: 200)
Book Load (0.1ms) SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT ? [["id", 200], ["LIMIT", 1]]
=> nil
>> Book.find(200)
Book Load (0.8ms) SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT ? [["id", 200], ["LIMIT", 1]]
/Users/bhumi/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/activerecord-7.1.3/lib/active_record/core.rb:253:in `find': Couldn't find Book with 'id'=200 (ActiveRecord::RecordNotFound)
So find
is actually equivalent to find_by!
.
>> Book.find_by!(id: 200)
Book Load (0.1ms) SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT ? [["id", 200], ["LIMIT", 1]]
/Users/bhumi/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/activerecord-7.1.3/lib/active_record/relation/finder_methods.rb:413:in `raise_record_not_found_exception!': Couldn't find Book with [WHERE "books"."id" = ?] (ActiveRecord::RecordNotFound)
The SQL queries generated by find
, find_by
, and find_by!
is the same:
SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT ? [["id", 200], ["LIMIT", 1]]`.
Tip 3 - Ruby pattern matching
Did you know that Ruby has pattern matching? It was introduced in Ruby 2.7 as an experimental feature. As of Ruby 3.1 the experimental warning is removed and is fully supported.
There are two forms. One is a modified case
statement with in
clauses. The other syntax is using =>
. Here are some examples:
# With the `case` `in` pattern matching
?> case [1, 2, 3, 4, 5]
?> in [*pre, 2, 3, *post]
?> p pre
?> p post
>> end
[1]
[4, 5]
# With the => one line pattern matching
>> config = {db: {user: 'admin', password: 'abc123'}}
>> config => {db: {user:}}
>> user
=> "admin"
>> config => {db: {user: rename_user}}
>> rename_user
=> "admin"
I’m not sure if Ruby’s pattern matching is inspired by Elixir’s, which looks a little cleaner with the |
.
>> [head | tail] = [1, 2, 3]
>>head
1
>>tail
[2, 3]
One common use case in the documentation is around parsing complex JSON structures. I have yet to use it or see it in an application.
Subscribe for more Ruby, Rails, and Hotwire
Short posts, clear explanations