如果块中的一个语句在Ruby中返回true,如何返回true?

时间:2011-07-30 02:06:18

标签: ruby

是否可以在Ruby中创建一个方法,该方法接受一个块并运行块中的每个语句,直到其中一个返回false?

done = prepare_it do |x|
  method_1
  method_2
  method_3
end
puts "Something did not work" unless done

我希望函数prepare_it运行每个语句,但如果其中一个语句失败,则该函数退出。如果函数返回true,则表示所有步骤都成功。

这似乎要求一个Exception,但我不确定如何触发和处理它。我希望我没有必要修改method_x函数以在失败时抛出异常,而是让prepare_it抛出异常,如果其中一个method_x失败(返回false)

修改 感谢您的建议,我想我需要在我的问题中添加更多细节。这是我第一次尝试元编程和创建DSL。 :)

我有一个类向路由器发送命令。总是需要按顺序执行许多步骤(连接,登录,传递等)。我认为如果用户愿意,可以让用户更改命令的顺序。例如,某些路由器不会要求用户直接进入密码提示。其他人完全跳过登录。在某些情况下,您需要提升权限或设置终端选项。

但是,如果任何命令失败,应该有一种机制使整个块失败。因此,如果由于某种原因密码失败(该方法返回false / nil),继续使用其余命令是没有意义的。 而且我应该告诉它失败了。

&&方法有效,但我认为它不是一个很好的界面。

也许我应该强迫用户给我一个较小的块,一次一个,这些块放在一个堆栈中,然后运行命令逐个产生?

my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup

我认为如果我能做到的话会更加清洁

my_router.setup do |cmd|
  cmd.send_pass
  cmd.set_terminal
end
puts "Done" if my_router.ready?

所以在幕后发生的任何神奇诡计都是受欢迎的! :)

5 个答案:

答案 0 :(得分:9)

解决方案

done = prepare_it do |x|
  method_1 && method_2 && method_3
end
puts "Something did not work" unless done

说明

&&运算符“短路”因此,如果method_1返回false,则不会调用2和3,done将是false

实施例

http://gist.github.com/1115117

答案 1 :(得分:1)

没有一种好方法可以像这样破解工作。我会做类似以下的事情之一:

done = prepare_it do |x|
  method_1 && method_2 && method_3
end

现在,在某些特定情况下,你可以通过魔术来实现这项工作。例如,如果应该在块参数x上调用这些方法:你可以这样做:

class SuccessProxy
  def initialize(obj)
    @object = obj
  end

  def method_missing(meth, *args, &block)
    @object.send(meth, *args, &block) || raise SuccessProxy::Exception
  end

  class Exception < ::Exception
  end
end

def prepare_it
  # however you usually generate x
  yield SuccessProxy.new(x)
rescue SuccessProxy::Exception
  false
end

prepare_it do |x|
  x.method_1
  x.method_2
  x.method_3
end

或者,如果要在上下文中的默认对象上调用方法method_1,则可以使用instance_eval而不是yield将代理放在范围内。< / p>

最后,如果你真的想要花哨,你实际上可以使用Ruby解析器解析单独的语句。

但老实说,我不相信你真的想要将元编程作为一种工具。也许如果你提供更好的细节,我可以更好地了解最佳解决方案是什么,但它可能与上面的第一个代码示例类似。

答案 2 :(得分:1)

自然紧凑将是您的首要任务,所以我相信这是最好的答案:

done = (1..3).all? { |n| send "method_#{n}" }
puts "Something did not work" unless done

答案 3 :(得分:0)

如果您使用例外,它不是很漂亮,但您不必依赖返回值:

done = prepare_it do |x|
  begin
    method_1
    method_2
    method_3
    true
  rescue
    false
  end
end
puts "Something did not work" unless done

def method_1
  # No issues, doesn't matter what we return
  return true
end

def method_2
  # Uh-oh, problem
  raise
end

答案 4 :(得分:0)

起初我想,“如果这是Lisp会不会很好......”但这让我意识到了确切的问题。做你想做的事(执行一个块中的每一步,直到一个是假的)会很好,但需要访问AST。

你的想法

my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup

正试图完成你在Lisp中所做的事情 - 构建一个列表并将列表移交给其他东西来执行每一件事,直到你得到一个错误的返回值。

如果你只是在没有任何参数的情况下调用类中的方法,那么如何定义带符号的东西呢?

class Router
  def run_setup *cmds
    # needs error handling
    while c = cmds.shift
      return false unless send(c)
    end
  end
end

my_router.run_setup :send_pass, :set_terminal, :enable_mode