How to set the path of a Tempfile in ruby - ruby

I have a couple of files that i'd like to turn into Tempfile objects.
Is there a way to do something like: Tempfile.new(path)?
The reason i'm asking is that I'm trying to run an ImageMagick command that creates multiple files from a single Tempfile. Unfortunatly the newly created files do not get garbage collected...
Thanks!

Just thought of something. Instead of dumping the new file into a Tempfile, just overwrite it.
tempfile = Tempfile.new('foo')
tempfile.close
FileUtils.mv path, tempfile.path
tempfile.open

Related

How to create a random unique file directly in /tmp using Ruby?

I am writing an application that creates and places a logfile in /tmp and afterwards moves this logfile to another directory. Unfortunately I faced some issues with this implementation and I would like to make this logfile more unique.
I came across mktemp, which would automatically create a file in /tmp. Perfect, just what I need! Unfortunately I cannot seem to get it to work in Ruby. I have tried the following without success:
def temporary_logfile
#temporary_logfile = `mktemp "#{File.basename($PROGRAM_NAME)}_#{Time.now.strftime('%Y%m%dT%H%M%S')}.logXXXX"`
end
I expected to see my logfile in /tmp but unfortunately nothing happens. I wonder what I did wrong?
The next step would be to use slice! to remove the random generated characters from mktemp from the logfile name and than move the file somewhere else.
Have a look at Tempfile: https://ruby-doc.org/stdlib-2.6.3/libdoc/tempfile/rdoc/Tempfile.html
file = Tempfile.new('foo')
begin
# ...do something with file...
ensure
file.close
file.unlink # deletes the temp file
end
Example is taken directly from the docu.

How to find text file in same directory

I am trying to read a list of baby names from the year 1880 in CSV format. My program, when run in the terminal on OS X returns an error indicating yob1880.txt doesnt exist.
No such file or directory # rb_sysopen - /names/yob1880.txt (Errno::ENOENT)
from names.rb:2:in `<main>'
The location of both the script and the text file is /Users/*****/names.
lines = []
File.expand_path('../yob1880.txt', __FILE__)
IO.foreach('../yob1880.txt') do |line|
lines << line
if lines.size >= 1000
lines = FasterCSV.parse(lines.join) rescue next
store lines
lines = []
end
end
store lines
If you're running the script from the /Users/*****/names directory, and the files also exist there, you should simply remove the "../" from your pathnames to prevent looking in /Users/***** for the files.
Use this approach to referencing your files, instead:
File.expand_path('yob1880.txt', __FILE__)
IO.foreach('yob1880.txt') do |line|
Note that the File.expand_path is doing nothing at the moment, as the return value is not captured or used for any purpose; it simply consumes resources when it executes. Depending on your actual intent, it could realistically be removed.
Going deeper on this topic, it may be better for the script to be explicit about which directory in which it locates files. Consider these approaches:
Change to the directory in which the script exists, prior to opening files
Dir.chdir(File.dirname(File.expand_path(__FILE__)))
IO.foreach('yob1880.txt') do |line|
This explicitly requires that the script and the data be stored relative to one another; in this case, they would be stored in the same directory.
Provide a specific path to the files
# do not use Dir.chdir or File.expand_path
IO.foreach('/Users/****/yob1880.txt') do |line|
This can work if the script is used in a small, contained environment, such as your own machine, but will be brittle if it data is moved to another directory or to another machine. Generally, this approach is not useful, except for short-lived scripts for personal use.
Never put a script using this approach into production use.
Work only with files in the current directory
# do not use Dir.chdir or File.expand_path
IO.foreach('yob1880.txt') do |line|
This will work if you run the script from the directory in which the data exists, but will fail if run from another directory. This approach typically works better when the script detects the contents of the directory, rather than requiring certain files to already exist there.
Many Linux/Unix utilities, such as cat and grep use this approach, if the command-line options do not override such behavior.
Accept a command-line option to find data files
require 'optparse'
base_directory = "."
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on('-d', '--dir NAME', 'Directory name') {|v| base_directory = Dir.chdir(File.dirname(File.expand_path(v))) }
end
IO.foreach(File.join(base_directory, 'yob1880.txt')) do |line|
# do lines
end
This will give your script a -d or --dir option in which to specify the directory in which to find files.
Use a configuration file to find data files
This code would allow you to use a YAML configuration file to define where the files are located:
require 'yaml'
config_filename = File.expand_path("~/yob/config.yml")
config = {}
name = nil
config = YAML.load_file(config_filename)
base_directory = config["base"]
IO.foreach(File.join(base_directory, 'yob1880.txt')) do |line|
# do lines
end
This doesn't include any error handling related to finding and loading the config file, but it gets the point across. For additional information on using a YAML config file with error handling, see my answer on Asking user for information, and never having to ask again.
Final thoughts
You have the tools to establish ways to locate your data files. You can even mix-and-match solutions for a more sophisticated solution. For instance, you could default to the current directory (or the script directory) when no config file exists, and allow the command-line option to manually override the directory, when necessary.
Here's a technique I always use when I want to normalize the current working directory for my scripts. This is a good idea because in most cases you code your script and place the supporting files in the same folder, or in a sub-folder of the main script.
This resets the current working directory to the same folder as where the script is situated in. After that it's much easier to figure out the paths to everything:
# Reset working directory to same folder as current script file
Dir.chdir(File.dirname(File.expand_path(__FILE__)))
After that you can open your data file with just:
IO.foreach('yob1880.txt')

Issues trying to copy file to dest folder with FileUtils.cp

I have tried so many variations to the following line to try and get the file located at file_path to copy to the dest "#{location}/#{customer}/#{version}/" but I continually get the error that "#{location}/#{customer}/#{version}/" is a folder.
The Ruby 1.9.3 docs it says that if dest is a folder it will copy to dest/src... so I'm at a loss here.
FileUtils.cp("#{file_path}", "#{location}/#{customer}/#{version}/")
Where file_path is
/home/testing/files/blah.txt
and "#{location}/#{customer}/#{version}/" resolves to:
/home/testing/backup/customername/versionnumber/
I know I'm probably doing something really easily fixable but I have tried many things including:
FileUtils.cp(file_path, "#{location}/#{customer}/#{version}/")
FileUtils.cp("#{file_path}", '#{location}/#{customer}/#{version}/')
Thanks for any help!
If anything under file_path is a directory and not a file, FileUtils.cp will definitely throw Errno::EISDIR: Is a directory.
If that's your situation, you would need to use FileUtils.cp_r instead.

Writing to file with Ruby in Compilr

I'm banging through Zed A. Shaw's learn code the hard way (Ruby) on compilr and am stuck on exercise 16.
filename = ARGV.first
target = File.open(filename, 'w')
target.truncate(target.size)
target.close()
In the console, I type
run sample.txt
This should wipe the sample.txt file, but it doesn't.
The file, sample.txt is in the same folder as the Start file.
Any clues?
Ok it's not the Ruby issue (as I expected) it's to do with how Compilr works. By running the code without having first created a sample.txt file, Compilr created the file for me by default in the content folder. So putting the uh... writable files... into the contents folder enables Compilr to write to them. Putting them all in the same folder (the script and the files) makes it not work.

Ruby: FileUtils.cp truncates file; FileUtils.mv it does not?

This is weird… and I can't figure out for the life of me why it's doing it this way.
I've got a folder full of various CoffeeScript, SASS, HTML, and XML files.
I've got a Ruby script that's taking them all, compiling them, and minifying them into one master XML file (it's for iGoogle Gadget development).
This script takes command line args using trollop (I only state this to clarify my code below).
I want this script to copy this file from the current directory where it's created to a destination directory where it will be run.
So far, the building/compiling/minifying step runs like magic. It's #3 that's borked to Twilight Zone-level.
#!/usr/bin/ruby
…
if opts[:deploy_local]
FileUtils.cp 'build.xml', '/path/to/destination/'
puts "Copied #{written_file_name} to #{output_destination}." if opts[:verbose]
end
When this copies the file, the destination file is truncated about 3/4 of the way through it. The source file is just fine. However, moving the file works like a charm, for some strange reason.
FileUtils.mv 'build.xml', '/path/to/destination/'
To add another level of weirdness, if I just do a system copy, it also gets truncated.
system("cp build.xml /path/to/destination")
FWIW, I'm running this script from zsh and not bash. In both instances (copying and moving) the source and destination files are not in use by any other process.
Can anybody explain this freaky behavior?
A few things:
Are you moving to the same disk volume? If so, then, yeah, cam's comment about atomicity is definitely true; the OS is probably just messing with the inode table during a move, as opposed to writing out the data. IF you're moving the data between volumes, then it wouldn't be so simple.
Have you tried passing
:verbose => true
to the FileUtils.cp command? That might give a diagnostic about the failure.

Resources