Copy folder and its content in ruby - ruby

My project structure looks like this:
Project_root
|__Templates
| |__Report_Template
|
|__Product
|__product.rb
What code should I write in product.rb in order to copy Report_Template folder and its content into Product folder?
I tried to use FileUtils.cp_r, but then I will have to give full path of source folder and if in a future I move Project_root, there will be issues.

As Kenney says, you can get the path from which the program started, in __dir__. Here I use Pathname class for easier path manipulation, but it is entirely optional (you can use File#join etc. just as well):
require 'pathname'
templates_pathname = Pathname.new(__dir__) + "../Templates/Report_Template"
# optional:
templates_path = templates_pathname.realpath.to_s
Pathname#realpath will give you the absolute path, if you need it; but FileUtils#cp_r will happily accept a Pathname (i.e. templates_pathname above), and won't mind it's not absolute.

Related

Relative paths from inside a file: `../` --> parent directory vs `./` --> file itself?

Suppose file_a and file_b live in the same directory, and file_a contains a require statement that requires file_b. The way to do this seems to be like so:
require File.expand_path('../file_b', __FILE__)
But kind of expected it to look like this instead:
require File.expand_path('./file_a', __FILE__)
I played around with it and sure enough, the ./ version doesn't work and the ../ version does. The ./ version returns a path like path/to/file_a/file_b. Is the concept that code inside file_a lives inside that file, much like file_a "lives" inside it's parent directory? I feel like I just answered my own question, but want to make sure I'm understanding this right.
File.expand_path(file_name [, dir_string] ) -> abs_file_name
Converts a pathname to an absolute pathname. Relative paths are referenced from the current working directory of the process unless dir_string is given, in which case it will be used as the starting point.
File.expand_path treats the second (optional) parameter as dir_string, it doesn't care whether it is actually a directory or not. So it's your job to make sure the second parameter passed in be a path to a directory.
If you want preserve the ./file_a part, you may change the second parameter passed in:
require File.expand_path('./file_a', File.dirname(__FILE__))

If I have the name of a file, how do I search a folder for a file that contains that filename?

I have an image with the filename media_httpfarm3static_mAyIi.jpg.
I would like to search the parent folder and all subfolders of that parent folder for a file that contains that name - it doesn't have to be the EXACT name, but must contain that string.
E.g. this file should be returned: 11605730-media_httpfarm3static_mAyIi.jpg
So this is a 2-part question:
How do I achieve the above?
Once I have the file, how do I return the path for that file?
Use Dir::[] and File::absolute_path:
partial_name = "media_httpfarm3static_mAyIi.jpg"
Dir["../**/*#{partial_name}"].each do |filename|
puts File.absolute_path(filename)
end
This uses the glob "../**/*media_httpfarm3static_mAyIi.jpg" (go up one directory, then search all sub directories (recursively), for any file ending in the partial string "media_httpfarm3static_mAyIi.jpg". The relative paths are then returned in an Array.
You can use Array#each, Array#map, etc. to convert this into what you need. To convert a relative path, into an absolute path, just pass it to File::absolute_path.
Once you have the absolute path, you can use it to open the file, read the file, etc.
On File Paths
The glob "../**/*media_httpfarm3static_mAyIi.jpg" is relative to the current working directory. Normally, this is the directory from which the program was run. Not the directory of the source file. This can change using various utilities to change it.
To always use a glob relative to the source code file, try:
Dir[File.expand_path('../**/*#{partial_name}', __FILE__)]
You can also use:
Dir[File.join(__dir__, "..", "**", "*#{partial_name}")]
Note: __dir__ was added in Ruby 2.0. For older versions of ruby use File.dirname(__FILE__)
In the first code sample File::absolute_path was used. In the last sample File::expand_path is used. In most situations these can be used interchangeably. There is a minor difference, per the documentations:
File::absolute_path
Converts a pathname to an absolute pathname. Relative paths are
referenced from the current working directory of the process unless
dir_string is given, in which case it will be used as the starting
point. If the given pathname starts with a “~” it is NOT expanded, it
is treated as a normal directory name.
File::expand_path
Converts a pathname to an absolute pathname. Relative paths are
referenced from the current working directory of the process unless
dir_string is given, in which case it will be used as the starting
point. The given pathname may start with a “~”, which expands to the
process owner’s home directory (the environment variable HOME must be
set correctly). “~user” expands to the named user’s home directory.

How do I load files from a specific relative path in Ruby?

I'm making a gem for internal use. In it, I load some YAML from another directory:
# in <project_root>/bin/magicwand
MagicWand::Configuration::Initializer.new(...)
# in <project_root>/lib/magicwand/configuration/initializer.rb
root_yaml = YAML.load_file(
File.expand_path("../../../../data/#{RootFileName}", __FILE__))
# in <project_root>/data/root.yaml
---
apple: 100
banana: 200
coconut: 300
I'd rather not depend on the location of data/root.yaml relative to initializer.rb. Instead, I'd rather get a reference to <project_root> and depend on the relative path from there, which seems like a smarter move.
First, is that the best way to go about this? Second, if so, how do I do that? I checked out the various File methods, but I don't think there's anything like that. I'm using Ruby 1.9.
Right now, I create a special constant and depend on that instead:
# in lib/magicwand/magicwand.rb
module MagicWand
# Project root directory.
ROOT = File.expand_path("../..", __FILE__)
end
but I'm not sure I like that approach either.
If there's a main file you always run you can use that file as a reference point. The relative path (between the current directory and) of that file will be in $0, so to get the relative path to data/root.yaml (assuming that is the relative path between the main file and root.yaml) you do
path_to_root_yaml = File.dirname($0) + '/data/root.yaml'

File paths in Ruby

So I want to make a file path relative to the directory it is in, in Ruby.
I have a project, and I want it to be able to find the file no matter what directory the project is unzipped into. (Say the code is run on different machines, for example) I can't figure it out for the life of me.
It seems for requires that I can do this:
require File.dirname(__FILE__) + '/comparison'
What can I do for a file that is in a different directory than my src folder?
Instead of listing,
file = 'C:/whole path/long/very_long/file.txt'
I'd like to say:
file = 'file.txt'
or
file = File.helpful_method + 'file.txt'
file = File.join(File.dirname(__FILE__), '..', 'another_dir', 'file.txt')
Replace '..', 'another_dir' with the relative path segments that reach 'file.txt'.
If you're running Ruby 1.9.2 or later, you can use require_relative instead:
require_relative '../somewhere/file.rb'
This doesn't solve the general problem of referring to files by their relative path, but if all you're doing is requiring the file, it should work.

RubyZip - files from different directories have path in zip

I'm trying to use RubyZip to package up some files. At the moment I have a method which happily zips on particular directory and sub-directories.
def zip_directory(zipfile)
Dir["#{#directory_to_zip}/**/**"].reject{|f| reject_file(f)}.each do |file_path|
file_name = file_path.sub(#directory_to_zip+'/','');
zipfile.add(file_name, file_path)
end
end
However, I want to include a file from a completely different folder. I have a the following method to solve this:
def zip_additional(zipfile)
additional_files.reject{|f| reject_file(f)}.each do |file_path|
file_name = file_path.split('\\').last
zipfile.add(file_name, file_path)
end
end
While the file is added, it also copies the directory structure instead of placing the file at the root of the folder. This is really annoying and makes it more difficult to work with.
How can I get around this?
Thanks
Ben
there is setting to include (or exclude) the full path for zip libraries, check that setting
Turns out it was because the filename had the pull path in. My split didn't work as the path used a / instead of a . With the path removed from the filename it just worked.

Resources