On Fri, Jun 17, 2011 at 9:33 AM, David Masover <ninja / slaphack.com> wrote:
> On Thursday, June 16, 2011 11:50:57 AM Piotr Szotkowski wrote:
>> Hm, I do prefer using require_relative over require + $LOAD_PATH
>> changes; it looks cleaner to me and doesn√’ touch shared
>> globals, but I guess that√‘ more of a personal preference.
>
> So, while writing the rest of this (long) post, I think I actually dug upolution for you, in the form of the 'autoloader' gem I wrote awhile back:
>
> https://github.com/masover/autoloader
>
> If everything in your 'lib' hierarchy is normally loaded via autoload, and
> follows a decent naming convention, I think this solves your problem. In your
> example, somewhere in 'lib/foo.rb', you could do this:
>
> equire 'autoloader'
> utoLoader << File.dirname(__FILE__)
>
> odule Foo
> nclude AutoLoader
> nd
>
> Ok, there's some __FILE__ ugliness, but in exchange, you automagically get
> 'autoload' statements generated for everything, and you don't have to modify
> your load path.

I think there is a cure for that:

module AutoLoader
  def self.included(cl)
    # "alc.rb:33:in `<class:X>'"
    file = caller[1][%r{\A(.*?):\d+:in\s}, 1]
    self << File.dirname(file)
  end

  # debug:
  def self.<<(d)
    puts "Adding directory: #{d}"
  end
end

> Anyway, what I really want is something like:
>
> utoload :Foo do
>  block to ensure Foo is defined
> nd
>
> In this case, autoload is the wrong word, but this would let me write the
> ultimate autoload-based replacement for the old Rails const_missing hack.hat
> I have now is in the 'autoloader' gem:
>
> AutoLoader << '/path/to/lib'
>
> It will crawl 'lib' one level deep, take every '.rb' file, and create an
> 'autoload' tag for it. But in order to map Foo::Bar to 'foo/bar.rb', I still
> need the user to have a foo.rb file which does some extra work:
>
> module Foo
> nclude AutoLoader
> end

In Ruby land we could at least do

auto_module :Foo do
  # no more "include" needed here
end

or even

auto_module :Foo

if there is nothing that needs to be defined here.  But then we have a
"require" statement and a file with a single line - that doesn't feel
right.

> I can't think of any way to make this work other than having some callback
> fire when the file actually gets loaded. Last I checked, Kernel#autoload would
> call 'require' from C when fired, so overloading 'require' doesn't help. And
> const_missing isn't a substitute, nor is there any way to duplicate
> Kernel#autoload's functionality from Ruby.
>
> I actually think autoloading via require_relative is a bad idea, versus just
> adding something to the load path. Some people think autoload itself is aad
> idea. There are probably workarounds enough for both of us. But it does
> frustrate me that Kernel#autoload is so inflexible, and it really doesn'teem
> like it needs to be.

Hm, for me this generally works pretty well: I have a lib directory
which is part of the load path and for larger libraries I have an
initial file (e.g. "foo.rb") where the root namespace is defined
together with a set of autoload directives which then load files like
"foo/class1", "foo/class2" etc.

Basically, as long as there is no fixed relationship between file name
and content, there will always be a level of manual intervention
needed.  True, you can have conventions manifest in code (like your
AutoLoader) but as long as there is no predominant convention that
fits all needs we won't have a generalized solution.  The advantage of
the current autoload approach is that it makes things explicit.
Downside is still that the relativity issue (with regard to pathnames,
not E=mc2) is still there.

But that can be solved with

class Module
  def autoload_r(const, path)
    file = caller[1][%r{\A(.*?):\d+:in\s}, 1]
    autoload(const, File.join(File.dirname(file), path))
  end
end

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/