Шаблон пустого объекта для ассоциаций в Rails

Несмотря на то, что я посмотрел здесь несколько ответов относительно Null Objects в рельсах, я не могу заставить их работать.

class User < ActiveRecord::Base
  has_one :profile
  accepts_nested_attributes_for :profile

  def profile
    self.profile || NullProfile #I have also tried
    @profile || NullProfile #but it didn't work either
  end
end

class NullProfile
  def display #this method exists on the real Profile class
    ""
  end
end

class UsersController < ApplicationController
  def create
    User.new(params)
  end
end

Моя проблема заключается в том, что при создании пользователя я передаю правильные вложенные атрибуты (profile_attributes) для профиля и в итоге получаю NullProfile для моего нового пользователя.

Я предполагаю, что это означает, что мой метод профиля вызывается при создании и возвращении NullProfile. Как правильно сделать этот NullObject, чтобы это происходило только при чтении, а не при первоначальном создании объектов.

4 ответа

Решение

Я проходил точно через и хотел чистый новый объект, если его не было (если вы делаете это просто так object.display не ошибается возможно object.try(:display) лучше) это тоже и вот что я нашел:

1: псевдоним /alias_method_chain

def profile_with_no_nill
  profile_without_no_nill || NullProfile
end
alias_method_chain :profile, :no_nill

Но так как alias_method_chain устарела, если вы остаетесь на краю, вам придется делать шаблон самостоятельно, вручную... Ответ здесь, кажется, обеспечивает лучшее и более элегантное решение

2(упрощенная / практическая версия из ответа):

class User < ActiveRecord::Base
  has_one :profile
  accepts_nested_attributes_for :profile

  module ProfileNullObject
    def profile
      super || NullProfile
    end
  end
  include ProfileNullObject
end

примечание: порядок выполнения этого вопроса (объяснено в связанном ответе)


На что ты пробовал:

Когда ты сделал

def profile
  @profile || NullProfile
end

Он не будет вести себя так, как ожидалось, потому что Ассоциация загружена лениво (если вы не сказали это :include в поиске), поэтому @profile равен нулю, поэтому вы всегда получаете NullProfile

def profile
  self.profile || NullProfile
end

Это не удастся, потому что метод вызывает себя, так что это похоже на рекурсивный метод, вы получаете SystemStackError: stack level too deep

Я нашел более простой вариант, чем включение частного модуля в принятый ответ.

Вы можете переопределить метод reader и извлечь связанный объект, используя association метод из ActiveRecord,

class User < ApplicationRecord
  has_one :profile

  def profile
    association(:profile).load_target || NullProfile
  end
end # class User

Вместо использования alias_method_chain используйте это:

def profile
  self[:profile] || NullProfile.new
end

Согласно документации Rails , методы ассоциации загружаются в модуль, поэтому их можно безопасно переопределить.

Итак, что-то вроде ...

      def profile
  super || NullProfile.new
end

Должен работать на вас.

Другие вопросы по тегам