Class: Gitlab::Styles::Rubocop::Cop::CodeReuse::ActiveRecord

Inherits:
RuboCop::Cop::Cop
  • Object
show all
Defined in:
lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb

Overview

Cop that denies the use of ActiveRecord methods outside of models.

Constant Summary collapse

MSG =
'This method can only be used inside an ActiveRecord model: ' \
'https://gitlab.com/gitlab-org/gitlab-foss/issues/49653'
NOT_ALLOWED =

Various methods from ActiveRecord::Querying that are denied. We exclude some generic ones such as ‘any?` and `first`, as these may lead to too many false positives, since `Array` also supports these methods.

The keys of this Hash are the denied method names. The values are booleans that indicate if the method should only be denied if any arguments are provided.

{
  average: true,
  calculate: true,
  count_by_sql: true,
  create_with: true,
  distinct: false,
  eager_load: true,
  exists?: true,
  find_by: true,
  find_by!: true,
  find_by_sql: true,
  find_each: true,
  find_in_batches: true,
  find_or_create_by: true,
  find_or_create_by!: true,
  find_or_initialize_by: true,
  first!: false,
  first_or_create: true,
  first_or_create!: true,
  first_or_initialize: true,
  from: true,
  group: true,
  having: true,
  ids: false,
  includes: true,
  joins: true,
  limit: true,
  lock: false,
  many?: false,
  offset: true,
  order: true,
  pluck: true,
  preload: true,
  readonly: false,
  references: true,
  reorder: true,
  rewhere: true,
  take: false,
  take!: false,
  unscope: false,
  where: false,
  with: true
}.freeze

Instance Method Summary collapse

Instance Method Details

#allowed_scopesObject



123
124
125
# File 'lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb', line 123

def allowed_scopes
  @allowed_scopes ||= Set.new
end

#autocorrect(node) ⇒ Object

We can not auto correct code like this, as it requires manual refactoring. Instead, we’ll just allow the surrounding scope.

Despite this method’s presence, you should not use it. This method exists to make it possible to allow large chunks of offenses we can’t fix in the short term. If you are writing new code, follow the code reuse guidelines, instead of allowing any new offenses.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb', line 88

def autocorrect(node)
  scope = surrounding_scope_of(node)
  indent = indentation_of(scope)

  lambda do |corrector|
    # This prevents us from inserting the same enable/disable comment
    # for a method or block that has multiple offenses.
    next if allowed_scopes.include?(scope)

    corrector.insert_before(
      scope.source_range,
      "# rubocop: disable #{cop_name}\n#{indent}"
    )

    corrector.insert_after(
      scope.source_range,
      "\n#{indent}# rubocop: enable #{cop_name}"
    )

    allowed_scopes << scope
  end
end

#indentation_of(node) ⇒ Object



111
112
113
# File 'lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb', line 111

def indentation_of(node)
  ' ' * node.loc.expression.source_line[/\A */].length
end

#on_send(node) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb', line 65

def on_send(node)
  receiver = node.children[0]
  send_name = node.children[1]
  first_arg = node.children[2]

  return unless receiver && NOT_ALLOWED.key?(send_name)

  # If the rule requires an argument to be given, but none are
  # provided, we won't register an offense. This prevents us from
  # adding offenses for `project.group`, while still covering
  # `Project.group(:name)`.
  return if NOT_ALLOWED[send_name] && !first_arg

  add_offense(node, location: :selector)
end

#surrounding_scope_of(node) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb', line 115

def surrounding_scope_of(node)
  i[def defs block begin].each do |type|
    if (found = node.each_ancestor(type).first)
      return found
    end
  end
end