Module: T::Private::Abstract::Validate
- Defined in:
- lib/types/private/abstract/validate.rb
Overview
typed: true
Constant Summary collapse
- Abstract =
T::Private::Abstract
- AbstractUtils =
T::AbstractUtils
- Methods =
T::Private::Methods
- SignatureValidation =
T::Private::Methods::SignatureValidation
Class Method Summary collapse
- .validate_abstract_module(mod) ⇒ Object
-
.validate_subclass(mod) ⇒ Object
Validates a class/module with an abstract class/module as an ancestor.
Class Method Details
.validate_abstract_module(mod) ⇒ Object
10 11 12 13 |
# File 'lib/types/private/abstract/validate.rb', line 10 def self.validate_abstract_module(mod) type = Abstract::Data.get(mod, :abstract_type) validate_interface(mod) if type == :interface end |
.validate_subclass(mod) ⇒ Object
Validates a class/module with an abstract class/module as an ancestor. This must be called after all methods on ‘mod` have been defined.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/types/private/abstract/validate.rb', line 17 def self.validate_subclass(mod) can_have_abstract_methods = !T::Private::Abstract::Data.get(mod, :can_have_abstract_methods) unimplemented_methods = [] T::AbstractUtils.declared_abstract_methods_for(mod).each do |abstract_method| implementation_method = mod.instance_method(abstract_method.name) if AbstractUtils.abstract_method?(implementation_method) # Note that when we end up here, implementation_method might not be the same as # abstract_method; the latter could've been overridden by another abstract method. In either # case, if we have a concrete definition in an ancestor, that will end up as the effective # implementation (see CallValidation.wrap_method_if_needed), so that's what we'll validate # against. implementation_method = T.unsafe(nil) mod.ancestors.each do |ancestor| if ancestor.instance_methods.include?(abstract_method.name) method = ancestor.instance_method(abstract_method.name) T::Private::Methods.maybe_run_sig_block_for_method(method) if !T::AbstractUtils.abstract_method?(method) implementation_method = method break end end end if !implementation_method # There's no implementation if can_have_abstract_methods unimplemented_methods << describe_method(abstract_method) end next # Nothing to validate end end implementation_signature = Methods.signature_for_method(implementation_method) # When a signature exists and the method is defined directly on `mod`, we skip the validation # here, because it will have already been done when the method was defined (by # T::Private::Methods._on_method_added). next if implementation_signature&.owner == mod # We validate the remaining cases here: (a) methods defined directly on `mod` without a # signature and (b) methods from ancestors (note that these ancestors can come before or # after the abstract module in the inheritance chain -- the former coming from # walking `mod.ancestors` above). abstract_signature = Methods.signature_for_method(abstract_method) # We allow implementation methods to be defined without a signature. # In that case, get its untyped signature. implementation_signature ||= Methods::Signature.new_untyped( method: implementation_method, mode: Methods::Modes.override ) SignatureValidation.validate_override_shape(implementation_signature, abstract_signature) SignatureValidation.validate_override_types(implementation_signature, abstract_signature) end method_type = mod.singleton_class? ? "class" : "instance" if !unimplemented_methods.empty? raise "Missing implementation for abstract #{method_type} method(s) in #{mod}:\n" \ "#{unimplemented_methods.join("\n")}\n" \ "If #{mod} is meant to be an abstract class/module, you can call " \ "`abstract!` or `interface!`. Otherwise, you must implement the method(s)." end end |