Copying a file to a directory? - ruby

I have a Ruby program that copies a file from source folder to destination folder.
C:\srcdir\testfile.txt is the source folder, and C:\targetdir is the destination folder.
The program keeps reporting an error:
copy_files.rb:11:in block in <main>': uninitialized constant FileUtils (NameError)
Why is it? This is my code:
sourcedir = "C:\\srcdir"
targetdir = "C:\\targetdir"
Dir.foreach(sourcedir){
|f|
filepath = "#{sourcedir}\\#{f}"
if !(File.directory?(filepath)) then
if File.exist?("#{targetdir}\\#{f}") then
puts("#{f} already exists in target directory (not copied)")
else
FileUtils.cp(filepath, targetdir)
puts("Copying... #{filepath}")
end
end
}

FileUtils is a module, it isn't part of the Ruby core. You need to require it to use it, like this:
require 'fileutils'
This stackoverflow question explains how to move a file using FileUtils: How do I move a file with Ruby?
Here is the documentation for the FileUtils module for Ruby 1.9.3: http://ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html

This is untested code, but is closer how I'd write it:
SOURCEDIR = 'C:/srcdir'
TARGETDIR = 'C:/targetdir'
Dir.foreach(SOURCEDIR) do |f|
filepath = File.join(SOURCEDIR, f)
if !File.directory?(filepath)
if File.exist?(File.join(TARGETDIR, f)
puts "#{ f } already exists in target directory (not copied)"
else
print "Copying #{ filepath }... "
FileUtils.cp(filepath, TARGETDIR)
puts "done"
end
end
end
Of course, your OS would make it even easier; Batch and shell files and OS-level commands are made just for this.

Related

FileUtil does not copy ".gitignore"

I used FileUtils.cp_r() to copy a whole folder. All files inside that folder is copied except .gitignore. If I change the file's name to gitignore (without period), it works fine.
I'm guessing it's because the file's name is not valid for Ruby. Is there a solution for this?
This is my code:
require "fileutils"
module MyApp
def self.create
# root of the gem dir
root = File.expand_path("..", File.dirname(__FILE__))
# "/template" is the folder that I want to copy
src_dir = File.join(root, "template")
# destination is where the command prompt opened
destination = Dir.pwd
FileUtils.cp_r( Dir["#{src_dir}/*"], destination)
end
end
I'm using Windows 8.1 Update 1. But my friend who uses Mac tested my gem and doesn't get the .gitignore too.
This is the problem:
Dir["#{src_dir}/*"]
Globbing does not include filenames starting with ..
Use other methods like this instead:
sources = Dir.entries("#{src_dir}/").reject{ |e| e == '.' || e == '..' }.map{ |e| "#{src_dir}/#{e}" }
FileUtils.cp_r(sources, destination)
You can also use File::FNM_DOTMATCH:
Dir.glob("#{src_dir}/*", File::FNM_DOTMATCH)

Ruby: check if a .zip file exists, and extract

2 small questions to create the effect I'm looking for.
How do I check if a file exists within a directory with the extension of .zip?
If it does exist I need to make a folder with the same name as the .zip without the .zip extension for the folder.
Then I need to extract the files into the folder.
Secondly, what do I do if there are more than one .zip files in the folder?
I'm doing something like this and trying to put it into ruby
`mkdir fileNameisRandom`
`unzip fileNameisRandom.zip -d fileNameisRandom`
On a similar post I found something like
Dir.entries("#{Dir.pwd}").select {|f| File.file? f}
which I know checks all files within a directory and makes sure they are a file.
The problem is I don't know how to make sure that it is only an extension of .zip
Also, I found the Glob function which checks the extension of a filename from: http://ruby-doc.org/core-1.9.3/Dir.html
How do I ensure the file exists in that case, and if it doesn't I can print out an error then.
From the comment I now have
if Dir['*.zip'].first == nil #check to see if any exist
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "#{file}"
end ## end for each loop
end
Here's a way of doing this with less branching:
# prepare the data
zips= Dir['*.zip'].select{ |f| File.file? }
# check if data is sane
if zips.empty?
puts "No zips"
exit 0 # or return
end
# process data
zips.each do |z|
end
This pattern is easier to follow for fellow programmers.
You can also do it using a ruby gem called rubyzip
Gemfile:
source 'https://rubygems.org'
gem 'rubyzip'
run bundle
unzip.rb:
require 'zip'
zips= Dir['*.zip'].select{ |f| File.file? }
if zips.empty?
puts "No zips"
exit 0 # or return
end
zips.each do |zip|
Zip::File.open(zip) do |files|
files.each do |file|
# write file somewhere
# see here https://github.com/rubyzip/rubyzip
end
end
end
I finally pieced together different information from tutorials and used #rogerdpack and his comment for help.
require 'rubygems/package'
#require 'zlib'
require 'fileutils'
#move to the unprocessed directory to unpack the files
#if a .tgz file exists
#take all .tgz files
#make a folder with the same name
#put all contained folders from .tgz file inside of similarly named folder
#Dir.chdir("awaitingApproval/")
if Dir['*.zip'].first == nil #check to see if any exist, I use .first because Dir[] returns an array
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "" #newlie for each file
puts "#{file}" #print out file name
#next line based on `mkdir fileNameisRandom`
`mkdir #{Dir.pwd}/awaitingValidation/#{ File.basename(file, File.extname(file)) }`
#next line based on `unzip fileNameisRandom.zip -d fileNameisRandom`
placement = "awaitingValidation/" + File.basename(file, File.extname(file))
puts "#{placement}"
`sudo unzip #{file} -d #{placement}`
puts "Unzip complete"
end ## end for each loop
end

Get directory of file that instantiated a class ruby

I have a gem that has code like this inside:
def read(file)
#file = File.new file, "r"
end
Now the problem is, say you have a directory structure like so:
app/main.rb
app/templates/example.txt
and main.rb has the following code:
require 'mygem'
example = MyGem.read('templates/example.txt')
It comes up with File Not Found: templates/example.txt. It would work if example.txt was in the same directory as main.rb but not if it's in a directory. To solve this problem I've added an optional argument called relative_to in read(). This takes an absolute path so the above could would need to be:
require 'mygem'
example = MyGem.read('templates/example.txt', File.dirname(__FILE__))
That works fine, but I think it's a bit ugly. Is there anyway to make it so the class knows what file read() is being called in and works out the path based on that?
There is an interesting library - i told you it was private. One can protect their methods with it from being called from outside. The code finds the caller method's file and removes it. The offender is found using this line:
offender = caller[0].split(':')[0]
I guess you can use it in your MyGem.read code:
def read( file )
fpath = Pathname.new(file)
if fpath.relative?
offender = caller[0].split(':')[0]
fpath = File.join( File.dirname( offender ), file )
end
#file = File.new( fpath, "r" )
end
This way you can use paths, relative to your Mygem caller and not pwd. Exactly the way you tried in your app/main.rb
Well, you can use caller, and a lot more reliably than what the other people said too.
In your gem file, outside of any class or module, put this:
c = caller
req_file = nil
c.each do |s|
if(s =~ /(require|require_relative)/)
req_file = File.dirname(File.expand_path(s.split(':')[0])) #Does not work for filepaths with colons!
break
end
end
REQUIRING_FILE_PATH = req_file
This will work 90% of the time, unless the requiring script executed a Dir.chdir. The File.expand_path depends on that. I'm afraid that unless your requirer passes their __FILE__, there's nothing you can do if they change the working dir.
Also you may check for caller:
def read(file)
if /^(?<file>.+?):.*?/ =~ caller(1).first
caller_dir, caller_file = Pathname.new(Regexp.last_match[:file]).split
file_with_path = File.join caller_dir, file
#file = File.new "#{file_with_path}", "r"
end
end
I would not suggest you to do so (the code above will break being called indirectly, because of caller(1), see reference to documentation on caller). Furthermore, the regex above should be tuned more accurately if the caller path is intended to contain colons.
This should work for typical uses (I'm not sure how resistant it is to indirect use, as mentioned by madusobwa above):
def read_relative(file)
#file = File.new File.join(File.dirname(caller.first), file)
end
On a side note, consider adding a block form of your method that closes the file after yielding. In the current form you're forcing clients to wrap their use of your gem with an ensure.
Accept a file path String as an argument. Convert to a Pathname object. Check if the path is relative. If yes, then convert to absolute.
def read(file)
fpath = Pathname.new(file)
if fpath.relative?
fpath = File.expand_path(File.join(File.dirname(__FILE__),file))
end
#file = File.new(fpath,"r")
end
You can make this code more succinct (less verbose).

How do I create directory if none exists using File class in Ruby?

I have this statement:
File.open(some_path, 'w+') { |f| f.write(builder.to_html) }
Where
some_path = "somedir/some_subdir/some-file.html"
What I want to happen is, if there is no directory called somedir or some_subdir or both in the path, I want it to automagically create it.
How can I do that?
You can use FileUtils to recursively create parent directories, if they are not already present:
require 'fileutils'
dirname = File.dirname(some_path)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
Edit: Here is a solution using the core libraries only (reimplementing the wheel, not recommended)
dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"
1.upto(tokens.size) do |n|
dir = tokens[0...n]
Dir.mkdir(dir) unless Dir.exist?(dir)
end
For those looking for a way to create a directory if it doesn't exist, here's the simple solution:
require 'fileutils'
FileUtils.mkdir_p 'dir_name'
Based on Eureka's comment.
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
How about using Pathname?
require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
Based on others answers, nothing happened (didn't work). There was no error, and no directory created.
Here's what I needed to do:
require 'fileutils'
response = FileUtils.mkdir_p('dir_name')
I needed to create a variable to catch the response that FileUtils.mkdir_p('dir_name') sends back... then everything worked like a charm!
Along similar lines (and depending on your structure), this is how we solved where to store screenshots:
In our env setup (env.rb)
screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
FileUtils.mkdir_p(screenshotfolder)
end
Before do
#screenshotfolder = screenshotfolder
...
end
And in our hooks.rb
screenshotName = "#{#screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
#browser.take_screenshot(screenshotName) if scenario.failed?
embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?
The top answer's "core library" only solution was incomplete. If you want to only use core libraries, use the following:
target_dir = ""
Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end
# Splits name into pieces
tokens = target_dir.split(/\//)
# Start at '/'
new_dir = '/'
# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|
# Builds directory path one folder at a time from top to bottom
unless n == (tokens.size - 1)
new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
else
new_dir << "#{tokens[n].to_s}" # Innermost folder
end
# Creates directory as long as it doesn't already exist
Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end
I needed this solution because FileUtils' dependency gem rmagick prevented my Rails app from deploying on Amazon Web Services since rmagick depends on the package libmagickwand-dev (Ubuntu) / imagemagick (OSX) to work properly.

How do I copy file contents to another file?

As basic as this seems, I simply can't manage to copy the contents of one file to another. Here is my code thus far:
#!/usr/bin/ruby
Dir.chdir( "/mnt/Shared/minecraft-server/plugins/Permissions" )
flist = Dir.glob( "*" )
flist.each do |mod|
mainperms = File::open( "AwesomeVille.yml" )
if mod == "AwesomeVille.yml"
puts "Shifting to next item..."
shift
else
File::open( mod, "w" ) do |newperms|
newperms << mainperms
end
end
puts "Updated #{ mod } with the contents of #{ mainperms }."
end
Why copy the contents of one file to another? Why not use either the OS to copy the file, or use Ruby's built-in FileUtils.copy_file?
ri FileUtils.copy_file
FileUtils.copy_file
(from ruby core)
------------------------------------------------------------------------------
copy_file(src, dest, preserve = false, dereference = true)
------------------------------------------------------------------------------
Copies file contents of src to dest. Both of src and
dest must be a path name.
A more flexible/powerful alternate is to use Ruby's built-in FileUtils.cp:
ri FileUtils.cp
FileUtils.cp
(from ruby core)
------------------------------------------------------------------------------
cp(src, dest, options = {})
------------------------------------------------------------------------------
Options: preserve noop verbose
Copies a file content src to dest. If dest is a
directory, copies src to dest/src.
If src is a list of files, then dest must be a directory.
FileUtils.cp 'eval.c', 'eval.c.org'
FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
This works for me
IO.copy_stream mainperms, mod
ยง copy_stream
I realize that this isn't the completely approved way, but
IO.readlines(filename).join('') # join with an empty string because readlines includes its own newlines
Will load a file into a string, which you can then output into newperms just like it was a string. There's good chance the reason this isn't working currently is that you are trying to write an IO handler to a file, and the IO handler isn't getting converted into a string in the way you want it to.
However, another fix might be
newperms << mainperms.read
Also, make sure you close mainperms before the script exits, as it might break something if you don't.
Hope this helps.

Resources