2012-04-24 13 views
4

私のRailsプロジェクトでは、各クラス(モデル)が独自の設定のための "名前空間"を持つ文字列インデックス付きハッシュにグローバル設定を保存します。たとえば、Newsモデルの設定が「news.stories_per_page」または「news.show_date」になっている可能性があります。mixinのアクセスクラス名

どこにでも名前をつけることを避けるために、これらの設定にアクセスするための一般的なクラスメソッドを提供する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

+0

@Phrogz:難しくて速いreq。 Rubyオブジェクトをクラスごとに(モデルごとに)アクセスできるデータベースにシリアル化することができます。私はそれのために "rails-settings-cached"という宝石を使っていますが、おそらく多くの方法があります。主に私が背後にある論理を理解したかったので、私はこの質問をしました。 – jforberg

答えて

5

クラスの名前がクラスのnameです:代わりにあなたがself.ancestors以上の詳細なself.ancestors.firstを使用することができself.classを使用して

+0

+1答えが簡単です。おそらく問題です: '#name'はクラスではなくStringを返します。 – knut

+0

@Phrogz:クラスメソッドから呼び出されるself.nameは、インスタンスメソッドから呼び出されるself.class.to_sに相当するとお考えですか? – jforberg

+1

@jforberg 'self。インスタンス内のクラス "はクラスの' 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

0

回避策の1つは、各クラスメソッド内でselfをインスタンス化し、インスタンスでclassを呼び出すことです。それは特に美しい解決策ではありませんが、うまくいくようです。それはClassThatMixesMeInから呼び出されるまで

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内のコードが解析されていない(またはそうに思えます)。それは正しい値を持ちます。

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

'self.new.class'は' self'と同じです。 – Phrogz

2

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

シングルトンクラスの代わりに 'extends Mixin'を使用してください。 – Phrogz

+0

@Phrogzありがとう - 私は私の答えに適応しました。私は簡単な方法があると確信していましたが、私はそれを臨時に見つけませんでした。 – knut