使用改进来修补核心模块,例如内核
Using refinements to patch a core Module such as Kernel
我正在研究 Facets API 并选择一些方法来包含在我的优化兼容补丁库中。
我在尝试修补内核时遇到了障碍。它是一个模块,而我修补的其他内容是 classes(字符串、数组等)
这是无法使用我的核心标准方法改进的证明 classes:
module Patch
refine Kernel do
def patched?
true
end
end
end
# TypeError: wrong argument type Module (expected Class)
# from (pry):16:in `refine'
我还尝试将内核模块包装在 class 中,并将对内核的全局引用更改为 class。
class MyKernel
include Kernel
extend Kernel
end
# not sure if Object::Kernel is really the global reference
Object::Kernel = MyKernel
module Patch
refine MyKernel do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
# NoMethodError: undefined method `patched?' for Test:Class
# from (pry):15:in `<class:Test>'
在这种情况下,我可以通过将内核替换为对象来成功获得相同的功能:
module Patch
refine Object do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
但我不确定是否可以与其他核心模块(例如 Enumerable)实现这种等效性。
正如我在问题中提到的,可以通过使用对象 class.
来执行与扩展内核模块等效的功能
我给出的另一个例子是 Enumerable 模块,事实证明它实际上可以通过 Enumerator 进行扩展 class:
module Patch
refine Enumerator do
def patched?
true
end
end
end
class Test
using Patch
Array.new.to_enum.patched?
end
所以我想一个可行的解决方案可能是不尝试将核心模块变成 classes,而是扩展已经包含它们的 classes。
在这种情况下,我可以使用 Enumerator < Enumerable
检查哪个 returns 为真,因为 Enumerator class 包含 Enumerable 模块(尽管它不检查它是否已扩展)
为了在查看 corefines 源代码后进行更新,我找到了一种有用的方法来查找包含模块
的所有 classes
自 ruby 2.4 起可以细化模块:
Module#refine
accepts a module as the argument now. [Feature #12534]
老caveat ("Refinements only modify classes, not modules so the argument must be a class") no longer applies (although it wasn't removed from the documentation until ruby 2.6).
示例:
module ModuleRefinement
refine Enumerable do
def tally(&block)
block ||= ->(value) { value }
counter = Hash.new(0)
each { |value| counter[block[value]] += 1 }
counter
end
end
end
using ModuleRefinement
p 'banana'.chars.tally # => {"b"=>1, "a"=>3, "n"=>2}
我正在研究 Facets API 并选择一些方法来包含在我的优化兼容补丁库中。
我在尝试修补内核时遇到了障碍。它是一个模块,而我修补的其他内容是 classes(字符串、数组等)
这是无法使用我的核心标准方法改进的证明 classes:
module Patch
refine Kernel do
def patched?
true
end
end
end
# TypeError: wrong argument type Module (expected Class)
# from (pry):16:in `refine'
我还尝试将内核模块包装在 class 中,并将对内核的全局引用更改为 class。
class MyKernel
include Kernel
extend Kernel
end
# not sure if Object::Kernel is really the global reference
Object::Kernel = MyKernel
module Patch
refine MyKernel do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
# NoMethodError: undefined method `patched?' for Test:Class
# from (pry):15:in `<class:Test>'
在这种情况下,我可以通过将内核替换为对象来成功获得相同的功能:
module Patch
refine Object do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
但我不确定是否可以与其他核心模块(例如 Enumerable)实现这种等效性。
正如我在问题中提到的,可以通过使用对象 class.
来执行与扩展内核模块等效的功能我给出的另一个例子是 Enumerable 模块,事实证明它实际上可以通过 Enumerator 进行扩展 class:
module Patch
refine Enumerator do
def patched?
true
end
end
end
class Test
using Patch
Array.new.to_enum.patched?
end
所以我想一个可行的解决方案可能是不尝试将核心模块变成 classes,而是扩展已经包含它们的 classes。
在这种情况下,我可以使用 Enumerator < Enumerable
检查哪个 returns 为真,因为 Enumerator class 包含 Enumerable 模块(尽管它不检查它是否已扩展)
为了在查看 corefines 源代码后进行更新,我找到了一种有用的方法来查找包含模块
的所有 classes自 ruby 2.4 起可以细化模块:
Module#refine
accepts a module as the argument now. [Feature #12534]
老caveat ("Refinements only modify classes, not modules so the argument must be a class") no longer applies (although it wasn't removed from the documentation until ruby 2.6).
示例:
module ModuleRefinement
refine Enumerable do
def tally(&block)
block ||= ->(value) { value }
counter = Hash.new(0)
each { |value| counter[block[value]] += 1 }
counter
end
end
end
using ModuleRefinement
p 'banana'.chars.tally # => {"b"=>1, "a"=>3, "n"=>2}