如何实现单例模型

我在rails中有一个站点,并希望在站点范围内进行设置。 如果发生特定事件,我的应用程序的一部分可以通过SMS通知管理员。 这是我希望通过站点范围设置进行配置的function示例。

所以我在想我应该有一个环境模型或其他东西。 它需要是一个模型,因为我希望能够使用has_many:SMS通知的联系人。

问题是数据库中只能有一个用于设置模型的post。 所以我在考虑使用Singleton模型,但这只会阻止创建新对象吗?

我还需要为每个属性创建getter和setter方法,如下所示:

def self.attribute=(param) Model.first.attribute = param end def self.attribute Model.first.attribute end 

直接使用Model.attribute可能不是最佳实践,但总是创建它的实例并使用它吗?

我该怎么办?

我不确定我是否会浪费数据库/ ActiveRecord / Model开销来满足这样的基本需求。 这个数据是相对静态的(我假设)并且不需要动态计算(包括数据库查找)。

话虽如此,我建议您使用站点范围的设置定义YAML文件,并定义一个初始化文件,将设置加载到常量中。 你不会有那么多不必要的活动部件。

没有理由说数据不能仅仅存在于内存中并且为您节省大量复杂性。 常量随处可用,无需初始化或实例化。 如果你将一个类作为一个单独使用是绝对关键的,我建议你做这两件事:

  1. undef初始化/新方法
  2. 只定义self。*方法,这样你就无法维持一个状态

(我同意@ user43685并且不同意@Derek P – 有很多很好的理由将站点范围内的数据保存在数据库中而不是yaml文件中。例如:您的设置将在所有Web服务器上可用(如果您有多个Web服务器);对您的设置的更改将是ACID;您不必花时间实现YAML包装器等。)

在rails中,这很容易实现,你只需要记住你的模型应该是数据库术语中的“单例”,而不是ruby对象术语。

实现此目的的最简单方法是:

  1. 添加一个新模型,为您需要的每个属性添加一列
  2. 添加一个名为“singleton_guard”的特殊列,并validation它始终等于“0”,并将其标记为唯一(这将强制数据库中只有一行用于此表)
  3. 向模型类添加静态辅助方法以加载单行

因此迁移应该如下所示:

 create_table :app_settings do |t| t.integer :singleton_guard t.datetime :config_property1 t.datetime :config_property2 ... t.timestamps end add_index(:app_settings, :singleton_guard, :unique => true) 

模型类应该看起来像这样:

 class AppSettings < ActiveRecord::Base # The "singleton_guard" column is a unique column which must always be set to '0' # This ensures that only one AppSettings row is created validates_inclusion_of :singleton_guard, :in => [0] def self.instance # there will be only one row, and its ID must be '1' begin find(1) rescue ActiveRecord::RecordNotFound # slight race condition here, but it will only happen once row = AppSettings.new row.singleton_guard = 0 row.save! row end end end 

在Rails> = 3.2.1中,您应该能够通过调用“ first_or_create! ”来替换“instance”getter的主体,如下所示:

 def self.instance first_or_create!(singleton_guard: 0) end 

我不同意共同意见 – 从数据库中读取属性没有任何问题。 您可以读取数据库值并冻结(如果您愿意),但是可以有更灵活的替代方法来进行简单冻结。

YAML与数据库有何不同? ..相同的钻取 – 在应用程序代码持久性设置之外。

数据库方法的好处在于它可以以或多或少的安全方式即时更改(不直接打开和覆盖文件)。 另一个好处是它可以在集群节点之间通过网络共享(如果正确实现)。

然而,问题仍然是使用ActiveRecord实现这种设置的正确方法。

您还可以强制执行最多一条记录,如下所示:

 class AppConfig < ActiveRecord::Base before_create :confirm_singularity private def confirm_singularity raise Exception.new("There can be only one.") if AppConfig.count > 0 end end 

这将覆盖ActiveRecord方法,以便在您已经存在时尝试创建该类的新实例时它会爆炸。

然后,您可以继续仅定义作用于一条记录的类方法:

 class AppConfig < ActiveRecord::Base attr_accessible :some_boolean before_create :confirm_singularity def self.some_boolean? settings.some_boolean end private def confirm_singularity raise Exception.new("There can be only one.") if AppConfig.count > 0 end def self.settings first end end 

我知道这是一个旧线程,但我只是需要相同的东西,并发现有一个gem: acts_as_singleton 。

安装说明适用于Rails 2,但它也适用于Rails 3。

赔率很高,你不需要单身人士。 不幸的是,从模式热潮中走出来的最糟糕的设计习惯之一也是最常采用的习惯之一。 我责怪简单的不幸外表,但我离题了。 如果他们称之为“静态全球”模式,我相信人们对使用它会更加羞怯。

我建议使用一个包装类,该类包含要用于单例的类的私有静态实例。 你不会在整个代码中引入紧密的情侣,就像你对单身人士一样。

有些人称之为单一模式。 我倾向于将其视为策略/代理概念的另一个转折,因为您可以通过实现不同的接口来实现更多的灵活性来公开/隐藏function。

简单:

 class AppSettings < ActiveRecord::Base before_create do self.errors.add(:base, "already one setting object existing") and return false if AppSettings.exists? end def self.instance AppSettings.first_or_create!(...) end end 

使用has_many :contacts并不意味着你需要一个模型。 has_many做了一些魔术,但最后它只是添加了一些指定合同的方法。 你没有理由不能实现那些方法(或你需要的某些子集)来使你的模型表现得像has_many :contacts但实际上并没有使用ActiveRecord模型(或模型)来实现Contact。

您也可以查看Configatron:

http://configatron.mackframework.com/

Configatron使您的应用程序和脚本配置变得exception简单。 不再需要使用常量或全局变量。 现在,您可以使用简单且无痛的系统来配置您的生活。 并且,因为它是所有Ruby,你可以做任何你想做的疯狂的事情!

 class Constant < ActiveRecord::Base after_initialize :readonly! def self.const_missing(name) first[name.to_s.downcase] end end 

恒:: FIELD_NAME