Rails を業務で使うようになって、数百行ある Fat なモデルの保守をすることも増えました。
Fat なモデルでは、同じようなメソッドが複数存在したり、使用されていないメソッドが存在したりなどカオスな状況が起こりやすいと思います。
行数が短いモデルが必ずしも ''正義'' だとは思いませんが、短い方がメンテが楽だと思っており、コードを書くときはなるべく行数が少なくなるように努力をしています。
今回は、モデルの行数を節約するために実践している ''Rails の標準機能を使うようにして、自前でメソッドを作らない方法'' をまとめたいと思います。
has_many で絞り込みを使う
関連オブジェクトの絞り込みをしたい場合、自前メソッドを用意するより has_many で絞る方が行数を節約できます。
# 自前メソッド例 class User < ApplicationRecord has_many :posts def recently_posts posts.order(created_at: :desc) end end # has_many で絞り込みを使う class User < ApplicationRecord has_many :recently_posts, -> { order(created_at: :desc) }, class_name: 'Post' end
scope を使う
関連オブジェクトやモデルのクエリを用意したい場合、自前クラスメソッドを用意するより scope を使う方が行数を節約できます。
# 自前メソッド例 class User < ApplicationRecord class << self def matches_email(email) where(email: email) end end end # scope を使う class User < ApplicationRecord scope :matches_email, ->(email) { where(email: email) } end
delegate を使う
関連オブジェクトのメソッドを利用したい場合、自前メソッドを用意するより delegate で委譲した方が行数を節約できます。
# 自前メソッド例 class Post < ApplicationRecord belongs_to :user def user_first_name user.first_name end def user_last_name user.last_name end end # delegate を使う class Post < ApplicationRecord belongs_to :user delegate :first_name, :last_name, to: :user, prefix: true end
composed_of を使う
年齢などの計算ロジックが必要な値を利用する場合、自前メソッドより値用のクラスを分けて composed_of を利用する方が行数を節約できます。
# 自前メソッド例 class User < ApplicationRecord def age current = Time.current.strftime('%Y%m%d').to_i birthed = birthed_on.strftime('%Y%m%d').to_i (current - birthed) / 10_000 end end # composed_of を使う class User < ApplicationRecord composed_of :age, mapping: [:birthed_on] end
カスタムバリデーションクラスを使う
自前のバリデーションが必要な場合、自前メソッドを用意するよりカスタムバリデーションクラスを用意した方が行数を節約できます。
# 自前メソッド例 class Tweet < ApplicationRecord CONTENT_MIN_LENGTH = 1 CONTENT_MAX_LENGTH = 140 validate :content_cannot_violate_length_restriction private def content_cannot_violate_length_restriction return if self.content.blank? unless self.content.length >= CONTENT_MIN_LENGTH && self.content.length <= CONTENT_MAX_LENGTH errors.add(:content, 'Violates length restriction') end end end # カスタムバリデーションクラスを使う class Tweet < ApplicationRecord validates_with TweetLengthValidator end
最後に
行数が短いモデルが必ずしも ''正義'' だとは思っていないので、これらの方法がどの状況でも良い選択になるかといえば違うと思います。
ただ、「Rails の機能を使って短く書く」をチーム内で意識すれば、コードにも統一感が出てメンテの面でもいい感じになりそうだなと思っています。
以上です。