模块变量不保持其状态

时间:2013-06-16 15:14:55

标签: ruby-on-rails ruby

我在Ruby中有一个包含一个方法和一个变量的模块:

module ApplicationHelper
  @var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
  def method1 v1, v2, v3 = @var1[0]
    #....
    # access to @var1
  end
end

我希望@var1成为变量的原因是,如果它是一个方法,我会在method1内每次访问它时对其进行评估。这是不合理的,因此,我希望它只被评估一次。

但是,当我通过ajax(使用它的基于ajax的对话框)调用此method1(它是一个辅助方法)时,@ var1结果为零。同样,在第一页加载期间,它按照我的预期填写。

我该怎么办?

1 个答案:

答案 0 :(得分:2)

你的背景不一样(见):

module ApplicationHelper
  p self # => ApplicationHelper

  def method1
    p self # => #<OtherClass:0x007fbe032cf6f0> an instance of an object
  end
end

Ruby不像Java一样工作,因为您无法像在模块(或类)的上下文中那样设置对象实例变量。在您的代码中,@var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }在任何实例上的实际模块对象var1上定义实例变量ApplicationHelper

在Ruby中,当你只想定义一次变量时,我们使用||= memoization技巧:

module ApplicationHelper
  def method1
    @var1 ||= MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
  end
end

这是一个利用第一次引用变量的事实,它将是nil,而nil在Ruby中被认为是fasely。所以上面这行是写作的捷径:

@var1 || @var1 = MyModel::CONST1.each_with_index.map { |item, i| [item, i] }

注意:请注意,如果@var1可能是值nilfalse,则每次都会对其进行重新评估。但是,在这种情况下。由于您使用的是Enumerable#map,因此如果没有商品,您将获得[],而不是nil。因此,在这种情况下,使用情况将如预期一样。

第一次调用method1时,边缘情况会出现问题,因为您正在使用它设置默认值。如果我们改为使用消息传递而不是直接变量访问,我们可以实现您的目标:

module ApplicationHelper
  def method1(v1, v2, v3 = var1.first)
    #....
    # all further access to @var1 is done by passing the message var1
    tmp = var1.select{ ... }
  end

  def var1
    @var1 ||= MyModel::CONST1.each_with_index.map { |item, i| [item, i] }
  end
end