2021-01-19 18:11:04 +00:00
# frozen_string_literal: true
module RuboCop
module Cop
module Gitlab
# Cop that enforces use of namespaced classes in order to better identify
# high level domains within the codebase.
2022-05-16 12:07:51 +00:00
#
2021-01-19 18:11:04 +00:00
# @example
# # bad
# class MyClass
# end
#
2022-05-16 12:07:51 +00:00
# module Gitlab
# class MyClass
# end
# end
#
# class Gitlab::MyClass
# end
#
2021-01-19 18:11:04 +00:00
# # good
# module MyDomain
# class MyClass
# end
# end
2022-05-16 12:07:51 +00:00
#
# module Gitlab
# module MyDomain
# class MyClass
# end
# end
# end
#
# class Gitlab::MyDomain::MyClass
# end
2022-06-28 09:09:38 +00:00
class NamespacedClass < RuboCop :: Cop :: Base
2022-05-26 21:08:54 +00:00
MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/321982'
2021-01-19 18:11:04 +00:00
2022-05-16 12:07:51 +00:00
# These namespaces are considered top-level semantically.
# Note: Nested namespace like Foo::Bar are also supported.
PSEUDO_TOPLEVEL = %w[ Gitlab ]
. map { _1 . split ( '::' ) } . freeze
2021-01-19 18:11:04 +00:00
def on_module ( node )
2022-05-16 12:07:51 +00:00
add_potential_domain_namespace ( node )
2021-01-19 18:11:04 +00:00
end
def on_class ( node )
2022-05-16 12:07:51 +00:00
# Add potential namespaces from compact definitions like `class Foo::Bar`.
# Remove class name because it's not a domain namespace.
add_potential_domain_namespace ( node ) { _1 . pop }
2022-06-28 09:09:38 +00:00
add_offense ( node . loc . name ) if domain_namespaces . none?
2022-05-16 12:07:51 +00:00
end
private
def domain_namespaces
@domain_namespaces || = [ ]
end
def add_potential_domain_namespace ( node )
return if domain_namespaces . any?
identifiers = identifiers_for ( node )
yield ( identifiers ) if block_given?
PSEUDO_TOPLEVEL . each do | namespaces |
identifiers . shift ( namespaces . size ) if namespaces == identifiers . first ( namespaces . size )
end
domain_namespaces . concat ( identifiers )
end
2021-01-19 18:11:04 +00:00
2022-05-16 12:07:51 +00:00
def identifiers_for ( node )
node . identifier . source . sub ( / ^:: / , '' ) . split ( '::' )
2021-01-19 18:11:04 +00:00
end
end
end
end
end