# frozen_string_literal: truemoduleRuboCopmoduleCopmodulePerformance# This cop is used to identify usages of `count` on an `Enumerable` that# follow calls to `select` or `reject`. Querying logic can instead be# passed to the `count` call.## @example# # bad# [1, 2, 3].select { |e| e > 2 }.size# [1, 2, 3].reject { |e| e > 2 }.size# [1, 2, 3].select { |e| e > 2 }.length# [1, 2, 3].reject { |e| e > 2 }.length# [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? }# [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? }# array.select(&:value).count## # good# [1, 2, 3].count { |e| e > 2 }# [1, 2, 3].count { |e| e < 2 }# [1, 2, 3].count { |e| e > 2 && e.odd? }# [1, 2, 3].count { |e| e < 2 && e.even? }# Model.select('field AS field_one').count# Model.select(:value).count## `ActiveRecord` compatibility:# `ActiveRecord` will ignore the block that is passed to `count`.# Other methods, such as `select`, will convert the association to an# array and then run the block on the array. A simple work around to# make `count` work with a block is to call `to_a.count {...}`.## Example:# Model.where(id: [1, 2, 3].select { |m| m.method == true }.size## becomes:## Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }classCount<CopincludeSafeModeMSG='Use `count` instead of `%s...%s`.'.freezeSELECTORS=[:reject,:select].freezeCOUNTERS=[:count,:length,:size].freezedefon_send(node)returnifrails_safe_mode?@selector,@selector_loc,@params,@counter=parse(node)check(node)endprivateattr_reader:selector,:selector_loc,:params,:counterdefcheck(node)returnunlesseligible_node?(node)&&eligible_params?&&eligible_method_chain?range=source_starting_at(node){@selector_loc.begin_pos}add_offense(node,range,format(MSG,@selector,@counter))enddefautocorrect(node)selector,selector_loc=parse(node)returnifselector==:rejectrange=source_starting_at(node){|n|n.loc.dot.begin_pos}lambdado|corrector|corrector.remove(range)corrector.replace(selector_loc,'count')endenddefeligible_node?(node)!(node.parent&&node.parent.block_type?)enddefeligible_params?!(params&&!params.block_pass_type?)enddefeligible_method_chain?COUNTERS.include?(counter)&&SELECTORS.include?(selector)enddefparse(node)head,counter=*nodeexpression,selector,params=*headifselector.is_a?(Symbol)selector_loc=selector_location(expression,head.loc)else_,selector,params=*expressionifcontains_selector?(expression)selector_loc=expression.loc.selectorendend[selector,selector_loc,params,counter]enddefselector_location(expression,head_loc)ifexpression&&expression.parent.loc.respond_to?(:selector)expression.parent.loc.selectorelsifhead_loc.respond_to?(:selector)head_loc.selectorendenddefcontains_selector?(node)node.respond_to?(:loc)&&node.loc.respond_to?(:selector)enddefsource_starting_at(node)begin_pos=ifblock_given?yieldnodeelsenode.source_range.begin_posendrange_between(begin_pos,node.source_range.end_pos)endendendendend