Class: RuboCop::Cop::Rails::UniqBeforePluck

Inherits:
Cop
  • Object
show all
Includes:
ConfigurableEnforcedStyle
Defined in:
lib/rubocop/cop/rails/uniq_before_pluck.rb

Overview

Prefer the use of uniq (or distinct), before pluck instead of after.

The use of uniq before pluck is preferred because it executes within the database.

This cop has two different enforcement modes. When the EnforcedStyle is conservative (the default) then only calls to pluck on a constant (i.e. a model class) before uniq are added as offenses.

When the EnforcedStyle is aggressive then all calls to pluck before uniq are added as offenses. This may lead to false positives as the cop cannot distinguish between calls to pluck on an ActiveRecord::Relation vs a call to pluck on an ActiveRecord::Associations::CollectionProxy.

Autocorrect is disabled by default for this cop since it may generate false positives.

Examples:

# bad
Model.pluck(:id).uniq

# good
Model.uniq.pluck(:id)
# this will return a Relation that pluck is called on
Model.where(cond: true).pluck(:id).uniq

# an association on an instance will return a CollectionProxy
instance.assoc.pluck(:id).uniq

Constant Summary collapse

MSG =
'Use `%s` before `pluck`.'.freeze
NEWLINE =
"\n".freeze
PATTERN =
'[!^block (send (send %s :pluck ...) ${:uniq :distinct} ...)]'
.freeze

Constants included from Util

Util::ASGN_NODES, Util::BYTE_ORDER_MARK, Util::CONDITIONAL_NODES, Util::EQUALS_ASGN_NODES, Util::LITERAL_REGEX, Util::LOGICAL_OPERATOR_NODES, Util::MODIFIER_NODES, Util::OPERATOR_METHODS, Util::SHORTHAND_ASGN_NODES

Instance Attribute Summary

Attributes inherited from Cop

#config, #corrections, #offenses, #processed_source

Instance Method Summary collapse

Methods included from ConfigurableEnforcedStyle

#alternative_style, #alternative_styles, #ambiguous_style_detected, #correct_style_detected, #detected_style, #detected_style=, #no_acceptable_style!, #no_acceptable_style?, #opposite_style_detected, #style, #style_detected, #supported_styles, #unexpected_style_detected

Methods inherited from Cop

#add_offense, all, autocorrect_incompatible_with, badge, #config_to_allow_offenses, #config_to_allow_offenses=, #cop_config, cop_name, #cop_name, #correct, department, #duplicate_location?, #excluded_file?, #find_location, #highlights, inherited, #initialize, #join_force?, lint?, match?, #message, #messages, non_rails, #parse, qualified_cop_name, #relevant_file?, #target_rails_version, #target_ruby_version

Methods included from AST::Sexp

#s

Methods included from NodePattern::Macros

#def_node_matcher, #def_node_search, #node_search, #node_search_all, #node_search_body, #node_search_first

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #support_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

begins_its_line?, comment_line?, double_quotes_required?, effective_column, ends_its_line?, escape_string, first_part_of_call_chain, interpret_string_escapes, line_range, needs_escaping?, on_node, operator?, parentheses?, parenthesized_call?, precede?, range_between, range_by_whole_lines, range_with_surrounding_comma, range_with_surrounding_space, same_line?, source_range, strip_quotes, stripped_source_upto, symbol_without_quote?, to_string_literal, to_supported_styles, to_symbol_literal, within_node?

Methods included from PathUtil

absolute?, find_file_upwards, match_path?, pwd, relative_path, reset_pwd, smart_path

Constructor Details

This class inherits a constructor from RuboCop::Cop::Cop

Instance Method Details

#autocorrect(node) ⇒ Object



63
64
65
66
67
68
69
70
# File 'lib/rubocop/cop/rails/uniq_before_pluck.rb', line 63

def autocorrect(node)
  lambda do |corrector|
    method = node.method_name

    corrector.remove(dot_method_with_whitespace(method, node))
    corrector.insert_before(node.receiver.loc.dot.begin, ".#{method}")
  end
end

#on_send(node) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rubocop/cop/rails/uniq_before_pluck.rb', line 51

def on_send(node)
  method = if style == :conservative
             conservative_node_match(node)
           else
             aggressive_node_match(node)
           end

  return unless method

  add_offense(node, location: :selector, message: format(MSG, method))
end