Why doesn't Ruby require "relative paths"?

This SO post answers the question by claiming that it require

will only search for the path where the script was run. But it doesn't really show... I will clarify.

I created a swift C extension and compiled it to mytest.so

. Then, in the same directory, I ran irb

.

irb(main):009:0> require 'mytest.so'
LoadError: cannot load such file -- mytest.so

      

This is expected because the other answer says it require

does a search as to where it was started irb

. In my case it will be /usr/bin/irb

. So I tried the solution required_relative

from another question:

irb(main):006:0> require_relative './mytest.so'
LoadError: cannot infer basepath

      

Bad luck. And FYI - mytest.so

the tab is displayed here, so irb

clearly knows that it is in the current working directory. Besides, I can easily prove it:

irb(main):004:0> system("pwd")
/home/mike/Documents/ruby_test
# => true
irb(main):005:0> File.expand_path("./")
# => "/home/mike/Documents/ruby_test"

      

Okay, the final test, I will assume that irb

runs in /usr/bin

, despite the evidence to indicate it.

irb(main):011:0> require '../../home/mike/Documents/ruby_test/mytest.so'
LoadError: cannot load such file -- ../../home/mike/Documents/ruby_test/mytest.so

      

I would really appreciate it if anyone can shed some light on what's going on with require

?

BTW, I know I can fix this problem by specifying the exact path to the file. This question is about trying to understand what is happening below the surface.

require '/home/mike/Documents/ruby_test/mytest.so' # this works

      

+3


source to share


2 answers


tl; dr: IRB is special and has some odd rules. Ruby generally works great with relative paths.

require

will look for the download path (which you can see by checking $:

or $LOAD_PATH

). This will not include the directory you ran IRB in:

> $:
 => ["/usr/local/rvm/rubies/jruby-head/lib/ruby/2.2/site_ruby", "/usr/local/rvm/rubies/jruby-head/lib/ruby/stdlib"]

      

So there is no joy there unless you explicitly add your directory to the download path. This is what Rubygems and Bundler spend most of their time on - they manage loading paths for gems, so you don't have to worry about it. However, this will not help you with single files.

In addition, it require_relative

will search from the directory in which it is located __FILE__

, but in IRB this value is not a directory (irb)

! This is why you get a "can not infer basepath" problem when trying require_relative

from IRB; since the current executable is __FILE__

not the correct path, require_relative

cannot determine where to start.



If you are not using IRB, this is not a problem; require_relative 'mytest.so'

should work fine when you execute it in a script, as the currently executable script will fill __FILE__

. That is, if you have loader.rb

both mytest.so

and and execute the bootloader through ruby loader.rb

, it require_relative

should work fine.

If you want to run this on IRB, consider something like:

require "#{__dir__}/mytest.so"

      

which will expand to the current working directory, which by default should be the directory you started it from. I would recommend that you do not do this in a script though, since it does not depend on __dir__

and it can be difficult to guarantee.

+3


source


From the documentation :

Loads the specified name, returning true if successful, and false if the function has already been loaded.

If the file name is not resolved to an absolute path, it will search the directories listed in $ LOAD_PATH ($ :).

If the filename has a ".rb" extension, it is loaded as the source file; if the extension is ".so", ".o", or ".dll" or the default is a shared library extension on the current platform, Ruby loads the shared library as a Ruby extension. Otherwise Ruby tries to add ".rb", ".so", etc. to the name. If a file with the name cannot be found, LoadError will be raised.

For Ruby extensions, the filename specified can use any shared library extension. For example, on Linux the socket extension is "socket.so" and requires "socket.dll" to load the socket extension.

The absolute path of the loaded file is appended to $ LOADED_FEATURES ($). The file will not be loaded again if its path has already appeared in $ ". For example, require 'a'; require './a' will no longer load a.rb.


The answer to your question is that it is simply not intended.



You can use require_relative

if you want the files to be associated with your file.

You can add the project library folder $LOAD_PATH

to to get the functionality you want, i.e.require 'my_library'

In IRB you can use to load local files instead load

, as it gives you the ability to load file / library multiple times, where it require

will load file / library only once. The variable __FILE__

works in IRB just as it should. This is the identification of the file (in this case the open process) in which the variable is called.

0


source







All Articles