87 lines
2.6 KiB
Ruby
87 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module RuboCop
|
|
module Cop
|
|
module UsageData
|
|
class LargeTable < RuboCop::Cop::Cop
|
|
# 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)
|
|
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, location: :expression, 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
|