コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

RailsでViewのErrorを減らす場合にはぼっち演算子を活用する

日頃のコードレビューの言語化です。

前提

  • Ruby on Railsのバージョンとしては6ぐらいを想定しています
  • viewの記述にはerbを想定しております。

よくあるケース

例えば下記のようなテーブル構成をしているModelがあるとする

  create_table "users", force: :cascade do |t|
    t.string "uid", null: false
    t.string "name", null: false
    t.string "image_url", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
  
    create_table "posts", force: :cascade do |t|
    t.bigint "owner_id"
    t.string "title", null: false
    t.text "homepage_url", null: false
    t.text "message", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["owner_id"], name: "index_posts_on_owner_id"
  end
class User < ApplicationRecord
  has_many :posts
end
class Post < ApplicationRecord
  belongs_to :user, foreign_key: :owner_id
end

そしてviewで以下のように記述したくなる事がある。

<div>
  最後の投稿は「<%= @user.posts.last.title =>」でした!
</div>

このような場合にもし、@user に対応するPostのレコードが1つもないと nil.last ということをやろうとしてエラーになってしまう。

解決策の一例

このような場合、 @user.posts.last.title が取得できないことを検知して、その内容を返せばいいので、ぼっち演算子.& )を活用する。

今回の例では present? を利用する。 present?nilに対しても備え付けられているメソッドなので、ぼっち演算子と一緒に利用しやすい

<% if @user.posts.last.title.present? %>
  <div>
    最後の投稿は「<%= @user&.posts&.last&.title =>」でした!
  </div>
<% endif %>

こうすることで、@user.postsnilだったとしても後続には続くため、最後の present?まで正しく動作をしてくれる。

他のアプローチ

どちらかというとViewでnilになりうる構造のデータを取り扱うこと自体がかなり使い勝手を悪くしている話でもあるので、ControllerやHelper、Decoratorなどの層を活用してなるべくerbなどviewのレイヤーで長いメソッドチェインを利用する記述を減らす方が肝要ではあります。

関連リンク