【问题标题】:Does Ruby have a method_missing equivalent for undefined instance variables?Ruby 是否有未定义实例变量的 method_missing 等价物?
【发布时间】:2011-11-30 20:39:54
【问题描述】:

当我调用一个不存在的方法时,method_missing 会告诉我该方法的名称。当我尝试访问尚未设置的变量时,该值只是nil

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

【问题讨论】:

  • 你知道警告以及如何打开它们吗?

标签: ruby metaprogramming method-missing


【解决方案1】:

有点晚了,但是instance_variable_missingmethod_missing 在一定程度上是一样的...上下面的课:

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

现在让我们获取一些实例变量:

t.pineapples     #=> [:pineapples]
t.pineapples = 5 #=> [:pineapples=,5]

不知道为什么这个方法对你来说是 nil...

编辑:

根据你想要完成的声音:

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"

【讨论】:

    【解决方案2】:

    见我的answer to another similar question。但仅仅因为你可以这样做并不意味着这是一个好主意。明智的设计通常可以克服对此类事物的需求,并允许您生成更具可读性和可维护性的代码。

    instance_variable_get 似乎是 PHP 的 __get 最接近的等价物,据我所知(尽管我不是 PHP 用户)。

    查看relevant Ruby source code,变量的唯一“缺失”方法是const_missing 用于常量,没有实例变量。

    【讨论】:

    • 没有帮助。我必须将我所有的实例变量使用转换为方法调用,这是完全不切实际的。
    • 好吧,除了我在其他答案中描述的之外,Ruby 中没有这样的工具可以动态访问实例变量。请参阅上面的更新答案。
    【解决方案3】:

    我不相信这在 Ruby 中是可能的。推荐的方法是在模板中使用“用户”方法而不是“@user”实例变量。

    这与你在外部处理 Ruby 对象的方式是一致的(''obj.user'' 是一个引用 ''@user'' 的方法,但实际上不是 ''@user'' 本身)。如果您需要任何具有属性的特殊逻辑,最好的办法是使用方法(或 method_missing),无论您是从对象内部还是外部访问它。

    【讨论】:

      【解决方案4】:

      没有 instance_variable_missing(至少我知道) 但是你为什么要访问随机命名的实例变量呢?

      如果您的线程通过方法调用(无论如何都应该)访问对象状态,那么您就不需要这个。

      如果您正在寻找一种方法来定义神奇的东西而不弄乱方法查找,您可能想要使用const_missing

      【讨论】:

      • 变量不是随机命名的。出于测试目的,我正在尝试渲染一封电子邮件,而无需通过负责设置实例变量的邮件程序。该视图将尝试访问诸如 @user.name 之类的内容,而无需设置 @user。我想我可能能够在第一次阅读时为 @user 之类的东西动态生成 FactoryGirl 对象。
      • 我明白了,谢谢。 AFAICT 恐怕您唯一的选择是读取模板文件并相应地设置变量(或者可能扭转问题并在您的电子邮件模板中使用method_missing)。这是一个耻辱,因为当你访问一个未定义的变量时,ruby 甚至 知道 并且会给你一个警告。
      猜你喜欢
      • 2011-02-21
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-11
      • 2014-03-08
      • 1970-01-01
      • 2012-05-20
      相关资源
      最近更新 更多