防止对象扩展

时间:2020-07-15 07:04:18

标签: ruby module

我有一个模块,该模块通过点语法为哈希提供一些懒惰查找:

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)

是否有一个钩子被触发了?之前对象被扩展了,而不是之后?

1 个答案:

答案 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
相关问题