如何在 Rails 控制器中使用闭包?

How use closures in Rails controller?

class MyController < ApplicationController
  def getuser
    data = params[:data]
    first.call
  end

  first = lambda { user = User.first.name + data }
end

但是在我看来我得到了

NameError undefined local variable or method `first' for

感谢您的帮助

创建 lambda 时收集 lambda 或 proc 的闭包。因此,lambda 的主体可以访问在创建 lambda 时定义和可见的所有局部变量。这不包括仅在调用 lambda 时定义的其他变量。

因此,您必须使用参数将 lambda 需要的任何数据(创建 lambda 时闭包中尚不可用的数据)传递给它。

最后,为了能够调用 lambda,您需要使其在您要使用它的范围内可用。这可能看起来像这样:

class MyController < ApplicationController

  # define a local variable which will be available in the lambda's closure
  scope = ENV['scope']

  # Assign the lambda to a constant. That way, it can be resolved
  # by every method in the current class
  FIRST = lambda do |data|

    # The scope variable is available here when the lambda is called
    # since it is available in the closure from the lambda's creation.
    #
    # Since the data variable is not available during the lamda's
    # creation, we need to get it from a parameter from the caller.
    User.where(scope: scope).first.name + data
  end

  def getuser
    data = params[:data]
    FIRST.call(data)
  end
end

话虽如此,一种更常见的方法是在对象上使用完整方法而不是传递 lambda:

class MyController < ApplicationController
  def getuser
    data = params[:data]
    first(data)
  end

  private

  def first(data)
    User.first.name + data
  end
end

这避免了对(大部分是隐式的且不是很明显的)闭包的内容进行推理,以支持可以访问当前对象的实例变量和方法的普通方法调用。

特别是如果您来自 Javascript(通常使用类似于 Ruby 的 lambda 的闭包传递匿名函数),您应该尝试适应 Ruby-way 并避免这种构造,支持具有显式方法的对象。