2014-09-02 3 views
0

У меня есть одноэлементный код в моем приложении, который используется повторно для всех приложений. Я хочу, чтобы singleton получал некоторые методы по умолчанию из моего класса, но также мог настраивать модуль/eigenclass. Прежде всего, я не хочу вызывать instance при каждом вызове утилиты singleton.Наличие модуля Singleton, расширяющего класс

Вот пример. Скажем, мой класс по умолчанию - Universe::Earth. Затем я хочу, чтобы в моем приложении был модуль Earth, который «расширяет» этот класс.

module Universe 
    class Earth 
    def self.grow! 
     @grown = true 
    end 
    end 
end 

module Earth 
    class < Universe::Earth << self; end 

    grow! 
end 

Когда это бежать, grow! является NoMethodError.

Пробовал эти подходы:

Class.new(Goodluck::Contest) << self 
class < Universe::Earth << self; end 
extend Universe::Earth 

Как заставить его работать?

+0

Не совсем. Я пытаюсь создать DSL для него. Поэтому каждый раз призывать «Землю» было бы болью. –

+0

Джонатан, извините за задержку в изменении моего ответа. Дайте мне знать, если он все еще не отвечает на ваш вопрос. –

ответ

0

Это что, вы ищите?

module Universe 
    class Earth 
    def self.grow! 
     @grown = true 
    end 
    end 
end 

module Earth 
    Universe::Earth.class_eval do 
    define_method(:instance_howdy) do 
     puts "instance_howdy!" 
    end 
    end 
    def (Universe::Earth).class_howdy 
    puts "class_howdy!" 
    end 
end 

Universe::Earth.methods(false)   #=> [:grow!, :class_howdy] 
Universe::Earth.instance_methods(false) #=> [:instance_howdy] 
Universe::Earth.new.instance_howdy  #=> instance_howdy! 
Universe::Earth.class_howdy    #=> class_howdy! 

[Edit: Если вы просто хотите установить @grown => true, и получить его значение, вы просто должны:

module Earth 
    Universe::Earth.grow! #=> true 
end 

Проверка:

Universe::Earth.instance_variable_get("@grown") #=> true 

Если вы хотите также добавьте аксессор для переменной экземпляра класса, вы можете сделать это:

def add_class_accessor(c, accessor, var) 
    c.singleton_class.class_eval("#{accessor} :#{var}") 
end 

Universe::Earth.methods(false) 
    #=> [:grow!] 

module Earth 
    Universe::Earth.grow! #=> true 
    add_class_accessor(Universe::Earth, "attr_accessor", "grown") 
end 

Universe::Earth.methods(false) 
    #=> [:grow!, :grown, :grown=] 

Universe::Earth.grown 
    #=> true 
Universe::Earth.grown = "cat" 
    #=> "cat" 
Universe::Earth.grown 
    #=> "cat" 

Object#singleton_class был добавлен в Ruby 1.9.2. Для более ранних версий вы можете сделать это:

def add_class_accessor(c, accessor, var) 
    eigenclass = class << c; self; end 
    eigenclass.class_eval("#{accessor} :#{var}") 
end 

Вы могли бы рассмотреть вопрос о создании add_class_accessor в модуле должны быть включены по мере необходимости. Другие методы, которые вы могли бы добавить к тому же модуль может быть:

add_instance_method(klass, method, &block) 
add_class_method(klass, method, &block) 
add_instance_accessor(klass, accessor, var) 

: Tide

+0

Я пытаюсь сделать «Earth.grow !; Earth.grown == true' (предположим, читатель attr) –