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

large_table.rb « usage_data « cop « rubocop - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 414c89209ed4eaae910e0ef7caee5d82b0f524ff (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
83
84
85
86
87
88
89
90
91
92
93
# frozen_string_literal: true

require_relative '../../usage_data_helpers'

module RuboCop
  module Cop
    module UsageData
      class LargeTable < RuboCop::Cop::Base
        include UsageDataHelpers

        # This cop checks that batch count and distinct_count are used in usage_data.rb files in metrics based on ActiveRecord models.
        #
        # @example
        #
        # # bad
        # Issue.count
        # List.assignee.count
        # ::Ci::Pipeline.auto_devops_source.count
        # ZoomMeeting.distinct.count(:issue_id)
        #
        # # Good
        # count(Issue)
        # count(List.assignee)
        # count(::Ci::Pipeline.auto_devops_source)
        # distinct_count(ZoomMeeting, :issue_id)
        MSG = 'Use one of the %{count_methods} methods for counting on %{class_name}'

        # Match one level const as Issue, Gitlab
        def_node_matcher :one_level_node, <<~PATTERN
          (send
            (const {nil? cbase} $...)
          $...)
        PATTERN

        # Match two level const as ::Clusters::Cluster, ::Ci::Pipeline
        def_node_matcher :two_level_node, <<~PATTERN
          (send
            (const
              (const {nil? cbase} $...)
            $...)
          $...)
        PATTERN

        def on_send(node)
          return unless in_usage_data_file?(node)

          one_level_matches = one_level_node(node)
          two_level_matches = two_level_node(node)

          return unless Array(one_level_matches).any? || Array(two_level_matches).any?

          if one_level_matches
            class_name = one_level_matches[0].first
            method_used = one_level_matches[1]&.first
          else
            class_name = "#{two_level_matches[0].first}::#{two_level_matches[1].first}".to_sym
            method_used = two_level_matches[2]&.first
          end

          return if non_related?(class_name) || allowed_methods.include?(method_used)

          counters_used = node.ancestors.any? { |ancestor| allowed_method?(ancestor) }

          unless counters_used
            add_offense(node, message: format(MSG, count_methods: count_methods.join(', '), class_name: class_name))
          end
        end

        private

        def count_methods
          cop_config['CountMethods'] || []
        end

        def allowed_methods
          cop_config['AllowedMethods'] || []
        end

        def non_related_classes
          cop_config['NonRelatedClasses'] || []
        end

        def non_related?(class_name)
          non_related_classes.include?(class_name)
        end

        def allowed_method?(ancestor)
          ancestor.send_type? && !ancestor.dot? && count_methods.include?(ancestor.method_name)
        end
      end
    end
  end
end