代码之家  ›  专栏  ›  技术社区  ›  metahamza

处理Rails中带有空对象的关联

  •  1
  • metahamza  · 技术社区  · 7 年前

    我正在使用 Null Object 模式来实现来宾用户帐户的概念。

    像许多应用程序一样,我在 ApplicationController 打电话 current_user .

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

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

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

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

    ActiveRecord::AssociationTypeMismatch: User expected, got GuestUser
    

    我的问题是,怎样才能优雅地处理此类案件。空对象模式的思想是,您可以透明地交换这个空对象,并使其本质上是真实对象的duck类型。

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

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

    5 回复  |  直到 7 年前
        1
  •  2
  •   jemonsanto    7 年前

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

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

    幸运的是,这个用例 solved

        2
  •  0
  •   korada    7 年前

    如果MyModel接受user\u id为null,则可以执行以下操作

    params.merge(user: current_user) unless current_user.is_a?(GuestUser)
    MyModel.new(params)
    
        3
  •  0
  •   max Mike Williams    7 年前

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

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

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

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

        4
  •  0
  •   Ryan Brunner    7 年前

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

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

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

    如果你不允许 nil user_id 列,您可以灵活地执行诸如指定sentinel值之类的操作,然后也可以在访问者中使用该值:

    def user
      if user_id == GUEST_USER_ID
        GuestUser.new
      else
        super
      end
    end
    
        5
  •  0
  •   unflores    4 年前

    我也有类似的问题。我只是从指定对象到指定 object.id 我在Null对象上将其设置为nil。我想这是一种黑客行为。