我有一个模块,该模块通过点语法为哈希提供一些懒惰查找:
module DotHash
def method_missing(method_name, *args, &block)
# look for keys...
end
def respond_to_missing?(method_name, _include_all = nil)
# some logic
end
end
我遇到了意外扩展nil
的问题:
# @hash == nil
@hash.extend(DotHash)
这会导致巨大的问题,因为现在nil
具有这种method_missing
逻辑,使事情变得混乱。
我虽然添加了一个钩子就可以解决问题:
module DotHash
def self.extended(base)
return if base.is_a?(Hash)
raise "must be a hash"
end
def method_missing(method_name, *args, &block)
# look for keys...
end
def respond_to_missing?(method_name, _include_all = nil)
# some logic
end
end
确实,它引发了一个错误:
[1] pry(main)> nil.extend(DotHash)
RuntimeError: must be a hash
但是无论如何,还是增加了逻辑:
[2] pry(main)> nil.foobar
Traceback (most recent call last):
9707: from bin/rails:6:in `<main>'
9706: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:291:in `require'
9705: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:257:in `load_dependency'
9704: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:291:in `block in require'
9703: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
9702: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
9701: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
9700: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
... 9695 levels...
4: from /usr/src/app/app/lib/dot_hash.rb:26:in `respond_to_missing?'
3: from /usr/src/app/app/lib/dot_hash.rb:14:in `method_missing'
2: from /usr/src/app/app/lib/dot_hash.rb:26:in `respond_to_missing?'
1: from /usr/src/app/app/lib/dot_hash.rb:14:in `method_missing'
/usr/src/app/app/lib/mapper/dot_hash.rb:26:in `respond_to_missing?': stack level too deep (SystemStackError)
是否有一个钩子被触发了?之前对象被扩展了,而不是之后?
答案 0 :(得分:2)
您可以覆盖extend_object
:(文档中包含类似的示例)
通过添加此模块的常量和方法(作为单例方法添加)来扩展指定的对象。这是
Object#extend
使用的回调方法。
module DotHash
def self.extend_object(obj)
raise TypeError, 'must be a hash' unless obj.is_a?(Hash)
super
end
def foo
123
end
end
h = {}
h.extend(DotHash)
h.foo #=> 123
n = nil
n.extend(DotHash) # TypeError: must be a hash
n.foo # NoMethodError: undefined method `foo' for nil:NilClass