Как создать блок с заданной привязкой в ​​ruby? - PullRequest
4 голосов
/ 08 июля 2011

Я пытаюсь написать версию assert_difference, которая будет принимать хэш в качестве аргумента, чтобы вместо записи

assert_difference 'thing1', 1 do
  assert_difference ['thing2a', 'thing2b'], 2 do
    assert_difference 'thing3', -3 do
      # some triple-indented code
    end
  end
end

Я могу написать

assert_difference 'thing1' => 1, ['thing2a', 'thing2b'] => 2, 'thing3' => 3 do
  # some single-indented code
end

Я дошел до

def assert_difference_with_hash_support(expression, difference = 1, message = nil, &block)
  if expression.is_a? Hash
    expression.each do |expr, diff|
      block = lambda do
        assert_difference_without_hash_support expr, diff, &block
      end
    end
    block.call
  else
    assert_difference_without_hash_support(expression, difference, message, &block)
  end
end
alias_method_chain :assert_difference, :hash_support

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

    b = block.send :binding
    expression.each do |expr, diff|
      block = lambda(b) do
        assert_difference_without_hash_support expr, diff, &block
      end
    end
    block.call

но я не видел способа создания нового блока с чем-либо, кроме текущей привязки. Как мне создать блок с данной привязкой?

1 Ответ

2 голосов
/ 08 июля 2011

Может быть, я что-то упускаю, но я думаю, что вы пытаетесь использовать очень сложные функции ruby, в то время как они не нужны для решения вашей проблемы.

Мое решение будет:

def assert_hash(hash, &block)
  if hash.length > 1
    assert_difference(*hash.shift) do
      assert_hash(hash, &block)
    end
  else
    assert_difference(*hash.first, &block)
  end
end

Конечно, отсутствует псевдоним, но это не главное.

РЕДАКТИРОВАТЬ:

Что касается создания блоков с пользовательскими привязками, ответ: нет.Но вы можете вызывать куски кода с другой привязкой, либо перехваченные с помощью метода binding, либо просто предоставив объект, связанный с ним связыванием.

Для этой цели вы можете использовать eval (он принимаетПривязка объекта в качестве второго аргумента) или лучше instance_eval, class_eval, instance_exec и class_exec.Вы можете начать копать в Запись в блоге Мысли Джея Филдса .

...