Ruby是否有一个类似于method_missing的方法来处理未定义的实例变量?

13

当我调用不存在的方法时,method_missing 将告诉我方法的名称。当我尝试访问一个未设置的变量时,其值就是简单的 nil

我正在尝试动态拦截访问 nil 实例变量并根据所访问的变量名称返回一个值。最接近的等效功能将是 PHP 的 __get。Ruby 中是否有任何等效该功能的方法?


你知道警告信息以及如何开启它们吗? - Andrew Grimm
4个回答

2
请参见我对另一个类似问题的回答。但是,仅仅因为你“可以”这样做,并不意味着这是一个好主意。合理的设计通常可以克服这种需要,让你能够产生更易读和易于维护的代码。
据我所见,instance_variable_get 似乎是最接近 PHP 的 __get 的 Ruby 等效物(尽管我不是 PHP 用户)。
查看相关的 Ruby 源代码,变量的唯一“缺失”方法是 const_missing,没有实例变量。

1
没有帮助。我必须把所有的实例变量用法都转换成方法调用,这完全不切实际。 - user229044
除了我在另一个答案中描述的方法外,Ruby 中没有动态访问实例变量的功能。请参见上面更新的答案。 - Carl Suster

2
我不认为在Ruby中可以实现这一点。推荐的方法是在模板中使用“user”方法而不是“@user”实例变量。
这与您在外部处理Ruby对象的方式一致(“obj.user”是一个指向“@user”的方法,但实际上并不是“@user”本身)。如果您需要任何属性的特殊逻辑,最好使用一个方法(或method_missing),无论您是从对象内部还是外部访问它。

0

目前我不知道有 instance_variable_missing 这个方法。

但是你为什么要随意访问命名的实例变量呢?如果你通过方法调用来访问对象状态(正如你应该做的那样),那么你就不需要这个方法了。

如果你想要定义一些神奇的东西,而又不想干扰方法查找,你可以使用 const_missing


变量并非随机命名。我正在尝试渲染电子邮件,而不通过设置实例变量的邮件程序进行测试。视图将尝试访问像@user.name这样的内容,而没有设置@user。我想我可以在第一次读取时动态生成FactoryGirl对象,以便为@user等内容提供支持。 - user229044
我明白了,据我所知,恐怕您唯一的选择是阅读模板文件并相应地设置变量(或者可能是将问题反转并在您的电子邮件模板中使用method_missing)。这很遗憾,因为Ruby甚至“知道”您何时访问未定义的变量并会给出警告。 - riffraff

-1
有点晚了,但是instance_variable_missing在某种程度上与method_missing相同...看下面的类:
class Test
 def method_missing(*args)
  puts args.inspect
 end
end 
t = Test.new

现在让我们获取一些实例变量:
t.pineapples     #=> [:pineapples]
t.pineapples = 5 #=> [:pineapples=,5]

不确定为什么这个方法对你是空的...
编辑:
听起来你想要完成以下任务:
t = SomeClass.new
t.property.child = 1

那么让我们尝试从之前的示例中返回一个Test对象:

class Test
 def method_missing(*args)
  puts args.inspect
  return Test.new
 end
end 

当我们调用时会发生什么:

t = Test.new
t.property.child = 1
#=>[:property]
#=>[:child=,1]

所以这表明确实可以做到。 OpenStruct 使用相同的技术来动态设置实例变量。在下面的示例中,我创建了EternalStruct,它正是您想要的:
require 'ostruct'
class EternalStruct < OpenStruct
  def method_missing(*args)
    ret = super(*args)
    if !ret
      newES = EternalStruct.new
      self.__send__((args[0].to_s + "=").to_sym, newES)
      return newES
    end
  end
end

EternalStruct 的使用方法:

t = EternalStruct.new
t.foo.bar.baz = "Store me!"
t.foo.bar.baz #=> "Store me!"
t.foo #=> #<EternalStruct bar=#<EternalStruct baz="Store me!">>
t.a = 1
t.a #=> 1
t.b #=> #<EternalStruct:...>
t.b = {}
t.b #=> {}
def t.c(arg)
  puts arg
end
t.c("hi there") #=> "hi there"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接