Autorequire.rb

just hacked this out - can anyone see holes in it?

     harp:~ > cat autorequire.rb
     class Module
       alias_method "const_missing!", "const_missing"
       def const_missing c
         lib4 = lambda{|k| k.to_s.downcase.gsub(%r/::/, File::SEPARATOR)}
         lib = Object == self ? lib4[c] : lib4["#{ name }::#{ c }"]
         begin; require lib; rescue LoadError; end
         const_missing! c unless const_defined? c
         const_get c
       end
     end

     if $0 == __FILE__
       p Object
       p CGI
       p CGI::Session
       p PStore
       p K
     end

     harp:~ > ruby autorequire.rb
     Object
     CGI
     CGI::Session
     PStore
     autorequire.rb:7:in `const_missing': uninitialized constant K (NameError)
             from autorequire.rb:17

regards.

-a

···

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

Depending on how smart you want it to be it will fail on stdlib's that don't follow a consistent downcase/spelling naming
convention, ie: ostruct => OpenStruct

It doesn't work with files which don't have a toplevel file to include, like 'net/ssh'. There is no 'net' file to load like there
is with 'cgi' in 'cgi/session'. Although I can't think of a way OTOH to get around this with const_missing, since you'll never see
the SSH part of Net::SSH because it will fail on Net.

Zach

···

ara.t.howard@noaa.gov wrote:

just hacked this out - can anyone see holes in it?

    harp:~ > cat autorequire.rb
    class Module
      alias_method "const_missing!", "const_missing"
      def const_missing c
        lib4 = lambda{|k| k.to_s.downcase.gsub(%r/::/, File::SEPARATOR)}
        lib = Object == self ? lib4[c] : lib4["#{ name }::#{ c }"]
        begin; require lib; rescue LoadError; end
        const_missing! c unless const_defined? c
        const_get c
      end
    end

    if $0 == __FILE__
      p Object
      p CGI
      p CGI::Session
      p PStore
      p K
    end

    harp:~ > ruby autorequire.rb
    Object
    CGI
    CGI::Session
    PStore
    autorequire.rb:7:in `const_missing': uninitialized constant K
(NameError)
            from autorequire.rb:17

regards.

-a

this is a possible workaround - it's broken for some cases now but i'm just
trying to show it's possible :

     harp:~ > ruby a.rb

     {"Net"=>"ConstProxy(Net)"}
     {"Net::FTP"=>Net::FTP}

     harp:~ > cat a.rb

     require 'pp'

     autorequire 'Net::FTP' => 'net/ftp'

     pp 'Net' => Net
     pp 'Net::FTP' => Net::FTP

     BEGIN {
       require 'enumerator'

       class Module
         alias_method "const_missing!", "const_missing"

         def const_missing c
           c = c.to_s

           if autorequire.has_key? c
             obj = autorequire[c]
             cp = obj.respond_to?('ancestors') and obj.ancestors.include?(ConstProxy)
             if cp
               return obj
             else
               libs = obj
               libs.each{|lib| begin; require lib; rescue LoadError; end}
             end
           else
             lib4 = lambda{|k| k.to_s.downcase.gsub(%r/::/, File::SEPARATOR)}
             lib = Object == self ? lib4[c] : lib4["#{ name }::#{ c }"]
             begin; require lib; rescue LoadError; end
           end

           if ancestors.include?(ConstProxy)
             to_const.const_get c
           else
             const_missing! c unless const_defined? c
             const_get c
           end
         end

         def autorequire c = nil, *libs
           if c
             h = Hash === c ? c : {c => libs}
             h.each do |c,libs|
               if c =~ %r/::confused:
               # hack for two deep nesting only
                 k,v = c.split %r/::confused:
                 cp = ConstProxy(k)
                 autorequire[k] = cp
                 cp.autorequire[v] = libs
               else
                 autorequire[c.to_s] = [libs].flatten
               end
             end
             self
           else
             @autorequire ||= {}
           end
         end
       end

       module ConstProxy
         def self.new name
           name = name.to_s
           raise ArgumentError unless name =~ %r/^[A-Z]/
           Module.new do
             include ConstProxy
             singleton_klass =
               class << self; self; end
             singleton_klass.module_eval do
               define_method('name'){ name }
               define_method('to_s'){ "ConstProxy(#{ name })" }
               define_method('inspect'){ "\"#{ to_s }\"" }
               define_method('to_const'){ Object.const_get(name) }
             end
           end
         end
         module ::Kernel
           def ConstProxy(*a, &b) ConstProxy.new(*a, &b) end
         end
       end

       class Object
         def autorequire(*a, &b)
           self.class.autorequire(*a, &b)
         end
       end
     }

i'll keep hacking on it and try to release something soon...

-a

···

On Sat, 8 Apr 2006, zdennis wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Depending on how smart you want it to be it will fail on stdlib's that don't follow a consistent downcase/spelling naming
convention, ie: ostruct => OpenStruct

It doesn't work with files which don't have a toplevel file to include, like 'net/ssh'. There is no 'net' file to load like there
is with 'cgi' in 'cgi/session'. Although I can't think of a way OTOH to get around this with const_missing, since you'll never see
the SSH part of Net::SSH because it will fail on Net.

Zach

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama