我正在尝试向所有对象添加一个方法,允许动态地向实例添加变量。但是,我遇到的问题是,当我开始class
块时,方法参数超出范围,因此不再可用。
如何从class
块中访问方法参数?或者有更好的方法吗?
class Object
def addvar(name, value = nil)
puts name # => "test"
class << self
puts name # => returns nil!
attr_accessor name # => nil is not a symbol nor a string (TypeError)
end
self.instance_variable_set("@" + name.to_s, value)
end
end
x = Object.new
x.addvar("test", 3)
也可以使用带字符串替换的eval,但出于安全原因,我宁愿避免这样做:
class Object
def addvar(name, value = nil)
eval("class << self
attr_accessor :#{name}
end
self.#{name} = #{value}")
end
end
x = Object.new
x.addvar(:test, 3)
puts x.test
答案 0 :(得分:1)
您可能需要使用instance_variable_set
,instance_variable_get
和其他方法在Ruby中进行元编程。老实说,这是attr_accessor
在幕后使用的内容。
以下代码使用define_singleton_method
向您调用add_var
方法的对象添加单例方法:
class Object
def addvar(name, value = nil)
instance_name = "@#{name}"
instance_variable_set(instance_name, value)
define_singleton_method(name) do
instance_variable_get(instance_name)
end
define_singleton_method("#{name}=") do |new_value|
instance_variable_set(instance_name, new_value)
end
end
end
x = Object.new
x.addvar("test1", 3)
p x.test1
p Object.new.test1
=> 3
=> 1.rb:20:in `<main>': undefined method `test1' for #<Object:0x007fc98b161e10> (NoMethodError)
如果要为类的任何对象声明方法,可以在类上使用define_method
:
class Object
def addvar(name, value = nil)
instance_name = "@#{name}"
instance_variable_set(instance_name, value)
self.class.send(:define_method, name) do
instance_variable_get(instance_name)
end
self.class.send(:define_method, "#{name}=") do |new_value|
instance_variable_set(instance_name, new_value)
end
end
end
x = Object.new
x.addvar("test1", 3)
p x.test1
p Object.new.test1
=> 3
=> nil
顺便说一句,你仍然可以使用attr_accessor
,你可以在类对象上调用它:
class Object
def addvar(name, value = nil)
instance_variable_set("@#{name}", value)
self.class.send(:attr_accessor, name)
end
end
x = Object.new
x.addvar("test1", 3)
p x.test1
x.test1 = 5
p x.test1
p Object.new.test1