Как передать условное выражение в качестве параметра в Ruby? - PullRequest
0 голосов
/ 10 июня 2010

Например, это то, что я пытаюсь сделать,

def method_a(condition, params={}, &block)
   if condition
      method_b(params, &block)
   else
      yield
   end
end

и я пытаюсь вызвать метод, подобный этому,

method_a(#{@date > Date.today}, {:param1 => 'value1', :param2 => 'value2'}) do

end

В результате условие всегда оценивается как истинное. Как мне заставить это работать?

Ответы [ 3 ]

4 голосов
/ 10 июня 2010

Разве вы не можете просто передать condition в результате условной оценки?

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do
    puts "Do stuff"
end
1 голос
/ 11 июня 2010

На самом деле, если бы у вас не было этого комментария в середине строки, он бы просто работал:

method_a(@date > Date.today, {:param1 => 'value1', :param2 => 'value2'}) do; end

Кстати: если бы последний аргумент метода былхеш, вы можете опустить фигурные скобки, что делает его читаемым почти как аргументы в стиле Python:

method_a(@date > Date.today, :param1 => 'value1', :param2 => 'value2') do; end

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

method_a(@date > Date.today, {param1: 'value1', param2: 'value2'}) do; end

Объединение двух действительно выглядит как аргументы ключевого слова:

method_a(@date > Date.today, param1: 'value1', param2: 'value2') do; end

В вашем method_a вы могли бы значительноулучшить удобочитаемость, используя выражение охраны вместо большого гудящего выражения if:

def method_a(condition, params={}, &block)
  return method_b(params, &block) if condition
  yield
end

Или наоборот, в зависимости от того, что вы считаете лучше:

def method_a(condition, params={}, &block)
  return yield unless condition
  method_b(params, &block)
end

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

В исходном коде это явно очевидно, поскольку у вас есть гигантское выражение if, окружающее весь метод, а код в двух ветвях полностью отличается.Это даже более очевидно, поскольку ветвь else не только имеет совершенно другой код, чем ветка then, но и полностью игнорирует аргументы, передаваемые в метод!Таким образом, метод не только ведет себя по-разному в зависимости от условия, он даже имеет различную сигнатуру !

То, что вы действительно хотите сделать, - это разделение метода на два метода.Пользователь method_a должен знать в любом случае , каково различие между этими двумя случаями, и он должен сам поставить условное выражение.Вместо этого он мог просто вызвать правильный метод в первую очередь.Итак, я бы разделил method_a на два:

def method_one(params={}, &block)
  method_b(params, &block)
end

def method_two
  yield
end

И клиент может решить, какой из них позвонить:

if @date > Date.today then
  method_two(param1: 'value1', param2: 'value2')
else
  method_one do
    # something
  end
end

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

То же самое касается method_two: все, что он делает, это вызывает блок.Клиент мог бы с таким же успехом запустить код в первую очередь.

Итак, теперь код нашей библиотеки выглядит так:

# there is no spoon

Это верно!Там не осталось кода библиотеки!(За исключением method_b, который не является частью вашего вопроса.)

И код клиента выглядит следующим образом:

if @date > Date.today then
  method_b(param1: 'value1', param2: 'value2')
else
  # something
end

Очень хороший пример метода, который нарушает это правило,* Module#instance_methods в базовой библиотеке Ruby.Он сообщает вам все методы экземпляра, определенные в определенном модуле и классе, и принимает логический аргумент, который решает, будет ли этот список включать методы, унаследованные от суперклассов. Никто не может когда-либо запомнить, передать ли false или true. Noone .Джим Вейрих использует этот пример в своих беседах о хорошем дизайне и обычно спрашивает аудиторию, что является унаследованным случаем, а какой - непосредственным.Как правило, высокий процент ошибается.Иногда этот процент на хуже, чем просто подбрасывание монеты !

Если вы посмотрите на документацию , это очень запутанно.Я не могу никогда помнить, как обходится условное условие, мне всегда приходится искать это в документации.Что не очень полезно, потому что официальная документация , которая является частью фактического исходного кода YARV и MRI, тоже неверно !

1 голос
/ 10 июня 2010

Я думаю, что разумный способ сделать это - использовать Proc или lambda:

def method_a(condition, params={}, &block)
   if condition.call
      method_b(params, &block)
   else
      yield
   end
end

method_a(lambda { @date > Date.today }, { :param1 => 'value1', :param2 => 'value2' }) do
  # ...
end

Лямбда не оценивается, пока вы не наберете call.

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