1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
# frozen_string_literal: true
require 'active_support/inflector'
module InjectEnterpriseEditionModule
def prepend_mod_with(constant_name, namespace: Object, with_descendants: false)
each_extension_for(constant_name, namespace) do |constant|
prepend_module(constant, with_descendants)
end
end
def extend_mod_with(constant_name, namespace: Object)
each_extension_for(
constant_name,
namespace,
&method(:extend))
end
def include_mod_with(constant_name, namespace: Object)
each_extension_for(
constant_name,
namespace,
&method(:include))
end
def prepend_mod(with_descendants: false)
prepend_mod_with(name, with_descendants: with_descendants) # rubocop: disable Cop/InjectEnterpriseEditionModule
end
def extend_mod
extend_mod_with(name) # rubocop: disable Cop/InjectEnterpriseEditionModule
end
def include_mod
include_mod_with(name) # rubocop: disable Cop/InjectEnterpriseEditionModule
end
private
def prepend_module(mod, with_descendants)
prepend(mod)
if with_descendants
descendants.each { |descendant| descendant.prepend(mod) }
end
end
def each_extension_for(constant_name, namespace)
Gitlab.extensions.each do |extension_name|
extension_namespace =
const_get_maybe_false(namespace, extension_name.upcase)
extension_module =
const_get_maybe_false(extension_namespace, constant_name)
yield(extension_module) if extension_module
end
end
def const_get_maybe_false(mod, name)
# We're still heavily relying on Rails autoloading instead of zeitwerk,
# therefore this check: `mod.const_defined?(name, false)`
# Is not reliable, which may return false while it's defined.
# After we moved everything over to zeitwerk we can avoid rescuing
# NameError and just check if const_defined?
# mod && mod.const_defined?(name, false) && mod.const_get(name, false)
result = mod && mod.const_get(name, false)
if result.name == "#{mod}::#{name}"
result
else
# This may hit into a Rails issue that when we try to load
# `EE::API::Appearance`, Rails might load `::Appearance` the first time
# when `mod.const_get(name, false)` is called if `::Appearance` is not
# loaded yet. This can be demonstrated as the following:
#
# EE.const_get('API::Appearance', false) # => Appearance
# EE.const_get('API::Appearance', false) # => raise NameError
#
# Getting a `NameError` is what we're expecting here, because
# `EE::API::Appearance` doesn't exist.
#
# This is because Rails will attempt to load constants from all the
# parent namespaces, and if it finds one it'll load it and return it.
# However, the second time when it's called, since the top-level class
# is already loaded, then Rails will skip this process. This weird
# behaviour can be worked around by calling this the second time.
# The particular line is at:
# https://github.com/rails/rails/blob/v6.1.3.2/activesupport/lib/active_support/dependencies.rb#L569-L570
mod.const_get(name, false)
end
rescue NameError
false
end
end
Module.prepend(InjectEnterpriseEditionModule)
|