带哈希参数的DRY Ruby初始化 [英] DRY Ruby Initialization with Hash Argument

查看:81
本文介绍了带哈希参数的DRY Ruby初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现自己在构造函数上使用了很多哈希参数,尤其是在编写用于配置的DSL或最终用户将要接触到的API的其他位时。我最终要做的事情如下所示:

I find myself using hash arguments to constructors quite a bit, especially when writing DSLs for configuration or other bits of API that the end user will be exposed to. What I end up doing is something like the following:

class Example

    PROPERTIES = [:name, :age]

    PROPERTIES.each { |p| attr_reader p }

    def initialize(args)
        PROPERTIES.each do |p|
            self.instance_variable_set "@#{p}", args[p] if not args[p].nil?
        end
    end

end

没有更多惯用的方式来实现这一目标?扔掉的常量和符号到字符串的转换似乎特别糟糕。

Is there no more idiomatic way to achieve this? The throw-away constant and the symbol to string conversion seem particularly egregious.

推荐答案

您不需要常量,但是我不要认为您可以消除符号到字符串:

You don't need the constant, but I don't think you can eliminate symbol-to-string:

class Example
  attr_reader :name, :age

  def initialize args
    args.each do |k,v|
      instance_variable_set("@#{k}", v) unless v.nil?
    end
  end
end
#=> nil
e1 = Example.new :name => 'foo', :age => 33
#=> #<Example:0x3f9a1c @name="foo", @age=33>
e2 = Example.new :name => 'bar'
#=> #<Example:0x3eb15c @name="bar">
e1.name
#=> "foo"
e1.age
#=> 33
e2.name
#=> "bar"
e2.age
#=> nil

BTW,您可以(如果尚未使用的话)查看 Struct 类生成器类,它与您的类有点相似在做,但是没有哈希类型的初始化(但是我想创建足够的生成器类并不难)。

BTW, you might take a look (if you haven't already) at the Struct class generator class, it's somewhat similar to what you are doing, but no hash-type initialization (but I guess it wouldn't be hard to make adequate generator class).

试图实现hurikhan的想法,这就是我的目的:

Trying to implement hurikhan's idea, this is what I came to:

module HasProperties
  attr_accessor :props
  
  def has_properties *args
    @props = args
    instance_eval { attr_reader *args }
  end

  def self.included base
    base.extend self
  end

  def initialize(args)
    args.each {|k,v|
      instance_variable_set "@#{k}", v if self.class.props.member?(k)
    } if args.is_a? Hash
  end
end

class Example
  include HasProperties
  
  has_properties :foo, :bar
  
  # you'll have to call super if you want custom constructor
  def initialize args
    super
    puts 'init example'
  end
end

e = Example.new :foo => 'asd', :bar => 23
p e.foo
#=> "asd"
p e.bar
#=> 23

由于我对元编程不太熟练,所以我制作了答案社区Wiki,因此任何人都可以自由地更改实现。

As I'm not that proficient with metaprogramming, I made the answer community wiki so anyone's free to change the implementation.

扩展Marc-Andre的答案,这是一个通用名称,基于 Struct 的方法来创建哈希初始化的类:

Expanding on Marc-Andre's answer, here is a generic, Struct based method to create hash-initialized classes:

class Struct
  def self.hash_initialized *params
    klass = Class.new(self.new(*params))
  
    klass.class_eval do
      define_method(:initialize) do |h|
        super(*h.values_at(*params))
      end
    end
    klass
  end
end

# create class and give it a list of properties
MyClass = Struct.hash_initialized :name, :age

# initialize an instance with a hash
m = MyClass.new :name => 'asd', :age => 32
p m
#=>#<struct MyClass name="asd", age=32>

这篇关于带哈希参数的DRY Ruby初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆