class RuboCop::Cop::Performance::CaseWhenSplat
Place `when` conditions that use splat at the end of the list of `when` branches.
Ruby has to allocate memory for the splat expansion every time that the `case` `when` statement is run. Since Ruby does not support fall through inside of `case` `when`, like some other languages do, the order of the `when` branches does not matter. By placing any splat expansions at the end of the list of `when` branches we will reduce the number of times that memory has to be allocated for the expansion.
This is not a guaranteed performance improvement. If the data being processed by the `case` condition is normalized in a manner that favors hitting a condition in the splat expansion, it is possible that moving the splat condition to the end will use more memory, and run slightly slower.
@example
# bad case foo when *condition bar when baz foobar end case foo when *[1, 2, 3, 4] bar when 5 baz end # good case foo when baz foobar when *condition bar end case foo when 1, 2, 3, 4 bar when 5 baz end
Constants
- ARRAY_MSG
- MSG
- OPEN_BRACKET
- PERCENT_CAPITAL_I
- PERCENT_CAPITAL_W
- PERCENT_I
- PERCENT_W
Public Class Methods
# File lib/rubocop/cop/performance/case_when_splat.rb, line 65 def initialize(*) super @reordered_splat_condition = false end
Public Instance Methods
# File lib/rubocop/cop/performance/case_when_splat.rb, line 86 def autocorrect(node) condition, = *node variable, = *condition if variable.array_type? correct_array_literal(condition, variable) else return if @reordered_splat_condition @reordered_splat_condition = true reorder_splat_condition(node) end end
# File lib/rubocop/cop/performance/case_when_splat.rb, line 70 def on_case(node) _case_branch, *when_branches, _else_branch = *node when_conditions = when_branches.each_with_object([]) do |branch, conditions| condition, = *branch conditions << condition end splat_offenses(when_conditions).reverse_each do |condition| range = condition.parent.loc.keyword.join(condition.source_range) variable, = *condition message = variable.array_type? ? ARRAY_MSG : MSG add_offense(condition.parent, range, message) end end
Private Instance Methods
# File lib/rubocop/cop/performance/case_when_splat.rb, line 117 def correct_array_literal(condition, variable) lambda do |corrector| array_start = variable.loc.begin.source if array_start.start_with?(OPEN_BRACKET) corrector.remove(condition.loc.operator) corrector.remove(variable.loc.begin) corrector.remove(variable.loc.end) else corrector.replace(condition.source_range, expand_percent_array(variable)) end end end
# File lib/rubocop/cop/performance/case_when_splat.rb, line 110 def error_condition?(condition) variable, = *condition (condition.splat_type? && variable.array_type?) || !condition.splat_type? end
# File lib/rubocop/cop/performance/case_when_splat.rb, line 148 def expand_percent_array(array) array_start = array.loc.begin.source elements = *array elements = elements.map(&:source) if array_start.start_with?(PERCENT_W) "'#{elements.join("', '")}'" elsif array_start.start_with?(PERCENT_CAPITAL_W) %Q("#{elements.join('", "')}") elsif array_start.start_with?(PERCENT_I) ":#{elements.join(', :')}" elsif array_start.start_with?(PERCENT_CAPITAL_I) %Q(:"#{elements.join('", :"')}") end end
# File lib/rubocop/cop/performance/case_when_splat.rb, line 132 def reorder_splat_condition(node) _case_branch, *when_branches, _else_branch = *node.parent current_index = when_branches.index { |branch| branch == node } next_branch = when_branches[current_index + 1] correction = "\n#{offset(node)}#{node.source}" range = Parser::Source::Range.new(node.parent, node.source_range.begin_pos, next_branch.source_range.begin_pos) lambda do |corrector| corrector.remove(range) corrector.insert_after(when_branches.last.source_range, correction) end end
# File lib/rubocop/cop/performance/case_when_splat.rb, line 100 def splat_offenses(when_conditions) found_non_splat = false when_conditions.reverse.each_with_object([]) do |condition, result| found_non_splat ||= error_condition?(condition) next unless condition.splat_type? result << condition if found_non_splat end end