Require from zip files - design and code review request

(Thomas Sondergaard) #1

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.

  1. 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?

  2. 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?

  3. 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.

  4. 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?



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

def getInputStream(entry, &aProc)
@zipFileList.each {
>zfName> {
return zf.getInputStream(entry, &aProc)
rescue Zip::ZipNoSuchEntryError
raise Zip::ZipNoSuchEntryError,
“No matching entry found in zip files ‘#{@zipFileList.join(’, ‘)}’
" for ‘#{entry}’"

module Kernel
alias :oldRequire :require

def require(moduleName)
zipRequire(moduleName) || oldRequire(moduleName)

def zipRequire(moduleName)
return false if alreadyLoaded?(moduleName)
getResource(ensureRbExtension(moduleName)) {
eval(; $" << moduleName
return true
rescue Zip::ZipNoSuchEntryError => ex
return false

def getResource(resourceName, &aProc)
zl =$:.grep(/.zip$/))
zl.getInputStream(resourceName, &aProc)

def alreadyLoaded?(moduleName)
moduleRE ="^"+moduleName+"(.rb|.so|.dll|.o)?$")
$".detect { |e| e =~ moduleRE } != nil

def ensureRbExtension(aString)
aString.sub(/(.rb)?$/i, “.rb”)

Copyright © 2002 Thomas Sondergaard

rubyzip is free software; you can redistribute it and/or

modify it under the terms of the ruby license.