access scope of 'required' files to get variables - ruby

If I require 'helper_file' in a program and there are constants and variables declared in that required file is there a way to access those variables and constants?
require 'helper_file'
...some nice code
x = ConstantFromRequireFile

You use require to load a library into your Ruby program. It will return true if successful.
So you have a file example.rb:
require 'library.rb'
# Some code
x = CONSTANTFROMREQUIREFILE
puts x # "Hello World"
method_from_required_file # "I'm a method from a required file."
and a file library.rb:
CONSTANTFROMREQUIREFILE = "Hello World"
def method_from_required_file
puts "I'm a method from a required file."
end
As you can see you access the constant and the method as you would access a constant and a method from the same file.
You can read more about require here: What is the difference between include and require in Ruby? and here: Kernal Module in Ruby

constants, global variables, instance variables and class variables defined in the top level scope in a required file will all be available in the scope of the requiring file, but local variables will not. What exactly is your problem?

Related

Include another script in Ruby script

Let's say I have the following scripts.
script1.rb
x=1
script2.rb
# Something like: import script1
y=2
puts x+y
Is it possible to "merge" these scripts, i.e. to somehow "import" script1.rb into script2.rb so that running script2.rb would print "3"?
I have already tried statements like require_relative, load but the result is:
b.rb:4:in <main>': undefined local variable or methodx' for
main:Object (NameError)
yes:
require_relative 'yourOtherFile'
You don't need to tack .rb on the end. The "relative" part in require_relative means "look in the same folder I am in for the other file"
If you want to access one variable from file A in file B then you have to make it a global ($x = 5). This will work but isn't a great way to organize your code (because it makes for an environment where the global scope is polluted with lots of different variables.
You should organize your code into classes:
class Planet
##x = 0
def self.doStuffToX
#xx += 4
end
def self.getX
##x
end
end
and you can require this file, then use Plant.getX and Plant.doStuffToX to access and change x.
Yes, using require_relative, but it will only work for global variables and constants. This is a good thing.
Any constants or globals within the loaded source file will be available in the calling program’s global namespace. However, local variables will not be propagated to the loading environment.
script1.rb
X = 1
script2.rb
require_relative 'script1.rb'
y = 2
puts X + y
To access a constant this is fine, nothing can change it.
But if you want to alter x then it has to be a global variable $x and that's not good. Globals are to be avoided because it's hard to control how they are used or modified. You should instead redesign the code in script1.rb to be a class.

irb access pre-load script local variables

i'm using irb -I . -r script.rb to require a script before starting an interactive session. functions defined in the global scope are available but variables aren't unless they are declared with #
is there a way to access local variables defined in the global context of a script or a better way to do this
script.rb:
def func() "..." end
a = "str"
#b = 1
then after irb starts:
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from /usr/bin/irb:11:in `<main>'
irb(main):002:0> #b
=> 1
irb(main):003:0> func
=> "..."
i'm assuming that the script's contents are executed as if defined in a function (eg: main in C-type languages) and so the variables at the global context are local variables that are not accessible outside that scope
so do most people use # variables when writing their scripts?
the use-case is narrow in scope (script development) and the solution is trivial (search-replace any variable with #variable), but I'm learning the semantics of the language and I'm curious about this. Can't the execution context be exposed and merged into the current context somehow?
No. You can't access local variables from a different scope. That's the whole point of local variables: they are local to the scope they are defined in.
In this case, a is local to the script body of script.rb.
Maybe binding.irb is what you are looking for.
script.rb:
def func() "..." end
a = "str"
#b = 1
binding.irb
From the documentation:
Opens an IRB session where binding.irb is called which allows for
interactive debugging. You can call any methods or variables available
in the current scope, and mutate state if you need to.

How to access members of another class in a different file

I'm using ruby-prolog. I want to run a task to query a fact.
demo.rb:
require 'ruby-prolog'
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
This works great. Now I want to run the query inside of Rake. That is a problem because I don't know how to access person[] from the other file.
Rakefile.rb:
require_relative 'demo.rb'
task :test do |variable|
puts 'all the names are: '
p query(person['name', :A])
end
Error:
all the names are: rake aborted! NameError: undefined local variable
or method `person' for main:Object
I'm hoping this can be solved by passing an object back somehow. I tried accessing c, but it did not work out. Any ideas?
In your demo file, both variables person and c are local variables and will not be accessible from outside of that context. If you require demo.rb into an irb session, the behavior should be the same; neither c nor person will be defined.
A good way of dealing with this in rake tasks is to keep any and all logic out of the rake task itself, and only call out to another object that takes care of the task. For a quick and dirty example, you could alter your code as such:
# demo.rb
require 'ruby-prolog'
class Demo
def self.run_demo
# Existing code:
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
end
end
and
# Rakefile.rb
require_relative 'demo.rb'
task :test do
Demo.run_demo
end

Using require to use file with hash

I am trying to refer to a hash written in another file, and tried:
require './filewithhash' #this file has hash
puts name_hash['somename'] #just trying access by the index
and got undefined local variable or method error.
The problem is that when requiring file in ruby, local variables are out of scope. Therefore any local variables defined in required file will be not available later on.
Solutions:
Make your program object oriented and add this hash as a field in some class
Make your variable constant (like this: NAME_HASH)
Make your variable global (like this: $name_hash)
Make your variable an instance variable (like this: #name_hash)
If program is very simple, I would choose option 3. However if it's suppose to grow, 1 is your best choice.
Requires in Ruby don't just inline the file like in PHP; each file has its own scope. That is, the code in the file will execute in a scope that means that all local variables are all constrained to that file's scope, and will not be visible outside of it.
In order to execute a file in the current scope, you're going to have to eval it with the current binding:
file = "file_with_hash.rb"
File.open(file) {|f| eval f.read, binding, file }
This is a bad idea and you shouldn't do it unless you know what you're doing and why. Instead, you should expose that hash as a member of a constant (which transcends the file scope):
# foo.rb
module Foo
PROGRAMMER_WORDS = {foo: 1, bar: 2, baz: 3}
...
end
# another file
require 'foo'
Foo::PROGRAMMER_WORDS # => {:foo=>1, :bar=>2, :baz=>3}

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