【问题标题】:Prevent object from being extended防止对象被扩展
【发布时间】:2020-11-04 14:56:47
【问题描述】:

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

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)

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

【问题讨论】:

    标签: ruby module


    【解决方案1】:

    您可以覆盖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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 2018-12-26
      • 1970-01-01
      相关资源
      最近更新 更多