I have some spare time coming up, and I want to finish a little module
for requiring ruby modules stored in zip files that I have written. A
number of questions have come up, and I’d like to get some opinions.
-
Performance vs Complexity vs Liveliness(?)
I can either gather the list of zip archives in $: and reread the zip
directories for every require, or I can keep a weak reference to the zip
archives and reuse them. Should I, then check the modification date of
the zip archives and rescan them if they have changed, or is it not
worth the extra complexity? After all, how often will the zip archives
change after the program has been started? -
What needs to be set?
What needs to be set when I eval() the contents of the require’d ruby
module? FILE and what else? Is there a better way than to call
eval() btw? -
Search order in $:
My prototype searches zip files before searching regular directories -
this means the order of the elements in $: is not obeyed. Does this
matter? The reason I’ve done it this way is that I can use the original
definition of require to load modules not stored in zip archives. -
Requiring .so/.dll files.
dlopen requires a filename and can thus not be loaded directly from a
zip archive. Is it worth to do some behind the scenes unpacking to /tmp?
Thanks,
Thomas
The prototype implementation is reasonably short (64 lines), so I’ve
appended it here. Comments are more than welcome.
#!/usr/bin/env ruby
require ‘zip’
class ZipList
def initialize(zipFileList)
@zipFileList = zipFileList
end
def getInputStream(entry, &aProc)
@zipFileList.each {
>zfName>
Zip::ZipFile.open(zfName) {
>zf>
begin
return zf.getInputStream(entry, &aProc)
rescue Zip::ZipNoSuchEntryError
end
}
}
raise Zip::ZipNoSuchEntryError,
“No matching entry found in zip files ‘#{@zipFileList.join(’, ‘)}’
”+
" for ‘#{entry}’"
end
end
module Kernel
alias :oldRequire :require
def require(moduleName)
zipRequire(moduleName) || oldRequire(moduleName)
end
def zipRequire(moduleName)
return false if alreadyLoaded?(moduleName)
getResource(ensureRbExtension(moduleName)) {
>zis>
eval(zis.read); $" << moduleName
}
return true
rescue Zip::ZipNoSuchEntryError => ex
return false
end
def getResource(resourceName, &aProc)
zl = ZipList.new($:.grep(/.zip$/))
zl.getInputStream(resourceName, &aProc)
end
def alreadyLoaded?(moduleName)
moduleRE = Regexp.new("^"+moduleName+"(.rb|.so|.dll|.o)?$")
$".detect { |e| e =~ moduleRE } != nil
end
def ensureRbExtension(aString)
aString.sub(/(.rb)?$/i, “.rb”)
end
end