How do you mock a break statement in RSpec? - ruby

I am having an issue with a LocalJumpError being raised when the example reaches the break statement.
Does anyone know how to stub out break, or if this is even the right approach?
The Method:
def foo(file_name, limit)
CSV.foreach(file_name, col_sep: "|") do |row|
row_count += 1
do something...
break if row_count >= limit
end
end
The Spec:
it 'does not exceed the limit' do
CSV.should_receive(:foreach).with(file_name, col_sep: "|") do |&block|
block.call(header_fields)
block.call(data)
end
foo(file_name, 2)
end

I might consider doing something like this as an integration-type test:
def foo(file_name, limit)
CSV.read(file_name, col_sep: "|").each_with_index do |row, index|
if index < limit
#do something...
end
end
end
end
it 'does not exceed the limit' do
filename = 'path/to/file.csv'
file = CSV.read(filename)
file_length = file.size
last_line = file.last
output = foo(filename, file_length - 1)
expect(output.last).to_not contain(last_line)
end
In this solution, you still iterate through every line of the CSV, but ignore the line if you're over the limit. It's not ideal, but it's one way to go.
The test would set the limit as one less than the file-length, then check if the last line got processed.
The assertion isn't quite right - it will depend on your # do something action.

Related

Read and Write Ruby

Can't seem to get my data to be read as an integer and print out the data, instead gets the 2nd option which is Error: first line of file is not a number.
def write(aFile, number)
aFile.puts(number)
index = 0
while (index < number)
aFile.puts(index)
index += 1
end
end
def read(aFile)
count = aFile.gets
if (is_numeric?(count))
count = count.to_i
else
count = 0
puts "Error: first line of file is not a number"
end
index = 0
while (count < index)
line = aFile.gets
puts "Line read: " + line
index += 1
end
end
def main
aFile = File.new("mydata.txt", "w") # open for writing
if aFile # if nil this test will be false
write(aFile, 10)
aFile.close
aFile = File.new("mydata.txt", "r")
read(aFile)
aFile.close
else
puts "Unable to open file to write or read!"
end
end
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
main
Any help on how to fix this would be great.
Your problem is a lack of return
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
This function ALWAYS returns false. There is no case where it ever returns true. Ruby functions always return at an explicit return() and if none is called, then the last line is returned. That means the true you have there does nothing. It's simply thrown away and false is returned.
A simplified form of this existing function is just:
def is_numeric?(obj)
false
end
To fix this problem, you need to return when it’s true:
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
return(true)
end
false
end
You can also simplify this to:
def is_numeric?(obj)
/[^0-9]/.match(obj).nil?
end
Also, if you’re using Ruby 2.4+, a more efficient way to do this would be to use the match? method and a negation. match sets up some handy MatchData (and backreferences), but since you if you don't need any of that, you can save the overhead by using match?, which simply returns a boolean.
def is_numeric?(obj)
!/[^0-9]/.match?(obj)
end
Another problem is your logic of count < index.
while (count < index)
line = aFile.gets
puts "Line read: " + line
index += 1
end
Since index is 0, the only time count will be less than index, is if count is less than 0. Perhaps you meant while (count > index)?
Notes:
https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/

How to stop outer block from inner block

I try to implement search function which looks for occurrence for particular keyword, but if --max options is provided it will print only some particular number of lines.
def search_in_file(path_to_file, keyword)
seen = false
File::open(path_to_file) do |f|
f.each_with_index do |line, i|
if line.include? keyword
# print path to file before only if there occurence of keyword in a file
unless seen
puts path_to_file.to_s.blue
seen = true
end
# print colored line
puts "#{i+1}:".bold.gray + "#{line}".sub(keyword, keyword.bg_red)
break if i == #opt[:max] # PROBLEM WITH THIS!!!
end
end
end
puts "" if seen
end
I try to use break statement, but when it's within if ... end block I can't break out from outer each_with_index block.
If I move break outside if ... end it works, but it's not what I want.
How I can deal with this?
Thanks in advance.
I'm not sure how to implement it in your code as I'm still learning Ruby, but you can try catch and throw to solve this.
def search_in_file(path_to_file, keyword)
seen = false
catch :limit_reached do
#put your code to look in file here...
throw :limit_reached if i == #opt[:max] #this will break and take you to the end of catch block
Something like this already exist here

Ruby Put Periodic Progress Messages While Mapping

I am mapping an array of items, but the collection can be quite large. I would like to put a message to console every so often, to give an indication of progress. Is there a way to do that during the mapping process?
This is my map statement:
famgui = family_items.map{|i|i.getGuid}
I have a def that I use for giving an update when I am doing a for each or while loop.
This is the def:
def doneloop(saymyname, i)
if (i%25000 == 0 )
puts "#{i} #{saymyname}"
end
end
I normally put x = 0 before I start the loop, then x +=1 once I am in the loop and then at the end of my loop, I put saymyname = "specific type items gathered at #{Time.now}"
Then I put doneloop(saymyname, x)
I am not sure how to do that when I am mapping, as there is no loop to construct this around. Does anyone have a method to give updates when using map?
Thanks!
You can map with index:
famgui = family_items.with_index.map {|item, index| item.getGuid; doneloop('sth', index)}
Only the last expression is returned from a map, so you can do something like:
famgui = family_items.with_index.map do |i, idx|
if idx % 100 == 0
puts # extra linefeed
# report every 100th round
puts "items left: #{family_items_size - idx}"
STDOUT.flush
end
current_item += 1
print "."
STDOUT.flush
i.getGuid
end
This will print "." for each item and a status report after every 100 items.
If you want, you can use each_with and populate the array yourself like:
famgui = []
family_items.each_with_index do |i, idx|
famgui << i.getGuid
puts "just did: #{idx} of #{family_items.size}"
end

How do I detect end of file in Ruby?

I wrote the following script to read a CSV file:
f = File.open("aFile.csv")
text = f.read
text.each_line do |line|
if (f.eof?)
puts "End of file reached"
else
line_num +=1
if(line_num < 6) then
puts "____SKIPPED LINE____"
next
end
end
arr = line.split(",")
puts "line number = #{line_num}"
end
This code runs fine if I take out the line:
if (f.eof?)
puts "End of file reached"
With this line in I get an exception.
I was wondering how I can detect the end of file in the code above.
Try this short example:
f = File.open(__FILE__)
text = f.read
p f.eof? # -> true
p text.class #-> String
With f.read you read the whole file into text and reach EOF.
(Remark: __FILE__ is the script file itself. You may use you csv-file).
In your code you use text.each_line. This executes each_line for the string text. It has no effect on f.
You could use File#each_line without using a variable text. The test for EOF is not necessary. each_line loops on each line and detects EOF on its own.
f = File.open(__FILE__)
line_num = 0
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
f.close
You should close the file after reading it. To use blocks for this is more Ruby-like:
line_num = 0
File.open(__FILE__) do | f|
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
end
One general remark: There is a CSV library in Ruby. Normally it is better to use that.
https://www.ruby-forum.com/topic/218093#946117 talks about this.
content = File.read("file.txt")
content = File.readlines("file.txt")
The above 'slurps' the entire file into memory.
File.foreach("file.txt") {|line| content << line}
You can also use IO#each_line. These last two options do not read the entire file into memory. The use of the block makes this automatically close your IO object as well. There are other ways as well, IO and File classes are pretty feature rich!
I refer to IO objects, as File is a subclass of IO. I tend to use IO when I don't really need the added methods from File class for the object.
In this way you don't need to deal with EOF, Ruby will for you.
Sometimes the best handling is not to, when you really don't need to.
Of course, Ruby has a method for this.
Without testing this, it seems you should perform a rescue rather than checking.
http://www.ruby-doc.org/core-2.0/EOFError.html
file = File.open("aFile.csv")
begin
loop do
some_line = file.readline
# some stuff
end
rescue EOFError
# You've reached the end. Handle it.
end

Ruby StringScanner used for lexing : how to get the line number?

I am using StringScanner for lexical analysis like this :
def next
#scanner.skip(/\s+/)
value,kind=nil,nil
TOKEN_DEF.each{|tok,regex| (kind=tok;break) if #scanner.scan(regex)}
return Token.new(kind,value,#line,#scanner.pos)
end
At first approximation, this works well, except that I can't figure out how to now get the #line number.
I have read the doc, where begin_of_line? method seems appropriate, but I cannot figure how to use it.
Keep the text that you are scanning in a variable and use 'count'
I use the following in my code:
def current_line_number; #text[0..#scanner.pos].count("\n") + 1; end
This code doesn't seem ready to go and for sure somewhere else more elegant solution, it just should give you something to think about.
class Retry < StandardError
end
class TextScanner
def initialize(filename)
#lines = IO.readlines(filename)
#fiber = Fiber.new do
#lines.each_with_index do |line, index|
#scanner = StringScanner.new(line)
#scanner.skip(/\s+/)
value, kind = nil, nil
begin
got_token = false
TOKEN_DEF.each do |tok, regex|
if #scanner.scan(regex)
Fiber.yield Token.new(tok, value, index, #scanner.pos)
got_token = true
end
end
raise Retry if got_token
rescue Retry
retry
end
end
"fiber is finished"
end
end
def next
#fiber.resume
end
end
text_scanner = TextScanner('sometextfile')
puts text_scanner.next #=> first token
puts text_scanner.next #=> second token
puts text_scanner.next #=> third token
...
puts text_scanner.next #=> "fiber is finished"
I think I have a simple solution. Here it is :
def next
#line+=1 while #scanner.skip(/\n/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
return :eof if #scanner.eos?
TOKEN_DEF.each { |tok,syntax| (kind=tok;break) if #scanner.scan(syntax)}
return Token.new(kind,nil,#line,#scanner.pos)
end

Resources