Read file that changes regularly - ruby

My program reads a configuration file every 3 sec. within a while loop. Once I change the configuration file with an external editor like notepad, my program creates a temporary file for some reason and keeps reading from there.
I want my program to read from the current configuration file in order to have the most recent changes in my program.
What can I do about it?
Simplified code:
while(true)
file = File.open(filename, "r")
data = JSON.parse(file.read) if file
file.close
sleep(3)
end

If you are on linux, you can try linux inotfy service, this is the gem. This is an example of how to use it.
First of all you have to run
gem install ruby-inotify
and then try this code
notifier = Inotify.new
notifier.add_watch(filename, Inotify::CREATE | Inotify::MODIFY)
notifier.each_event do |ev|
file = File.open(filename, "r")
data = JSON.parse(file.read)
file.close
end

If you are open to using a gem for this, use the following.
https://github.com/thomasfl/filewatcher
Usage
FileWatcher.new(["lib/", "Rakefile"]).watch do |filename|
puts "Changed " + filename
end

Related

Ruby : As a file is getting written; write those line to a different file

Due to some OS restrictions I am not able to install ruby and mysql on my prod node. I have to process a Log file written on that node. So I planning to write the contents of the log file to a different file as its written to the log and work on that new file.
def watch_for(file)
f = File.open(file,"r")
f.seek(0,IO::SEEK_END)
while true do
select([f])
line = f.gets
open('myfile.out', 'a+') { |s|
s.puts "#{line}"
}
end
end
But this seems to failing; Any help to this is much appreciated.
It's not a great idea to reopen the output file each time through the loop. If you are just planning on appending and not reading the file, use 'a' rather than 'a+' for the IO mode. Also, there's no good reason to use string interpolation like "#{line}". I rewrote the method to open each file just once:
def watch_for(file)
f = File.open(file,"r")
f.seek(0,IO::SEEK_END)
s = File.open('myfile.out', 'a')
while true do
s.write(f.readline)
end
end
Probably better if this isn't a method at all since there's no way to exit except to kill the process. And, of course, you can just use tail -f $file >> myfile.out as mentioned in this comment.

Opening a Text File in Ruby

I am trying to create a program that will count the word frequency within a text file that I have created. I have a text file titled moms_letter.txt and this is my code:
word_count = {}
File.open("moms_letter.txt", "r") do |f|
f.each_line do |line|
words = line.split(' ').each do |word|
word_count[word] += 1 if word_count.has_key? word
word_count[word] = 1 if not word_count.has_key? word
end
end
end
puts word_count
The problem I am getting is when I go to run the file, I get the error:
there is no such file or directory - moms_letter.txt (Errno: : ENOENT)
Not quite sure why this is occurring when I have the text file created.
Any help is appreciated.
I am also newbie in Ruby, so thanks for the patience.
You must be executing your program from outside the directory where your moms_letter.txt file resides. You need to use an absolute path to open your file. Or, execute your program always from the directory where the .txt is. So, instead of using "moms_letter.txt" go with "complete/path/to/file/moms_letter.txt".
I'm fairly new to Ruby too, but have worked with text files a bit recently. It may seem like an obvious question, but is the text file you're trying to open in the same directory as your .rb file? Otherwise you'll need to include the relative path to it.
For troubleshooting sake, try File.new("temp.txt", "w") and then File.open("temp.txt", "r") to see if that works. Then you'll know if it's an issue with your code or with the txt file you're trying to access.
Also using File.exists?("moms_letter.txt") will help you determine whether you can access that file from within your .rb script.
Hope that helps!

Ruby: Reading from a file written to by the system process

I'm trying to open a tmpfile in the system $EDITOR, write to it, and then read in the output. I can get it to work, but I am wondering why calling file.read returns an empty string (when the file does have content)
Basically I'd like to know the correct way of reading the file once it has been written to.
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.rewind
puts file.read # this puts out an empty string "" .. why?
puts IO.read(file.path) # this puts out the contents of the file
Yes, I will be running this in an ensure block to nuke the file once used ;)
I was running this on ruby 2.2.2 and using vim.
Make sure you are calling open on the file object before attempting to read it in:
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.open
puts file.read
file.close
file.unlink
This will also let you avoid calling rewind on the file, since your process hasn't written any bytes to it at the time you open it.
I believe IO.read will always open the file for you, which is why it worked in that case. Whereas calling .read on an IO-like object does not always open the file for you.

Failure reading PNG files with Ruby on Windows

I am creating a Zip file containing both text files and image files. The code works as expected when running on MacOS, but it fails when running on Windows because the image file contents are not read correctly.
The snippet below always reads PNG image files as '‰PNG', adding a 5 bytes file in the Zip for each PNG image.
Is it an issue regarding Windows environment?
zip_fs.file.open(destination, 'w') do |f|
f.write File.read(file_name)
end
from Why are binary files corrupted when zipping them?
io.get_output_stream(zip_file_path) do |out|
out.write File.binread(disk_file_path)
end
You need to tell Ruby to read/write the files in binary mode. Here are some variations on a theme:
zip_fs.file.open(destination, 'wb') do |f|
File.open(file_name, 'rb') do |fi|
f.write fi.read
end
end
zip_fs.file.open(destination, 'wb') do |f|
f.write File.read(file_name, 'mode' => 'rb')
end
zip_fs.file.open(destination, 'wb') do |f|
f.write File.readbin(file_name)
end
A potential problem with the code is the input file is being slurped, which, if it's larger than the available space, would be a bad thing. It'd be better to read the input file in blocks. This is untested but should work:
BLOCK_SIZE = 1024 * 1024
zip_fs.file.open(destination, 'wb') do |f|
File.open(file_name, 'rb') do |fi|
while (block_in = fi.read(BLOCK_SIZE)) do
f.write block_in
end
end
end
The file that was opened will never be closed. Use File.binread(file_name)
My initial code was written to show that binary mode needed to be used, and used open because it's "more traditional", but forgot to use the block mode. I modified my sample code to fix that problem.
However, the file would be closed implicitly by Ruby as the interpreter shuts down when the script ends, as part of housekeeping that occurs. However, it's better to explicitly close the file. If the OP is using RubyZip like I think, that will automatically happen if a block is passed to open. Otherwise, read and readbin will both read to EOF and close the file. Code using those methods needs to be sensitive to the need to read blocks if the input file is an unknown size or larger than available buffer space.
I had a similar problem when I was reading Lib files. Here's my solution:
File.open(path + '\Wall.Lib')
Where the path corresponded to a javascript file that inputs filenames.

Ruby System Call Executing Before Script Finishes

I have a Ruby script that produces a Latex document using an erb template. After the .tex file has been generated, I'd like to make a system call to compile the document with pdflatex. Here are the bones of the script:
class Book
# initialize the class, query a database to get attributes, create the book, etc.
end
my_book = Book.new
tex_file = File.open("/path/to/raw/tex/template")
template = ERB.new(tex_file.read)
f = File.new("/path/to/tex/output.tex")
f.puts template.result
system "pdflatex /path/to/tex/output.tex"
The system line puts me in interactive tex input mode, as if the document were empty. If I remove the call, the document is generated as normal. How can I ensure that the system call isn't made until after the document is generated? In the meantime I'm just using a bash script that calls the ruby script and then pdflatex to get around the issue.
The File.new will open a new stream that won't be closed (saved to disk) until the script ends of until you manually close it.
This should work:
...
f = File.new("/path/to/tex/output.tex")
f.puts template.result
f.close
system "pdflatex /path/to/tex/output.tex"
Or a more friendly way:
...
File.open("/path/to/tex/output.tex", 'w') do |f|
f.puts template.result
end
system "pdflatex /path/to/tex/output.tex"
The File.open with a block will open the stream, make the stream accessible via the block variable (f in this example) and auto-close the stream after the block execution. The 'w' will open or create the file (if the file already exists the content will be erased => The file will be truncated)

Resources