Ruby动态地创建方法和变量。

时间:2022-10-07 13:16:23

How can I replace add_entry method with something more sensible?

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

class MyStorageClass

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

end

So then I can retrieve value as follows:

这样我就可以得到如下的值:

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

4 个解决方案

#1


6  

Rather than an instance variable per key, which requires some unnecessarily bulky code, why not just a single Hash like below. Also, define_method and define_singleton_method can be your friend to avoid bad bad evals.

而不是每个键都有一个实例变量,这需要一些不必要的冗长代码,为什么不像下面这样只有一个散列呢?此外,define_method和define_singleton_method可以成为你的朋友,避免出现糟糕的情况。

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

You may want to check that you're not overriding a predefined method first (!@data.key?(key) && self.respond_to?(key) at the top of the add_entry method would do), but that's for another conversation. Could be bad if someone tried to add a key called inspect, class, or, oh, get_entry, for example!

您可能想要检查您并没有首先覆盖预定义的方法(@data.key?(key) && && self.respond_to?(key)在add_entry方法的顶部),但是这是另一个对话。如果有人试图添加名为inspect、类或get_entry(例如)的键,可能会很糟糕!

#2


1  

IMO this is a Really Bad Idea. Do not do this! You will be adding complexity with very little benefit.

在我看来,这真是个坏主意。不要这样做!您将增加复杂性,但几乎没有什么好处。

I recommend instead an OpenStruct. These are great objects -- you can call getters and setters on them at will without specifying the attributes in advance. Perhaps a little inefficient, but that usually doesn't matter.

我推荐的是OpenStruct。这些都是很好的对象——您可以随意调用getter和setter,而无需预先指定属性。也许有点低效,但这通常并不重要。

A side benefit of OpenStruct is that you can group your attributes into logical sets, e.g. connection_options, formatting_options, etc. Here's a sample script to illustrate:

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"}

#3


0  

I am not sure what you call “more sensible,” but here is the template without evals to start with:

我不确定你所说的“更明智”是什么,但这里有一个没有事件开始的模板:

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

The getter might be defined in more readable manner:

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

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

#4


0  

This can be achieved using instance_variable_set and attr_accessor:

这可以通过使用instance_variable_set和attr_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

However as others have suggested, a cleaner approach is to simply use a Hash rather than defining a new instance method for every variable.

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

#1


6  

Rather than an instance variable per key, which requires some unnecessarily bulky code, why not just a single Hash like below. Also, define_method and define_singleton_method can be your friend to avoid bad bad evals.

而不是每个键都有一个实例变量,这需要一些不必要的冗长代码,为什么不像下面这样只有一个散列呢?此外,define_method和define_singleton_method可以成为你的朋友,避免出现糟糕的情况。

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

You may want to check that you're not overriding a predefined method first (!@data.key?(key) && self.respond_to?(key) at the top of the add_entry method would do), but that's for another conversation. Could be bad if someone tried to add a key called inspect, class, or, oh, get_entry, for example!

您可能想要检查您并没有首先覆盖预定义的方法(@data.key?(key) && && self.respond_to?(key)在add_entry方法的顶部),但是这是另一个对话。如果有人试图添加名为inspect、类或get_entry(例如)的键,可能会很糟糕!

#2


1  

IMO this is a Really Bad Idea. Do not do this! You will be adding complexity with very little benefit.

在我看来,这真是个坏主意。不要这样做!您将增加复杂性,但几乎没有什么好处。

I recommend instead an OpenStruct. These are great objects -- you can call getters and setters on them at will without specifying the attributes in advance. Perhaps a little inefficient, but that usually doesn't matter.

我推荐的是OpenStruct。这些都是很好的对象——您可以随意调用getter和setter,而无需预先指定属性。也许有点低效,但这通常并不重要。

A side benefit of OpenStruct is that you can group your attributes into logical sets, e.g. connection_options, formatting_options, etc. Here's a sample script to illustrate:

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"}

#3


0  

I am not sure what you call “more sensible,” but here is the template without evals to start with:

我不确定你所说的“更明智”是什么,但这里有一个没有事件开始的模板:

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

The getter might be defined in more readable manner:

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

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

#4


0  

This can be achieved using instance_variable_set and attr_accessor:

这可以通过使用instance_variable_set和attr_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

However as others have suggested, a cleaner approach is to simply use a Hash rather than defining a new instance method for every variable.

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