[英]How do I use hash keys as methods on a class?
I have a class and a hash. 我有一个类和一个哈希。 How can I get the members of the hash to dynamically become methods on the class with the key as the method name? 如何使用键作为方法名称,使哈希的成员动态地成为类的方法?
class User
def initialize
@attributes = {"sn" => "Doe", "givenName" => "John"}
end
end
For example, I would like to be able to have the following output Doe
: 例如,我希望能够有以下输出Doe
:
u = User.new
puts u.sn
Just use OpenStruct: 只需使用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
Explanation: If you call a method, which does not exist, method_missing is invoked with the name of the method as the first parameter, followed by the arguments given to the method and the block if one was given. 说明:如果调用一个不存在的方法,则调用method_missing,并将方法的名称作为第一个参数,然后是给定方法的参数和块(如果给出的话)。
In the above we say that if a method, which was not defined, is called without arguments and without a block and the hash has an entry with the method name as key, it will return the value of that entry. 在上面我们说如果一个没有定义的方法被调用而没有参数而没有一个块,并且哈希有一个方法名为key的条目,它将返回该条目的值。 Otherwise it will just proceed as usual. 否则它将照常进行。
The solution by sepp2k is the way to go. sepp2k的解决方案是可行的方法。 However, if your @attributes never change after the initialization and you need speed, then you could do it in this way: 但是,如果您的@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"
This generates all the methods in advance... 这会提前生成所有方法......
Actually severin
have a better idea, just because usage of method_missing is a bad practice, not all the time, but most of it. 实际上, severin
有一个更好的主意,因为使用method_missing是一种不好的做法,不是所有的时间,而是大部分时间。
One problem with that code provided by severin
: it returns value that have been passed to initializer, so you cannot change it. 由severin
提供的代码有一个问题:它返回已传递给初始化程序的值,因此您无法更改它。 I suggest you a little different approach: 我建议你采取一些不同的方法:
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
Lets check it: 让我们检查一下:
u = User.new(:name => 'John')
p u.name
u[:name] = 'Maria'
p u.name
And also you can do it with Struct: 你也可以用Struct来做到这一点:
attrs = {:name => 'John', :age => 22, :position => 'developer'}
keys = attrs.keys
user = Struct.new(*keys).new(*keys.map { |k| attrs[k] })
Lets test it: 让我们测试一下:
p user
p user.name
user[:name] = 'Maria'
p user.name
user.name = 'Vlad'
p user[:name]
Or even OpenStruct, but be careful it will not create method if it already have it in instance methods, you can look for that by using OpenStruct.instance_methods
(because of type is used, I'm now using second approach): 或者甚至OpenStruct,但要小心它不会创建方法,如果它已经在实例方法中有它,你可以通过使用OpenStruct.instance_methods
来寻找它(因为使用了类型,我现在使用第二种方法):
attrs = {:name => 'John', :age => 22, :position => 'developer'}
user = OpenStruct.new(attrs)
Yep, so easy: 是的,这么容易:
user.name
user[:name] # will give you an error, because OpenStruct isn't a Enumerable or Hash
You can "borrow" ActiveResource for this. 你可以为此“借用”ActiveResource。 It even handles nested hashes and assignment: 它甚至可以处理嵌套的哈希和赋值:
require 'active_resource'
class User < ActiveResource::Base
self.site = '' # must be a string
end
Usage: 用法:
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')
Note that u.job is a User::Job - this class is auto-created. 请注意,u.job是User :: Job - 此类是自动创建的。 There's a gotcha when assigning to a nested value. 分配给嵌套值时有一个问题。 You can't just assign a hash, but must wrap it in the appropriate class: 您不能只分配哈希,但必须将其包装在适当的类中:
u.job = User::Job.new 'foo' => 'bar'
u.job.foo # => 'bar
Unfortunately, when you want to add a nested hash that doesn't have a corresponding class, it's uglier because you have to force ARes to create the class from the hash: 不幸的是,当你想添加一个没有相应类的嵌套哈希时,它更加丑陋,因为你必须强制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.