Andrew Walrond wrote:
My final solution, taking the above into account:
c.rb
class Glibc
end
b.rb
inline 'c.rb'
class Gcc
end
a.rb
module Packages
def Packages.inline(filename)
eval(IO.readlines(filename).join,nil,filename)
end
inline('c.rb')
end
Packages::Gcc.new
Packages::Glibc.new
My final puzzle before sleep; How to throw an exception if any of the package files try to use require() or load() instead of inline() ? (My package developers might forget and pollute the global namespace)
Is there a neat way?
If you can arrange that the context in which those nested require/load calls occur is an object of your own choosing (rather than ruby's top-level object), then you can make require and load do whatever you want.
Actually, this could be a way to unify your #inline with #load/#require in such a way that it does the right thing, depending on where the referenced file is located. That way, package developers are insulated from the issue, and can still require standard libraries if they need to. I can explain better with some code ...
In my "script" library (findable on RAA), there is a subclass of Module called Script (probably too bland a name, but oh well). Script has a module method Script.load that creates a new Script instance:
require 'script'
script = Script.load("scripts/simple-script.rb")
The script object returned from Script.load can be used to access all the constants and top-level methods defined in the file. (A top level method is one defined like "def foo ... end" without an explicit class or module context.)
For example, if scripts/simple-script.rb has:
VALUE = [1,2,3]
def foo; "FOO"; end
Then the main program (with the Script.load line) can call
script.foo
script::VALUE
This is a convenient way to construct objects in a script and return them to the program that loaded the script.
The underlying approach is to use module_eval, as others have suggested on this thread, to load the definitions in the given file into a module and return them, encapsulated into an instance of Script.
Here's the twist: instances of Script have their own #load and #require implementation that augments Kernel's, and this affects nested loading. Script#load behaves like Script.load: definitions go into the Script instance (a module), rather than into the global namespace. Script#require first tries the local dir (where the original script file--in this case "scripts/simple-script.rb"--is located). Then it falls back to Kernel#require. It maintains its own equivalent of $LOADED_FEATURES so that you have the usual require semantics on the local dir. So, as long as you doing it from the the top level, requiring a local file puts the definitions into the script instance's scope, but requiring a normal library file puts the definitions into the global scope, as usual.
For example, if scripts/simple-script.rb might have this code:
require 'rbtree' # not found in scripts/, so look in usual
# places.
require 'other-script' # if found locally, load it in to the
# current Script context
That's really about all Script does, though there are some bells and whistles: a way of passing in arguments to the loaded script, correct reporting of file name and line number in exceptions generated by the loaded files, a #to_s method, #autoscript (like #autoload).
If the script lib is not exactly what you want, you might still want to take a look at the source (it's short) to see if it can be adapted. You could change the require definition to raise an exception (note that this only affects the top level, not require called dynamically from within methods defined inside classes or modules other than the script itself).
HTH.
···
On Saturday 02 April 2005 00:34, Andrew Walrond wrote: