1

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

像许多应用程序一样,我有一个方法ApplicationController调用current_user.

在未登录用户的情况下,我想使用我的来宾用户空对象。

它在许多情况下都有效,但随后会遇到以下情况 -

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

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

ActiveRecord::AssociationTypeMismatch: User expected, got GuestUser

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

很明显,如何为对象上调用的方法执行此操作,但在这种情况下,我希望能够将其传入并基本上将关联列设置为null,而不是需要一大堆自定义逻辑(避免无论如何都是空对象模式的重点)。

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

4

5 回答 5

2

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

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

幸运的是,这个用例已经解决了

于 2018-03-01T09:32:21.983 回答
0

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

params.merge(user: current_user) unless current_user.is_a?(GuestUser)
MyModel.new(params)
于 2018-03-01T04:30:14.263 回答
0

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

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

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

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

于 2018-03-01T14:51:52.280 回答
0

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

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

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

如果您不允许niluser_id列中使用,您可以灵活地执行诸如分配哨兵值之类的操作,然后您也可以在访问器中使用它:

def user
  if user_id == GUEST_USER_ID
    GuestUser.new
  else
    super
  end
end
于 2018-03-01T15:05:10.053 回答
0

我有一个类似的问题。我刚刚从分配对象到分配object.id我在 Null 对象上设置为 nil 的对象。我认为这是一种黑客行为。

于 2020-11-26T09:56:25.627 回答