Ruby:你能确定一个对象是否有一个被调用的方法吗?

时间:2015-02-20 05:58:18

标签: ruby-on-rails ruby class hash openstruct

我不确定我是否在问正确的问题。我可能不正确地接近这个问题,但基本上我有这种情况:

obj = get_user(params)
obj.profile => {:name => "John D", :age => 40, :sex => "male"} #Has to be of class Hash
obj.profile.name => "John D"
obj.profile[:name] => "John D"
obj.profile.job => nil

所以基本上,我必须满足所有这些条件,我不确定如何处理这个问题(我今天刚学会了Ruby)。

注意用于访问内部变量的点表示法,否则我只是将 profile 作为符号的哈希值。所以我尝试了两种方法,只是让我在那里

方法1:使个人资料成为OpenStruct

因此,这允许我使用点表示法访问姓名,年龄和性别,如果某个键不存在,它会自动返回nil,但 obj.profile 的类型为 OpenStruct 而不是哈希

方法2:使个人资料成为自己的类

通过这个我将它们设置为实例变量,如果它们不存在,我可以使用 method_missing 返回nil。但是,我再次遇到 obj.profile 不是正确类型/类的问题

有什么我想念的吗?有没有办法可以区分

obj.profile
obj.profile.name
在getter函数中

并返回哈希或其他?

我可以更改自定义类为配置文件返回的内容,因此它会返回Hash吗?

我甚至尝试在 obj.profile 的get函数中检查 args 和** kwargs,它们似乎都没有帮助,或者如果我调用它们就填充 obj.profile.something

3 个答案:

答案 0 :(得分:1)

如果绝对必须是Hash

require 'pp'

module JSHash
  refine Hash do
    def method_missing(name, *args, &block)
      if !args.empty? || block
        super(name, *args, &block)
      else
        self[name]
      end
    end
  end
end

using JSHash

profile = {:name => "John D", :age => 40, :sex => "male"}

pp profile.name    # "John D"
pp profile[:name]  # "John D"
pp profile.job     # nil
pp profile.class   # Hash

但最好不要成为Hash,除非绝对需要:

require 'pp'

class Profile < Hash
  def initialize(hash)
    self.merge!(hash)
  end
  def method_missing(name, *args, &block)
    if !args.empty? || block
      super(name, *args, &block)
    else
      self[name]
    end
  end
end

profile = Profile.new({:name => "John D", :age => 40, :sex => "male"})

pp profile.name
pp profile[:name]
pp profile.job

答案 1 :(得分:1)

对于少数哈希键,您可以轻松定义单例方法,如下所示:

def define_getters(hash)
  hash.instance_eval do
    def name
      get_val(__method__)
    end

    def job
      get_val(__method__)
    end

    def get_val(key)
      self[key.to_sym]
    end
  end
end

profile = person.profile #=> {name: "John Doe", age: 40, gender: "M"}
define_getters(profile)

person.profile.name #=> "John Doe"
person.profile.job #=> nil

也反映更改的值(如果您想知道):

person.profile[:name] = "Ralph Lauren"
person.profile.name #=> "Ralph Lauren"

使用这种方法,您不必覆盖method_missing,创建继承自Hash的新类,或者对Hash类进行猴子修补。

但是,为了能够通过方法调用访问未知密钥并返回nil而不是错误,您 method_missing

答案 2 :(得分:1)

Hash覆盖将完成您要执行的操作。您需要做的就是将它包含在您已加载的一个类文件中。

class Hash
  def method_missing(*args)
    if args.size == 1  
      self[args[0].to_sym]
    else
      self[args[0][0..-2].to_sym] = args[1] # last char is chopped because the equal sign is included in the string, print out args[0] to see for yourself
    end
  end
end

请参阅以下IRB输出以确认:

1.9.3-p194 :001 > test_hash = {test: "testing"}
 => {:test=>"testing"} 
1.9.3-p194 :002 > test_hash.test
 => "testing" 
1.9.3-p194 :003 > test_hash[:test]
 => "testing" 
1.9.3-p194 :004 > test_hash.should_return_nil
 => nil 
1.9.3-p194 :005 > test_hash.test = "hello"
 => "hello" 
1.9.3-p194 :006 > test_hash[:test]
 => "hello" 
1.9.3-p194 :007 > test_hash[:test] = "success"
 => "success" 
1.9.3-p194 :008 > test_hash.test
 => "success" 
1.9.3-p194 :009 > test_hash.some_new_key = "some value"
 => "some value" 
1.9.3-p194 :011 > test_hash[:some_new_key]
 => "some value"