2009-06-09 4 views
1

У меня есть класс, который содержит некоторые частные атрибуты. То, что я хотел бы сделать, - это динамически добавить некоторые сеттеры для них только для выполнения определенного блока.Расширение только для одного вызова блока

Пример того, что я хотел бы быть в состоянии:

class Content 
    attr_reader :a, :b 

    def initialize 
    @a = 1 
    @b = "plop" 
    end 

    def set(&block) 
    extend(Setter) 
    instance_eval(&block) 
    unextend(Setter) ???? 
    end 

    module Setter 
    def a(value) 
     @a = value 
    end 
    def b(value) 
     @b = value 
    end 
    end 
end 

content = Content.new 
content.set do 
    a 2 
    b "yeah!" 
end 
content.a # should return 2 

EDIT: Спасибо большое за ответы до сих пор. Я уточнил этот вопрос, потому что мне действительно нужно определить читателей атрибутов в самом классе, которые могут противоречить сеттерам, определенным в модуле. Я забыл об этой части при публикации вопроса. (Было поздно ^^)

CLARIFICATION: Этот класс предназначен для DSL для записи конфигурационного файла. Он нацелен на не-разработчика, поэтому чем меньше операторов, тем лучше.

В настоящее время я реализую это с использованием прокси-класса, который instance_eval блок, но мне нужно общаться с instance_variable_set, чтобы установить значения, и мне это не нравится. Я просто пытаюсь понять, могу ли я сделать код более читаемым.

+0

Есть ли какая-либо причина вы пишете собственные писатель , вместо использования attr_writer для их создания для вас? – rampion

ответ

2
def set(&block) 
    extend(Setter) 
    instance_eval(&block) 
    Setter.instance_methods.each do |m| 
     instance_eval "undef #{m}" 
    end 
    end 

Я не знаю ни одного метода, который будет делать это для вас, хотя там может быть что-то .. Это должно сделать работу, хотя, находя все методы экземпляра присваивателя и их не определенное Контента.

+0

Более эффективное выполнение instance_eval {undef m}. – Chuck

+0

не работает, потому что undef берет 'm' как литерал вместо того, чтобы его оценивать ... хотя можно было бы сделать instance_eval {undef: "# {m}"}, но я не думаю, что это намного яснее. – cloudhead

+0

О, извините. Должен был undef_method, но тогда вам нужно будет заставить класс singleton называть его. Вероятно, это будет намного быстрее. – Chuck

3

В Ruby нет собственного способа «расширения» модулей. Драгоценный камень mixology реализует этот шаблон как расширение C (и Java, для JRuby), создавая методы mixin и unmix. Похоже, вам, возможно, потребуется применить patch, если вам нужна поддержка Ruby 1.9.

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

class Content 
    def initialize 
    @a = 1 
    @b = "plop" 
    end 

    def set(&block) 
    instance_eval(&block) 
    end 

    private 

    def a(val) 
    @a = val 
    end 

    def b(val) 
    @b = val 
    end 

end 

content = Content.new 

#This will succeed 
content.set do 
    a 2 
    b "yeah!" 
end 

# This will raise a NoMethodError, as it attempts to call a private method 
content.a 3 
+0

Спасибо за частный трюк, я об этом не знал. К сожалению, мне также нужны читатели атрибутов в моем первоначальном классе, и они конфликтуют со смешанными атрибутами. Я проясню вопрос, потому что я понимаю, что это не может быть тривиальным в конце концов. –

2

Вы можете использовать _why's mixico library (доступно on github)

Это позволило бы вы делаете это:

require 'mixology' 
#... 
def set(&block) 
    Setter.mix_eval(Setter, &block) 
end 

mixology gem делает почти то же самое, только немного иначе.

0

, если вы чувствуете в экспериментальном настроении также проверить: dup_eval

Это похоже на некоторых способах mixico, но с некоторыми интересными статистами (object2module)