繁体   English   中英

如何在类上使用哈希键作为方法?

[英]How do I use hash keys as methods on a class?

我有一个类和一个哈希。 如何使用键作为方法名称,使哈希的成员动态地成为类的方法?

class User
  def initialize
    @attributes = {"sn" => "Doe", "givenName" => "John"}
  end
end

例如,我希望能够有以下输出Doe

u = User.new
puts u.sn

只需使用OpenStruct:

require 'ostruct'
class User < OpenStruct
end

u = User.new :sn => 222
u.sn
def method_missing(name, *args, &blk)
  if args.empty? && blk.nil? && @attributes.has_key?(name)
    @attributes[name]
  else
    super
  end
end

说明:如果调用一个不存在的方法,则调用method_missing,并将方法的名称作为第一个参数,然后是给定方法的参数和块(如果给出的话)。

在上面我们说如果一个没有定义的方法被调用而没有参数而没有一个块,并且哈希有一个方法名为key的条目,它将返回该条目的值。 否则它将照常进行。

sepp2k的解决方案是可行的方法。 但是,如果您的@attributes在初始化后永远不会改变并且您需要速度,那么您可以这样做:

class User
  def initialize
    @attributes = {"sn" => "Doe", "givenName" => "John"}
    @attributes.each do |k,v|
      self.class.send :define_method, k do v end
    end
  end
end

User.new.givenName # => "John"

这会提前生成所有方法......

实际上, severin有一个更好的主意,因为使用method_missing是一种不好的做法,不是所有的时间,而是大部分时间。

severin提供的代码有一个问题:它返回已传递给初始化程序的值,因此您无法更改它。 我建议你采取一些不同的方法:

class User < Hash
  def initialize(attrs)
    attrs.each do |k, v|
      self[k] = v
    end
  end

  def []=(k, v)
    unless respond_to?(k)
      self.class.send :define_method, k do
        self[k]
      end
    end

    super
  end
end

让我们检查一下:

u = User.new(:name => 'John')
p u.name
u[:name] = 'Maria'
p u.name

你也可以用Struct来做到这一点:

attrs = {:name => 'John', :age => 22, :position => 'developer'}
keys = attrs.keys

user = Struct.new(*keys).new(*keys.map { |k| attrs[k] })

让我们测试一下:

p user
p user.name
user[:name] = 'Maria'
p user.name
user.name = 'Vlad'
p user[:name]

或者甚至OpenStruct,但要小心它不会创建方法,如果它已经在实例方法中有它,你可以通过使用OpenStruct.instance_methods来寻找它(因为使用了类型,我现在使用第二种方法):

attrs = {:name => 'John', :age => 22, :position => 'developer'}
user = OpenStruct.new(attrs)

是的,这么容易:

user.name
user[:name] # will give you an error, because OpenStruct isn't a Enumerable or Hash

你可以为此“借用”ActiveResource。 它甚至可以处理嵌套的哈希和赋值:

require 'active_resource'
class User < ActiveResource::Base
  self.site = ''  # must be a string
end

用法:

u = User.new "sn" => "Doe", "givenName" => "John", 'job'=>{'description'=>'Engineer'}
u.sn  # => "Doe"
u.sn = 'Deere'
u.job.description  # => "Engineer"
# deletion
u.attributes.delete('givenName')

请注意,u.job是User :: Job - 此类是自动创建的。 分配给嵌套值时有一个问题。 您不能只分配哈希,但必须将其包装在适当的类中:

u.job = User::Job.new 'foo' => 'bar'
u.job.foo  # => 'bar

不幸的是,当你想添加一个没有相应类的嵌套哈希时,它更加丑陋,因为你必须强制ARes从哈希创建类:

# assign the hash first
u.car = {'make' => 'Ford'}
# force refresh - this can be put into a method
u = User.new Hash.from_xml(u.to_xml).values.first

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM