【问题标题】:Modeling an activity feed为活动提要建模
【发布时间】:2015-09-18 18:45:20
【问题描述】:

我想对我正在尝试建模的活动提要提出第二意见。我想出了三种方法。

我有一个 Project、Membership、Bucket、Upload 和 Document 模型,它们都是 ActiveRecord 模型。一个项目有许多成员和许多桶。一个bucket有很多uploads,一个uploads有很多documents。我想跟踪成员资格、存储桶、上传和文档(更新、删除、创建等)的活动。

class Project
  has_many :memberships
  has_many :buckets
  has_many :documents, through: :buckets
end

class Bucket
  belongs_to :project
  has_many :uploads
  has_many :documents, through: :uploads
end

class Upload
  belongs_to :bucket
  has_many :documents
end

class Document
  belongs_to :upload
end

这本身相当容易。但有些存储桶是私有的,只有管理员才能看到。这意味着他们的活动以及他们上传的活动和他们的文档只能对管理员可见。

方法一:多态Activity类

Activity 属于可跟踪对象,在本例中为 MembershipDocumentUploadBucket

class Activity
  belongs_to :project
  belongs_to :trackable, polymorphic: true
end

这让我可以做@project.activities 之类的事情。为了解决隐私问题,我可以使用范围。

scope :public, -> { joins(:trackable).where(trackable: { private: false }) }

但只有 Bucket 对象以及它们的 Upload 对象和 Document 对象具有隐私标志。会员制没有。我必须为所有可跟踪模型添加隐私标志,以便它们可以与同一个界面对话。

class Membership
  def private; false; end
end

class Upload
  delegate :private, to: :project
end

class Document
  delegate :private, to: :upload
end

然后我可以在某个策略对象中执行此操作。

@activities = if current_user.admin?
  @project.activities
else
  @project.activities.public
end

方法 2:在活动上添加私有标志

我可以在 Activity 上添加隐私标志。

@bucket.activities.build(project: @project) do |a|
  a.private = @bucket.private?
end

然后我可以在某个策略对象中执行此操作。

@activities = if current_user.admin?
  @project.activities
else
  @project.activities.public
end

这很容易,但不灵活。如果我们向存储桶添加访问表,以便它们的可见性现在基于访问权限而不是用户角色,我们将不得不痛苦地重构。

有没有更好的方法来为这个 Feed 建模?你会怎么做?我不能使用任何像公共活动这样的宝石,所以请不要推荐它们。

【问题讨论】:

    标签: ruby-on-rails ruby postgresql database-design data-modeling


    【解决方案1】:

    我认为你的第一种方法要好得多,我唯一可以做得更好的是:

    class Activity
      scope :auth_with, -> (auth) { joins(:trackable).where(trackable: { private: trackable_access(auth) }) }
    
      class << self
      private
        def trackable_access(auth)
          return false unless auth
          auth.admin?
        end
      end
    end
    

    那你可以叫漂亮

    @project.activities.auth_with(current_user)
    

    不要担心访问问题,您的访问逻辑也将只在一个地方,并且是它所关注的唯一地方 - Activity,因此更改访问逻辑将归结为将 trackable_access 中的 auth.admin? 替换为任何其他类型的身份验证。

    顶部的樱桃正在将 trackable_access 设为私有,因此您不能在没有 eval 的情况下调用 Activity.trackable_access(...)

    这当然只是我对此事的看法,但我希望我能以某种方式帮助:)

    【讨论】:

    • 谢谢。你能告诉我你的想法吗?基本上有两种类型的活动:工作区和桶活动。工作区活动是公开的。它们包括会员资格。存储桶活动是私有的还是公共的,具体取决于存储桶。它们包括上传、文档和 cmets。饲料可以由两者组成。这使得检索存储桶活动变得容易:如果用户是管理员,则获取当前工作区中存储桶的所有活动,否则获取属于公共存储桶的所有活动。我们将它们与工作空间活动结合起来。你怎么看?
    • 此外,单个提要的一个吸引人的地方是,如果需要,我可以为每个存储桶提供一个独立的提要。这将包括文档和上传活动,以及属于单个存储桶的文档的 cmets。
    • 我还担心第一种方法对性能的影响。
    • 我认为这种做法是可以理解和合理的。如果你保持你的身份验证逻辑分离和内部它应该是好的和干净的。
    • 我想我会走另一条路。我将创建一个将活动映射到用户的连接表。如果用户有权访问活动,我将使用后台作业在该表中创建一个映射。我不知道它的可扩展性如何,但看起来还不错。如果我在一个项目中有 50 个人,我将不得不添加 50 个映射,但它是幂等的,并且解决了权限问题。 stackoverflow.com/questions/31186928/…
    【解决方案2】:

    我会诚实地结合这两种方法。第二种方法可以获得您真正希望数据反映的内容 - 此活动是否私有?

    然后我可能会将Activity 上的:private 字段设置在before_save 挂钩中,其逻辑类似于“如果我属于私人Bucket,请将其设置为私人”。

    我更喜欢这种方法,因为如果您的 Activity 私有逻辑每次更改,或者您需要为其他类型的访问添加标志(例如 viewable_to_managers),您可以添加一个类似的钩子并运行用于更新所有过去的 Activity 对象的脚本。

    【讨论】:

    • 感谢您的参与。您能阅读我对 Piotr Kruczek 回答的评论并告诉我您的想法吗?
    • 如果您关心性能,那么在 Activites 表上的数据库中肯定有一个字段将允许更快的查询,而不是仅仅为了了解某事是否存在而进行联接应该包括在内。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 2013-01-29
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多