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)
Related
I need to open each file inside a directory. My attempt at this looks like:
Dir.foreach('path/to/directory') do |filename|
next if filename == '.' || filename == '..'
puts "working on #{filename}"
# this is where it crashes
file = File.open(filename, 'r')
#some code
file.close
# more code
end
My code keeps crashing at File.open(filename, 'r'). I'm not sure what filename should be.
The filename should include the path to the file when the file is not in the same directory than the Ruby file itself:
path = 'path/to/directory'
Dir.foreach(path) do |filename|
next if filename == '.' || filename == '..'
puts "working on #{filename}"
file = File.open("#{path}/#{filename}", 'r')
#some code
file.close
# more code
end
I recommend using Find.find.
While we can use various methods from the Dir class, it will look and retrieve the list of files before returning, which can be costly if we're recursively searching multiple directories or have a huge number of files embedded in the directories.
Instead, Find.find will walk the directories, returning both the directories and files as each is found. A simple check lets us decide which we want to continue processing or whether we want to skip it. The documentation has this example which should be easy to understand:
The Find module supports the top-down traversal of a set of file paths.
For example, to total the size of all files under your home directory, ignoring anything in a “dot” directory (e.g. $HOME/.ssh):
require 'find'
total_size = 0
Find.find(ENV["HOME"]) do |path|
if FileTest.directory?(path)
if File.basename(path)[0] == ?.
Find.prune # Don't look any further into this directory.
else
next
end
else
total_size += FileTest.size(path)
end
end
I'd go for Dir.glob or File.find. But not Dir.foreach as it returns . and .. which you don't want.
Dir.glob('something/*').each do |filename|
next if File.directory?(filename)
do_something_with_the_file(filename)
end
I am in the process of creating some build scripts, using Rake, that will be used as part of the overall process of deploying our web services to the cloud via Docker containers. In order to accomplish this we combine resources from several repos using Rake to "assemble" the directory/file layout. This all work well save for one item, .htaccess files.
Here is the copy function that I've created:
require 'fileutils'
EXT_ALLOWED = ["html", "css", "js", "svg", "otf", "eot", "ttf", "woff", "jpeg", "map", "ico", "map", "png", "db", "php", "conf"]
def copy_to(dest, src, trim="")
files = FileList.new()
EXT_ALLOWED.each {|ext| files.include "#{src}/**/*.#{ext}"}
files.each do |file|
dir = File.dirname(file)
filename = File.basename(file)
trimming = "/shared/" + trim + "(.*)"
path = dir.match(trimming)
if path == nil || dest == path[1] + '/'
bin = dest
else
bin = File.join(dest, path[1] + '/')
end
puts "copying #{file} to #{bin}"
FileUtils.mkdir_p(bin)
FileUtils.cp file, bin
end
end
The usage for this would be:
desc 'copies from shared/admin to the base server directory'
task :admin do
# Copy admin over
dest = 'www-server/'
src = '../shared/admin'
trim = "admin/"
copy_to dest, src, trim
end
The trim variable is there to make sure files are copied to the appropriate directories. In this case files in admin are copied directly to www-server without an admin subdirectory.
I, naively, tried adding "htaccess" to the EXT_ALLOWED array, but that failed.
I have also followed some items online, but most have to do with Octopress which does not solve the problem.
The .htaccess file is in ../shared/admin and needs to end up in www-server/, can I make that happen within this function? Or do I need to write something specifically for file names beginning with dots?
In this case, looking for a quick and dirty (yes...I feel dirty doing it this way!) option, I wrote a function which specifically looks for the .htaccess file in a particular directory:
def copy_htaccess(src, dest)
files = Dir.glob("#{src}/.*")
files.each do |file|
filename = File.basename(file)
if filename == ".htaccess"
puts "copying #{file} to #{dest}"
FileUtils.mkdir_p(dest)
FileUtils.cp file, dest
end
end
end
With the usage being performed this way:
desc 'copies the .htaccess file from one root to the web root'
task :htaccess do
src = '../shared/admin'
dest = 'www-server/'
copy_htaccess src, dest
end
Here I am able to use Dir.glob() to list all files starting with a ., then test for the .htaccess file and perform the copying.
I will be looking into ways to modifying the single copy function to make this cleaner, if possible. Perhaps this can be done by globbing the directory and adding the files starting with . to the files array.
EDIT: Rather than creating an additional function I found that I could just push the .htaccess file's information onto the end of the files array in the original copying function, after first checking if it exists in the source directory:
if File.file?("#{src}/.htaccess")
files.push("#{src}/.htaccess")
end
Making the whole function as shown below:
def copy_to(dest, src, trim="")
files = FileList.new()
EXT_ALLOWED.each {|ext| files.include "#{src}/**/*.#{ext}"}
if File.file?("#{src}/.htaccess")
files.push("#{src}/.htaccess")
end
files.each do |file|
dir = File.dirname(file)
filename = File.basename(file)
trimming = "/shared/" + trim + "(.*)"
path = dir.match(trimming)
if path == nil || dest == path[1] + '/'
bin = dest
else
bin = File.join(dest, path[1] + '/')
end
puts "copying #{file} to #{bin}"
FileUtils.mkdir_p(bin)
FileUtils.cp file, bin
end
end
Note that I am using .file? to test for an actual file where .exists? can return a directories truthiness. In the end you can use either method depending on your situation.
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.
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.
I want to search my filesystem for any files with the extension .template.
The below works fine for everything except .htaccess.template
FileList.new(File.join(root, '**', '*.template')).each do |file|
# do stuff with file
end
because windows doesn't like nameless files, grrrr
How do I make this work on Windows? This code works fine on Linux....
How about
Dir.glob([".*.template", "*.template"])
Assuming that FileList here is the FileList class from rake then the problem is in Ruby's underlying Dir class (which is used by FileList) not matching files starting with . for the * wildcard. The relevant portion of rake.rb is
# Add matching glob patterns.
def add_matching(pattern)
Dir[pattern].each do |fn|
self << fn unless exclude?(fn)
end
end
Below is an ugly hack that overrides add_matching to also include files starting with . Hopefully someone else will be along to suggest a more elegant solution.
class Rake::FileList
def add_matching(pattern)
files = Dir[pattern]
# ugly hack to include files starting with . on Windows
if RUBY_PLATFORM =~ /mswin/
parts = File.split(pattern)
# if filename portion of the pattern starts with * also
# include the files matching '.' + the same pattern
if parts.last[0] == ?*
files += Dir[File.join(parts[0...-1] << '.' + parts.last)]
end
end
files.each do |fn|
self << fn unless exclude?(fn)
end
end
end
Update: I have just tested this on Linux here and the files starting with . are not included either. e.g. If I have a directory /home/mikej/root with 2 subdirectories a and b where each contains first.template and .other.template then
Rake::FileList.new('home/mikej/root/**/*.template')
=> ["/home/mikej/root/a/first.template", "/home/mikej/root/b/first.template"]
so I would double check the behaviour on Linux and verify that there isn't something else causing the difference in behaviour.