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.
Related
I want to be able to statically analyze my code. That is, to know from the plain text of the file where every function and variable comes from. IDEs and text editor plugins work better when they can trace the origin of every symbol as well.
So for example, if I have application code like this:
#...
Y.some_method()
#...
Then I want to see Y in an include/import/require/extend/def statement somewhere on the page.
In other languages I use, one can explicitly choose which sub-parts of a namespace to bring in to the current context.
Python:
from X import Y
Haskell:
import X (Y)
Elixir:
alias X.Y, as: Y
And while it's possible to import all contained names in Python, the "wildcard import" is frowned upon:
from X import *
". . . they make it unclear which names are present in the namespace, confusing both readers and many automated tools."
In Ruby, it seems that this fully implicit "wildcard" way is the only way to bring in a contained name:
include X
This makes Y available, but is there some way to make this explicit? The docs for Ruby include don't show any options.
What I'd really like to do in Ruby is something like one of these:
from X include Y
include X::Y as Y
The best I've come up with so far is:
require 'x/y' ; Y = X::Y
Here's a crazy hack in the answer to another question which would enable this.
Try this. But I agree with #tadman that you should consider doing it in the Ruby way.
Object.define_singleton_method(:include) do |*mths, from: nil|
mod = from || mths.first
mod = mod.dup
if from
all_mths = mod.instance_methods
(all_mths - mths).each { |mth| mod.send :undef_method, mth }
end
super(mod)
end
module Foobar
def foo
puts :foo
end
def bar
puts :bar
end
end
class Abc
include Foobar
end
Abc.new.foo # => 'foo'
Abc.new.bar # => 'foo'
class AbcWithoutBar
include :foo, from: Foobar
end
AbcWithoutBar.new.foo # => 'foo'
AbcWithoutBar.new.bar # => NoMethodError
Ruby always executes the code that you require
And since there is no partial execution of a file there cannot be partial require.
When you require a feature Ruby locates the corresponding file using the load paths in $: and then double checks against the list of loaded files in $" and if the file has not yet been loaded executes the file.
Ruby is a dynamic language, the best way to reason about its source code is halting a running program rather than statically. In fact even class and def are not declarations but just method calls that are executed at runtime. Consider for example this contrived example
class Surprise < [Array, Hash, Fixnum, Object].sample
end
If you want to know where a method or class has been defined best use pry. You can require pry and then use binding.pry to stop anywhere in your source code and spelunk around to inspect objects and source code. Two of the most useful commands are ls and $
ls prints all methods of an object or class
$ prints the file location and source code of a method
Coming to Ruby from a PHP background, I'm used to being able to use require, require_once, include, or include_once which all have a similar effect, but the key being they continue to process code in the same scope where the include / require command was invoked.
Example:
sub.php
<?php
echo $foo;
main.php
<?php
$foo = 1234;
include('sub.php'); // outputs '1234'
When I first started using Ruby I tried to include / require / require_relative / load other .rb files, and after becoming a little frustrated with not having it work how I would expect it to I decided that there were better ways to go about breaking up large files and that Ruby didn't need to behave in the same way PHP did.
However, occasionally I feel that for testing purposes it would be nice to to load code from another .rb file in the way PHP does - in the same scope with access to all the same variables - without having to use class / instance variables or constants. Is this possible? Maybe somehow using a proc / binding / or eval command?
Again, I'm not advocating that this should be used during development - but I am curious if it is possible - and if so, how?
Yes, this is possible, although certainly not something I'd recommend doing. This works:
includer.rb:
puts var
include.rb:
var = "Hello!"
eval(File.read("include.rb"), binding)
Running this (Ruby 2.2.1, Ruby 1.9.3) will print Hello!. It works simply: eval takes an optional binding with which to evaluate the code it is passed, and Kernel#binding returns the current binding.
To have code run in same binding, you could simply eval the file contents as follows:
example.rb
class Example
def self.called_by_include
"value for bar"
end
def foo
puts "Called foo"
end
eval( File.read( 'included.rb' ) )
end
Example.new.bar
included.rb
BAR_CONSTANT = called_by_include
def bar
puts BAR_CONSTANT
end
Running ruby example.rb produces output
value for bar
The important thing is the eval( File.read( 'included.rb' ) ) code, which if you really wanted you could define as a class method on Object, to allow arbitrary source to be included with a convenience function*. The use of constants, class variables etc just shows influences working in both directions between the two pieces of source code.
It would be bad practice to use this in any production code. Ruby gives you much better tools for meta-programming, such as ability to use mix-ins, re-open classes, define methods from blocks etc.
* Something like this
class Object
def self.include_source filename
eval( File.read( filename ) )
end
end
And the line in example.rb would become just
include_source 'included.rb'
Again I have to repeat this is not such a great idea . . .
To import external .rb file in your code, I'm not sure but I think it have to be a gem.
Use require followed by the name of the gem you want to import.
Example
require 'foobar'
# do some stuff
Or you can use load to import entire rb file
load 'foobar.rb'
# do some stuff
Good luck and sorry for my english
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}
I am trying to run a very basic test with Terminal and Sublime Text 3. My simple test runs, but fails (undefined local variable or method 'x')
My folder hierarchy looks like this:
spec_helper.rb looks like this:
require_relative '../test'
require 'yaml'
test_spec.rb is extremely basic
require 'spec_helper.rb'
describe "testing ruby play" do
it "finds if x is equal to 5" do
x.should eql 5
end
end
and my test.rb file has x = 5 That's it.
Will a variable only be recognizable if it's part of a class? And do I need to call a new class every time I run my test?
From the docs
require(name) → true or false
Loads the given name, returning true if successful and false if the feature is already
loaded.
[snip]
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.
You could use a constant in your required file:
X = 5
...
X.should eql 5 # => passes
But you probably want to do something entirely different here. Perhaps you could expand on the question and explain what you are trying to accomplish.
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?