2012-04-24 3 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

+0

@Phrogz: жесткий и быстрый запрос. заключается в возможности сериализации объектов ruby ​​в базу данных, к которым можно получить доступ на основе каждого класса (для каждой модели). Для этого я использую драгоценный камень «rails-settings-cached», но есть, вероятно, много способов. Я задал этот вопрос в основном потому, что хотел понять логику. – jforberg

ответ

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

+0

@Phrogz: Вы сказали бы, что self.name, вызванное методом класса, является эквивалентом self.class.to_s, вызванного методом экземпляра? – jforberg

+1

@jforberg Поскольку 'self.class' в экземпляре _is_ 'self' класса, ваш вопрос действительно _" is ['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

Одним из способов является создание экземпляра 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

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

Используйте 'extend Mixin' вместо того, чтобы проходить через одноэлементный класс. – Phrogz

+0

@Phrogz Спасибо - я адаптировал свой ответ. Я был уверен, что есть более простой способ, но я не нашел его ad hoc. – knut