Rails中关联的空对象模式

时间:2013-02-27 20:28:09

标签: ruby-on-rails-3 null-object-pattern

尽管在这里看到一些关于轨道中的Null对象的答案,我似乎无法让它们起作用。

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 个答案:

答案 0 :(得分:3)

我完全准备好了,如果它不存在我想要一个干净的新对象(如果你这样做那么object.display不错,也许object.try(:display)更好)这也是这就是我发现的:

1:alias / alias_method_chain

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

但是由于alias_method_chain已被弃用,如果你处于边缘,你必须亲自手动完成模式...... The answer here似乎提供了更好,更优雅的解决方案

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是nil,这就是为什么你总是得到NullProfile

def profile
  self.profile || NullProfile
end

它会失败,因为该方法正在调用自身,因此它类似于递归方法,您得到SystemStackError: stack level too deep

答案 1 :(得分:1)

不使用alias_method_chain,请使用:

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

答案 2 :(得分:1)

我找到了一个比接受答案中包含私人模块更简单的选项。

您可以使用association中的ActiveRecord方法覆盖reader方法并获取关联的对象。

class User < ApplicationRecord
  has_one :profile

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

答案 3 :(得分:0)

根据 Rails docs,关联方法被加载到一个模块中,因此覆盖它们是安全的。

所以,像……

def profile
  super || NullProfile.new
end

应该适合你。