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
- .def_with_visibility(mod, name, visibility, method = nil, &block) ⇒ Object
-
.replace_method(mod, name, original_only = false, &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
.def_with_visibility(mod, name, visibility, method = nil, &block) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/types/private/class_utils.rb', line 72 def self.def_with_visibility(mod, name, visibility, method=nil, &block) mod.module_exec do # Start a visibility (public/protected/private) region, so that # all of the method redefinitions happen with the right visibility # from the beginning. This ensures that any other code that is # triggered by `method_added`, sees the redefined method with the # right visibility. send(visibility) if method define_method(name, method) else define_method(name, &block) end if block && block.arity < 0 && respond_to?(:ruby2_keywords, true) ruby2_keywords(name) end end end |
.replace_method(mod, name, original_only = false, &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). If ‘original_only` is false, 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).
If ‘original_only` is true, return the `UnboundMethod` representing the original method.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/types/private/class_utils.rb', line 100 def self.replace_method(mod, name, original_only=false, &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." end end overwritten = original_owner == mod T::Configuration.without_ruby_warnings do T::Private::DeclState.current.without_on_method_added do def_with_visibility(mod, name, original_visibility, &blk) end end if original_only original_method else new_method = mod.instance_method(name) ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility) end end |