Продлить только на один блок вызова - PullRequest
1 голос
/ 10 июня 2009

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

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

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

РЕДАКТИРОВАТЬ : Спасибо за отличные ответы до сих пор. Я прояснил вопрос, потому что на самом деле мне нужно определить средства чтения атрибутов в самом классе, которые могут конфликтовать с установщиками, определенными в модуле. Я забыл об этой части при публикации вопроса. (Было уже поздно ^^)

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

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

Ответы [ 4 ]

3 голосов
/ 10 июня 2009

В Ruby нет собственного способа «расширить» модули. Gem mixology реализует этот шаблон как расширение C (и Java, для JRuby), создавая методы mixin и unmix. Похоже, что вам может потребоваться применить патч , если вам нужна поддержка 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
2 голосов
/ 10 июня 2009

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

Это позволит вам сделать это:

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

Драгоценный камень mixology делает то же самое, только немного по-другому.

2 голосов
/ 10 июня 2009
  def set(&block)
    extend(Setter)
    instance_eval(&block)
    Setter.instance_methods.each do |m| 
      instance_eval "undef #{m}"
    end
  end

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

0 голосов
/ 23 июля 2009

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

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

...