The scope of Require - ruby

I am having trouble accessing a module I declared in another file, and I am wondering if I have this right conceptually. Let me explain.
If I have file a, b and c setup like so
File a
module HelloWorld
def greet
"hello world"
end
end
File b
require "a"
File c
require "b"
include HelloWorld
puts greet
So it's a -> b -> c, will this work?
Now what if I do it this way
File a
module HelloWorld
def greet
"hello world"
end
end
File b
include HelloWorld
puts greet
File c
require "a"
require "b"
Would that change anything? It seems to me, gems - once required - are accessible no matter what file, but I am having trouble accessing modules in the second scenario. Any help is appreciated.
UPDATE: I tested both scenarios, and they both work, which leads me to believe this is not a conceptual problem, but a bug in my code. I am going to work on debugging the project.

When I try to run your code I get the following error message
$ ruby c.rb
~/.rbenv/versions/2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- a (LoadError)
from /Users/Sonna/.rbenv/versions/2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from c.rb:2:in `<main>'
It is simply saying that it uses the Kernel's require method
it cannot find the a.rb file and then raises and LoadError exception.
In order to require the file you can use the Kernal's require_relative method
require_relative "a"
require_relative "b"
and it will a those a & b files relative to the c file.
Or you can add the following lines of code to your c.rb file, which is a
common convention used in Ruby Gems to load their custom scripts/libraries
current_directory = File.expand_path("../", __FILE__)
$LOAD_PATH.unshift(current_directory) unless $LOAD_PATH.include?(current_directory)
This will add the current directory ../ from the current file __FILE__,
expand it to be an aboslute path of said current directory and add it to the
existing Load Path global variable; e.g.
puts $LOAD_PATH
# => ["~/Projects/ruby/stackoverflow_questions/the_scope_of_require",
# "/usr/local/Cellar/rbenv/1.0.0/rbenv.d/exec/gem-rehash",
# "~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib",
# "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0",
# "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby/2.3.0/x86_64-darwin15",
# "~/.rbenv/versions/2.3.1/lib/ruby/site_ruby",
# "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0",
# "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin15",
# "~/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby",
# "~/.rbenv/versions/2.3.1/lib/ruby/2.3.0",
# "~/.rbenv/versions/2.3.1/lib/ruby/2.3.0/x86_64-darwin15"]
Which require internally uses to find by filename, if it is not given an
absolute path
If the filename does not resolve to an absolute path, it will be searched for
in the directories listed in $LOAD_PATH ($:).
-- Kernel's require method
So when I run you code again I see the following
$ ruby c.rb
hello world
It should be noted that
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.
-- Module: Kernel (Ruby 2_4_0)
So as long as one of your require methods occur once before one of the methods in
that file are called, it should work; so both of your examples will work (as
long as either the directory the sit is added to the $LOAD_PATH or you use
require_relative instead)

Related

Calling classes from files

I am writing a program which includes two file. I’m the first file all I am doing is initializing the next file which includes multiple classes. I call the class at the bottom of the file, but it runs the class at the top of the file.
Btw I am running ruby
First file:
Require ‘./secondfile.rb’
newfile.Lastclass
Second file:
class Firstclass
end
class Lastclass
end
Hint #1. It's better to write without .
It depends on directory from where you run your app.
For example you have directory folder. And there you have two files:
file.rb
puts "I'm file.rb"
main.rb
require './file.rb'
If you run your app from folder - it's ok:
folder$ ruby main.rb
I'm file.rb
But if you'll run it from high level - it will be error:
folder$ cd ..
:~$ ruby folder/main.rb
kernel_require.rb:55:in `require': cannot load such file -- ./file.rb (LoadError)
But how to write in this case?
Hint #2. You can use special method
main.rb
require "./#{__dir__}/file.rb"
Now it's ok in all cases:
~$ ruby folder/main.rb
I'm file.rb
folder$ ruby main.rb
I'm file.rb
But you can write it better.
Hint #3. You can use require_relative
main.rb
require_relative 'file.rb'
And even better.
main.rb
require_relative 'file'
Ruby understand it.
~$ ruby folder/main.rb
I'm file.rb
folder$ ruby main.rb
I'm file.rb
Hope this helps you.
Classes are injected into the root namespace if they're defined. If another file defines a class, all files get it:
require_relative './secondfile.rb'
Lastclass
This is unlike JavaScript (e.g. Node.js) where you must explicitly import classes from other files. In Ruby it happens by manipulating the root namespace.
Another note is to pay close attention to which slashes you use. In your code you used a backslash, which is incorrect. You also used the wrong quotes. Often every character counts when programming.

Why is the "./" (dot + forward slash) needed in Ruby files when using the require keyword?

When attempting to place Ruby dependency files into a Ruby file, why is the "./" (dot + forward slash) necessary when typing out the file directory? Oddly enough it is only needed when using the require keyword and not the load keyword.
i.e.:
module.rb (the dependency)
module SomeModule
def someMethod
puts "hello"
end
end
method.rb
require "./module.rb"
#require "module.rb" does not work
class Animal
include SomeModule
end
class Person
include SomeModule
end
animal = Animal.new
animal.someMethod
person = Person.new
person.someMethod
#irb --> need to also write require " ./method.rb" to call it
If module.rb and method.rb are in the same directory, instead of using require you should use require_relative. Thus, the top of method.rb would look like this
require_relative 'module'
class Animal
It is only needed when using the require keyword and not the load
keyword. Why?
load, require and require_relative are all methods which take a filename as an argument that is to be loaded. They need to locate the filename passed an an argument
load
load checks for the file in the LOAD PATH which can be accessed using the global variable $LOAD_PATH or $: Even though the current working directory (designated by .) is NOT actually in the LOAD_PATH, the load method acts as if it is so is able to locate a file without explicitly appending the current directory to the file name passed as an argument
require
require is similar to load with two main differences
The first is that it's unaware about the current working directory. It does not add it to the LOAD PATH so once ruby searches it, it doesn't find the file. This is why you have to explicitly tell ruby about the current working directory and how to locate the file from the current directory using ./path_to_file.
If you don't want to add ./, you have to add the current directory to your Load Path
$: << '.'
require 'module.rb'
This will work as the current directory is now in the load path which is where ruby will search for the file.
The second main difference is that when you make multiple calls to require in a file passing it the same filename as an argument, the file will be required only the first time. Ruby keeps track of the files required. However with multiple calls to load with the same filename as an argument, the file is always loaded.
require 'time' => true
require 'time' => false #second call within the same file
require_relative
require_relative searches relative to the file in which the method call was executed which is why you don't need to alter the LOAD PATH by explicitly adding the current working directory

Ruby: cannot load such file - LoadError

I got this 'require' cannot load such file error.
I got this previously and I added
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
unless $LOAD_PATH.include?(__LIB_DIR__)
$LOAD_PATH.unshift(__LIB_DIR__)
end
I would like to know what this does? I added this in a main 'require' file of my project.
Now I write a test case,
$:.unshift File.join(File.dirname(__FILE__), ".")
I try to run it, I get the LoadError. I also tried require_relative no luck.
Structure:
Main
Git
lib
files.rb
base.rb
test
test1.rb
I have the first code block above in base.rb where I do all 'requires'
and when i try to run the test. I get LoadError.
'Please',Explain the first and second code blocks also give me a solution
For clarity, instead of
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
use
__LIB_DIR__ = File.expand_path('..', File.dirname(__FILE__))
What does this do?
unless $LOAD_PATH.include?(__LIB_DIR__)
$LOAD_PATH.unshift(__LIB_DIR__)
end
Consider this:
ary = %w[a b]
ary # => ["a", "b"]
ary.unshift('c')
ary # => ["c", "a", "b"]
I try to run it, I get the LoadError. I also tried require_relative no luck.
That could be for a number of reasons, but, unfortunately you didn't share the code where it occurs. require and require_relative are both used to load code, but have different syntax in the parameter passed. We'd need to know what you're trying to load, and where it is in the file hierarchy in relation to your calling script.
Perhaps one of these, or their related questions, would help:
"ruby `require': cannot load such file"
"Ruby 'require' error: cannot load such file"
"require cannot load such file"
"Cannot load files using require"

How do I require a Ruby file?

I have a file called "go.rb" that contains:
require 'turboname'
dictionary = Turboname::Random.new
100999032982389.times do
name = Turboname::Domain.new(:from => dictionary)
name.save if name.length < 15 and name.available?
tld = name.tldize
name.save(tld) if tld and name.length < 15 and name.available?(tld)
end
turboname.rb is located in the same directory as go.rb. It's the same level. I just want to include this file in this script. I don't want to deal with gems or bundles.
./turboname.rb:1:in `require': no such file to load -- turboname/version (LoadError)
from ./turboname.rb:1
from go.rb:1:in `require'
from go.rb:1
Use a require_relative Statement
Recent Ruby versions no longer add . to the load path stored in $:. However, one solution is to use Kernel#require_relative to require a file relative to the current value of __FILE__. For example:
require_relative './turboname'
Note that this doesn't work in interactive REPL sessions with irb or pry, but works fine within actual source files.
The error isn't telling you it can't find ./turboname.rb. It's telling you that it found that file, but the first line of ./turboname.rb tries to require 'turboname/version', which Ruby can't find. Does ./turboname/version.rb exist? If so, is it readable by the current user?
If everything else checks out, then you have a load-path problem. At the top of go.rb, explicitly add the current working directory (or whichever directory contains turboname.rb and turboname/version.rb (possibly ./lib/) to your load path:
$LOAD_PATH << File.dirname(__FILE__) # for ./
# or
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib') # for ./lib/
With Ruby 2.0:
require "#{__dir__}/turboname"

Is there a shorter way to require a file in the same directory in ruby?

Is there a shorter way to require a file located in the same directory (as the script being executed)?
require File.expand_path(File.dirname(__FILE__) + '/some_other_script')
I read that require "my_script" and require "./my_script" will actually load the script twice (ruby will not recognize that it is actually the same script), and this is the reason why File.expand_path is recommended: if it is used every time the script is required, then it will only be loaded once.
It seems weird to me that a concise language like Ruby does not seem to have a shorter solution. For example, python simply has this:
import .some_other_module_in_the_same_directory
I guess I could monkey-patch require... but that's just evil! ;-)
Since ruby 1.9 you can use require_relative.
Check the latest doc for require_relative or another version of the Core API.
Just require filename.
Yes, it will import it twice if you specify it as filename and ./filename, so don't do that. You're not specifying the .rb, so don't specify the path. I usually put the bulk of my application logic into a file in lib, and then have a script in bin that looks something like this:
#!/usr/bin/env ruby
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'app.rb'
App.new.run(ARGV)
Another advantage is that I find it easier to do unit testing if the loading the application logic doesn't automatically start executing it.
The above will work even when you're running the script from some other directory.
However, inside the same directory the shorter forms you refer to work as expected and at least for ruby 1.9 won't result in a double-require.
testa.rb
puts "start test A"
require 'testb'
require './testb'
puts "finish test A"
testb.rb
puts "start test B"
puts "finish test B"
running 'ruby testa.rb' will result in:
start test A
start test B
finish test B
finish test A
However, the longer form will work even from another directory (eg. ruby somedir/script.rb)
Put this in a standard library directory (somewhere that's already in your default loadpath $:):
# push-loadpath.rb
if caller.first
$: << File.expand_path(File.dirname(caller.first))
end
Then, this should work
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
load 'push-loadpath.rb'
require 'lib1'
require 'lib2'
#...
caller gives you access to the current callstack, and tells you what file and where, so push-loadpath.rb uses that to add the file that load'd it to the loadpath.
Note that you should load the file, rather than require it, so the body can be invoked multiple times (once for each time you want to alter the loadpath).
Alternately, you could wrap the body in a method,
# push-loadpath.rb
def push_loadpath
$: << File.expand_path(File.dirname(caller.first))
end
This would allow you to require it, and use it this way:
% ls /path/to/
bin.rb lib1.rb lib2.rb #...
% cat /path/to/bin.rb
require 'push-loadpath'
push_loadpath
require 'lib1'
require 'lib2'
#...

Resources