【问题标题】:Ruby: Can you determine if an object is having one of its methods called?Ruby:你能确定一个对象是否调用了它的某个方法吗?
【发布时间】:2015-02-20 05:58:17
【问题描述】:

我不确定我是否问对了问题。我可能错误地处理了这个问题,但基本上我在这里遇到了这种情况:

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 成为符号的散列。所以我尝试了两种方法,这只能让我到达那里

方法一:将profile设为OpenStruct

所以这允许我使用点符号访问姓名、年龄和性别,如果键不存在,它会自动返回 nil,但是 obj.profile 属于 类型OpenStruct 而不是 Hash

方法2:将配置文件设为自己的类

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

我有什么遗漏吗?有没有办法区分

obj.profile
obj.profile.name

在 getter 函数中并返回哈希或其他?

我可以更改我的自定义类为配置文件返回的内容,因此它返回一个哈希值吗?

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

【问题讨论】:

  • 我认为obj.profile => {:name => "John D", :age => 40, :sex => "male"} 只意味着它必须返回一个看起来像哈希表示的表示。这并不一定意味着它必须是 Hash 的一个实例。以这种方式解释期望是错误的吗?
  • 提示说“返回一个符号化的哈希”,并且可以以完全相同的方式访问 OpenStruct。唯一的区别是它的类不一样......我会看看我解释它的方式是否可能。如果没有,那么我将使用 OpenStruct

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


【解决方案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

【讨论】:

  • merge! 是破坏性的merge:后者返回结果而不影响接收者(或参数),前者改变接收者。大多数情况下,a.merge!(b) 等价于a = a.merge(b),但这改变了a 的身份;显然改变self 的身份是行不通的。
【解决方案2】:

只需几个哈希键,您就可以轻松定义单例方法,如下所示:

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

【讨论】:

    【解决方案3】:

    这个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" 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-05
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      • 2018-11-30
      相关资源
      最近更新 更多