Использование доработок для исправления основного модуля, такого как ядро

Я иду через 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, я нашел полезный метод для поиска всех классов, которые включают в себя модуль

classes_including_module

Модули могут быть улучшены, начиная с 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}
Другие вопросы по тегам