Ruby on rails 在Rails中处理带有空对象的关联

Ruby on rails 在Rails中处理带有空对象的关联,ruby-on-rails,rails-activerecord,null-object-pattern,Ruby On Rails,Rails Activerecord,Null Object Pattern,我正在Rails应用程序中使用该模式来实现来宾用户帐户的概念 像许多应用程序一样,我在ApplicationController上有一个名为current\u user的方法 对于未登录的用户,我希望使用来宾用户null对象 它在很多情况下都是有效的,但随后会出现如下情况- params.merge({ user: current_user }) MyModel.new(params) 当然,这失败了,但有以下例外 ActiveRecord::AssociationTypeMismatch:

我正在Rails应用程序中使用该模式来实现来宾用户帐户的概念

像许多应用程序一样,我在
ApplicationController
上有一个名为
current\u user
的方法

对于未登录的用户,我希望使用来宾用户null对象

它在很多情况下都是有效的,但随后会出现如下情况-

params.merge({ user: current_user })
MyModel.new(params)
当然,这失败了,但有以下例外

ActiveRecord::AssociationTypeMismatch: User expected, got GuestUser
我的问题是,如何优雅地处理此类案件。空对象模式的思想是,您可以透明地交换这个空对象,并使它本质上是真实对象的duck类型

对于在对象上调用的方法,如何实现这一点是显而易见的,但在本例中,我希望能够传入它,并基本上让它将关联列设置为
null
,而不需要一大堆自定义逻辑(无论如何,避免这就是null对象模式的全部要点)


多态关系并不完全是这样。

如果您的MyModel接受用户id为null,那么您可以这样做

params.merge(user: current_user) unless current_user.is_a?(GuestUser)
MyModel.new(params)

快速回答:没有优雅的处理方式(我不确定优雅是如何量化的)

您必须创建一个关注点,模仿空对象所基于的模型(用户)的持久性方法。您还必须编写一些方法来安抚ActiveRecord,使相关列成为
nil


幸运的是,这个用例在这里使用空对象模式肯定不是一个好主意,因为如果您希望用户在“注册”之前具有任何类型的持久性,那么您需要数据库生成的ID来构建关联并维护引用完整性

允许在没有用户id的情况下创建一个
MyModel
,实际上会创建一个孤立的记录,只会给您在屏幕后面将其链接到用户的另一个问题。这就是为什么您的模式首先不应该允许它

相反,您希望在需要时创建来宾用户记录(如来宾用户将第一项添加到购物车时),并使用定期任务(如Cron选项卡)定期清除垃圾记录


我也会考虑如果你真的想把访客用户设置成一个单独的类,因为STI和多态在加入时会变得非常混乱。只需使用时间戳列(帐户激活时的记录)或枚举即可。

一个选项是覆盖
用户=
方法,以便它知道
GuestUser
的存在(并可以适当处理):

Rails中的所有质量分配方法(创建、更新等)都将使用适当的setter来设置值。如果这是应用程序中的常见模式,则很容易引起关注

如果在
user\u id
列中不允许
nil
,则您可以灵活地执行诸如分配哨兵值之类的操作,然后也可以在访问者中使用该值:

def user
  if user_id == GUEST_USER_ID
    GuestUser.new
  else
    super
  end
end

我也有类似的问题。我刚从分配对象转到分配
object.id
,我在空对象上将其设置为nil。但我认为这是一种黑客行为。

我不确定你在这里的意图。使用现有的开发良好的工具(如Desive和Rolify)可能会更好。我不认为这是一个很好的模式使用这里。我想说的是链接的答案是相当优雅:)
def user
  if user_id == GUEST_USER_ID
    GuestUser.new
  else
    super
  end
end