【问题标题】:Why is yield not passing the result to block (Rails)?为什么产量不将结果传递给块(Rails)?
【发布时间】:2019-10-04 13:10:12
【问题描述】:

我知道有几个 SO 问题以及关于在 Rails 中使用 yield 的在线文章。但是我仍然无法理解下面的代码有什么问题,如果有任何建议,我将不胜感激。

在我的应用中,我有:

  • 一个controller,将数据传递给command类的run方法,并根据Command.run的结果返回请求状态(true/false

  • 一个command 类,处理实际的过程,如果成功则为yields true,如果失败则为false

但是,command 类似乎无法将结果传递给我的yield controller。根据我运行测试时的错误消息,我在controller 中的块似乎没有被识别为块:

# If I use "yield result": 
LocalJumpError: no block given (yield)

# If I use "yield result if block_given?":
# (This is because I have "assert_response :success" in my tests)
Expected response to be a <2XX: success>, but was a <400: Bad Request>

我应该如何重写块(下面控制器中的do ... end 部分)以使yield 正常工作?或者如果问题出在其他地方,我做错了什么?

我在下面提供了我的代码的简化版本。提前谢谢!

# controller

def create
  Command.run(params) do
    render json: { message: 'Successfully processed request' }
    return
  end
  render json: { message: 'Encountered an error' }, status: :bad_request
end
# command class

def run(params)
  # Do some stuff, then send HTTP request
  # "result" below returns true or false
  result = send_http_request.parsed_response == 'ok'
  yield result
end

def self.run(params)
  new.run(params)
end

注意:如果我在控制器中使用 if true... else... 而不是块,并且仅使用 return 布尔结果而不是 yielding 它,则此代码有效。但在这里我想知道如何使yield 工作。

【问题讨论】:

  • 1.你确定Command.run 没有在其他任何地方都没有被调用吗? 2. 您使用的是哪个 Ruby 版本?
  • @eyevan 1. 控制器文件是唯一调用Command.run 的地方。 2. 我正在使用2.6.2

标签: ruby-on-rails ruby yield


【解决方案1】:

在您的控制器中,您需要有一个结果变量。

def create
  Command.run(params) do |result|
    if result
        render json: { message: 'Successfully processed request' }, status: :success
    else
        render json: { message: 'Encountered an error' }, status: :bad_request
    end
    return
  end
  render json: { message: 'Encountered an error' }, status: :bad_request
end

(编辑)

此外,您正在调用调用实例方法的类方法。您必须将块从调用代码传递给您正在调用的实例方法。

def self.run(params, &block)
  new.run(params, &block)
end

【讨论】:

  • 感谢您的想法,但我仍然遇到同样的错误 - LocalJumpError: no block given (yield)
  • 如果我在yield result 之前插入byebug 运行测试,并检查result 的值,它会返回true。所以模型位可能没有问题。我写这个块的方式一定有问题。
  • 非常感谢!明确地传递块有效。作为旁注 - 似乎没有必要为结果块使用变量。正如comment 解释的那样,该块可以像这样简单并且没有变量:foo(1, 2) { "from the block" }。所以控制器代码看起来还不错。
  • 是的,但我以为不管成功还是失败你都想通过。
【解决方案2】:

编辑:啊,所以你有一个类方法run 和实例方法run

  1. 要么按照 Marlin 的建议进行操作,要么将块从类方法显式提供给实例方法。

  2. 或者只使用我最初建议的类方法(它没有 在您的情况下,似乎有任何理由实例化 Command):

    def self.run(params, &block)
      result = send_http_request.parsed_response == 'ok'
      block.yield(result)
    end
    

【讨论】:

  • 感谢您的建议 - 这返回了 NameError: undefined local variable or method 'block'。但是,如果您无法重现该行为,也许我的简化代码过于简化并且不是准确的表示?我实际上并没有直接调用类方法,而是我有函数runself.run,就像我刚刚在我的问题中更新的那样。这和它有关系吗?
  • 例如,我是否应该在self.run 中调用yield 而不是run
  • @Risa 啊,我明白了。所以你有一个类方法run和实例方法run。嗯,这就是问题的原因。
  • 谢谢!你说得对,原因是我使用了类方法。出于不同的原因,我需要在这里使用类方法,所以我采用了方法 #1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
  • 1970-01-01
  • 2017-11-26
  • 2013-06-25
  • 2019-03-04
  • 2014-08-20
相关资源
最近更新 更多