1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ActiveRecord の has_one 関連でサクッと Null Object パターンを実装する

Posted at

小ネタです。

Rails の ActiveRecord で has_one 関連を使う場合、参照先が存在しないことがあります。


class Account < ApplicationRecord
  def some_method
  end
end

class Supplier < ApplicationRecord
  has_one :account
end

Supplier.find(id).account # Account or nil

こうなると場合によってインターフェイスが合わなくなるため、メソッドチェーンをつなげるためにはあの目障りなボッチ演算子(&.)のお世話になる必要が出ます。

Supplier.find(id).account&.some_method

これを解決する典型的な方法としては、参照先レコードが存在しない場合は nil の代わりに Account のダミーオブジェクト・いわゆるNullObjectを返すことでインターフェイスを保つ方法があります。

具体的には関連名と同じメソッドを定義するだけです。

class Supplier < ApplicationRecord
  has_one :account
  def account
    # 参照先レコードが存在しない場合は has_one で自動定義される build_association を呼び出す。
    super || build_account
  end
end

これで憎きボッチ演算子が駆逐できました。

Supplier.find(id).account.some_method # .account は必ず Account のインスタンスなのでボッチ演算子は不要

更にActiveRecordのメソッドだってチェーンできるようになります。便利ですね。

Supplier.find(id).account.update(name: 'hoge') # insert or updateを自動判別

ここまでくるといいとこ尽くめですが、1点注意があります。

レコードが存在するかどうかの判定方法が変わります。 .nil?, .present? が使えません。(必ず true になる。)
.new_record?, .persisted?, .id.nil? などで判断する必要があります。

ただ、NullObjectパターンを使っていてレコードの存在を確認する必要があるということは、NullObjectパターンが適していないことが考えられますので、その場合は諦めたほうが良いかもしれません。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?