Использование yield внутри define_method в Ruby - PullRequest
27 голосов
/ 21 февраля 2010

Можно ли сделать так, чтобы ключевое слово yield работало внутри блока, указанного для define_method? Простой пример:

class Test
  define_method :test do |&b|
    puts b    # => #<Proc:...>
    yield
  end
end

Test.new.test {
  puts "Hi!"
}

Этот код выдает следующую ошибку в Ruby 1.8.7 и 1.9.0:

test.rb: 4: в `test ': блок не указан (LocalJumpError) из test.rb: 8

Странно то, что блочная переменная b != nil, но block_given? возвращает false. Является ли намеренное поведение Ruby не распознавать блоки по Proc объектам?

Редактировать: С уважением к Beerlington ответ: b.call() это не то, что я ищу. Переменная блока использовалась только для указания того, что блок фактически задан и не обнаружен внутри define_method.

Причина, по которой мне нужно использовать yield вместо block.call

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

Таким образом, подобная семантика не может быть принята во внимание, потому что это заставляет пользователей моей библиотеки использовать только один правильный способ передать блок. Это нарушает правило TIMTOWTDI и не делает мою библиотеку прозрачной.

Пример из реальной жизни

Код ниже можно упростить до кода выше, так как my_def использует define_method:

require 'my_library'

class Test
  # client can write 'my_def' instead of 'def' since
  # my_library extends Class class
  my_def :test, "some parameter" do
    yield        # oh no, error :(
  end
end

Test.new.test {
  puts "Hi!"
}

Ответы [ 2 ]

21 голосов
/ 21 февраля 2010

Я думаю, это то, что вы ищете:

class Test
  define_method :test do |&b|
    b.call
  end
end

Test.new.test {
  puts "Hi!"
}

Больше на http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/

18 голосов
/ 18 ноября 2010

Вы не можете использовать yield внутри блока define_method. Это потому, что блоки захвачены замыканиями, обратите внимание:

def hello
  define_singleton_method(:bye) { yield }
end

hello { puts "hello!" }

bye {  puts "bye!" } #=> "hello!"

Я не думаю, что ваши пользователи будут возражать против того, чтобы вы не могли использовать «yield» в том виде, в котором вы указали ---- синтаксис ничто как обычный синтаксис определения метода Ruby, поэтому вряд ли быть в замешательстве.

Дополнительная информация о том, почему вы не можете неявно передавать блоки в методы, найденные здесь: http://banisterfiend.wordpress.com/2010/11/06/behavior-of-yield-in-define_method/

...