简体   繁体   English

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

[英]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.

相关问题 如何将哈希键映射到封装的Ruby类(无表模型)的方法? - How to map hash keys to methods for an encapsulated Ruby class (tableless model)? 方法如何在Ruby中使用哈希参数? - How do methods use hash arguments in Ruby? 如何在哈希值上使用Time类? - How do I use the Time class on hash values? 如何获取带有嵌套哈希的多个哈希键? - How do I fetch multiple hash keys with a nested hash? 如何使用命名索引(键)数组在嵌套哈希中设置值? - How do I use an array of named indices (keys) to set a value in a nested hash? 如何使用define_method创建类方法? - How do I use define_method to create class methods? 如何在类中创建自定义过程以便我可以这样做:params_hash.downcase_keys 而不是 downcase_keys(params_hash)? - How to make a custom proc in a class so I can do: params_hash.downcase_keys instead of downcase_keys(params_hash)? Ruby-如何从嵌套在哈希中的数组访问键 - Ruby - How do I access keys from an array nested in a hash 如何定义一个返回带有弦键的Hash的FactoryGirl工厂? - How do I define a FactoryGirl factory that returns a Hash with stringed keys? 在Ruby中,如何按键中单词的数量对哈希中的元素进行排序? - In Ruby, how do I sort the elements in a hash by the quantity of words in the keys?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM