Разрешить блоку ссылаться на классы / модули, которые в данный момент не находятся в области видимости, но будут ли они в области видимости при вызове? - PullRequest
1 голос
/ 18 сентября 2011

Возможно ли сделать что-то подобное в Ruby (1.9.2-p290)?

class SomeClass
  include SomeModuleThatProvidesLotOfConstants

  def build(&block)
    singleton_class.instance_eval(&block)
  end
end

obj = SomeClass.new
obj.build do
  some_class_method SomeConstant, :an => :option
  ...
end

Где some_class_method - метод, доступный для SomeClass (не для его экземпляров)) и SomeConstant является классом / модулем, который находится в области видимости внутри SomeClass, но должен быть ссылками как SomeClass::SomeConstant извне.

Я могу заставить это работать, если я всегда передам полностью-квалифицированные имена классов внутри моего блока, но я пытаюсь эффективно «изменить область» блока, когда он вызывается.Это возможно?Я уверен, что RSpec и другие подобные инструменты, которые интенсивно используют блоки, достигают чего-то вроде этого:)

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

РЕДАКТИРОВАТЬ |Хорошо, вот не псевдо версия того, что я пытаюсь достичь.Я пытаюсь добавить некоторые свойства DataMapper во время выполнения, но только для одноэлементных классов ... Я не хочу, чтобы они внезапно появлялись во всех экземплярах модели.

class Post
  include DataMapper::Resource

  property :id,         Serial
  property :title,      String
  property :created_at, DateTime
  ... etc ...

  def virtualize(&block)
    singleton_class.instance_eval(&block)
    self
  end
end

def suspend_post
  @post = Post.get!(1).virtualize do
    property :delete_comments, Boolean
  end
end

Я знаю, что есть и другиеспособы создания виртуальных атрибутов (в настоящее время я использую несколько разных подходов, в зависимости от сложности), но я просто экспериментирую с несколькими идеями, чтобы не загромождать определения моей модели временными методами, которые используются только для передачи данных формыв одной конкретной части сайта и ничего не значат, когда вы читаете исходный код модели сам по себе.Один или два виртуальных атрибута в порядке, но когда они начинают монтироваться на часто используемых моделях, я начинаю исследовать такие вещи;)

В приведенном выше описании ресурс будет иметь все стандартные свойства, определенные вконкретный класс, плюс любой, добавленный в метод #virtualize.Это ссылка на Boolean без префикса DataMapper::Property::, которая его отбрасывает.

Ответы [ 2 ]

0 голосов
/ 18 сентября 2011

Что не так с этим:

class SomeClass
  SOME_CONSTANT = 42

  class << self
    def some_class_method
      'foo'
    end
  end

  def build &block
    self.class.instance_eval(&block)
  end
end

SomeClass.new.build do
  puts "#{some_class_method} #{SOME_CONSTANT}"
end
#=>foo 42
0 голосов
/ 18 сентября 2011

У вас уже есть то, что вы хотите в отношении методов. Если вы определите some_class_method следующим образом:

def Foo.some_class_method(name)
  define_method name do
    puts("this is the method #{name}")
  end
end

и сделать

f = Foo.new
f.build { some_class_method "new_method" }
f.singleton_methods # => [:new_method]

Вы определили поведение только на одном экземпляре.

Однако я не думаю, что вы можете получить то, что вы ищете в отношении констант. Одним из вариантов будет использование методов вместо констант для этих аргументов. Другим было бы смешивать код клиента в любом модуле, определяющем константы.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...