lib/rubocop/cop/rails/where_exists.rb in rubocop-rails-2.7.1 vs lib/rubocop/cop/rails/where_exists.rb in rubocop-rails-2.8.0
- old
+ new
@@ -1,13 +1,19 @@
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
- # This cop enforces the use of `exists?(...)` over `where(...).exists?`.
+ # This cop enforces consistent style when using `exists?`.
#
- # @example
+ # Two styles are supported for this cop. When EnforcedStyle is 'exists'
+ # then the cop enforces `exists?(...)` over `where(...).exists?`.
+ #
+ # When EnforcedStyle is 'where' then the cop enforces
+ # `where(...).exists?` over `exists?(...)`.
+ #
+ # @example EnforcedStyle: exists (default)
# # bad
# User.where(name: 'john').exists?
# User.where(['name = ?', 'john']).exists?
# User.where('name = ?', 'john').exists?
# user.posts.where(published: true).exists?
@@ -15,29 +21,48 @@
# # good
# User.exists?(name: 'john')
# User.where('length(name) > 10').exists?
# user.posts.exists?(published: true)
#
+ # @example EnforcedStyle: where
+ # # bad
+ # User.exists?(name: 'john')
+ # User.exists?(['name = ?', 'john'])
+ # User.exists?('name = ?', 'john')
+ # user.posts.exists?(published: true)
+ #
+ # # good
+ # User.where(name: 'john').exists?
+ # User.where(['name = ?', 'john']).exists?
+ # User.where('name = ?', 'john').exists?
+ # user.posts.where(published: true).exists?
+ # User.where('length(name) > 10').exists?
class WhereExists < Cop
+ include ConfigurableEnforcedStyle
+
MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
def_node_matcher :where_exists_call?, <<~PATTERN
(send (send _ :where $...) :exists?)
PATTERN
+ def_node_matcher :exists_with_args?, <<~PATTERN
+ (send _ :exists? $...)
+ PATTERN
+
def on_send(node)
- where_exists_call?(node) do |args|
+ find_offenses(node) do |args|
return unless convertable_args?(args)
range = correction_range(node)
message = format(MSG, good_method: build_good_method(args), bad_method: range.source)
add_offense(node, location: range, message: message)
end
end
def autocorrect(node)
- args = where_exists_call?(node)
+ args = find_offenses(node)
lambda do |corrector|
corrector.replace(
correction_range(node),
build_good_method(args)
@@ -45,22 +70,60 @@
end
end
private
+ def where_style?
+ style == :where
+ end
+
+ def exists_style?
+ style == :exists
+ end
+
+ def find_offenses(node, &block)
+ if exists_style?
+ where_exists_call?(node, &block)
+ elsif where_style?
+ exists_with_args?(node, &block)
+ end
+ end
+
def convertable_args?(args)
+ return false if args.empty?
+
args.size > 1 || args[0].hash_type? || args[0].array_type?
end
def correction_range(node)
- node.receiver.loc.selector.join(node.loc.selector)
+ if exists_style?
+ node.receiver.loc.selector.join(node.loc.selector)
+ elsif where_style?
+ node.loc.selector.with(end_pos: node.loc.expression.end_pos)
+ end
end
def build_good_method(args)
+ if exists_style?
+ build_good_method_exists(args)
+ elsif where_style?
+ build_good_method_where(args)
+ end
+ end
+
+ def build_good_method_exists(args)
if args.size > 1
"exists?([#{args.map(&:source).join(', ')}])"
else
"exists?(#{args[0].source})"
+ end
+ end
+
+ def build_good_method_where(args)
+ if args.size > 1
+ "where(#{args.map(&:source).join(', ')}).exists?"
+ else
+ "where(#{args[0].source}).exists?"
end
end
end
end
end