Ruby - adding a directory to $LOAD_PATH - what does it do? - ruby

This is actually a question about this question: Adding a directory to $LOAD_PATH (Ruby)
What happens when you add a directory to the $LOAD_PATH? Is it like adding a script file to the execution context as in JavaScript? (You can access global methods/objects in another files) If not, how do I call the methods of other ruby files in the current directory?

When you add the /Users/you/scripts/ruby directory to the load path, you can use:
require 'example'
instead of:
require '/Users/you/scripts/ruby/example.rb'

Think of the $LOAD_PATH as to being similar to the PATH variable on a operating system. If certain directories are in the LOAD_PATH, you can just write require "some_module".
It's also the reason for being able to require files from the current directory.
By default, the LOAD_PATH no longer includes the current directory . having been removed in Ruby 1.9.2.

Related

What is the conventional purpose of the "lib" directory in Ruby projects?

Take this ruby gem: https://github.com/ai/autoprefixer-rails
The gem's structure includes a "lib", "spec" and "vendor" directory, and some gemfiles in the root directory.
What exactly is the purpose of directories called "lib" in ruby gems?
It looks like "lib" contains all of the ruby code provided by the gem. So does "lib" mean "the source code files that constitute the library?" (It's hard to say if "lib" means "the library," since arguably the gemfiles are "part of the library," too.) Or does it mean something else? And is the meaning of the directory names in ruby gems defined anywhere? Or is directory naming completely arbitrary?
I am trying to determine if the name "lib" has any special significance as I set out to write my own gem. I'd like to know, because if it doesn't, I might like to pick a different name.
The default "require path," i.e. the directory which will by default be added to $LOAD_PATH when the gem is activated, is "lib". See the rubygems specification:
REQUIRE_PATHS=(val)
Paths in the gem to add to $LOAD_PATH when this gem is activated. If
you have an extension you do not need to add "ext" to the require
path, the extension build process will copy the extension files into
“lib” for you.
The default value is "lib"
Usage:
# If all library files are in the root directory...
spec.require_paths = ['.']
The fact the name is chosen as a default implies it is also a good default naming decision to avoid extra configuration and to help navigate newcomers to the gem.

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.

How to I load a gem from source?

I have git cloned a repo from Github, now I want to experiment with it, as in I want to poke around the code and mess with it. I've created a file test.rb that should load this gem, but I want to load my locally checked out version, what's the right way to do this?
Right now I'm just using a bunch of "require_relative 'the_gem_name/lib/file'", which feels wrong.
When you require 'foo' Ruby checks all the directories in the load path for a file foo.rb and loads the first one it finds. If no file named foo.rb is found, and you’re not using Rubygems, a LoadError is raised.
If you are using Rubygems (which is likely given that it is included in Ruby 1.9+), then instead of immediately raising a LoadError all the installed Gems are searched to see if one contains a file foo.rb. If such a Gem is found, then it is added to the load path and the file is loaded.
You can manipulate the load path yourself if you want to ensure a particular version of a library is used. Normally this isn’t something that’s recommended, but this is the kind of situation that you’d want to do it.
There are two ways of adding directories to the load path. First you can do it in the actual code, using the $LOAD_PATH (or $:) global variable:
$LOAD_PATH.unshift '/path/to/the/gems/lib/'
require 'the_gem'
Note that you normally want to add the lib dir of the gem, not the top level dir of the gem (actually this can vary depending on the actual Gem, and it’s possible to need to add more than one dir, but lib is the norm).
The other way is to use the -I command line switch to the ruby executable:
$ ruby -I/path/to/the/gems/lib/ test.rb
This way might be a bit cleaner, as normally you don’t want to be messing with the load path from inside your code, but if you’re just testing the library it probably doesn’t matter much.
Following apneadiving's suggestion in the comments, I created a Gemfile and added this line
source "http://rubygems.org"
gem 'gem_name', path: '~/path/to/gem/source/folder'
Then bundle install, and bundle exec ruby test.rb and it worked.

How to change working directory in JRuby?

I have to run my Ruby script from path that is higher than a script. My Ruby file is in folder lib. I start it in console:
jruby --1.9 -Clib main.rb
but it doesn't work correctly. It changes Dir.pwd, but require doesn't see it and another library DataMapper doesn't see it too.
I know I can add path to be seen by require by -Ilib, but it doesn't fix DataMapper issue and it is ugly I think.
require loads a file from the $LOAD_PATH. If the directory the file you want to load is in is not on the $LOAD_PATH, then require won't find it. If you want to load a file not from the $LOAD_PATH but relative to the position of the currently executing file, you need to use require_relative.
Assuming this is your folder structure
app/other/some_class.rb
app/lib/main.rb
If you navigate to the lib folder
cd app/lib
Then run your main.rb script
jruby main.rb
You can refer to the some_class.rb file in your main.rb script with this line
require "../other/some_class.rb"

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.

Resources