Шаблон пустого объекта для ассоциаций в 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
Должен работать на вас.