Ruby模块变量访问器未按预期工作

时间:2012-12-19 22:32:42

标签: ruby

所以我想要一个带变量和访问方法的模块 我的代码看起来像这样

module Certificates
  module Defaults

  class << self
    attr_accessor :address

    def get_defaults
      address = "something"
      make_root_cert
    end

    def make_root_cert
      blub = address
      # do somthing
    end
  end
end

我用撬检查了它 结果是

  • Certificates :: Defaults具有名为address和address =的方法。
  • 如果我在get_defaults方法中调用地址,则会按预期返回“某事”
  • 如果我在make_root_cert中调用它,则返回nil

我在另一个模块中使用这种方式创建attr_accessor并且工作正常。我希望我只是误解了ruby的工作方式,有人可以解释为什么这个例子不起作用。也许使用ruby对象模型的实现细节。

杰里米是对的。

我的发现

这似乎与我不一致。

  • 如果使用表达式“address”并且尚未设置实例变量,则返回本地变量
  • 如果已设置实例变量,则局部变量不返回实例变量。
  • 如果两者都已设置,则返回本地变量。

另一方面,address =“test”始终设置局部变量。

2 个答案:

答案 0 :(得分:5)

get_defaults方法中,address是一个局部变量。要使用setter,您必须输入:

self.address = "something"

这将正确调用address=方法。

答案 1 :(得分:0)

这种相当混乱的行为发生是因为Ruby解释器将局部变量定义置于比方法调用更高的优先级。这里有一致性,但除非你事先知道它是如何工作的,否则很难看清楚。

鉴于Ruby中的许多东西都是对象和方法调用,因此可以自然地假设变量定义是某种某种方法(如内核或主要或定义它的对象或其他)。并且结果变量是某种对象。如果是这种情况,你会猜测解释器会根据方法查找规则解决变量定义和其他方法之间的名称冲突,并且只有在找不到与之同名的方法时才会定义新变量。首先是潜在的变量定义。

但是,变量定义不是方法调用,变量不是对象。相反,变量只是对象的引用,而变量定义是解释器在语言表面下跟踪的东西。这就是Kernel.local_variables返回符号数组的原因,并且无法获取某种局部变量对象的数组。

因此,Ruby需要一组特殊的规则来处理变量和方法之间的名称冲突。非局部变量有一个特殊的前缀,表示它们的范围($,@等),它修复了这个问题,但对于局部变量则不然。如果Ruby在方法之后需要parens,那也可以解决这个问题,但是我们可以不必这样做。为了方便在没有前缀的情况下引用局部变量并调用没有parens的方法,语言只是默认假设你想要局部变量,只要它在范围内。它可能是以另一种方式设计的,但是你会遇到奇怪的情况,你定义了一个局部变量,并且它被一些遥远的方法黯然失色,在程序的中途有相同的名称,所以它可能更像这样。

The Ruby Programming Language,p。 88,有这个说:

  

“...局部变量没有标点字符作为前缀。这意味着局部变量引用看起来就像方法调用表达式。如果Ruby解释器   已经看到对局部变量的赋值,它知道它是变量而不是方法,并且它可以返回变量的值。如果没有赋值,则Ruby将表达式视为方法调用。如果不存在该名称的方法,Ruby会引发NameError。“

接着解释了在nil中呼叫address时获得make_root_cert的原因:

  

“通常,因此,在初始化之前尝试使用局部变量会导致错误。有一个怪癖 - 当Ruby解释器看到该变量的赋值表达式时,变量就会出现。这是即使该分配实际上没有执行也是如此。存在但尚未赋值的变量将被赋予默认值nil。例如:

   a = 0.0 if false # This assignment is never executed
   print a          # Prints nil: the variable exists but is not assigned
   print b          # NameError: no variable or method named b exists"

使用attr_accessor获得的setter方法会导致解释器在调用setter方法之前创建变量,但必须调用它以将该变量赋值为nil以外的值。 address = "something"中的get_defaults在该方法中定义了一个名为address的局部变量,该变量在方法结束时超出了范围。当您调用make_root_cert时,没有名为address的局部变量,因此调用address获取的getter方法attr_accessor并返回nil,因为setter尚未调用方法给它一些其他值。 self.address=让解释器知道您希望类方法address=而不是新的局部变量,从而解决歧义。