Can a Ruby script tell what directory it’s in? - ruby

Inspired by "Getting the source directory of a Bash script from within", what's the Ruby way to do this?

For newer versions of Ruby, try:
__dir__
For older versions of Ruby (< 2.0), the script being run can be found using:
File.dirname(__FILE__) - relative path; or
File.expand_path(File.dirname(__FILE__)) - the absolute path.
Note: Using __dir__ will return the script path even after a call to Dir.chdir; whereas, using the older syntax may not return the path to the script.

Use __dir__
As of Ruby 2.0, __dir__ is the simplest way to get this. It
Returns the canonicalized absolute path of the directory of the file
from which this method is called.
See the __dir__ documentation, and "Why is __FILE__ uppercase and __dir__ lowercase?".

use __dir__
File.dirname(__FILE__) is not a proper way to get directory where script is stored.
At start working directory and directory with script file is the same, but it may change.
For example:
Dir.chdir('..') do
puts __dir__
puts File.expand_path(File.dirname(__FILE__))
end
for script file stored in /Desktop/tmp running it will give output
/home/mateusz/Desktop/tmp
/home/mateusz/Desktop

ENV["PWD"] seems the simplest way for me under Linux. I don't know of an OS-agnostic way.

Related

Reading files elsewhere in directory w/ Ruby

I've got a project structure as follows:
info.config (just a JSON file w/ prefs+creds)
main.rb
tasks/
test.rb
In both main.rb (at the root of the project), and test.rb (under the tasks folder), I want to be able to read and parse the info.config file. I've figured out how to do that in main.rb with the following:
JSON.parse(File.read('info.config'))
Of course, that doesn't work in test.rb.
Question: How can I read the file from a test.rb even though it's one level deeper in the hierarchy?
Appreciate any guidance I can get! Thanks!
Use relative path:
path = File.join(
File.dirname(File.dirname(File.absolute_path(__FILE__))),
'info.config'
)
JSON.parse(File.read(path))
File.dirname(File.absolute_path(__FILE__)) will give you the directory where test.rb resides. -> (1)
File.dirname(File.dirname(File.absolute_path(__FILE__))) will give you parent directory of (1).
Reference: File::absolute_path, File::dirname
UPDATE
Using File::expand_path is more readable.
path = File.expand_path('../../info.config', __FILE__)
JSON.parse(File.read(path))
What I usually do is:
Create file called environment or similar in your project root. This file has only one purpose - to extend load path:
require 'pathname'
ROOT_PATH = Pathname.new(File.dirname(__FILE__))
$:.unshift ROOT_PATH
Require this file at the beginning of your code. From now on every time you call require, you can use relative_path to you root directory, without worrying where file you are requiring it from is located.
When using File, you can simple do:
File.open(ROOT_PATH.join 'task', 'test.rb')
You can do as below using File::expand_path :
path = File.expand_path("info.config","#{File.dirname(__FILE__)}/..")
JSON.parse(File.read(path))
File.dirname(__FILE__) will give you the path as "root_path_of_your_projet/tasks/".
"#{File.dirname(__FILE__)}/.." will give you the path as "root_path_of_your_projet/". .. means go one level up from the current directory.
File.expand_path("info.config","root_path_of_your_projet/") will give you the actual path to the file as "root_path_of_your_projet/info.config".
You can also use __dir__ instead of File.dirname(__FILE__).
__dir__ : Returns the canonicalized absolute path of the directory of the file from which this method is called.
Hope that explanation helps.

Confusing behavior of File.dirname

I have written a couple of small Ruby scripts for system administration using Ruby 1.9.3. In one script I use:
File.dirname(__FILE__)
to get the directory of the script file. This returns a relative path, however when I call the script from a second script File.dirname returns an absolute path.
Ruby Doc lists an absolute return path in its example whereas I found a discussion on Ruby Forum where a user says dirname should only return a relative path.
I am using the suggested solution from Ruby Forums to use File.expand_path to always get the absolute path like this:
File.expand_path(File.dirname(__FILE__))
but is there a way to make the behaviour of dirname consistent?
UPDATE:
To expand on Janathan Cairs answer, I made two scripts:
s1.rb:
puts "External script __FILE__: #{File.dirname(__FILE__)}"
s0.rb:
puts "Local script __FILE__: #{File.dirname(__FILE__)}"
require './s1.rb'
Running ./s0.rb gives the following output:
Local script __FILE__: .
External script __FILE__: /home/kenneth/Pictures/wp/rip_vault
File.dirname should return an absolute path if given an absolute path, and a relative path if given a relative path:
File.dirname('/home/jon/test.rb') # => '/home/jon'
File.dirname('test.rb') # => '.'
__FILE__ returns the name of the current script, which is therefore a relative path from the current directory. That means you should always use expand_path if you want to get the absolute path with File.dirname(__FILE__).
NB Ruby 2.0.0 introduces the __dir__ constant
If you already upgraded to Ruby 2.0, you can use the new constant
__dir__
otherwise you can use
File.expand_path('..', __FILE__)
which is shorter than
File.expand_path(File.dirname(__FILE__))
File.expand_path documentation

Ruby require path

I have a Ruby code with different classes in a few files. In one file, I start the execution. This file requires my other files.
Is this a good way to start a ruby code?
When I run the code from a symbolic link, for example DIR2/MyRubyCode is a link to the main file DIR1/MyRubyCode.rb, then my requires will fail. I solved the problem by adding the path DIR1 to $LOAD_PATH before the require, but I think there would be much better ways to do it. Do you have any suggestions about that?
If you're using Ruby 1.9 or greater, user require_relative for your dependencies.
require_relative 'foo_class'
require_relative 'bar_module'
If you want to check if a Ruby file is being 'require'ed or executed with 'ruby MyRubyCode.rb', check the __FILE__ constant.
# If the first argument to `ruby` is this file.
if $0 == __FILE__
# Execute some stuff.
end
As far as the require/$LOAD_PATH issue, you could always use the relative path in the require statement. For example:
# MyRubyCode.rb
require "#{File.dirname(__FILE__)}/foo_class"
require "#{File.dirname(__FILE__)}/bar_module"
Which would include the foo_class.rb and bar_module.rb files in the same directory as MyRubyCode.rb.
I know this is an old question, but there is an updated answer to it, and I wanted to post it:
Starting in a more recent version of Ruby (I'm not sure when), you can require files in the same directory by using the following:
require './foo_class'
require './bar_module'
and it'll load files called foo_class.rb and bar_module.rb in the same directory.
For checking if your file is being required or ran normally, check the other answer.

Ruby path management

What is the best way to manage the require paths in a ruby program?
Let me give a basic example, consider a structure like:
\MyProgram
\MyProgram\src\myclass.rb
\MyProgram\test\mytest.rb
If in my test i use require '../src/myclass' then I can only call the test from \MyProgram\test folder, but I want to be able to call it from any path!
The solution I came up with is to define in all source files the following line:
ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(ROOT) and then always use require "#{ROOT}/src/myclass"
Is there a better way to do it?
As of Ruby 1.9 you can use require_relative to do this:
require_relative '../src/myclass'
If you need this for earlier versions you can get it from the extensions gem as per this SO comment.
Here is a slightly modified way to do it:
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "src"))
By prepending the path to your source to $LOAD_PATH (aka $:) you don't have to supply the root etc. explicitly when you require your code i.e. require 'myclass'
The same, less noisy IMHO:
$:.unshift File.expand_path("../../src", __FILE__)
require 'myclass'
or just
require File.expand_path "../../src/myclass", __FILE__
Tested with ruby 1.8.7 and 1.9.0 on (Debian) Linux - please tell me if it works on Windows, too.
Why a simpler method (eg. 'use', 'require_relative', or sg like this) isn't built into the standard lib? UPDATE: require_relative is there since 1.9.x
Pathname(__FILE__).dirname.realpath
provides a the absolute path in a dynamic way.
Use following code to require all "rb" files in specific folder (=> Ruby 1.9):
path='../specific_folder/' # relative path from current file to required folder
Dir[File.dirname(__FILE__) + '/'+path+'*.rb'].each do |file|
require_relative path+File.basename(file) # require all files with .rb extension in this folder
end
sris's answer is the standard approach.
Another way would be to package your code as a gem. Then rubygems will take care of making sure your library files are in your path.
This is what I ended up with - a Ruby version of a setenv shell script:
# Read application config
$hConf, $fConf = {}, File.expand_path("../config.rb", __FILE__)
$hConf = File.open($fConf) {|f| eval(f.read)} if File.exist? $fConf
# Application classpath
$: << ($hConf[:appRoot] || File.expand_path("../bin/app", __FILE__))
# Ruby libs
$lib = ($hConf[:rubyLib] || File.expand_path("../bin/lib", __FILE__))
($: << [$lib]).flatten! # lib is string or array, standardize
Then I just need to make sure that this script is called once before anything else, and don't need to touch the individual source files.
I put some options inside a config file, like the location of external (non-gem) libraries:
# Site- and server specific config - location of DB, tmp files etc.
{
:webRoot => "/srv/www/myapp/data",
:rubyLib => "/somewhere/lib",
:tmpDir => "/tmp/myapp"
}
This has been working well for me, and I can reuse the setenv script in multiple projects just by changing the parameters in the config file. A much better alternative than shell scripts, IMO.

Rake and current directory

How do I get the directory where the rakefile.rb is located?
I want to use this as my root directory to locate everything off.
use __FILE__ to get the file name then you can get the directory from there:
in test.rb
puts __FILE__
output:
/users/foo/test.rb
__FILE__ resolves to the full path of the file it is in.
Use this to get the dir name:
File.dirname(__FILE__)
You can get it by calling application.original_dir method. In task you can achieve application object using application method on task object.
Why not just use Dir.pwd
?
As of Ruby 2 you can use __dir__ instead of File.dirname(__FILE__) to get the directory that contains the current script.
If this is a RoR app your Rakefile.rb should be in your RAILS_ROOT directory. So in any script you can specify file location like
config.load_paths += %W( #{RAILS_ROOT}/extras )

Resources