ホワイトヘルスケア - テックブログ

ホワイトヘルスケアは、日本のヘルスケア領域における社会課題に正面から向き合い、現実世界を生きる人々の不安や痛みといった見過ごされるべきでない問題に対して本質的な解決に取り組むことで、持続的な医療システム・社会の実現を目指します。

ActiveRecordにおけるscopeとclass methodの使い分けに対する考察

概要

  • ActiveRecord には、class method と同じような挙動をする scopeが定義できる
  • class methodscope を使い分ける方針として下記に統一すると良いと考える
    • 戻り値としてActiveRecord::Relationを期待する場合は scope
    • 戻り値としてActiveRecord::Relationを期待しない場合は class method

はじめに

ホワイトヘルスケア エンジニアの林です。

本記事は、Rails 初学者が scopeclass method の使い分けに悩み、最終的にどのように使い分けるか考察した内容を記載しています。

scope とは

公式より scope の説明を抜粋します。

よく使うクエリをスコープに設定すると、関連オブジェクトやモデルへのメソッド呼び出しとして参照できるようになります。スコープでは、where、joins、includes など、これまでに登場したメソッドをすべて使えます。どのスコープメソッドも、常に ActiveRecord::Relation オブジェクトを返します。

railsguides.jp

私はホワイトヘルスケアに join するまで Django を扱っていましたが、この scope という概念は Django にありません。また、プロジェクトに途中から join したこともあり、コード内に定義されていた scopeclass method を見て、ふわっとした理解から入ったため、使い分けに悩みました。

使い分けに悩んだ点は、scopeclass method で同じ挙動を定義できる点です。 例えば、以下の scopeclass method は同じ挙動をします。

class Article < ApplicationRecord
  # 公式ページに挙げられているscope例
  scope :published, -> { where(published: true) }

  # 上記scopeと同じ挙動をするclass method
  def self.method_published
    where(published: true)
  end
end

Ruby には for 文のように、同じ挙動を異なる表現で実装できるということもあり、使い分ける必要があるのか非常に悩みました。

scope の位置付けと class method の使い分け考察

使い分けを考えた際、Rails における scope の役割や位置付けは何かを調べました。結論として、位置付けに関しては、全てこの一文に集約されると考えています。

どのスコープメソッドも、常に ActiveRecord::Relation オブジェクトを返します。

また、scope という単語が表す「範囲」という意味からも、レコードの範囲を絞るために使う(= filter として使う= ActiveRecord::Relation オブジェクトを返す)ことが想定されている役割であると考えました。

(余談)個人的には、この一文は Rails における scope の位置付けを示しており、コーディング規約(もっというと Rails の思想)に関わるため、もっと強い言葉にした方が良いと思っています。

では、scope はレコードの範囲を絞るために使う、とした場合に、class method はどのように扱うべきか? 結論としては、ActiveRecord::Relation オブジェクトを返さない場合は全て class method にする、という結論に至りました。

上記を実装方針とすることで、scopeclass method の役割が明確かつ分離されるため、レビュー時やリーディング時の認知負荷軽減にも役立つと考えています。(私が認識していなかっただけで、そのような実装方針は定まっていました)。

まとめ

scopeclass method の使い分けをまとめると以下になります。

  • 戻り値としてActiveRecord::Relationを期待する場合は scope
  • 戻り値としてActiveRecord::Relationを期待しない場合は class method
    • ex.1. ActiveRecord::Relationの値を変更する(戻り値は成功/失敗を表す bool)
    • ex.2. 特定条件に合わせたレコードを作成する(戻り値は作成したレコード)

このような実装方針とすることで、認知負荷を下げ統一のある実装になると考えています。

採用情報

Whitehealthcare に興味が出てきた方はこちらからチェックをどうぞ

https://hrmos.co/pages/whitehealthcare