Ruby动态创建方法和变量

时间:2016-05-05 10:56:49

标签: ruby variables dynamic methods instance

如何用更合理的东西替换add_entry方法?

class MyStorageClass

    def add_entry key, value
        eval "(@#{key} ||= []) << value; def #{key}; @#{key}; end"
    end

end

那么我可以按如下方式检索值:

def get_entry key
    begin
        self.send key.to_sym
    rescue NoMethodError
        nil
    end
end

4 个答案:

答案 0 :(得分:6)

而不是每个键的实例变量,这需要一些不必要的庞大代码,为什么不是像下面这样的单个哈希。此外,define_methoddefine_singleton_method可以成为您的朋友,以避免糟糕的错误eval

class MyStorageClass
  def initialize
    @data = {}
  end

  def add_entry(key, value)
    (@data[key] ||= []) << value
    define_singleton_method(key){ @data[key] }
  end

  def get_entry(key)
    @data.key?(key) or raise NoMethodError
    @data[key]
  end
end

您可能想要检查您是否首先覆盖了预定义的方法(!@data.key?(key) && self.respond_to?(key)方法顶部的add_entry会这样做),但这是另一个对话的问题。 。如果有人试图添加名为inspectclass或哦,get_entry的密钥,可能会很糟糕!

答案 1 :(得分:1)

IMO这是一个非常不好的主意。不要这样做!你将增加复杂性而没什么好处。

我建议使用OpenStruct。这些都是很棒的对象 - 您可以随意调用getter和setter,而无需事先指定属性。也许效率有点低,但这通常并不重要。

OpenStruct的一个附带好处是您可以将属性分组为逻辑集,例如connection_options,formatting_options等。这是一个示例脚本来说明:

#!/usr/bin/env ruby

require 'ostruct'

class MyClass

  attr_reader :config_options # only if you want to expose this

  def initialize
    @config_options = OpenStruct.new
  end

  def do_something
    config_options.color = 'yellow'
    config_options.size = 'medium'
  end

  def to_s
    config_options.to_h.to_s
  end
end

my_class = MyClass.new
my_class.do_something
puts my_class  # outputs: {:color=>"yellow", :size=>"medium"}

答案 2 :(得分:0)

我不确定你称之为“更明智”,但这里是没有eval s的模板:

def add_entry key, value
  # define instance variable unless it is already defined
  instance_variable_set :"@#{key}", [] \
    unless instance_variable_defined? :"@#{key}"
  # add value to the array
  instance_variable_set :"@#{key}", instance_variable_get(:"@#{key}") + value
  # define getter
  self.class.send :define_method key { instance_variable_get :"@#{key}" } \
    unless self.class.instance_methods.include?(key)
end

可以以更易读的方式定义getter:

  self.class.send :attr_reader, key \
    unless self.class.instance_methods.include?(key)

答案 3 :(得分:0)

可以使用instance_variable_setattr_accessor

来实现
class MyStorageClass
  def add_entry(key, value)
    if respond_to?(key)
      key << value
    else
      instance_variable_set("@#{key}", [value])
      self.class.send(:attr_accessor, key)
    end
  end
end

然而正如其他人所建议的那样,更简洁的方法是简单地使用Hash而不是为每个变量定义新的实例方法。