【问题标题】:Make object method_missing behave like class method_missing使对象 method_missing 表现得像类 method_missing
【发布时间】:2013-05-23 09:03:20
【问题描述】:

我创建了一个类,我有一些恒定的哈希值。我想输入Myclass.myhash.hashkey 并显示哈希值。现在我用method_missing 创建了一个类似的行为,但我必须初始化对象,所以我像Myclass.new.myhash.hashkey 一样调用它,它可以工作。到目前为止,这是我的代码:

class Myclass
  def initialize
    @attributes = []
  end

  def method_missing(name, *args)
    @attributes << name
    if @attributes.length == 2
      eval("#{@attributes.first.upcase}[:#{@attributes.last.downcase}]")
    else
      self
    end
  end

  MYHASH = {
    id: 1,
    description: "A nice hash",
    hashkey: "hash key"
  }
end

如何在没有初始化和没有new 的情况下做到这一点,这样它就不会每次都创建MyClass 的对象?

更新: 第一个问题是由 toro2k 解释的,但我不知道使用它是否可以有我的第二个问题的行为......

问题 2 我的类中有很多openstructs,如何动态地将它们定义为类方法,而无需每次都添加以下内容:

  def self.myhash
    MYHASH
  end   

【问题讨论】:

    标签: ruby-on-rails ruby oop metaprogramming


    【解决方案1】:

    您可以使用OpenStruct 对象代替Hash

    class MyClass
      MYHASH = OpenStruct.new(id: 1, 
                              description: 'A nice Ostruct', 
                              hashkey: 'hash key')
      def self.myhash
        MYHASH
      end      
    end
    
    MyClass.myhash.id # => 1
    MyClass.myhash.description # => "A nice Ostruct"
    MyClass.myhash.foo # => nil
    

    更新您可以使用类实例变量替换常量,如下所示:

    class MyClass
      def self.myhash
        @myhash ||= OpenStruct(id: ...)
      end
    end
    
    MyClass.myhash.id
    

    或者你可以使用类变量和cattr_reader:

    class MyClass
      cattr_reader :myhash
      @@myhash = OpenStruct(id: ...)
    end
    
    MyClass.myhash.id
    

    或者你可以去掉myhash方法直接访问常量:

    class MyClass
      MYHASH = OpenStruct(id: ...)
    end
    
    MyClass::MYHASH.id
    

    【讨论】:

    • 我还应该添加 require 'ostruct' 吗?我尝试了 require ostruct 并且没有它,但它对我不起作用(我使用的是 ruby​​ 2.0.0-p195,也许我做错了什么)。
    • 即使没有require 'ostruct',它也应该可以工作。你得到什么错误?
    • NoMethodError: undefined method `OpenStruct' for Myclass:Class ,也许它必须这样做,我试图从 Rails 内部运行它?
    • 对不起,那是我的错误。我已经更新了答案,你必须使用OpenStruct.new(...) 而不是OpenStruct(...)
    • 哦,对了,我没看到。太好了,它正在工作!谢谢。
    【解决方案2】:

    我也终于找到了第二个问题的解决方案:

      class << self
        Myclass.constants.each do |constant|
          define_method(constant.to_s.downcase) do
            eval("#{constant}")
          end
        end
      end
    

    在我定义了所有的 openstruct 变量之后,我只需将它添加到类的末尾即可。

    【讨论】:

    • 很好,但考虑将false 传递给constants 方法,这样您就不必为基类定义的常量定义方法。
    • 感谢您的建议,但我不确定我是否完全理解它。我应该猴子补丁常量并在那里添加一个错误?否则会自动包含在该模块所包含的所有类中?有没有更好的解决方案?谢谢!
    • Myclass.constants(false)而不是Myclass.constants,前者只返回Myclass内部定义的常量,而后者返回Myclass及其所有祖先定义的所有常量。
    • 啊哈!非常感谢您的建议!
    • 不客气,再来一个 :-)。您可以将 eval("#{constant}") 替换为 const_get(constant)
    猜你喜欢
    • 2012-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 2020-11-18
    • 1970-01-01
    • 2011-04-04
    • 2017-04-18
    相关资源
    最近更新 更多