gitlab-org--gitlab-foss/lib/gitlab/utils/override.rb

112 lines
2.8 KiB
Ruby
Raw Normal View History

module Gitlab
module Utils
module Override
class Extension
def self.verify_class!(klass, method_name)
instance_method_defined?(klass, method_name) ||
raise(
NotImplementedError.new(
"#{klass}\##{method_name} doesn't exist!"))
end
def self.instance_method_defined?(klass, name, include_super: true)
klass.instance_methods(include_super).include?(name) ||
klass.private_instance_methods(include_super).include?(name)
end
attr_reader :subject
def initialize(subject)
@subject = subject
end
def add_method_name(method_name)
method_names << method_name
end
def add_class(klass)
classes << klass
end
def verify!
classes.each do |klass|
index = klass.ancestors.index(subject)
parents = klass.ancestors.drop(index + 1)
method_names.each do |method_name|
parents.any? do |parent|
self.class.instance_method_defined?(
parent, method_name, include_super: false)
end ||
raise(
NotImplementedError.new(
"#{klass}\##{method_name} doesn't exist!"))
end
end
end
private
def method_names
@method_names ||= []
end
def classes
@classes ||= []
end
end
# Instead of writing patterns like this:
#
# def f
# raise NotImplementedError unless defined?(super)
#
# true
# end
#
# We could write it like:
#
# extend ::Gitlab::Utils::Override
#
# override :f
# def f
# true
# end
#
# This would make sure we're overriding something. See:
# https://gitlab.com/gitlab-org/gitlab-ee/issues/1819
def override(method_name)
return unless ENV['STATIC_VERIFICATION']
if is_a?(Class)
Extension.verify_class!(self, method_name)
else # We delay the check for modules
Override.extensions[self] ||= Extension.new(self)
Override.extensions[self].add_method_name(method_name)
end
end
def included(base = nil)
return super if base.nil? # Rails concern, ignoring it
super
if base.is_a?(Class) # We could check for Class in `override`
# This could be `nil` if `override` was never called
Override.extensions[self]&.add_class(base)
end
end
alias_method :prepended, :included
def self.extensions
@extensions ||= {}
end
def self.verify!
extensions.values.each(&:verify!)
end
end
end
end