我有一个类和一个哈希。我怎样才能让哈希的成员动态地成为类的方法,并且使用方法名作为键?如何使用散列键作为类的方法?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
例如,我想能够有以下输出Doe
:
u = User.new
puts u.sn
我有一个类和一个哈希。我怎样才能让哈希的成员动态地成为类的方法,并且使用方法名作为键?如何使用散列键作为类的方法?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
例如,我想能够有以下输出Doe
:
u = User.new
puts 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方法被调用,方法的名称作为第一个参数,然后是给定的方法和块的参数(如果给出的话)。
在上面我们说如果一个没有定义的方法在没有参数的情况下被调用,并且没有一个块并且这个散列有一个方法名作为关键字的条目,它将返回该条目的值。否则,它将照常进行。
sepp2k的解决方案是要走的路。但是,如果你的@属性初始化后,永远不会改变,你需要速度,那么你可以做这样:
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
,你也可以用结构做到这一点:
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,但是是ca reful它不会产生方法,如果它已经有它在实例方法,您可以通过使用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
你的解释和扩展例子真的很棒。谢谢! – 2013-03-07 16:55:43
只需使用OpenStruct:
require 'ostruct'
class User < OpenStruct
end
u = User.new :sn => 222
u.sn
您可以 “借用” 的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')
注意,美。job是一个User :: Job - 这个类是自动创建的。 分配给嵌套值时有一个问题。你不能只分配一个哈希,但必须在适当的类包起来:
u.job = User::Job.new 'foo' => 'bar'
u.job.foo # => 'bar
不幸的是,当你想添加不具有对应的级嵌套哈希,这是丑陋的,因为你必须强制AR从哈希创建类:
# 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
请务必查看OpenStruct(标准库中的struct.rb)。这与你所要求的有点不同:它允许OpenStruct的任何方法调用成为一个访问器,无论它是否已经被定义。但是它不需要编写代码,有时候可以加上代码。 – 2010-02-10 22:22:28