Open file with Ruby - ruby

I need to see if a file exists, and then if it exists, I want to open the file, and see what it contains.
I have these methods:
def Utility.exist_request_xml(filexml)
puts("exist_request_xml")
if(File.exist?("#{PATH_WEBSERVICES_REQUEST}/#{filexml}"))
puts 'file exists'
puts(File.exist?("#{PATH_WEBSERVICES_REQUEST}/#{filexml}"))
else
puts 'file not exist'
end
end
def Utility.open_request_xml(filexml)
puts("open_request_xml")
if(Utility.exist_request_xml(filexml))
f=File.open("#{PATH_WEBSERVICES_REQUEST}/#{filexml}","r")
f.each_line do |line|
puts line
end
else
puts 'there is no file to open'
end
end
The first method works. I cannot open the file in the second method. The problem is that, even if the file exists, because I recall the first method in the second, it doesn't open the file.
Can you help me?

Your Utility.exist_request_xml method returns nil, which is falsey in an if statement, so it falls through to the else where you don't open the file.
It returns nil because by default the last evaluated expression is the return value and your last expression is an if. Similarly the return value of an if is the last thing it evaluates, which is a puts (in either branch). puts returns nil.
Return the value of the existence check instead:
def Utility.exist_request_xml filexml
File.exist? "#{PATH_WEBSERVICES_REQUEST}/#{filexml}"
end

Related

Unexpected Result When Using Fibers

I wrote a simple ruby fiber program to see how they work and got an unexpected result.
#! /usr/bin/env ruby
#encoding: utf-8
#frozen_string_literal: true
sg = Fiber.new do
File.open(begin print "Enter filename: "; gets.chomp end).each{|l| Fiber.yield l}
end
begin
loop do
puts sg.resume
end
rescue => err
puts "Error: #{err.message}"
end
datafile
This is the first
This is the second
This is the third
This is the fourth
This is the fifth
And the output from the above program
Enter filename: datafile
This is the first
This is the second
This is the third
This is the fourth
This is the fifth
#<File:0x0000557ce26ce3c8>
Error: attempt to resume a terminated fiber
I'm not sure why its displaying #File:0x0000557ce26ce3c8 in the output.
Note: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
From the docs,
Upon yielding or termination the Fiber returns the value of the last executed expression
In addition to Fiber.yield calls, a fiber (like an ordinary function) returns the result of the final expression in the fiber.
The body of your fiber is this.
File.open(begin print "Enter filename: "; gets.chomp end).each{|l| Fiber.yield l}
Inside the .each, you yield each line of the file, which gets printed out as you've already observed. But then, when the fiber is done, it yields a final value, which is the result of File.open. And File.open returns the File object itself. So your sg.resume actually sees six results, not five.
"This is the first"
"This is the second"
"This is the third"
"This is the fourth"
"This is the fifth"
(the file object itself)
This actually points out a small issue in your program to begin with: You never close the file. You can do that either with File#close or by passing a block to File::open. In order to be completely safe, your fiber code should probably look like this.
sg = Fiber.new do
print "Enter filename: "
filename = gets.chomp
# By passing File.open a block, the file is closed after the block automatically.
File.open(filename) do |f|
f.each{|l| Fiber.yield l}
end
# Our fiber has to return something at the end, so let's just return nil
nil
end
begin
loop do
# Get the value. If it's nil, then we're done; we can break out of the loop.
# Otherwise, print it
value = sg.resume
break if value.nil?
puts value
end
rescue => err
puts "Error: #{err.message}"
end
Now, in addition to dealing with that pesky file handle, we have a way to detect when the fiber is done and we no longer get the "attempt to resume a terminated fiber" error.

.include? is not triggered when reading a txt file

I am under the impression that the following code should return either the search item word or the message no match - as indicated by the ternary operator. I can't diagnose where/why include? method doesn't work.
class Foo
def initialize(word)
#word=word
end
def file_open
IO.foreach('some_file.txt') do |line|
line.include?(#word) ? "#{#word}" : "no match"
end
end
end
print "search for: "
input = gets.chomp.downcase
x = Foo.new(input)
puts x.file_open
The input exists in some_file.txt. My ternary operator syntax is also correct. IO reads the text fine (I also tried File.open() and had the same problem). So I must be making a mistake with my include? method.
You need to control the returned value. file_open defined above will always return nil. The ternary will be executed properly, but nothing is done with its value. Instead you can do the following:
class Foo
def initialize(word)
#word=word
end
def file_open
IO.foreach('some_file.txt') do |line|
return line if line.include?(#word)
end
return "no match"
end
end

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 skips items from list tasks

I am trying to make an app which if give the option to type, it types false then it skips the certain element from the list and it jumps to the next executing the same task.
That is the basic idea of the following code:
string["items"].each do |item|
p continue.to_s + "<- item"
begin
Anemone.crawl("http://" + item["displayLink"] + "/") do |anemone|
anemone.on_every_page do |page|
if continue.chomp.to_bool == false
raise "no more please"
end
request = Typhoeus::Request.new(page.url, followlocation: true)
response = request.run
email = /[-0-9a-zA-Z.+_]+#[-0-9a-zA-Z.+_]+\.[a-zA-Z]{2,4}/.match(response.body)
if email.nil?
else
p email
begin
continue = Timeout::timeout(2) do
p "insert now false/nothing"
gets
end
rescue Timeout::Error
continue = "true"
end
end
end
end
rescue
continue = true
next
end
p "---------------------------------------------------------"
end
As the code shows, if the user types false when prompted the app should skip the item and go to the next one. However what it does is: when the user types false the app skips the current item and then doesn't execute any of the code that should be executed for all of the other items except the printing ( the second line of code );
Here is how the output looks like:
$ruby main.rb
"1"
"true<- item"
#<MatchData "support#keycreative.com">
"insert now false/nothing"
false
"true<- item"
"true<- item"
"true<- item"
As I'm doing my best to show after false is entered the code does skip the certain item from the list but it also never ever executes code for the other items as it should since it is an each loop
First I thought that maybe the continue is false however as you can see from the output the continue is true which makes me wonder why does ruby skip my code?
UPDATE
Here is where the to_bool method comes from:
class String
def to_bool()
return true if self == "true"
return false if self == "false"
return nil
end
end
In your last rescue statement add:
rescue => e
puts e.message
continue = true
next
end
and inspect the output. Most likely your code is throwing an exception other than "no more please" (I expect undefined method to_bool for true:TrueClass). Note that using exception for skipping the loop element is a terrible idea. Why can't you just get rid of this rescue and do:
if continue.chomp.to_bool == false
continue = true
next
end
There are a lot of things in this code which makes it very un-ruby-like. If you want to improve it please paste it to StackExchange CodeReview page. (link in the comment).
UPDATE:
My bad, you are in nested loop, so the if statement won't work. You might look at sth similar to raise/rescue bit, namely throw/catch, see example here: How to break from nested loops in Ruby?. I still think you should post it to codereview though for refactoring advises.
As to your actual code (without refactoring). You are calling to_bool method on continue, and in your rescue block you assign true instead of 'true'. Hence your to_bool method raises exception which is then rescued same way as 'no more please' exception.

ruby koan sandwich line count

I was working through the about_sandwich_code example in the Ruby Koans example, completed code link here about_sandwich_code.rb for reference. The following is the relevant reference code for easy reference.
def count_lines(file_name)
file = open(file_name)
count = 0
while file.gets
count += 1
end
count
ensure
file.close if file
end
def test_counting_lines
assert_equal 4, count_lines("example_file.txt")
end
# ------------------------------------------------------------------
def find_line(file_name)
file = open(file_name)
while line = file.gets
return line if line.match(/e/)
end
ensure
file.close if file
end
def test_finding_lines
assert_equal "test\n", find_line("example_file.txt")
end
def file_sandwich(file_name)
file = open(file_name)
yield(file)
ensure
file.close if file
end
In my attempt at writing find_line2 method I tried the following code which compiled.
def find_line2(file_name)
file_sandwich(file_name) do |file|
while file.gets
return file.gets if file.gets.match(/e/)
end
end
end
def test_finding_lines2
assert_equal "test\n", find_line2("example_file.txt")
end
For reference, example_file.txt.
However, the koans returned the following in the terminal window: Expected "test\n" to equal nil
This raised my awareness as the penultimate koan code/test for the analogous finding lines function pre-sandwich code solved that koan.
def test_finding_lines
assert_equal "test\n", find_line("example_file.txt")
end
As I tried different options, I realized that the following find_line2 implementation
def find_line2(file_name)
file_sandwich(file_name) do |file|
while line = file.gets
return line if line.match(/e/)
end
end
When run with
def test_finding_lines2
assert_equal "test\n", find_line2("example_file.txt")
end
resolves the koan rather than tell me that the test should equal nil as the prior screenshot shows. So what this amounts to, as I understand in this moment is that my first implementation somehow changes what the koan program expects as the check, which sort of baffles me. I think it means my attempt somehow broke the koan, but I'm not sure why. Actually, after running file_sandwich and my first implementation of find_line2 I see the call find_line2("example_file.txt") to return nil, so for some reason file.gets acts differently than using line after the line = file.gets in the while statement.
Can someone explain why my first implementation and the answer don't equal? I believe that the answer lies in a more clear understanding of blocks?
def find_line2(file_name)
file_sandwich(file_name) do |file|
while file.gets #gets the next line in the file
if file.gets.match(/e/) #gets the next line and checks for a match
return file.gets #gets the next line and returns it.
end #restructured for clarity
end
end
end
So with the example file: This\nis\na\ntest you code gets This\n, gets is\n and checks it (fail). On the next iteration, your code gets a\n, gets test\n and checks it (true), then gets the next line (nil) and returns it.

Resources