2012-04-24 23 views
4

在我的Rails項目中,我將全局設置存儲在字符串索引散列中,其中每個類(模型)都有一個用於其自己設置的「名稱空間」。例如,新聞模型可能具有設置'news.stories_per_page'或'news.show_date'。mixin訪問類名

爲了避免出現無處不在的名稱混亂,我有一個混合類,它提供訪問這些設置的常規類方法。使用這個mixin,我可以訪問'news.show_date',代碼如下:

News.setting :show_date 
=> true 

現在,這是問題所在。爲了生成字符串「news.show_date」,我需要知道我的混合模塊中的模型的類名,但一類方法中,

self.class 
=> Class 

這是不是對我很有幫助。在我幼稚的實現中,這導致所有模型將它們的設置存儲在「類」下。命名空間,這是不可接受的。

我很抱歉無法更清楚地說明問題。我對Ruby有點新,並沒有完全理解它的對象模型。這個問題可能與kludge which seems to be required in Ruby to mix in class methods有關。

+1

有這麼許多方法可以做到這一點(其中大部分與我們所擁有的不同)。你的硬性要求是什麼?你必須有一個使用字符串的散列嗎? – Phrogz 2012-04-24 20:45:23

+0

@Progrog:硬性要求。是能夠將ruby對象序列化爲一個數據庫,可以在每個類(每個模型)的基礎上訪問。我正在使用gem「rails-settings-cached」,但可能有很多方法。我問這個問題主要是因爲我想了解背後的邏輯。 – jforberg 2012-04-26 10:18:33

回答

5

一類的名稱是一類的name

module Foo 
    def whoami 
    self.name 
    end 
end 

class Bar 
    extend Foo 
end 

p Bar.whoami #=> "Bar" 

我不會創建一些字符串;我想要麼創建的每類設置一個新的哈希:

module Settings 
    def setting(name,value=:GIT_DA_VALUE) 
    @_class_settings ||= {} # Create a new hash on this object, if needed 
    if value==:GIT_DA_VALUE 
     @_class_settings[name] 
    else 
     @_class_settings[name] = value 
    end 
    end 
end 

class Foo 
    extend Settings 
end 
class Bar 
    extend Settings 
end 
Foo.setting(:a,42) 

p Foo.setting(:a), #=> 42 
    Foo.setting(:b), #=> nil 
    Bar.setting(:a) #=> nil (showing that settings are per class) 

...不然我會被類對象本身索引一個全局哈希(如果需要):

module Settings 
    # A single two-level hash for all settings, indexed by the object 
    # upon which the settings are applied; automatically creates 
    # a new settings hash for each object when a new object is peeked at 
    SETTINGS = Hash.new{ |h,obj| h[obj]={} } 
    def setting(name,value=:GIT_DA_VALUE) 
    if value==:GIT_DA_VALUE 
     SETTINGS[self][name] 
    else 
     SETTINGS[self][name] = value 
    end 
    end 
end 

# Usage is the same as the above; settings are unique per class 
+0

+1你的答案比我的更容易。也許是一個問題:'#name'返回一個字符串,而不是類。 – knut 2012-04-24 21:34:16

+0

@Phrogz:你會說從類方法調用的self.name與從實例方法調用的self.class.to_s等價嗎? – jforberg 2012-04-26 10:22:45

+1

@jforberg自從'自我。class'在一個實例中是_is_類的'self',你的問題真的是_「是['to_s'](http://www.ruby-doc.org/core-1.9.3/Module.html#method -i-to_s)與['name'](http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-name)相同?「_當你可以從描述,源代碼和實驗中看到,答案是「通常,是,但不總是。」_ – Phrogz 2012-04-26 12:58:55

0

一種解決方法是在每個類方法中實例化self,並在實例上調用class。這不是一個非常漂亮的解決方案,但它似乎工作。

module SettingsMixin 
    def self.included receiver 
    receiver.extend ClassMethods 
    end 

    module ClassMethods 
    def setting(key) 
     class_name = self.new.class # => ClassThatMixesMeIn 

     # Setting-fetching logic here... 

    end 
    end 
end 

ClassMethods的代碼不會被解析(或者看起來是),直到它從ClassThatMixesMeIn調用。它將具有正確的價值。

+0

'self.new.class'與'self'相同。 – Phrogz 2012-04-24 21:42:22

2

而不是使用self.class你可以使用self.ancestors或更詳細的self.ancestors.first

module Mixin 
    def setting(name) 
    puts "call #{self.ancestors.first}.#{__method__} with #{name}" 
    end 
end 

class A 
    extend Mixin 
end 

A.setting :a #-> call A.setting with a 
+0

使用'擴展Mixin'而不是通過單例類。 – Phrogz 2012-04-24 21:00:11

+0

@Progrog謝謝 - 我改編了我的答案。我確信有一個更簡單的方法,但我沒有發現它特設的。 – knut 2012-04-24 21:07:09