【问题标题】:Ruby - Ensure only one class objectRuby - 确保只有一个类对象
【发布时间】:2018-03-02 18:01:44
【问题描述】:

我有一个模型Bot,我想确保我的数据库中只有一个Bot 对象。我还需要确保它被持久化并且未被篡改。

我最初的想法是在迁移中执行此操作,该迁移将遵循:bots 表迁移。它将包括如下一行:

Bot.all.size == 0 ? Bot.create! : nil

也许这会防止 AR 对象在未来的迁移中被弄乱?


奖励:能够对此类对象进行即时和全局访问真是太棒了。我正在考虑在我的Bot 类中使用singleton 模块,这样我就可以始终引用Bot.instance 并可以访问该特定对象。


用例:

我的数据库中有 4 种类型的用户,这个机器人将通过我们的应用内消息传递功能向他们传递特定于角色的消息。

Class Bot 将与BotMessage/bot_messages 有一个has_many 关联。在bot_messages 表上将是user_role 的枚举字段。

消息将由公司管理员创建并存储在这些表格中,因为我们希望通过查看用户和机器人之间的“对话”线程随时查看它们。

如果只有 1 个机器人,就是这样。我不需要额外的 Bot 对象。此外,由于只有一个对象,因此能够有一种方法明确地定位该对象而无需运行查询来找到它。

例如,与 User 可能有 1000 条记录不同,为了找到特定的记录,您需要执行 @user = User.find_by_email('foo@bar.com') 之类的操作,因为只有一条记录可用于机器人,所以没有必要为机器人执行此类操作寻找。这就是让我相信在这里拥有 singleton 对象可能是值得的,所以每当我需要为特定角色提取消息时,我可以运行 Bot.instance.bot_messages.where(user_role: 1) 或类似的东西

【问题讨论】:

  • 了解您实际上想要做什么可能会有所帮助。例如,您是否考虑过使用常量而不是数据库记录创建一个普通的旧 ruby​​ 类?
  • 我更新了帖子以包含用例场景和对我的需求的解释。
  • 听起来您需要将 Bot 的 internals 保存在表格中,例如每个设置一行,并将 Bot 本身作为某种单例包装器那个。
  • 你有没有遇到这个问题?
  • 嘿,采用了完全不同的方法。不需要单例,因为我只使用了 bot_messages 的简单 1 模型方法。

标签: ruby-on-rails ruby activerecord singleton instance


【解决方案1】:

根据您的用例,我认为Bot 没有理由成为模特。

假设您有一个名为 cool_user 的角色,并且您想获取该角色的所有 bot_messages,您可能会执行以下操作:

class Bot

  class << self

    def bot_messages(user_role)
      BotMessage.send(user_role)
    end

  end

end

作为 cmets 中一个非常周到但可能匿名的超级代码猴子笔记,您还可以这样做:

class Bot

  def self.bot_messages(user_role)
    BotMessage.send(user_role)
  end

end

有些人可能觉得这更易读。 IMO,这有点个人喜好问题。

无论哪种情况,你都应该能够做到

Bot.bot_messages(:cool_user)

因为,正如docs 中所述,

还将提供基于枚举字段允许值的范围。

所以,我相信BotMessage 和正确设置的enum 应该响应cool_user 并返回该角色的所有bot_messages

您可能需要检查docs 以确保语法完全正确。

我相信这也应该满足您的奖金要求。

【讨论】:

  • 我没有仔细看问题,但是在Bot的单例类上定义实例方法bot_messages相当于在Bot上定义bot_messages作为类方法: def self.bot_messages....后者对某些读者来说可能更清楚。
  • 快速搜索产生this问题和答案。我不清楚是否有明确的答案。我认为这是个人喜好问题。但是,我可能弄错了。
  • 修正了归因。嗯。 Syntactic Sugar... 还有,你给我点赞...咳咳我今天把它收拾好了。 ;-) 说真的,谢谢。
  • 您的链接问题的答案应该是必读的。 Ruby 中确实没有“类方法”这样的东西。所有方法都只是在单个对象或类的所有实例上定义的方法。 def self.method 只是 语法糖 的一种形式。出于这个原因,我认为教 Ruby 新手最初使用 class &lt;&lt; self 来定义类的方法是有道理的。稍后他们可以根据需要切换到def self.method。 (读者,jvillian,在他上面的评论中,显然预料到了这个评论。)
【解决方案2】:

一个行之有效的解决方案是在User 上使用STI(带有user_type 列)

class User < ApplicationRecord
  ...
end

class Bot < User
  has_many :bot_messages, foreign_key: :user_id
end

这是你要找的吗?

【讨论】:

  • Sandi Metz 做了一场精彩的演讲,你可以看到 here。在 25:38,她指出“继承是为了专业化”。在我看来,这里建议的方法有点滥用继承的概念(我并不是要以粗鲁的方式说)。 IMO,Bot不是User 的特殊形式。 BotUser 是一个完全不同的概念。因此,虽然这可能有效(我不确定它是否有效),但在我看来似乎走错了路。 FWIW。
  • 当然,继承是为了专业化..这是一个基础知识.. :) 我的方法是说BotUser(作为促进者)的一种,具有细节( bot 消息、名称、ACL 等)。如果我们从 OOP 方面(这是 Ruby 的目的)讨论,这是一个专业化。这是一个在BotBotMessage 之间具有一对N 关系的STI,如果我们从面向模型的方面来讨论(这是ActiveRecord 的目的)。 Bot也可以是独立模型。在我看来,将其制成 DB 的事实违反了 EDM 的原则,因为 Bot,正如它所描述的那样,是一个实体..
  • 感谢您的回复!我不知道 EDM 是什么(除了电子舞曲——我非常喜欢)。或 ACL(最好保持完整的前交叉韧带除外)。在您的方法中,您如何执行“仅 1 个Bot”要求?如果User 的字段同时由 ActiveRecord 和数据库验证(例如,性别、名字、姓氏、电子邮件地址),那么您为 1 Bot 分配什么值?跨度>
  • 哦,顺便说一句,我同意 OP 所描述的 Bot 是一个数据库实体。然而,就我所见,在 OP 的用例中没有任何内容要求 Bot 是数据库实体。所以,也许 OP 手上有一个XY Problem
  • 也感谢您的回复。我喜欢两个热情的开发者之间的讨论。实际上。 EDM 代表实体数据模型。您可以使用简单的before_create 回调轻松确保只有一个Bot。我同意您对STI 和其他字段验证的看法。所以这就是为什么我告诉你Bot可以是一个独立的模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-03
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2021-12-04
  • 1970-01-01
相关资源
最近更新 更多