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 eval
s.
而不是每个键都有一个实例变量,这需要一些不必要的冗长代码,为什么不像下面这样只有一个散列呢?此外,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 eval
s 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 eval
s.
而不是每个键都有一个实例变量,这需要一些不必要的冗长代码,为什么不像下面这样只有一个散列呢?此外,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 eval
s 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.
然而,正如其他人所建议的,更简洁的方法是简单地使用散列,而不是为每个变量定义一个新的实例方法。