Can `require` be replaced by `require_relative`? - ruby

Given that there is require_relative, which accepts relative as well as absolute paths, is there any reason to particularly use require, which accepts only absolute paths? Can all require be replaced by require_relative?
Edit
Sergio Tulentsev noticed me that require_relative cannot be used for gems. Why is it designed so? What problem would arise if require_relative were merely a superset of require and require be deprecated? Or, is it just because require_relative appeared later than the incorporation of gems into the Ruby core system, and the author of require_relative did not do modification to the code around the gems system?

require 'json'
require can be used also to require code from gems and require_relative can't do that. Ergo, the latter can not be a replacement for the former.
RE: edit
I think the answer to your additional question is: code complexity. require has additional power to search lib path. require_relative substitutes base path and so on. Unifying all features in the same function would probably overcomplicate it. The more complex code is, the more likely it is to have bugs. That's my guess.

Can all require be replaced by require_relative?
No: it's the other way around: require_relative is a convenient subset of require:
require_relative('path')
equals:
require(File.expand_path('path', File.dirname(__FILE__)))
if __FILE__ is defined, or it raises LoadError otherwise.
is there any reason to particularly use require?
You can only do the following operations with require and not require_relative:
requires relative to the current directory (not current file): require './a.rb'
uses the search path ($LOAD_PATH) to require: require 'a.rb'
This is not possible with require_relative because the docs say that path search only happens when "the filename does not resolve to an absolute path" (i.e. starts with / or ./ or ../), which is always the case for File.expand_path.
I have explained this in more detail at: https://stackoverflow.com/a/26565471/895245

Ruby has the -I flag for including directories for the purpose of require lookup. So if you either push the folder to the $: variable (which is to say ENV['PATH']) or use the Isome_lib at the command line then require can be used without specifying the base folder location for that file.
require_relative is used for a different purpose, which is to use the __FILE__ directory location to refer to location for the file you are wanting to require. So it could be used to require files from a gem, but it could be more work to do so. Realizing that gems can be 'vendored' and located in a directory that is very close, or even contained, by your application. Normally, gems that are installed are placed into the $PATH for ease of lookup, so this is not normally needed.
Requiring gems using require_relative is an anti-pattern, though, as it should probably be required as a gem in your Gemfile where you can specify the source of the file and the environment will be loaded for the application using Bundler, perhaps.

Related

Why doesn't Ruby 'require' allow relative paths?

This SO post sort of answers the question by claiming that require will only search relative to the path that the script was run in. But this really does not appear to be true. I will elaborate.
I created a quick C extension and compiled it to mytest.so. Then, in the same directory I fired up irb.
irb(main):009:0> require 'mytest.so'
LoadError: cannot load such file -- mytest.so
This is expected because the claim in the other answer is that require is searching relative to where irb was run from. In my case that would be /usr/bin/irb. So I tried the required_relative solution from the other question:
irb(main):006:0> require_relative './mytest.so'
LoadError: cannot infer basepath
No luck. And FYI - mytest.so is tab-completing here so irb is clearly aware it is in the current working directory. Furthermore, I can easily prove this:
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 final test, I will assume that irb is being executed in /usr/bin despite the evidence pointing against that.
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 greatly appreciate if anyone can shed some light on what is going on with require?
BTW, I am aware I can solve this issue by giving the exact file path. This question is about trying understand what is happening beneath the surface.
require '/home/mike/Documents/ruby_test/mytest.so' # this works
tl;dr: IRB is special and has some odd rules. Ruby in general works just fine with relative paths.
require will search the load path (which you can see by inspecting $: or $LOAD_PATH). This will not include the directory that you launched IRB from:
> $:
=> ["/usr/local/rvm/rubies/jruby-head/lib/ruby/2.2/site_ruby", "/usr/local/rvm/rubies/jruby-head/lib/ruby/stdlib"]
So there's no joy there, unless you explicitly add your directory to the load path. This is what Rubygems and Bundler spends most of their time doing - they manage the load paths for gems so you don't have to worry about it. However, this doesn't help you with single files.
Additionally, require_relative will search from the directory that __FILE__ is in, but in IRB, this is a non-directory (irb) value! This is why you get the "can't infer basepath" issue when trying require_relative from IRB; since the currently executing file, __FILE__, isn't a proper path, require_relative can't figure out where to start from.
When you are not running from IRB, this isn't really an issue; require_relative 'mytest.so' should work just fine when you execute it in a script, since the currently-executing script will populate __FILE__. That is, if you have loader.rb and mytest.so and execute loader via ruby loader.rb, require_relative should work just fine.
If you want to run this in IRB, consider something like:
require "#{__dir__}/mytest.so"
which will expand out to the current working directory, which should by default be the directory you've launched it from. I would recommend that you not do this in a script, though, since it depends on __dir__ not having been changed, and that may be difficult to guarantee.
From the documentation:
Loads the given name, returning true if successful and false if the
feature is already loaded.
If the filename does not resolve to an absolute path, it will be
searched for in the directories listed in $LOAD_PATH ($:).
If the filename has the extension “.rb”, it is loaded as a source
file; if the extension is “.so”, “.o”, or “.dll”, or the default
shared library extension on the current platform, Ruby loads the
shared library as a Ruby extension. Otherwise, Ruby tries adding
“.rb”, “.so”, and so on to the name until found. If the file named
cannot be found, a LoadError will be raised.
For Ruby extensions the filename given may use any shared library
extension. For example, on Linux the socket extension is “socket.so”
and require 'socket.dll' will load the socket extension.
The absolute path of the loaded file is added to $LOADED_FEATURES
($"). A file will not be loaded again if its path already appears in
$". For example, require 'a'; require './a' will not load a.rb again.
The answer to your question is that it simply isn't designed to.
You could use require_relative if you want to require files relative to your files' path.
You could add your projects library folder to the $LOAD_PATH to get the functionality you are asking for, that is, require 'my_library'
In IRB for loading local files, you may want to use load instead, as it gives you the ability to load file/library multiple times, where require will load the file/library only once. The __FILE__ variable works in IRB just like it should. It is the identification of the file (in this case the open process) in which the variable is invoked.

ruby require './blahblah.rb' vs require File.expand_path('../blahblah', __FILE__)

What is the difference between
require 'blahblahlblah.rb'
vs
require './blahblah.rb'
vs
require File.expand_path('../blahblah', __FILE__)
I see both of them being used. Wondering what's better, and under what circumstance is one better than the other.
Thanks!
require blaba.rb is searching to your default gem path to load the file, which depends on the ruby version you are using. For example RVM will search in $HOME/.rvm/rubies/... while a system wide ruby will search in the distribution's default path. Note that this is where gems are located, but you could manually add a library say mylibrary.rb in the same path and use it in any of your programs. However, that's an awful thing to do, it's a much cleaner procedure to create gems and install them in your system.
require ./blabla.rb loads a file that is sitting in your working directory. You could add the full path like require /home/username/library/myproject/models/sample.rb. It will work just about the same. In the UNIX-like world the ./ sign means current directory. This solution is often used in irb to load say a rails Model i.e. users.rb into irb or pry and work with it. To give you an example in a shell environment (if you are familiar with UNIX shells, you'll figure it out):
GreyJewel ~ » ls myports.txt
myports.txt
GreyJewel ~ » ls ./myports.txt
./myports.txt
The third solution require File.expand_path('../sample.rb', __FILE__) is used in programs, because it explicitly creates a full path using as an anchor the directory which the file holding the line sits, which is a much more secure approach compared to require ./sample.rb. Note that when you load a ruby file, you can omit the file extension .rb.
Hope this clarifies a bit the situation.

Can I determine which paths RubyGems added to the load path to make my command line app work?

In a gemspec, I can specify the require_paths, which represent paths I want to be able to require from at runtime. These get put into the $LOAD_PATH by RubyGems.
My question is: is there a way I can determine what these paths are at runtime? Can I examine elements of $LOAD_PATH and know which ones were added just for my gem?
Update: Ultimately, I would like to dynamically load ruby files from inside the gem, e.g.
load_from 'foo/bar'
And have that find $MY_GEMS_LIB_DIR/foo/bar/*.rb. I can certainly go through the entire $LOAD_PATH looking for foo/bar, but I'd rather restrict it just to the gem.
I don't know if I understood your needing (my english is bad :-/ ); anyway, if the problem is to determine the directories that will be loaded when you require a gem you can use Gem::Specification.lib_dirs_glob:
Gem::Specification.find_by_name('irbtools').lib_dirs_glob
#=> "/home/my_user/.rvm/gems/ruby-1.9.3-p125/gems/irbtools-1.2.2/lib"
Gem::Specification.find_by_name('xyz').lib_dirs_glob
# raises a Gem::LoadError
So a possible implementation of load_from could be:
def load_from(gem_name, path)
path_to_load = File.join(Gem::Specification.find_by_name(gem_name).lib_dirs_glob, path)
Dir.glob(path_to_load).each(&method(:load))
end
Trying to load Thor::CoreExt :
Thor::CoreExt #=> NameError: uninitialized constant Thor
load_from 'thor', 'thor/core_ext/*.rb'
Thor::CoreExt #=> Thor::CoreExt
This works on my machine with ruby 1.9.3 and gem 1.8.21 .
If I understand you correctly, this should do (Ruby 1.9.3):
before = $LOAD_PATH.dup
require 'the_gem'
added_paths = $LOAD_PATH - before
Of course, this will include the paths added by the dependencies.
You can use the $: global in irb. There is also the gem which command which gives you a library path, but I'm not sure if that includes exactly what you want.
Looks like Gem.find_files may help you.

Why do I have to interpolate Dir.pwd when using require or autoload in Ruby?

This seems to be a Ruby 1.9 problem for me, but anytime I try to require or autoload source with something like require "lib/mylibrary" Ruby fails with a "No such file to load" error. I always have to interpolate Dir.pwd thusly: require "#{Dir.pwd}/lib/mylibrary"
I see source everywhere that doesn't need to look up the present working directory to include source files. What am I missing?
The $LOAD_PATH variable determines the places that Ruby will check for files to load. As of Ruby 1.9, the current directory is not in the load path by default, but you can use the require_relative method to require files relative to the current working directory.
See this question for more details.

Ruby - working with `require` command

I am editing a gem in which there are the usual require commands, pointing at the loaded gem (the gem I'm talking about is called nirvana, and the files in it contain require 'nirvana', require 'nirvana/shell' and so on).
When I use the bin-file of the application (/mypath/nirvana/bin/nirvana), I want the require 'nirvana' command written inside it to point to the files in the local fork of that gem (the ones I am editing), and I want not to load the original nirvana gem, that is installed with the classic gem install.
I don't want to substitute all the require 'nirvana' commands with
require File.dirname(File.expand_path(__FILE__)) + '/../lib/nirvana.rb'
... this would resolve my problem, but it's ugly! Is there a way to do not load nirvana gem, and to make require 'nirvana' load my libraries (maybe adding them in the $LOAD_PATH...) ?
You might be running into the require vs. require_relative conundrum in 1.9+.
require is good for loading a gem that is loaded via the normal gems paths, i.e., installed into Ruby's space.
require_relative is good for loading relative to a particular file, for instance, if you're loading a module you wrote and its in the same or a sub-directory or relative directory of yours.
`require_relative 'some/sub/dir/to/file'`
You should only be 'requiring' nirvana.rb once, if you're doing so from your gems binary executable. So this line only needs to appear once. It's quite common to see it appear in these files.
Do note your example can be better written as
require File.expand_path('../lib/nirvana.rb', __FILE__)
As File::expand_path takes an optional second argument (a directory String).
A lot of authors will also shift the lib directory into the $LOAD_PATH before executing the binary so the local files are loaded before attempting to load any installed gems.
If you're using rvm, have a look at gemsets. You can create a gemset that doesn't have the nirvana gem installed, then when you require 'nirvana' you'll only get your local libraries required, as there isn't a nirvana gem to include.
(I'm assuming you're using ruby 1.9, as if you're using 1.8 you could just omit require 'rubygems'.)

Resources