Module: T::Private::ClassUtils
- Defined in:
- lib/types/private/class_utils.rb
Overview
Cut down version of Chalk::Tools::ClassUtils with only :replace_method functionality. Extracted to a separate namespace so the type system can be used standalone.
Defined Under Namespace
Classes: ReplacedMethod
Class Method Summary collapse
-
.replace_method(mod, name, &blk) ⇒ Object
Replaces a method, either by overwriting it (if it is defined directly on ‘mod`) or by overriding it (if it is defined by one of mod’s ancestors).
Class Method Details
.replace_method(mod, name, &blk) ⇒ Object
Replaces a method, either by overwriting it (if it is defined directly on ‘mod`) or by overriding it (if it is defined by one of mod’s ancestors). Returns a ReplacedMethod instance on which you can call ‘bind(…).call(…)` to call the original method, or `restore` to restore the original method (by overwriting or removing the override).
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/types/private/class_utils.rb', line 76 def self.replace_method(mod, name, &blk) original_method = mod.instance_method(name) original_visibility = visibility_method_name(mod, name) original_owner = original_method.owner mod.ancestors.each do |ancestor| break if ancestor == mod if ancestor == original_owner # If we get here, that means the method we're trying to replace exists on a *prepended* # mixin, which means in order to supersede it, we'd need to create a method on a new # module that we'd prepend before `ancestor`. The problem with that approach is there'd # be no way to remove that new module after prepending it, so we'd be left with these # empty anonymous modules in the ancestor chain after calling `restore`. # # That's not necessarily a deal breaker, but for now, we're keeping it as unsupported. raise "You're trying to replace `#{name}` on `#{mod}`, but that method exists in a " \ "prepended module (#{ancestor}), which we don't currently support. Talk to " \ "#dev-productivity for help." end end overwritten = original_owner == mod T::Configuration.without_ruby_warnings do T::Private::DeclState.current.skip_on_method_added = true mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend T::Private::DeclState.current.skip_on_method_added = false end mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend new_method = mod.instance_method(name) ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility) end |