其他人可以帮助我通过一个与覆盖先前定义的方法有关的方案吗?
我有一个可以接收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
[还有其他人可以帮助我解决与覆盖先前定义的方法有关的一种情况吗?我有一个可以接收哈希值的类,以便在运行时为每个...创建实例变量...
而不是使用默认的getter和setter:
这是另一种可能的解决方法。有点相似,但这通过overriding