Ruby: How to Calculate a Path Relative to Another One

Ruby: How to calculate a path relative to another one?

Use Pathname#relative_path_from:

require 'pathname'

first = Pathname.new '/first/path'
second = Pathname.new '/second/path'

relative = second.relative_path_from first
# ../../second/path

first + relative
# /second/path

Converting an absolute path to a relative one

I believe Pathname#relative_path_from is what you're looking for. See this answer I gave to another question.

require 'pathname'

first = Pathname.new '/first/path'
second = Pathname.new '/second/path'

relative = second.relative_path_from first
# ../../second/path

first + relative
# /second/path

Relative path to your project directory

You can get current directory (directory of current file) with this

File.dirname(__FILE__)

You can then join it with relative path to the root

File.join(File.dirname(__FILE__), '../../') # add proper number of ..

Or you can use expand_path to convert relative path to absolute.

ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', File.dirname(__FILE__))

Or you can calculate relative path between two dirs.

require 'pathname'; 
puts Pathname.new('/').relative_path_from(Pathname.new('/some/child/dir/')).to_s
# => ../../..

get relative paths to files in ruby

You can use Dir.glob:

Starting with Ruby 2.5 there's a base argument:

cpp_file_paths = Dir.glob('**/*.cpp', base: 'FolderA')
#=> ["a.cpp", "b.cpp", "c.cpp", "FolderB/trial.cpp", "FolderB/trial1.cpp", "FolderC/srcFolder/anothercppfile.cpp"]

For older Rubies, you can chdir into the base directory:

Dir.chdir('FolderA') do
cpp_file_paths = Dir.glob('**/*.cpp') #=> ["a.cpp", "b.cpp", "c.cpp", "FolderB/trial.cpp", "FolderB/trial1.cpp", "FolderC/srcFolder/anothercppfile.cpp"]
end

Note that the paths are relative, i.e. they don't start with a /. Passing a block to chdir ensures that the current directory is restored afterwards (thanks Arup Rakshit).

Opening relative paths from gem

Short answer

If I understand it correctly, you don't need to change anything.

Inside app.rb and your gem, relative paths will be understood relatively to Dir.pwd.

If you run ruby app.rb from inside /home/user/my_app :

  • Dir.pwd will be /home/user/my_app
  • both app.rb and my_gem will look for 'data.txt' inside /home/user/my_app.

Useful methods, just in case

Dir.chdir

If for some reason Dir.pwd isn't the desired folder, you could change directory :

Dir.chdir('/home/user/my_app') do
# relative paths will be based from /home/user/my_app
# call your gem from here
end

Get the directory of current file :

__dir__ will help you get the directory :

Returns the canonicalized absolute path of the directory of the file
from which this method is called.

Get the current file :

__FILE__ will return the current file. (Note : uppercase.)

Concatenate file paths :

If you need to concatenate file paths, use File.expand_path or File.join. Please don't concatenate strings.

If you don't trust that the relative path will be correctly resolved, you could send an absolute path to your method :

my_gem.load_from_file(File.expand_path('data.txt'))

Determine if one path is under another path in Ruby?

Another way:

def child?(root, target)
raise ArgumentError, "target.size=#{target.size} < #{root.size} = root.size"\
if target.size < root.size

target[0...root.size] == root &&
(target.size == root.size || target[root.size] == ?/)
end

root = "/root/app"

p child?(root, "/root/app/some/path") # => true
p child?(root, "/root/apple") # => false
p child?(root, "/root/app") # => true
p child?(root, "/root") # => ArgumentError: target.size = 5 < 9 = root.size

Find the relative depth of child folder

The most simple way to solve this will look like:

parent = '/home/user/path/parent/'
child = '/home/user/path/parent/g/g/child/'

# Split the paths
child_chunks = child.split(File::SEPARATOR).reject(&:empty?)
parent_chunks = parent.split(File::SEPARATOR).reject(&:empty?)

# Find the diff between the two lists
puts (child_chunks - parent_chunks).length # => 3

The better way though would be to use Pathname helper:

require 'pathname'

parent = Pathname.new('/home/user/path/parent/')
child = Pathname.new('/home/user/path/parent/g/g/child/')

puts child.relative_path_from(parent).to_s.split(File::SEPARATOR).length # => 3


Related Topics



Leave a reply



Submit