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.
Note: the functionality to “restore” a method was removed, because it is no longer being used by sorbet-runtime. Restoring a method requires care–if you need to reintroduce this functionality, consult the git history for how to do it safely.
Class Method Summary collapse
- .def_with_visibility(mod, name, visibility, method = nil, &block) ⇒ Object
-
.replace_method(original_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).
-
.visibility_method_name(mod, name) ⇒ Object
‘name` must be an instance method (for class methods, pass in mod.singleton_class).
Class Method Details
.def_with_visibility(mod, name, visibility, method = nil, &block) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/types/private/class_utils.rb', line 28 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(original_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).
Takes the ‘original_method` as a parameter, so it does not return anything.
Can also avoid ‘T.let` pinning errors by letting the caller pre-compute the `original_method`, so it knows that it will always be defined (because it doesn’t know that the block will always run once)
Does not share code with ‘replace_method_with_handle`, for performance (do not want to increase the call stack, as this is a very sensitive code path).
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/types/private/class_utils.rb', line 60 def self.replace_method(original_method, mod, name, &blk) 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 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 nil end |
.visibility_method_name(mod, name) ⇒ Object
‘name` must be an instance method (for class methods, pass in mod.singleton_class)
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/types/private/class_utils.rb', line 13 def self.visibility_method_name(mod, name) if mod.public_method_defined?(name) :public elsif mod.protected_method_defined?(name) :protected elsif mod.private_method_defined?(name) :private else # Raises a NameError formatted like the Ruby VM would (the exact text formatting # of these errors changed across Ruby VM versions, in ways that would sometimes # cause tests to fail if they were dependent on hard coding errors). mod.method(name) end end |