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

如果块中的一条语句在Ruby中返回true,如何返回true?,ruby,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,则表示所有步骤都成功 这似乎需要一个异常,但我不确定如何触发和处理它。我希望我不必修改方

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

done = prepare_it do |x|
  method_1
  method_2
  method_3
end
puts "Something did not work" unless done
我想让函数prepare_it运行每条语句,但如果其中一条语句失败,函数将退出。如果函数返回true,则表示所有步骤都成功

这似乎需要一个异常,但我不确定如何触发和处理它。我希望我不必修改方法_x函数来在失败时抛出异常,而是让prepare_x在其中一个方法_x失败时抛出异常(返回false)

编辑: 谢谢你的建议,我想我需要为我的问题添加更多细节。这是我第一次尝试元编程和创建DSL

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

但是,如果任何命令失败,应该有一种机制使整个块失败。因此,如果由于某种原因密码失败(该方法返回false/nil),则继续执行其余命令是没有意义的。 我应该指出有什么东西失败了

&&方法可以工作,但我认为它不是一个好的接口

也许我不应该得到一个大的块,也许我应该强迫用户给我更小的块,一次一个,放在堆栈中,然后一个run命令一个接一个地产生

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?
因此,任何发生在幕后的魔法骗局都是受欢迎的!:)

解决方案 解释
和&
操作符会“短路”因此,如果
方法1
返回
false
,则不会调用2和3,并且
完成
将是
false

例子

如果您使用异常,它就没有那么漂亮了,但您不必依赖于返回值:

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

没有一个好方法可以让事情像那样运作。我将执行以下操作之一:

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
类成功代理
def初始化(obj)
@对象=对象
结束
def方法_缺失(meth、*args和block)
@object.send(meth、*args和block)| | raisesuccessproxy::Exception
结束
类异常<::异常
结束
结束
你准备好了吗
#但是通常会生成x
收益率成功代理。新(x)
rescue SuccessProxy::异常
假的
结束
准备|它做| x|
x、 方法1
x、 方法2
x、 方法3
结束
或者,如果要像上下文中默认对象上的
method\u 1
一样调用方法,则可以使用
instance\u eval
而不是
yield
将代理放入范围

最后,如果您真的想变得更花哨,您可以使用Ruby解析器解析单独的语句


但老实说,我不相信你真的想将元编程作为一种工具来实现这一点。如果您提供更好的详细信息,我可能会对您提供更好的帮助,知道什么是最佳解决方案,但这可能与上面的第一个代码示例类似。

自然,紧凑性将是您的最高优先级,因此我相信这是最好的答案:

done = (1..3).all? { |n| send "method_#{n}" }
puts "Something did not work" unless done
起初我想,“如果这是口齿不清会不会很好…”但这让我意识到了确切的问题。做你想做的事情(在一个块中执行每一步直到其中一步为false)会很好,但是需要访问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

短路真的会像这样工作吗?这难道不需要方法_1、方法_2和方法_3返回布尔值吗?我的Ruby非常生锈,但这对我来说似乎有点可疑。它确实有效,这里是要点,你可以自己看一下。如果其中一个方法返回Ruby认为的
false
,则会发生短路,这是(令人惊讶的!)
false
,也是
nil
。每隔一次返回将求值为
true
,导致调用继续。返回值将为false,或者如果方法_1和方法_2的求值结果不为nil或false,则无论方法_3的值是什么。因为除nil或false之外的任何值都会导致执行if块,这是非常令人满意的。没错,短路它们就可以了。有没有办法将块转换为&&语句?或者我应该让用户write done=my\u router.send\u pass&&my\u router.set\u term&&my\u router.set\u。例外是为例外情况保留的。谢谢,但是方法_1,2,3只是方法真实名称的占位符:)正确性、完整性、紧凑性。标题上写的是“一个声明”,意思是
任何?
,但从那以后已经有很多编辑。我不确定我是否完全理解这里发生的事情。似乎只有在方法丢失时才有效,如果方法返回false则无效。
SuccessProxy
对象未定义任何方法(除了对象上的默认值,如果需要,可以取消这些方法的定义)。它向实际对象发送所有方法调用,如果它们的返回值计算为false(从技术上讲,是
fal),则引发异常
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