Meaning of __FILE__ = $0 as given on ruby-lang.org - ruby

Short Tutorial on Ruby-Lang says the following:
if __FILE__ == $0
__FILE__ is the magic variable that contains the name of the current file. $0 is the name of the file used to start the program. This check says “If this is the main file being used…”
This allows a file to be used as a library, and not to execute code in that context, but if the file is being used as an executable, then execute that code.
But the bold lines above are not clear, since I am new to Ruby.

__FILE__ returns the name of the current file. $0 returns the name of the script currently being executed.
Imagine you have this file
# foo.rb
if __FILE__ == $0
puts 'foo'
else
puts 'bar'
end
and you run ruby foo.rb from the command line then it will output foo because both – __FILE__ and $0 – return "foo.rb".
But if you have the same foo.rb file and require it in another bar.rb file like this
# bar.rb
require 'foo'
and you run the other file ruby bar.rb then the script will print bar because __FILE__ would still return "foo.rb" but $0 would now return "bar.rb".

Suppose your file is foo.rb, and defines a Foo class. The file can be used in one of two ways.
The first, which you're already familiar with, is to include it in another file or in IRB. It'll go something like:
# in otherfile.rb
require 'foo'
foo = Foo.new
The if __FILE__ == $0 line is for the second use case, where you make the file executable and call it directly from the shell. It'll go something like
# in the shell
./foo.rb
# alternatively: ruby foo.rb

if __FILE__ == $0
#Do something
else
#Do something else
end
This means, the code inside this block will be executed only if you run the file explicitly not if you load it via require or include.

Related

Ruby: How to get the correct full path of a source file after a chdir?

See the following example:
puts __FILE__ #test.rb
puts File.expand_path(__FILE__) #C:/TEMP/test.rb
Dir.chdir('..')
puts __FILE__ #test.rb
puts File.expand_path(__FILE__) #C:/test.rb
After a (global) chdir the expand_path returns a wrong result.
How can I get the correct result?
I tried to use the 2nd parameter of File.expand_path:
puts File.expand_path(__FILE__, 'temp') #C:/TEMP/test.rb
puts File.expand_path(__FILE__, 'c:/temp') #C:/TEMP/test.rb
But to use it, I must know the path of __FILE__.
The command require_relative seems to ignore all chdir-actions. So I have the hope, there is a way to get the 'real' directory of a file.
Remarks:
I know the block-version of Dir.chdir - for my specific task i can#t use it.
My actual solution: I store the fullpath before I change the directory (I could also store Dir.pwd before I change the directory).
__FILE__ builtin is an instance of String class:
puts __FILE__.class
# ⇒ String
That means you should not expect any voodoo magic from it. It stores the relative path, this file was loaded at.
ruby C:\TEMP\test.rb # ⇒ __FILE__ == 'C:\TEMP\test.rb'
cd C:\TEMP && ruby test.rb # ⇒ __FILE__ == 'test.rb'
In ruby 2.0 was new builtin __dir__ introduced. It looks like what you are looking for, in case 2.0-only solution is OK with you.

What can I check to see if rspec is running?

So some ruby libraries double as executables, using the trick:
if __FILE__ == $0
# act as executable...
end
I'm writing a mini-library, and was wondering if there were a similar trick I could use to embed my rspec tests in the file. Some sort of constant or something I could check, like:
if RSPEC_TARGET == $0
describe 'Foo' do
it "should foo" #...
end
end
ARGV contains the name of the script. I use the following in my modules:
if ARGV.include? File.basename(__FILE__)
# unit tests here
end

rspec test ruby script

say we have a ruby file.rb like:
if __FILE__ == $0 then
if ARGV[0] == 'foo'
puts "working"
# Dir.chdir(../)
v = Someclass.new
v.do_something
end
end
it suppose to print working only if the file was triggered like ruby file.rb foo.
My question: how can that kind of stuf be tested within rspec?
My try is below. The file ran but not in the scope of rspec test:
Dir expected :chdir with (any args) once, but received it 0 times
it 'should work' do
FILE = File.expand_path('file.rb')
RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
#v = Someclass.new
Someclass.should_receive(:new).and_return #v
#v.should_receive(:do_something)
`#{RUBY} #{FILE} foo`
end
Backticks runs new shell, executes command, and returns result as a string. Thats why it runs outside your scope. Backticks does not care about contents of your script: ruby, bash, or something else.
chdir, of course, applied only to this new shell, so there seems no way to check you sample script for directory changing (except of tracing system calls). Maybe some 'real' script will do something, output more, thus providing more possibilities to check it.

Can I write Ruby code that is executed only when my script is run, but not when it is required?

I want to write a Ruby script something like this:
class Foo
# instance methods here
def self.run
foo = Foo.new
# do stuff here
end
end
# This code should only be executed when run as a script, but not when required into another file
unless required_in? # <-- not a real Kernel method
Foo.run
end
# ------------------------------------------------------------------------------------------
I want to be able to unit test it, which is why I don't want the code outside of the class to run unless I execute the script directly, i.e. ruby foo_it_up.rb.
I know I can simply put the Foo class in another file and require 'foo' in my script. In fact, that is probably a better way to do it, just in case Foo's functionality is needed somewhere else. So my question is more academic than anything, but I'd still be interested in knowing how to do this in Ruby.
This is usually done with
if __FILE__ == $0
Foo.run
end
but I prefer
if File.identical?(__FILE__, $0)
Foo.run
end
because programs like ruby-prof can make $0 not equal __FILE__ even when you use --replace-progname.
$0 refers to the name of the program ($PROGRAM_NAME), while __FILE__ is the name of the current source file.

What does __FILE__ mean in Ruby?

I see this all the time in Ruby:
require File.dirname(__FILE__) + "/../../config/environment"
What does __FILE__ mean?
It is a reference to the current file name. In the file foo.rb, __FILE__ would be interpreted as "foo.rb".
Edit: Ruby 1.9.2 and 1.9.3 appear to behave a little differently from what Luke Bayes said in his comment. With these files:
# test.rb
puts __FILE__
require './dir2/test.rb'
# dir2/test.rb
puts __FILE__
Running ruby test.rb will output
test.rb
/full/path/to/dir2/test.rb
The value of __FILE__ is a relative path that is created and stored (but never updated) when your file is loaded. This means that if you have any calls to Dir.chdir anywhere else in your application, this path will expand incorrectly.
puts __FILE__
Dir.chdir '../../'
puts __FILE__
One workaround to this problem is to store the expanded value of __FILE__ outside of any application code. As long as your require statements are at the top of your definitions (or at least before any calls to Dir.chdir), this value will continue to be useful after changing directories.
$MY_FILE_PATH = File.expand_path(File.dirname(__FILE__))
# open class and do some stuff that changes directory
puts $MY_FILE_PATH
__FILE__ is the filename with extension of the file containing the code being executed.
In foo.rb, __FILE__ would be "foo.rb".
If foo.rb were in the dir /home/josh then File.dirname(__FILE__) would return /home/josh.
In Ruby, the Windows version anyways, I just checked and __FILE__ does not contain the full path to the file. Instead it contains the path to the file relative to where it's being executed from.
In PHP __FILE__ is the full path (which in my opinion is preferable). This is why, in order to make your paths portable in Ruby, you really need to use this:
File.expand_path(File.dirname(__FILE__) + "relative/path/to/file")
I should note that in Ruby 1.9.1 __FILE__ contains the full path to the file, the above description was for when I used Ruby 1.8.7.
In order to be compatible with both Ruby 1.8.7 and 1.9.1 (not sure about 1.9) you should require files by using the construct I showed above.

Resources