class RuboCop::Cop::Rails::WhereExists

User.where(‘length(name) > 10’).exists?
user.posts.where(published: true).exists?
User.where(‘name = ?’, ‘john’).exists?
User.where([‘name = ?’, ‘john’]).exists?
User.where(name: ‘john’).exists?
# good
user.posts.exists?(published: true)
User.exists?([‘name = ?’, ‘john’])
User.exists?(name: ‘john’)
# bad
@example EnforcedStyle: where
user.posts.exists?(published: true)
User.where(‘length(name) > 10’).exists?
User.exists?(name: ‘john’)
# good
user.posts.where(published: true).exists?
User.where(‘name = ?’, ‘john’).exists?
User.where([‘name = ?’, ‘john’]).exists?
User.where(name: ‘john’).exists?
# bad
@example EnforcedStyle: exists (default)
—-
#=> Perform ‘preload` behavior and `ActiveRecord::StatementInvalid` error occurs.
Author.includes(:articles).exists?(articles: {id: id})
#=> Perform `eager_load` behavior (`LEFT JOIN` query) and get result.
Author.includes(:articles).where(articles: {id: id}).exists?
—-
[source,ruby]

This cop is unsafe for autocorrection because the behavior may change on the following case:
@safety
`where(…).exists?` over `exists?(…)`.
When EnforcedStyle is ’where’ then the cop enforces
then the cop enforces ‘exists?(…)` over `where(…).exists?`.
Two styles are supported for this cop. When EnforcedStyle is ’exists’
Enforces consistent style when using ‘exists?`.

def build_good_method(args, dot:)

def build_good_method(args, dot:)
  if exists_style?
    build_good_method_exists(args)
  elsif where_style?
    build_good_method_where(args, dot&.source || '.')
  end
end

def build_good_method_exists(args)

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, dot_source)

def build_good_method_where(args, dot_source)
  if args.size > 1
    "where(#{args.map(&:source).join(', ')})#{dot_source}exists?"
  else
    "where(#{args[0].source})#{dot_source}exists?"
  end
end

def convertable_args?(args)

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)

def correction_range(node)
  if exists_style?
    node.receiver.loc.selector.join(node.loc.selector)
  elsif where_style?
    node.loc.selector.with(end_pos: node.source_range.end_pos)
  end
end

def exists_style?

def exists_style?
  style == :exists
end

def find_offenses(node, &block)

def find_offenses(node, &block)
  if exists_style?
    where_exists_call?(node, &block)
  elsif where_style?
    exists_with_args?(node, &block)
  end
end

def on_send(node)

def on_send(node)
  find_offenses(node) do |args|
    return unless convertable_args?(args)
    range = correction_range(node)
    good_method = build_good_method(args, dot: node.loc.dot)
    message = format(MSG, good_method: good_method, bad_method: range.source)
    add_offense(range, message: message) do |corrector|
      corrector.replace(range, good_method)
    end
  end
end

def where_style?

def where_style?
  style == :where
end