【发布时间】:2020-03-30 07:31:00
【问题描述】:
其他人可以帮我解决一个与覆盖先前定义的方法有关的场景吗?
我有一个类可以接收Hash,以便在运行时为每个键值对创建实例变量。如果值是Hash,那么我们需要实例化一个新的Config 类并将其分配给上下文中的实例变量,这在initialize 方法中是固定的。
此外,这个类必须响应不同的方法,如果它们不存在,那么我们需要动态创建它们。这可以通过覆盖 method_missing 方法并评估给定方法的值是否为 Hash 来解决,然后应用与初始化程序中相同的逻辑。
class Config
def initialize(parameters = {})
raise ArgumentError.new unless parameters.is_a?(Hash)
parameters.each do |key, value|
raise ArgumentError.new unless key.is_a?(String) || key.is_a?(Symbol)
create_new_method_on_instance(key, value)
end
end
def method_missing(method_name, *args)
name = method_name.to_s.delete_suffix('=')
create_new_method_on_instance(name, args.first)
end
private
def create_new_method_on_instance(name, value)
singleton_class.send(:attr_accessor, name)
if value.is_a?(Hash)
instance_variable_set("@#{name}", Config.new(value))
else
instance_variable_set("@#{name}", value)
end
end
end
一切正常,但问题是现在,我需要即时覆盖foo 方法。例如,首先创建一个Config.new({foo => 23}) 对象,该对象将具有一个foo 实例变量,然后我想传递一个新值(重新分配它),例如config.foo = {x: 23}。
由于这个新值是hash,所以我需要截取它并应用与以前相同的逻辑,使用该值创建一个新的Config 对象并将其分配给foo 实例变量。
这里的问题是,由于foo 方法已经定义,我无法在method_missing 方法中截取它的新赋值来应用所需的逻辑。
当我们动态调用 setter 方法时,有人知道如何拦截吗?
测试:
describe 'VerifiedConfig' do
it 'should return nil for non-existing config values' do
config = Config.new
expect(config.foo).to be_nil
expect(config.bar).to be_nil
end
it 'should allow assigning new simple config values' do
config = Config.new
config.foo = 13
config.bar = "foo-bar"
expect(config.foo).to eq(13)
expect(config.bar).to eq("foo-bar")
end
it 'should allow assigning hash values' do
config = Config.new
config.foo = {bar: {'baz' => 'x'}}
config.bar = {'foo' => {bar: [12, 13], baz: 14}}
expect(config.foo).to be_a(Config)
expect(config.foo.bar).to be_a(Config)
expect(config.foo.bar.baz).to eq('x')
expect(config.bar.foo.bar).to eq([12, 13])
expect(config.bar.foo.baz).to eq(14)
end
it 'should allow initialization through constructor' do
config = Config.new({'foo' => {bar: [12, 13], baz: 14}})
expect(config.foo.bar).to eq([12, 13])
expect(config.foo.baz).to eq(14)
end
it 'should override values' do
config = Config.new({'foo' => {bar: 'baz'}})
config.foo = 10
config.foo = {x: {y: 'z'}}
expect(config.foo.x.y).to eq('z')
end
it 'should raise an error when keys have illegal type' do
config = Config.new
expect {config.x = {14 => 15}}.to raise_error(ArgumentError)
end
it 'should not accept anything that Hash in the constructor' do
expect {Config.new(11)}.to raise_error(ArgumentError)
expect {Config.new('test')}.to raise_error(ArgumentError)
end
end
这是失败的场景:
it 'should override values' do
config = Config.new({'foo' => {bar: 'baz'}})
config.foo = 10
config.foo = {x: {y: 'z'}}
expect(config.foo.x.y).to eq('z')
end
注意:我不能使用OpenStruct
【问题讨论】:
-
为什么不能使用
OpenStruct? ... -
我在您的代码中没有看到任何覆盖。
-
@TomLord 因为这是对锻炼的限制!
-
@JörgWMittag
method_missing被覆盖了,你不这么认为吗?
标签: ruby hash metaprogramming dynamic-programming