处理Rails中具有null对象的关联

问题描述 投票:0回答:4

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

像许多应用程序一样,我在ApplicationController上有一个名为current_user的方法。

对于未登录的用户,我想使用guest用户null对象。

它在许多情况下都有效,但之后遇到类似以下的情况 -

params.merge({ user: current_user })
MyModel.new(params)

当然这失败了,但有以下例外。

ActiveRecord::AssociationTypeMismatch: User expected, got GuestUser

我的问题是,优雅处理这种情况的方法是什么。 Null对象模式的想法是,您可以透明地交换此null对象,并使其基本上是真实对象的鸭子类型。

对于在对象上调用的方法,显然该如何做到这一点,但在这种情况下,我希望能够传递它并基本上将它设置为null的关联列,而不是需要一大堆自定义逻辑(避免这无论如何都是空对象模式的重点)。

多态关系并不完全。

ruby-on-rails rails-activerecord null-object-pattern
4个回答
1
投票

快速回答:没有一种优雅的方法可以解决这个问题(我不确定如何量化优雅)。

您将不得不创建一个模仿您的null对象所基于的模型的持久性方法(User)的问题。您还必须编写方法来安抚ActiveRecord,以使关联列成为nil

幸运的是,这个用例一直是solved


0
投票

如果您的MyModel对user_id接受null,那么您可以这样做

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

0
投票

在这里使用null对象模式绝对不是一个好主意,因为如果你打算在“注册”之前让用户拥有任何类型的持久性,你需要数据库生成的id来构建关联并保持参照完整性。

允许在没有user_id的情况下创建MyModel本质上会创建一个孤立的记录,并且只会给你另一个将它链接到屏幕后面的用户的问题。这就是为什么你的架构不应该首先允许它。

而是希望在需要时创建访客用户记录(例如,当访客用户将第一个项目添加到购物车时)并使用定期任务(如Cron选项卡)定期清理垃圾记录。

我还会考虑你是否真的想要将访客用户设置为单独的类,因为STI和多态性在加入时往往会变得非常混乱。只需使用时间戳列(激活帐户时的记录)或枚举。


0
投票

一种选择是覆盖user=方法,以便它知道GuestUser的存在(并且可以适当地处理):

def user=(value)
  if value.is_a?(GuestUser)
    super(nil)
  else
    super
  end
end

Rails中的所有批量赋值方法(创建,更新等)都将使用适当的setter来设置值。如果这是您的应用程序中的常见模式,则可以很容易地将其置于关注之中。

如果您不允许在nil列中使用user_id,您可以灵活地执行诸如分配标记值之类的操作,然后您可以在访问者中使用该值:

def user
  if user_id == GUEST_USER_ID
    GuestUser.new
  else
    super
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.