How to get the cyclomatic complexity of a ruby function? [closed] - ruby

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 months ago.
Improve this question
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!

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.

Related

Do something x times per second

I've been given an interesting ruby question for a technical test. I did not find an answer in the time given, but i think it was interesting and would like someone else's take on this. How would you write code to do:
2.times.each_second { p 'works' }
I tried extending the enumerator class, with a per_second method. The per_second method just sleeps for the correct number of milliseconds. But i was stuck trying to pass the "2" argument to the per_second method in an elegant fashion. Maybe using the length of the enumerator returned from 2.times?
Using the magic of modern Ruby, we can write a refinement to Integer that augments the times method, but only in scopes that opt into this refinement behavior.
module IntegerExt
# Define a refinement of the Integer type. This is like
# monkeypatching but is local to modules or files that opt in to the
# change.
refine Integer do
# Re-write the times function.
def times(&block)
if block
# If a block was provided, then do the "normal" thing.
super(&block)
else
# Otherwise, get an enumerator and augment its singleton class
# with PerSecondEnumerator.
super().extend PerSecondEnumerator
end
end
end
# This module provides the per_second method on enumerators that
# need it.
module PerSecondEnumerator
def per_second(&block)
loop do
sleep(1.0/self.size)
block.call
end
end
end
end
# If a module wishes to have this functionality, it needs to opt in
# with this 'using' statement. The per_second syntax only works on
# objects constructed within the lexical scope of this refinement.
using IntegerExt
puts 2.times.inspect # Still works
2.times.per_second { p 'works' }
This deals with a couple of the concerns of Aria's answer. We're no longer globally monkey-patching a built-in class. We only modify the class in the lexical scope of any module that wishes to see our changes. Further, since we've refined times rather than Enumerator directly, our per_second only works on enumerators constructed via times, so it's impossible to do something nonsensical like [1, 2, 3, 4, 5].per_second { ... }.
You could do it like that:
Enumerator.define_method(:per_second) do |&block|
loop do
sleep(1.0/self.size)
block.call
end
end
3.times.per_second { puts "works" }
But here are some warnings:
It's not recommended to expand a Ruby base class like Enumerator, Integer, etc.
Like someone said in a comment on your post, it's not good to use size, since you can use a string or array instead of a integer.
Here is another option using a simple object
obj = Object.new
def obj.each_second(times: 1, duration: Float::INFINITY)
return to_enum(__method__, times: times, duration: duration) unless block_given?
i = 0
until i >= duration * times do
i += 1
yield(i)
sleep(1.0/times)
end
end
enum = obj.each_second(times: 5, duration: 3)
enum.each { |n| puts "#{n} Works"}
# 1 Works
# 2 Works
# 3 Works
# 4 Works
# 5 Works
# 6 Works
# 7 Works
# 8 Works
# 9 Works
# 10 Works
# 11 Works
# 12 Works
# 13 Works
# 14 Works
# 15 Works
#=> nil
You can specify the number of times to execute the block per second and the duration of the execution in seconds. (Defaults to 1 iteration per second indefinitely)
Note: This does not take into account the actual blocking execution time of the block itself so the number of iterations per second is not guaranteed and is likely to be less than the specified number.

How do I destructure a range in Ruby?

Is it possible to use destructuring in ruby to extract the end and beginning from a range?
module PriceHelper
def price_range_human( range )
"$%s to $%s" % [range.begin, range.end].map(:number_to_currency)
end
end
I know that I can use array coercion as a really bad hack:
first, *center, last = *rng
"$%s to $%s" % [first, last].map(:number_to_currency)
But is there a syntactical way to get begin and end without actually manually creating an array?
min, max = (1..10)
Would have been awesome.
You can use minmax to destructure ranges:
min, max = (1..10).minmax
min # => 1
max # => 10
If you are using Ruby before 2.7, avoid using this on large ranges.
The beginning and end? I'd use:
foo = 1..2
foo.min # => 1
foo.max # => 2
Trying to use destructuring for a range is a bad idea. Imagine the sizes of the array that could be generated then thrown away, wasting CPU time and memory. It's actually a great way to DOS your own code if your range ends with Float::INFINITY.
end is not the same as max: in 1...10, end is 10, but max is 9
That's because start_val ... end_val is equivalent to start_val .. (end_val - 1):
start_value = 1
end_value = 2
foo = start_value...end_value
foo.end # => 2
foo.max # => 1
foo = start_value..(end_value - 1)
foo.end # => 1
foo.max # => 1
max reflects the reality of the values actually used by Ruby when iterating over the range or testing for inclusion in the range.
In my opinion, end should reflect the actual maximum value that will be considered inside the range, not the value used at the end of the definition of the range, but I doubt that'll change otherwise it'd affect existing code.
... is more confusing and leads to increased maintenance problems so its use is not recommended.
No, Until I am proven incorrect by Cary Swoveland, Weekly World News or another tabloid, I'll continue believing without any evidence that the answer is "no"; but it's easy enough to make.
module RangeWithBounds
refine Range do
def bounds
[self.begin, self.end]
end
end
end
module Test
using RangeWithBounds
r = (1..10)
b, e = *r.bounds
puts "#{b}..#{e}"
end
Then again, I'd just write "#{r.begin.number_to_currency}..#{r.end.number_to_currency}" in the first place.
Amadan's answer is fine. you just need to remove the splat (*) when using it since it is not needed
eg,
> "%s to %s" % (1..3).bounds.map{|x| number_to_currency(x)}
=> "$1.00 to $3.00"

Ruby Loop Countdown method not Counting down [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to define a method that countdowns 10 to 0 and at the end returns HAPPY NEW YEARS! however i don't want i"am doing wrong?
def countdown(number)
while number > 0
puts "#{number} SECONDS(S)!"
number -= 1
end
"HAPPY NEW YEAR!"
end
A quick Google search revealed that you are apparently trying to solve https://learn.co/lessons/countdown-to-midnight (you really should have included that link)
Your code is not passing the spec, because it contains an additional S:
puts "#{number} SECONDS(S)!"
# ^
It has to be:
puts "#{number} SECOND(S)!"
def countdown(number)
while number > 0
puts "#{number} SECONDS(S)!"
number -= 1
end
puts "HAPPY NEW YEAR!"
end
I added a puts on the last line of your code. You method is counting down to 0 seconds, but the last line only return the string "HAPPY NEW YEAR!", and does not print it to the screen. Or if you need to return the string and print it on the screen, you can do p "HAPPY NEW YEAR!"

Improving calculate function where input string does not follow normal computational order

I am working through some sample interview questions and came across this problem where I have to write a calculate function. The caveat is that the input string isn't written in computational order - instead numbers are first and operators come after.
Test cases:
calculate("2 3 +") # => 5
calculate("12 2 /") #=> 6
calculate("48 4 6 * /") # => 2
def calculate(string)
numbers = string.scan(/\d+/)
operators = string.scan(/\D\W/).map{|o|o.gsub(" ","")} #TODO Better regex to remove space without having to map
formatted_string = numbers.inject(""){|string, b| string+"#{b}#{operators.shift}" }
eval(formatted_string)
end
I was able to come up with a solution, but am wondering if there is a more efficient/better way to solve this problem. I don't come from a programming background so I am not familiar with any tools/algorithms that may help.
Any feedback is greatly appreciated.
Ooh, fun! This syntax is called Reverse Polish notation (RPN), aka "postfix notation" and is still used by some powerful calculators (the HP-48G made me fall in love with programming!).
The typical way to parse and calculate RPN is to split the input string into whitespace-separated tokens, and for each token, push it onto a stack if it's an integer, otherwise perform the arithmetic operation designated by the token using the last two items on the stack as operands and push the result back on.
Here is a solution I came up with quickly:
def calculate(str)
stack = []
tokens = str.split(/\s+/) # Split tokens by whitespace.
tokens.each do |token|
if token =~ /^\d+$/ # It's a number
stack.push(token.to_i) # ...push it on to the stack.
elsif %w(+ - * /).include? token # It's an operator
a, b = stack.pop(2) # ...grab the last two numbers
c = a.send(token, b) # ...perform the operation
stack.push(c) # ...and push the resulting value.
else
raise "invalid token: #{token}" # Punt!
end
end
stack.pop # Return the last value pushed to the stack.
end
calculate("2 3 +") # => 5, since 2+3 = 5
calculate("12 2 /") # => 6, since 12/2 = 6
calculate("48 4 6 * /") # => 2, since 48/(4*6) = 2

Is there an opposite of String.next? [duplicate]

This question already has an answer here:
What is the opposite of string.next?
(1 answer)
Closed 9 years ago.
What is the opposite of next in Ruby?
I am trying to find a function that is like prev which doesn't exist.
"b".prev would == "a", just like "a".next == "b"
# A grad student at a local university thinks he has discovered a formula to
# predict what kind of grades a person will get. He says if you own less than
# 10 books, you will get a "D". If you own 10 to 20 books, you will get a "C",
# and if you own more than 20 books, you will get a "B".
# He further hypothesizes that if you actually read your books, then you will
# get a full letter grade higher in every case.
#
# grade(4, false) # => "D"
# grade(4, true) # => "C"
# grade(15, true) # => "B"
If your case is limited to unique character the following will allow you to get the previous
or next character:
def next_char(c)
(c.chr.ord + 1).chr
end
def prev_char(c)
(c.chr.ord - 1). chr
end
puts next_char('A')
puts prev_char('B')
However, as you deal with grades, I'd go for a kind a Enumeration of grade. For instance, take a look at that blog post for possible implementation.
Ref:
String.chr
String.ord
Integer.chr

Resources