I am looking for a function or gem that gives me the cyclomatic complexity of a function.
For example, using rubocop, If I write
def my_func(foo)
foo.details['errors'].each do |attr, message|
case attr
when 1 then foo.errors.add(:err1, :format)
when 2 then foo.errors.add(:err3, :format)
when 3 then foo.errors.add(:err5, :format)
when 4 then foo.errors.add(:err7, :format)
when 5 then foo.errors.add(:err9, :format)
when 6 then foo.errors.add(:err11, :format)
when 7 then foo.errors.add(:err13, :format)
else foo.errors.add(:base, message)
end
end
return foo
end
When I run rubocop
, then I got the error:
Metrics/CyclomaticComplexity: Cyclomatic complexity for my_func is too high. [9/7]
def my_func(foo) ..."
And I know my cylcomatic complexity is [9/7]
.
If I change my function and no error is raised by rubocop, how to get the function cyclomatic complexity ? A code snippet with an example would be great ! (I am not looking for a manual computation).
Bonus: Provide a solution for JavaScript functions also.
Thanks!
CodePudding user response:
Have you tried looking at cycromatic?
CodePudding user response:
From Rubocop::Cop::Metrics::CyclomaticComplexity
itself;
# Checks that the cyclomatic complexity of methods is not higher
# than the configured maximum. The cyclomatic complexity is the number of
# linearly independent paths through a method. The algorithm counts
# decision points and adds one.
#
# An if statement (or unless or ?:) increases the complexity by one. An
# else branch does not, since it doesn't add a decision point. The &&
# operator (or keyword and) can be converted to a nested if statement,
# and ||/or is shorthand for a sequence of ifs, so they also add one.
# Loops can be said to have an exit condition, so they add one.
# Blocks that are calls to builtin iteration methods
# (e.g. `ary.map{...}) also add one, others are ignored.
#
# def each_child_node(*types) # count begins: 1
# unless block_given? # unless: 1
# return to_enum(__method__, *types)
#
# children.each do |child| # each{}: 1
# next unless child.is_a?(Node) # unless: 1
#
# yield child if types.empty? || # if: 1, ||: 1
# types.include?(child.type)
# end
#
# self
# end # total: 6
So, going part by part in your code, you get an initial count of 1, plus 1 for the each
loop and plus 1 for each when
branch;
def my_func(foo) # count begins: 1
foo.details['errors'].each do |attr, message| # each{}: 1
case attr
when 1 then foo.errors.add(:err1, :format) # when: 1
when 2 then foo.errors.add(:err3, :format) # when: 1
when 3 then foo.errors.add(:err5, :format) # when: 1
when 4 then foo.errors.add(:err7, :format) # when: 1
when 5 then foo.errors.add(:err9, :format) # when: 1
when 6 then foo.errors.add(:err11, :format) # when: 1
when 7 then foo.errors.add(:err13, :format) # when: 1
else foo.errors.add(:base, message)
end
end
return foo
end # total: 9
You could check the class examples to get a better idea as well.