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