Включение методов в блоки - PullRequest
2 голосов
/ 04 марта 2010

Кто-нибудь знает, как заставить это работать, если это возможно?

class Foo
  def self.go(&block)
    class << block
      include Bar
    end    
    puts "Within Foo#go: #{block.methods.include? 'baz'}"
    block.call
  end
end

module Bar
  def baz
    puts "I'm happily in my place!"
  end
end

Foo.go { 
  puts "Within actual block: #{methods.include? 'baz'}"
  baz
}

Это дает вывод:

Within Foo#go: true
Within actual block: false
NameError: undefined local variable or method ‘baz’ for main:Object

РЕДАКТИРОВАТЬ: когда я распечатываю класс блока в Foo #иди, это Proc, но когда я распечатываю его в Proc, это Object.Может ли это быть связано?

Ответы [ 3 ]

3 голосов
/ 04 марта 2010

Вы можете использовать eval с Proc#binding:

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  eval('include Bar', block.binding)
  block[]
end

baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"

Но если вы не используете структуру mixin / mixout (например, mixico или mixology), вы будете помещать методы из включенного модуля в лексическую область видимости, поэтому они будут доступны после возвращения блока.

require 'rubygems'
require 'mixico'

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  Module.mix_eval(Bar, &block)
end

baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError

Здесь хорошая статья о различных способах использования DSL из блока.

3 голосов
/ 04 марта 2010

Вы не можете сделать это. Причина того, что вы видите, в том, что здесь есть два разных контекста. Одним из них является контекст блока, который закрывает более контекст, в котором он определен. Другой - это контекст обёртки объекта Proc, который точно такой же, как и любой другой объектный контекст, и совершенно не связан с контекстом самого блока.

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

Другой вариант - передать блок фактическому получателю для метода baz.

2 голосов
/ 05 марта 2010

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

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  dup_context = eval('self', block.binding).dup
  dup_context.send(:include, Bar)
  dup_context.instance_eval &block
end

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

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