Welcome to mirror list, hosted at ThFree Co, Russian Federation.

namespaced_class.rb « gitlab « cop « rubocop - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ce46e055c216d60b84fafd4fd99215dffca481fa (plain)
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
# 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.
      #
      # @example
      #   # bad
      #   class MyClass
      #   end
      #
      #   module Gitlab
      #     class MyClass
      #     end
      #   end
      #
      #   class Gitlab::MyClass
      #   end
      #
      #   # good
      #   module MyDomain
      #     class MyClass
      #     end
      #   end
      #
      #   module Gitlab
      #     module MyDomain
      #       class MyClass
      #       end
      #     end
      #   end
      #
      #   class Gitlab::MyDomain::MyClass
      #   end
      class NamespacedClass < RuboCop::Cop::Cop
        MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/321982'

        # These namespaces are considered top-level semantically.
        # Note: Nested namespace like Foo::Bar are also supported.
        PSEUDO_TOPLEVEL = %w[Gitlab]
          .map { _1.split('::') }.freeze

        def on_module(node)
          add_potential_domain_namespace(node)
        end

        def on_class(node)
          # 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 }

          add_offense(node) if domain_namespaces.none?
        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

        def identifiers_for(node)
          node.identifier.source.sub(/^::/, '').split('::')
        end
      end
    end
  end
end