Рельсы переопределяют по умолчанию getter для отношения (принадлежит_to)

Итак, я знаю, как переопределить получатели по умолчанию для атрибутов объекта ActiveRecord, используя

def custom_getter
  return self[:custom_getter] || some_default_value
end

Я пытаюсь добиться того же, но для ассоциации принадлежит. Например.

class Foo < AR
  belongs_to :bar

  def bar
    return self[:bar] || Bar.last
  end
end

class Bar < AR
  has_one :foo
end

Когда я говорю:

f = Foo.last

Я бы хотел, чтобы метод f.bar возвращал последнюю строку, а не nil, если эта связь еще не существует.

Однако это не работает. Причина в том, что self [: bar] всегда undefined. Это на самом деле self [: bar_id].

Я могу сделать что-то наивное, как:

def bar
  if self[:bar_id]
    return Bar.find(self[:bar_id])
  else
    return Bar.last
  end
end

Однако это всегда вызовет вызов db, даже если Bar уже был извлечен, что, безусловно, не идеально.

Есть ли у кого-нибудь представление о том, как я могу иметь отношения, так что атрибут belongs_to загружается только один раз и имеет значение по умолчанию, если оно не установлено.

Ответ 1

alias_method - ваш друг здесь.

alias_method :original_bar, :bar
def bar
  self.original_bar || Bar.last
end

Как это работает, так это то, что вы по умолчанию используете "бар" как "исходный бар", а затем реализуете свою собственную версию "bar". Если вызов original_bar возвращает nil, тогда вы возвращаете последний экземпляр Bar.

Ответ 2

я обнаружил, что использование "супер" - лучший способ

def bar
  super || Bar.last
end

Надеюсь, это поможет вам: D

Ответ 3

Ответ Рэнди на месте, но есть более простой способ написать его, используя alias_method_chain:


def bar_with_default_find
  self.bar_without_default_find || Bar.last
end
alias_method_chain :bar, :default_find

Это создает два метода - bar_with_default_find и bar_without_default_find и псевдонимы bar к методу with. Таким образом, вы можете явно вызвать тот или другой, или просто оставить значения по умолчанию как есть.