Problems with requiring files inside gem [duplicate] - ruby

What is the difference between require_relative and require in Ruby?

Just look at the docs:
require_relative complements the builtin method require by allowing you to load a file that is relative to the file containing the require_relative statement.
For example, if you have unit test classes in the "test" directory, and data for them under the test "test/data" directory, then you might use a line like this in a test case:
require_relative "data/customer_data_1"

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.
This implies that:
require_relative 'a' and require_relative './a' require relative to the current file (__FILE__).
This is what you want to use when requiring inside your library, since you don't want the result to depend on the current directory of the caller.
eval('require_relative("a.rb")') raises LoadError because __FILE__ is not defined inside eval.
This is why you can't use require_relative in RSpec tests, which get evaled.
The following operations are only possible with require:
require './a.rb' requires relative to the current directory
require 'a.rb' uses the search path ($LOAD_PATH) to require. It does not find files relative to current directory or path.
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.
The following operation is possible with both, but you will want to use require as it is shorter and more efficient:
require '/a.rb' and require_relative '/a.rb' both require the absolute path.
Reading the source
When the docs are not clear, I recommend that you take a look at the sources (toggle source in the docs). In some cases, it helps to understand what is going on.
require:
VALUE rb_f_require(VALUE obj, VALUE fname) {
return rb_require_safe(fname, rb_safe_level());
}
require_relative:
VALUE rb_f_require_relative(VALUE obj, VALUE fname) {
VALUE base = rb_current_realfilepath();
if (NIL_P(base)) {
rb_loaderror("cannot infer basepath");
}
base = rb_file_dirname(base);
return rb_require_safe(rb_file_absolute_path(fname, base), rb_safe_level());
}
This allows us to conclude that
require_relative('path')
is the same as:
require(File.expand_path('path', File.dirname(__FILE__)))
because:
rb_file_absolute_path =~ File.expand_path
rb_file_dirname1 =~ File.dirname
rb_current_realfilepath =~ __FILE__

Summary
Use require for installed gems
Use require_relative for local files
require uses your $LOAD_PATH to find the files.
require_relative uses the current location of the file using the statement
require
Require relies on you having installed (e.g. gem install [package]) a package somewhere on your system for that functionality.
When using require you can use the "./" format for a file in the current directory, e.g. require "./my_file" but that is not a common or recommended practice and you should use require_relative instead.
require_relative
This simply means include the file 'relative to the location of the file with the require_relative statement'. I generally recommend that files should be "within" the current directory tree as opposed to "up", e.g. don't use
require_relative '../../../filename'
(up 3 directory levels) within the file system because that tends to create unnecessary and brittle dependencies. However in some cases if you are already 'deep' within a directory tree then "up and down" another directory tree branch may be necessary. More simply perhaps, don't use require_relative for files outside of this repository (assuming you are using git which is largely a de-facto standard at this point, late 2018).
Note that require_relative uses the current directory of the file with the require_relative statement (so not necessarily your current directory that you are using the command from). This keeps the require_relative path "stable" as it always be relative to the file requiring it in the same way.

From Ruby API:
require_relative complements the
builtin method require by allowing you
to load a file that is relative to the
file containing the require_relative
statement.
When you use require to load a file,
you are usually accessing
functionality that has been properly
installed, and made accessible, in
your system. require does not offer a
good solution for loading files within
the project’s code. This may be useful
during a development phase, for
accessing test data, or even for
accessing files that are "locked" away
inside a project, not intended for
outside use.
For example, if you have unit test
classes in the "test" directory, and
data for them under the test
"test/data" directory, then you might
use a line like this in a test case:
require_relative "data/customer_data_1"
Since neither
"test" nor "test/data" are likely to
be in Ruby’s library path (and for
good reason), a normal require won’t
find them. require_relative is a good
solution for this particular problem.
You may include or omit the extension
(.rb or .so) of the file you are
loading.
path must respond to to_str.
You can find the documentation at http://extensions.rubyforge.org/rdoc/classes/Kernel.html

The top answers are correct, but deeply technical. For those newer to Ruby:
require_relative will most likely be used to bring in code from another file that you wrote.
for example, what if you have data in ~/my-project/data.rb and you want to include that in ~/my-project/solution.rb? in solution.rb you would add require_relative 'data'.
it is important to note these files do not need to be in the same directory. require_relative '../../folder1/folder2/data' is also valid.
require will most likely be used to bring in code from a library someone else wrote.
for example, what if you want to use one of the helper functions provided in the active_support library? you'll need to install the gem with gem install activesupport and then in the file require 'active_support'.
require 'active_support/all'
"FooBar".underscore
Said differently--
require_relative requires a file specifically pointed to relative to the file that calls it.
require requires a file included in the $LOAD_PATH.

I just saw the RSpec's code has some comment on require_relative being O(1) constant and require being O(N) linear. So probably the difference is that require_relative is the preferred one than require.

I want to add that when using Windows you can use require './1.rb' if the script is run local or from a mapped network drive but when run from an UNC \\servername\sharename\folder path you need to use require_relative './1.rb'.
I don't mingle in the discussion which to use for other reasons.

absolute path
require './app/example_file.rb'
shortened name
require_relative 'example_file'

Related

Ruby: Require Fails To Import - Need To Set Root Directory

Forgive my inexperience with Ruby, but I am unable to run a script within a third-party project with the following structure:
˅ alpha
˅ lib
˅ bravo
golf.rb
˅ charlie
˃ delta
˅ echo
foxtrot.rb
require "charlie/delta/echo/__init"
__init.rb
require "bravo/golf"
What should my command-line be to run the script, 'foxtrot.rb', as the following generates an error:
ruby "c:\arby\lib\bravo\charlie\delta\echo\foxtrot.rb"
"'require': cannot load such file -- charlie/delta/echo/__init (LoadError)"
If this is the code inside of __init.rb, it won't work.
require "charlie/delta/echo/__init"
__init.rb
require "bravo/golf"
require tells ruby to load the code inside a ruby file. In order for it to work, the files need to be organized correctly. You can also use require_relative but they still need a relative path from the file calling them. See What is the difference between require_relative and require in Ruby?

How does an executable within a gem reference the greater gem?

Say that my gem is VideoPlayer. The folders tructure is:
VideoPlayer/
/bin
vidplay.rb
/lib
VideoPlayer.rb
Subtitler.rb
Screenshotter.rb
I want people to invoke vidplay from the command line, and for vidplay to reference code in the VideoPlayer, Subtitler and Screenshotter files.
If I just write, within vidplay.rb, require '../lib/VideoPlayer.rb', it will throw an error, saying that it cannot require such file. I thought "Maybe it automatically requires everything in lib/", but it apparently doesn't; if I don't require anything, it'll say that VideoPlayer is an uninitialised constant.
So how does this work?
I usually add the lib dir to the library load path ($:). You can add this to the top of your bin file.
lib = File.expand_path('../../lib', __FILE__)
$:.unshift(lib) unless $:.include?(lib)
Then you can do a normal require:
require 'videoplayer'
Hope this helps.

Why is .gemspec using so complicated code to get the lib directory (Ruby)?

I've tried to create a Ruby gem by doing 'bundle gem [gem_name]' and all went well (there is a main folder and inside there is lib and spec folders). In the .gemspec file I saw this:
lib = File.expand_path('../lib', __FILE__)
while produces an absolute path to the /lib. However, the same result can be accomplished with:
File.expand_path('lib')
There was some explanation in this post File.expand_path("../../Gemfile", __FILE__) How does this work? Where is the file? on how complicated the first approach is, so I was wondering, does it really have an advantage relative to the second and simpler approach?
File.expand_path assumes Dir.getwd as the default reference point, which is not necessarily the same as "#{__FILE__}/.." (or Ruby 2.0's __dir__).
Since Ruby 2.0, you can write:
lib = File.expand_path('lib', __dir__)
which is better than the original.
as long as you are intending for your destination filepath string to be the current working directory, Ruby defaults that second parameter for you. If for some reason you'd want to create an absolute path to another location, you can pass the second parameter:
source: http://ruby-doc.org/core-1.9.3/File.html#method-c-expand_path

Creating Ruby Main (command line utility) program with multiple files

I am trying to use the main gem for making command line utilities. This was presented in a recent Ruby Rogues podcast.
If I put all the code in one file and require that file, then rspec gives me an error, as the main dsl regards rpsec as a command line invocation of the main utility.
I can break out a method into a new file and have rspec require that file. Suppose you have this program, but want to put the do_something method in a separate file to test with rspec:
require 'main'
def do_something(foo)
puts "foo is #{foo}"
end
Main {
argument('foo'){
required # this is the default
cast :int # value cast to Fixnum
validate{|foo| foo == 42} # raises error in failure case
description 'the foo param' # shown in --help
}
do_something(arguments['foo'].value)
}
What is the convenient way to distribute/deploy a ruby command line program with multiple files? Maybe create a gem?
You are on the right track for testing - basically you want your "logic" in separate files so you can unit test them. You can then use something like Aruba to do an integration test.
With multiple files, your best bet is to distribute it as a RubyGem. There's lots of resources out there, but the gist of it is:
Put your executable in bin
Put your files in lib/YOUR_APP/whatever.rb where "YOUR_APP" is the name of your app. I'd also recommend namespacing your classes with modules named for your app
In your executable, require the files in lib as if lib were in the load path
In your gemspec, make sure to indicate what your bin files are and what your lib files are (if you generate it with bundle gem and are using git, you should be good to go)
This way, your app will have access to the files in lib at runtime, when installed with RubyGems. In development, you will need to either do bundle exec bin/my_app or RUBYLIB=lib bin/my_app. Point is, RubyGems takes care of the load path at runtime, but not at development time.

How do I require a file from inside a directory with Ruby?

I think I'm missing something here. I have a directory like this:
myapp
|-lib
|-package1
|-dostuff.rb
|-package2
|-dostuff.rb
From an irb console I'm trying to test the library before I add it to my real project (a Rails app). However, typing this:
require 'lib/package1/dostuff'
returns an error saying it can't find the file to load. I added the lib directory to the load path but I'm not able to load the file.
What am I forgetting? The two filenames don't have to be the same but that's how they are to begin with (they are auto-generated from some web services I need to call using soap4r; each package represents a different web service API group)
If the directory "lib" is in the load path, the argument to require must be relative to lib. So require 'package1/dostuff' without the lib, otherwise it will look for lib/lib/package1/dostuff.rb.
In Ruby 1.9 there's the new require_relative method, which would let you do require_relative "../package2/dostuff" from within package1/dostuff.rb.

Resources