How to share variables across my .rb files? - ruby

I have a few .rb files and I want to use the same variables in all of them. Let's say variable test_variable = "test" should be accessible from all my .rb files. How can I achieve that?
I created settings.rb file with test_variable = "test" then used require 'settings' in another .rb file, but it didn't work. I would like to use require not load.
I tried to make the variable global by prefixing the variable name with $, but I am still getting undefined local variable or method 'test_variable' for main:Object (NameError).

Constants (which include modules and classes) are added to the shared global environment:
phrogz$ cat constants1.rb
TEST_VARIABLE = "test"
phrogz$ cat constants2.rb
require_relative 'constants1'
p TEST_VARIABLE
phrogz$ ruby constants2.rb
"test"
Instance variables declared in main are all part of the same main:
phrogz$ cat instance1.rb
#test_variable = "test"
phrogz$ cat instance2.rb
require_relative 'instance1'
p #test_variable
phrogz$ ruby instance2.rb
"test"
Global variables are also all part of the same environment (tested in 1.8.6, 1.8.7, and 1.9.2):
phrogz$ cat global1.rb
$test_variable = "test"
phrogz$ cat global2.rb
require_relative 'global1'
p $test_variable, RUBY_DESCRIPTION
phrogz$ ruby global2.rb
"test"
"ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.7.0]"

Ruby will never share local variables between files. You can wrap them in a Module though:
module SharedVaribles
#test_var="Hello, World"
def self.test_var
return #test_var
end
def self.test_var=(val)
#test_val=val;
end
end
Put that in settings.rb, require it into all your files, and use SharedVaribles.test_var and SharedVaribles.test_var= to access the variable. Remember, Ruby's require is nothing like C's #include, it is much more complex. It executes the file, then imports all constants, modules, and classes to the requireer.

module Foo
attr_accessor :test_var
def initialize
#test_var = "hello world"
end
end
Create the module with your variable or variables in your config file and then include the module in whatever class you intend to use.
require_relative 'foomod.rb'
class Bar
include Foo
end
foobar = Bar.new
foobar.test_var = "goodbye"
puts foobar.test_var
Each instance of the class will initialize with whatever value you would like.

You shouldn't need to wrap these variables in a module.
Simply adding them to another_file.rb and using require_relative 'another_file' (if it's in the same directory) or require 'path/to/another_file' should be sufficient to share those variables across files.

Related

The scope of Require

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)

Ruby: Can I 'require' a file which contains only a class?

I have a file /project/lib/invaccessor.rb with the following content
class InvAccessor
def initialize
#browser = "browser"
end
end
and a spec file project/spec/invaccessor_spec.rb which requires it
require_relative '../lib/invaccessor'
describe Invaccessor do
it {expect(2).to be_even}
end
When I run rspec spec/invaccessor.rb I get an uninitialized constant error for Invaccessor. Do I have to put all file contents in a module in order to access them?
I'm using Ruby 2.2.2.
Yes, you can.
Try this inside the directory where your classfile.rb lies:
>> require './classfile'
=> true
>> A
=> A
You definitely don't have to put a class into a module to require it.

How to call a method from a module of an other ruby file

I have to Ruby files: one contains a module with some methods for statistical calculation, in the other file I want to call one of the methods in the module.
How can I do that in Ruby?
Is that the right way?
require 'name of the file with the module'
a=[1,2,3,4]
a.method1
Require needs the absolute path to the file unless the file is located in one of Ruby's load paths. You can view the default load paths with puts $:. It is common to do one of the following to load a file:
Add the main file's directory to the load path and then use relative paths with require:
$: << File.dirname(__FILE__)
require "my_module"
Ruby 1.8 code that only loads a single file will often contain a one-liner like:
require File.expand_path("../my_module", __FILE__)
Ruby 1.9 added require_relative:
require_relative "my_module"
In the module you will need to define the methods as class methods, or use Module#module_function:
module MyModule
def self.method1 ary
...
end
def method2
...
end
module_function :method2
end
a = [1,2,3,4]
MyModule.method1(a)
Your way is correct if your module file is in the require search path.
If your module provide methods to be used by the object itself, you must do:
require 'name of the file with the module'
a=[1,2,3,4]
a.extend MyModule # here "a" can use the methods of MyModule
a.method1
See Object#extend.
Otherwise, if you'll use the methods directly by the module, you'll use:
MyModule.method1(a)

Rspec, mapping spec files to ruby files under test

What I want is a way of not having to 'require' the class under test in each spec file.
So hoping there is a means of setting the root of the source code under test and rspec automatically mapping my tests, or any other means of automatically mapping specs to ruby files.
In Rspec for rails this happens magically, but this is not a rails project and I can't find any useful information.
I am assuming you have a lib folder and a spec folder within your project where you have code and specs respectively. Create a spec/spec_helper.rb and add
# project_name/spec/spec_helper.rb
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'spec'
require 'main_file_within_lib_folder_that_requires_other_files'
Now within your individual spec files now you just need to add the following line like rails
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
What you have to do is to redefine Object.const_missing.
Found this basic example, modify it to fit your needs (set the right path, etc.):
def Object.const_missing(name)
#looked_for ||= {}
str_name = name.to_s
raise "Class not found: #{name}" if #looked_for[str_name]
#looked_for[str_name] = 1
file = str_name.downcase
require file
klass = const_get(name)
return klass if klass
raise "Class not found: #{name}"
end

in ruby, how can I know what module is defined as result of a 'load' or 'require'?

In ruby, if I do "require foo", is there a way to subsequently determine the name of the module or modules defined in foo.rb?
For example, say I have a ruby file named foo.rb that looks like this:
# foo.rb
module MyModule
def self.summary
"this does something useful"
end
...
end
In another script, after I do "require foo", how can I determine that I now have a module named MyModule?
Ultimate what I'm after is to be able to do something like this:
file = someComputedFileName()
require file
puts "summary of #{file}: #{???::summary}
While I could force myself to make module names and file names identical, I'd rather not. I want a bit more freedom in making short filenames but more expressive module names. However, I am guaranteeing to myself that each file will only define a single module (think: plugins).
I don't know if this is the best solution, but off the top of my head this seems to work:
all_constants = Object.constants
require 'foo'
foo_constants = Object.constants - all_constants
foo_constants should give you only the modules, classes or other constants that were defined by foo.rb.
One approach would be to use ObjectSpace.each_object(Module) to find all defined modules before requiring the file. Then, after you require the file, loop over all defined modules again and see if there are any new ones.
require "set"
old_modules = SortedSet.new
ObjectSpace.each_object(Module) {|m| old_modules.add(m) }
file = someComputedFileName()
require file
new_modules = SortedSet.new
ObjectSpace.each_object(Module) {|m| new_modules.add(m) unless old_modules.include?(m) }
puts "summary of #{file}: #{new_modules.to_a.map{|m|m.summary}.join(',')}"
That would also let you define more than one module in the file.

Resources