ruby: how to load .rb file in the local context
Solution 1
You certainly could hack out a solution using eval and File.read, but the fact this is hard should give you a signal that this is not a ruby-like way to solve the problem you have. Two alternative designs would be using yaml for your config api, or defining a simple dsl.
The YAML case is the easiest, you'd simply have something like this in main.rb:
Class App
def loader
config = YAML.load('config.yml')
p config['var'] # => "val"
end
end
and your config file would look like:
---
var: val
Solution 2
The config file.
{ 'var' => 'val' }
Loading the config file
class App
def loader
config = eval(File.open(File.expand_path('~/config.rb')).read)
p config['var']
end
end
Solution 3
As others said, for configuration it's better to use YAML or JSON. To eval a file
binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb")
binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")
This syntax would allow you to see filename in backtraces which is important. See api docs [1].
Updated eval
command to avoid FD (file descriptor) leaks. I must have been sleeping or maybe should have been sleeping at that time of the night instead of writing on stackoverflow..
[1] http://www.ruby-doc.org/core-1.9.3/Binding.html
Solution 4
I do NOT recommend doing this except in a controlled environment.
Save a module to a file with a predetermined name that defines an initialize
and run_it
methods. For this example I used test.rb as the filename:
module Test
@@classvar = 'Hello'
def initialize
@who = 'me'
end
def get_who
@who
end
def run_it
print "#{@@classvar} #{get_who()}"
end
end
Then write a simple app to load and execute it:
require 'test'
class Foo
include Test
end
END {
Foo.new.run_it
}
# >> Hello me
Just because you can do something doesn't mean you should. I cannot think of a reason I'd do it in production and only show it here as a curiosity and proof-of-concept. Making this available to unknown people would be a good way to get your machine hacked because the code could do anything the owning account could do.
Solution 5
I just had to do a similar thing as I wanted to be able to load a "Ruby DLL" where it returns an anonymous class ( a factory for instances of things ) I created this which keeps track of items already loaded and allows the loaded file to return a value which can be anything - a totally anonymous Class, Module, data etc. It could be a module which you could then "include" in an object after it is loaded and it could could supply a host of "attributes" or methods. you could also add an "unload" item to clear it from the loaded hash and dereference any object it loaded.
module LoadableModule
@@loadedByFile_ = {};
def self.load(fileName)
fileName = File.expand_path(fileName);
mod = @@loadedByFile_[fileName];
return mod if mod;
begin
Thread.current[:loadReturn] = nil;
Kernel.load(fileName);
mod = Thread.current[:loadReturn];
@@loadedByFile_[fileName] = mod if(mod);
rescue => e
puts(e);
puts(e.backtrace);
mod = nil;
end
Thread.current[:loadReturn] = nil;
mod
end
def self.onLoaded(retVal)
Thread.current[:loadReturn] = retVal;
end
end
inside the loaded file:
LoadableModule.onLoaded("a value to return from the loaded file");
disfated
Updated on July 08, 2022Comments
-
disfated almost 2 years
How this simple task can be done in Ruby?
I have some simple config file=== config.rb config = { 'var' => 'val' }
I want to load config file from some method, defined in
main.rb
file so that the local variables fromconfig.rb
became local vars of that method.
Something like this:=== main.rb Class App def loader load('config.rb') # or smth like that p config['var'] # => "val" end end
I know that i can use global vars in
config.rb
and then undefine them when done, but i hope there's a ruby way ) -
disfated over 13 yearsYaml won't work: config.rb is just an example - there should be some proccessings - not just serialized data. Actually, I need a simple command "exec_file_as_it_was_typed_here(file)". btw, php can do this )
-
disfated over 13 yearsAlso, dsl and especially eval are not variants. At least for now. I just want to keep things simple.
-
NZKoz over 13 yearsThe short version is that ruby can't do this, the longer version is that you should be defining and executing a DSL rather than relying on a clever hack with scoping.
-
PJP over 13 years"php can do this" ... trying not to scream... there are untold numbers of websites on the internet that are proof that PHP can do that, and that cause the rest of us to curse the programmers who allowed it.
-
disfated over 13 yearsthanks for the approach, but as you can see yourself - this is more redundant solution than even 'php style'...
-
disfated over 13 yearsI'm not particularly enthusiastic about metaprogramming, but php is a hell for me. I thought that so powerful dynamic tool like ruby can do such a primitive task. Bad news. It can't. Thanks all for your attention!
-
Automatico about 11 yearsYou might need to change the line
require 'test'
torequire './test'