2008-12-30 102 views
21

我有一個網站在rails中,並希望有站點範圍的設置。我的應用程序的一部分可以通過短信通知管理員,如果發生特定事件。這是我希望通過站點範圍設置進行配置的功能示例。如何實現一個單身模型

所以我想我應該有一個設置模型或什麼的。它需要成爲一個模型,因爲我希望能夠has_many:短信通知的聯繫人。

問題是在設置模型中只能有一個數據庫文章。所以我正在考慮使用Singleton模型,但這隻會阻止創建新的對象?

我還需要創建getter和setter方法對每個屬性,像這樣:

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

def self.attribute 
    Model.first.attribute 
end 

是它也許不是最好的做法是直接使用Model.attribute但總是創建它的一個實例,並使用該?

我應該在這裏做什麼?

回答

7

我不知道我會浪費數據庫/ ActiveRecord /模型的開銷這種基本需求。這個數據是相對靜態的(我假設),並且實時計算不是必須的(包括數據庫查找)。儘管如此,我建議您使用站點範圍設置定義一個YAML文件,並定義一個初始化文件,將該設置加載到一個常量中。你不會有幾乎不必要的運動部​​件。

沒有理由數據不能坐在內存中,併爲您節省大量的複雜性。常量在任何地方都可用,並且它們不需要被初始化或實例化。如果它是絕對關鍵的,你利用類作爲一個單身,我建議你在做這兩件事情:

  1. 民主基金初始化/新方法
  2. 只定義自我*方法,這樣,它是不可能的你保持一個狀態
+8

使用。 yml文件不靈活,不適合用戶(而不是網站作者)更新設置。 – Undistraction 2014-06-27 08:43:50

3

賠率是好的,你不需要單身。不幸的是,模式狂熱中出現的最糟糕的設計習慣之一也是最常被採用的一種。我責怪簡單的不幸外觀,但我離題了。如果他們稱之爲「靜態全局」模式,我相信人們會更加不願意使用它。

我建議使用一個包裝類與你想用於單例的類的私有靜態實例。你不會在你的代碼中引入一對緊密的情侶,就像你將與單身人士一樣。

有些人稱之爲單色模式。我傾向於認爲它只是策略/代理概念的另一種轉折,因爲您可以通過實現不同的接口來公開/隱藏功能來實現更大的靈活性。

1

使用has_many :contacts並不意味着你需要一個模型。 has_many做了一些魔術,但最終它只是增加了一些方法與指定的合同。沒有理由爲什麼你不能實現這些方法(或者你需要的一些子集)來使你的模型像它一樣行爲has_many :contacts但實際上並沒有實際使用ActiveRecord模型(或模型)來進行聯繫。

1

您還可以檢查出Configatron:

http://configatron.mackframework.com/

Configatron使配置您的應用程序和腳本非常容易。不再需要使用常量或全局變量。現在你可以使用一個簡單而無痛的系統來配置你的生活。而且,因爲這都是Ruby,你可以做任何你想做的瘋狂事情!

23

我不同意共同意見 - 從數據庫中讀取屬性沒有任何問題。你可以讀取數據庫的值並凍結,但是可以有更簡單的凍結方式。

YAML與數據庫有什麼不同?相同的鑽取 - 應用程序代碼持久設置外部。

數據庫方法的好處在於,它可以通過或多或少安全的方式進行更改(不直接打開和覆蓋文件)。另一個好處是它可以在羣集節點之間通過網絡共享(如果正確實施)。

然而問題仍然是使用ActiveRecord實現這種設置的正確方法。

+1

我在閱讀,因爲我有類似的挑戰 - 但同意YAML文件是一個糟糕的選擇。我的網站將有多個擁有管理員權限的用戶可以編輯網站設置,我不能指望他們編輯配置文件。 – Jeff 2011-12-23 04:21:01

46

(我同意@ user43685並且不同意@Derek P - 有很多好的理由可以保留數據庫中的全部數據而不是yaml文件,例如:您的設置將在所有網頁上提供服務器(如果您有多個Web服務器);對設置的更改將爲ACID;您不必花時間實施YAML封裝等等)

在rails中,這很容易實現,只需要記住你的模型應該是數據庫術語中的「單例」,而不是ruby對象術語。

實現最簡單的方法是:

  1. 添加一個新的模式,與一列您需要的每個
  2. 添加一個名爲「singleton_guard」特列屬性,並驗證它總是等於爲「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!」的「實例」獲得者

+3

萬一它節省了幾分鐘的時間,我建議不要使用名爲「Config」的模型:http://oldwiki.rubyonrails.org/rails/pages/ReservedWords – Ricky 2012-11-03 02:00:06

+0

謝謝,@Ricky,我已經將模型更名爲 – Rich 2012-11-04 16:59:53

6

我知道這是一箇舊線程,但我只是需要同樣的東西,並發現這有一個gem:acts_as_singleton

安裝說明適用於Rails 2,但它也適用於Rails 3。

9

你也可以執行最多一個記錄如下:

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 
0
class Constant < ActiveRecord::Base 
    after_initialize :readonly! 

    def self.const_missing(name) 
    first[name.to_s.downcase] 
    end 
end 

恆:: FIELD_NAME

1

簡單:

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