How to get parent path from filepath? - ruby

Let says I've this path:
path = "D:\Google Drive\Samples\Black Octopus Sound\Leviathan\Drums - Kicks\Lev_Kick_A_003.wav"
what's the smart and clean way to get the parent path from this string/path? i.e.:
D:\Google Drive\Samples\Black Octopus Sound\Leviathan\Drums - Kicks\

Couple of ways of doing it as a pure string, either via a regular expression or using split,pop,join.
path = "D:\\Google Drive\\Samples\\Black Octopus Sound\\Leviathan\\Drums - Kicks\\Lev_Kick_A_003.wav"
items = path.split("\\")
items.pop
result = items.join("\\")
puts result
> D:\Google Drive\Samples\Black Octopus Sound\Leviathan\Drums - Kicks
Note I've replaced "\" with "\\" in all cases to escape the slashes
Or in a one liner:
path.split("\\").reverse.drop(1).reverse.join("\\")
The best way is probably Pathname as per other answers, but if you can't use that then string manipulation should work.

Use Pathname:
require 'pathname'
path = "D:\\Google Drive\\Samples/Drums - Kicks\\Lev_Kick_A_003.wav"
Pathname(path).parent
#=> #<Pathname:D:\Google Drive\Samples\Drums - Kicks>
Pathname correctly handles the specific file path syntax of the given operating system.

Related

get path of a file that lives in desktop using rails

I want to save the path of a file that lives in the desktop but seem that rails can not recognise it with the path specified. I tried this:
def calculate_hash
require 'digest'
file_path = "Users/crs/Desktop/index.xml"
sha1 = Digest::SHA1.file file_path
puts "Checksum SHA1: #{sha1.hexdigest}"
end
If I run this method I get an error saying " No such file or directory # rb_sysopen - /Users/crsDesktop/index.xml
"
Please hep me how I can make it recognisable.
Since Users is a root directory you must specify the full path:
file_path = "/Users/crs/Desktop/index.xml"
Notice the / prefix.
Another thing to note is paths use slash (/) and not backslash (\\). If you use a backslash by accident it will "disappear" in most cases:
file_path = "/Users/crs\Desktop/index.xml"
# => "/Users/crsDesktop/index.xml"
Where \D has special meaning within double-quoted strings and means, in this case, literal capital D.

How to navigate two directories up ruby

In Ruby,I have global variable $file_folder that gives me the location of the current config file:
$file_folder = "#{File}"
$file_folder = /repos/.../test-folder/features/support
I need to access a file sitting in a different folder and this folder is two levels up and two levels down. Is there any way to navigate to this target path using the current location?
target path = /repos/..../test-folder/lib/resources
There are two ways to do this (well, there are several, but here are two good ones). First, using File.expand_path:
original_path = "/repos/something/test-folder/features/support"
target_path = File.expand_path("../../lib/resources", original_path)
p target_path
# => "/repos/something/test-folder/lib/resources"
Note that File.expand_path will always return an absolute path, so it's not appropriate if you want to get a relative path back. If you use it, either make sure that the second argument is an absolute path, or, if it's a relative path, that you know what it will expand to (which will depend on the current working directory).
Next, using Pathname#join (which is aliased as Pathname#+):
require "pathname"
original_path = Pathname("/repos/something/test-folder/features/support")
target_path = original_path + "../../lib/resources"
p target_path
# => #<Pathname:/repos/something/test-folder/lib/resources>
You can also use Pathname#parent, but I think it's kind of ugly:
p original_path.parent.parent
# => #<Pathname:/repos/something/test-folder>
p original_path.parent.parent + "lib/resources"
# => #<Pathname:/repos/something/test-folder/lib/resources>
I prefer Pathname because it makes working with paths very easy. In general you can pass the Pathname object to any method that takes a path, but once in awhile a method will balk at anything other than a String, in which case you'll need to cast it as a string first:
p target_path.to_s
# => "/repos/something/test-folder/lib/resources"
P.S. foo = "#{bar}" is an antipattern in Ruby. If bar is already a String you should just use it directly, i.e. foo = bar. If bar isn't already a String (or you're not sure), you should cast it explicitly with to_s, i.e. foo = bar.to_s.
P.P.S. Global variables are a nasty code smell in Ruby. There is almost always a better way to go than using a global variable.

How do I find the location of the gem?

I'm developing a library that provides access to gem metadata, including it's location on the file system. The idea was to let gem authors set it to a relative path from any script:
# $root/example.gemspec
Example::Gem.root '.' # => $root/
# $root/lib/example/gem.rb
Example::Gem.root '../..' # => $root/
Then, the path of the current script would be used to compute the absolute path. My implementation is currently as follows:
def root(relative_to = nil, file = __FILE__)
unless relative_to.nil?
#root = File.expand_path relative_to, File.dirname(file)
end
#root
end
I thought __FILE__ would return the path to the caller's script, but that assumption is wrong.
It worked within the library itself, but broke down when I tried to integrate it with one of my other gems; the generated path was always relative to the support library itself.
How can I implement this without having to pass the current __FILE__ on every call? Otherwise, there isn't much value to be gained; writing root('../..', __FILE__) is almost the same as writing an actual method to do the same thing.
If it's possible to figure out the path without having to specify anything, that would be even better, but I couldn't think of anything. How does Rails do it?
By the way, I'm aware of Gem::Specification#gem_dir, but it always returns paths relative to the installation directory, even if the gem is not actually there, which makes it useless in a development environment.
You can always make use of the backtrace facility provided:
caller.first
It produces an amalgam of file and line but is usually separated by :. I'd be careful to allow for filenames or paths that may contain colon for whatever reason by ignoring the line information but preserving the rest. In other words, do not split but sub:
caller.first.sub(/:\d+:in .*$/, '')

slash and backslash in Ruby

I want to write a application that works in windows and linux. but I have a path problem because windows use "\" and Linux use "/" .how can I solve this problem.
thanks
In Ruby, there is no difference between the paths in Linux or Windows. The path should be using / regardless of environment. So, for using any path in Windows, replace all \ with /. File#join will work for both Windows and Linux. For example, in Windows:
Dir.pwd
=> "C/Documents and Settings/Users/prince"
File.open(Dir.pwd + "/Desktop/file.txt", "r")
=> #<File...>
File.open(File.join(Dir.pwd, "Desktop", "file.txt"), "r")
=> #<File...>
File.join(Dir.pwd, "Desktop", "file.txt")
=> "C/Documents and Settings/Users/prince/Desktop/file.txt"
As long as Ruby is doing the work, / in path names is ok on Windows
Once you have to send a path for some other program to use, especially in a command line or something like a file upload in a browser, you have to convert the slashes to backslashes when running in Windows.
C:/projects/a_project/some_file.rb'.gsub('/', '\\') works a charm. (That is supposed to be a double backslash - this editor sees it as an escape even in single quotes.)
Use something like this just before sending the string for the path name out of Ruby's control.
You will have to make sure your program knows what OS it is running in so it can decide when this is needed. One way is to set a constant at the beginning of the program run, something like this
::USING_WINDOWS = !!((RUBY_PLATFORM =~ /(win|w)(32|64)$/) || (RUBY_PLATFORM=~ /mswin|mingw/))
(I know this works but I didn't write it so I don't understand the double bang...)
Take a look at File.join: http://www.ruby-doc.org/core/classes/File.html#M000031
Use the Pathname class to generate paths which then will be correct on your system:
a_path = Pathname.new("a_path_goes_here")
The benefit of this is that it will allow you to chain directories by using the + operator:
a_path + "another_path" + "and another"
Calling a_path.to_s will then generate the correct path for the system that you are on.
Yes, it's annoying as a windows users to keep replacing those backslashes to slashes and vice-versa if you need the path to copy it to your filemanager, so i do it like his.
It does no harm if you are on Linux or Mac and saves a lot of nuisance in windows.
path = 'I:\ebooks\dutch\_verdelen\Komma'.gsub(/\\/,'/')
Dir.glob("#{path}/**/*.epub").each do |file|
puts file
end

A Ruby method for resolving relative paths while keeping absolute paths?

I am working on writing a rake build scrip which will work cross platform ( Mac OSX, Linux , Windows ). The build script will be consumed by a CI server.
I want the logic of my script to be as follows:
If the path is determined to be relative, make it absolute by making output_path = FOO_HOME + user_supplied_relative_path
If the path is determined to be absolute, take it as-is
I'm currently using Pathname.new(location).absolute? but it's not working correctly on windows.
What approach would you suggest for this?
require 'pathname'
(Pathname.new "/foo").absolute? # => true
(Pathname.new "foo").absolute? # => false
The method you're looking for is realpath.
Essentially you do this:
absolute_path = Pathname.new(path).realpath
N.B.: The Pathname module states that usage is experimental on machines that do not have unix like pathnames. So it's implementation dependent. Looks like JRuby should work on Windows.
There is a built-in function that covers both cases and does exactly what you want:
output_path = File.absolute_path(user_supplied_path, FOO_HOME)
The trick is supplying a second argument. It servers as a base directory if (and only if) the first argument is a relative path.
Pathname can do all that for you
require "pathname"
home= Pathname.new("/home/foo")
home + Pathname.new("/bin") # => #<Pathname:/bin>
home + Pathname.new("documents") # => #<Pathname:/home/foo/documents>
I am not sure about this on windows though.
You could also use File.expand_path if the relative directory is relative to the current working directory.
I checked on Linux and windows and didn't have any issues.
Assuming FOO_HOME is the working directory, the code would be:
output_path = File.expand_path user_supplied_relative_path

Resources