Использование доработок для исправления основного модуля, такого как ядро
Я иду через Facets API и выбираю некоторые методы для включения в мою улучшенную библиотеку патчей.
Я попал в ловушку, пытаясь залатать ядро. Это модуль, в то время как другие вещи, которые я исправил, были классы (String, Array и т. Д.)
Вот доказательство того, что не может быть улучшено, используя мой стандартный подход для основных классов:
module Patch
refine Kernel do
def patched?
true
end
end
end
# TypeError: wrong argument type Module (expected Class)
# from (pry):16:in `refine'
Я также попытался обернуть модуль ядра в класс и изменить глобальную ссылку на ядро на этот класс.
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>'
В этом случае я мог бы успешно получить ту же функциональность, заменив ядро на Object:
module Patch
refine Object do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
Но я не уверен, смогу ли я получить эту эквивалентность с другими базовыми модулями, такими как Enumerable.
1 ответ
Как я уже упоминал в этом вопросе, можно выполнить функциональный эквивалент расширения модуля Kernel, используя вместо этого класс Object.
Другим примером, который я привел, был модуль Enumerable, который, как оказывается, может быть практически расширен через класс Enumerator:
module Patch
refine Enumerator do
def patched?
true
end
end
end
class Test
using Patch
Array.new.to_enum.patched?
end
Поэтому я думаю, что реальным решением может быть не пытаться превратить основные модули в классы, а вместо этого расширять классы, которые уже включают их.
В этом случае я мог бы проверить с Enumerator < Enumerable
который возвращает true, потому что класс Enumerator включает модуль Enumerable (хотя он не проверяет, был ли он расширен)
Чтобы обновить после просмотра исходного кода corefines, я нашел полезный метод для поиска всех классов, которые включают в себя модуль
Модули могут быть улучшены, начиная с ruby 2.4:
Module#refine
принимает модуль в качестве аргумента сейчас. [Feature # 12534]
Старая оговорка ("Уточнения изменяют только классы, а не модули, поэтому аргумент должен быть классом") больше не применяется (хотя он не был удален из документации до ruby 2.6).
Пример:
module ModuleRefinement
refine Enumerable do
def tally(initial_count = 0, &block)
block ||= ->(value) { value }
counter = Hash.new { |h, k| h[k] = initial_count }
each do |value|
counter[block[value]] += 1
end
counter
end
end
end
using ModuleRefinement
p 'banana'.chars.tally # => {"b"=>1, "a"=>3, "n"=>2}