Rubocop complains about Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, and Metrics/PerceivedComplexity
I am wondering how this method can be simplified (or split into multiple methods, elegantly?) to satisfy Rubocop. I do not want to make it too terribly terse at the expense of readability.
It needs to split the given multi-line string into the “Things” part and the non Things part:
// this comment goes into things
Things blah blah blah {
part of things
// this comment goes into things
}
// this comment goes into items
Item1 blah blah
Item2 blah blah
# Splits the given multi-line string into things and items
def self.split_things_items(src)
things_nest_level = 0
things = []
items = []
comments = []
src.lines.map(&:rstrip).reject(&:empty?).each do |line|
case line
when %r{^\s*//} then comments << line
when /^\s*(Thing|Bridge)\s+/
things.concat comments.slice!(0..), [line]
things_nest_level += 1 if line[-1] == '{'
else
if things_nest_level.positive?
things.concat comments.slice!(0..), [line]
things_nest_level -= 1 if line[-1] == '}'
else
items.concat comments.slice!(0..), [line]
end
end
end
[things.join("\n"), items.join("\n")]
end
There is a lot that can be done there as refactoring.
The simplest thing to start with is to extract the code of each branch of the case statement into its own tiny well-named method. Afterwards, you can extract the entire case statement into its own method. When you do so, it would be much simpler to share variables via instance variables. I do not know the full purpose of your method, so I probably did not come up with the best names for the submethods. I am sure you can come up with much better names.
Here is an example of a fully refactored version that is OK for rubocop (I verified it):
# frozen_string_literal: true
# Splits the given multi-line string into things and items
module Splitter
def self.split_things_items(src)
@things_nest_level = 0
@things = []
@items = []
@comments = []
process_src_lines(src)
[@things.join("\n"), @items.join("\n")]
end
def self.process_src_lines(src)
src.lines.map(&:rstrip).reject(&:empty?).each do |line|
case line
when %r{^\s*//}
process_string(line)
when /^\s*(Thing|Bridge)\s+/
process_thing_bridge(line)
else
process_else(line)
end
end
end
def self.process_string(line)
@comments << line
end
def self.process_thing_bridge(line)
@things.concat @comments.slice!(0..), [line]
@things_nest_level += 1 if line[-1] == '{'
end
def self.process_else(line)
if @things_nest_level.positive?
@things.concat @comments.slice!(0..), [line]
@things_nest_level -= 1 if line[-1] == '}'
else
@items.concat @comments.slice!(0..), [line]
end
end
end
1 Like
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.