On Friday, June 17, 2011 06:06:33 AM Robert Klemme wrote:
> On Fri, Jun 17, 2011 at 9:33 AM, David Masover <ninja / slaphack.com> wrote:
> >  require 'autoloader'
> >  AutoLoader << File.dirname(__FILE__)
> > 
> >  module Foo
> >    include AutoLoader
> >  end
> > 
> > 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

That needs some work. Dirname isn't enough, since right now, things may be 
nested arbitrarily deep -- I could have lib/foo/bar.rb which looks similar:

  module Foo
    module Bar
      include AutoLoader
    end
  end

But I clearly still want 'lib' to be the top-level directory for this magic. 
That is, I want foo/bar/baz.rb to map to Foo::Bar::Baz, not to Bar::Baz.

I really don't mind the __FILE__ ugliness, given that it only has to happen 
once, at the very top of the project. The assumption is that you'd never have 
a reason to require anything other than this top-level library, since 
everything else is included with autoload, so you'll only get the stuff you 
need -- that is, there's no advantage to:

  require 'foo/bar'
  Foo::Bar.do_something!

rather than

  require 'foo'
  Foo::Bar.do_something!

So I have no motivation to get rid of __FILE__, though maybe there should be 
some convenience version, maybe something like:

  AutoLoader.add_relative_path 'path/to/lib'   # defaults to '.'

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

Yeah, it doesn't really solve the problem. 'include' isn't the problem. The 
problem is that this library shouldn't force me to add _any_ special syntax or 
calls, let alone special files, to make it work. It should just let me pretend 
that my entire directory got slurped -- this was inspired by Rails' 
const_missing behavior, but also by Ramaze's "acquire" which just requires 
every ruby file in a given path.

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

I don't mind this, except for two things:

 - I shouldn't have to modify the load path. I should be doing it anyway, 
maybe, but it shouldn't be a requirement.

 - It's annoying having to explicitly autoload everything, especially when I'm 
usually following a convention.

> Basically, as long as there is no fixed relationship between file name
> and content, there will always be a level of manual intervention
> needed.

I like convention over configuration. Not convention instead of configuration 
-- there's always manual autoload if I need them, and I suspect the first 
thing I'd change about AutoLoader at this point is being able to specify which 
files are included and which aren't. But when 99% of the time, the process is:

 - Create foo/bar.rb.
   - Create class Foo::Bar in foo/bar.rb.
   - Add autoload line to foo.rb.
 - Did I really want it called bar.rb? Maybe baz.rb is a better name.
   - git mv foo/bar.rb foo/baz.rb
   - Change Foo::Bar to Foo::Baz in foo/baz.rb
   - Change autoload line in foo.rb.
   - Update any other references.
 - Maybe I want to split it into foo/one.rb and foo/two.rb.
   - create foo/one.rb, foo/two.rb.
   - edit/move some stuff, update references.
   - git add foo/one.rb foo/two.rb
   - git rm foo/baz.rb
   - edit foo.rb and update the autoload line.

This is annoying. Sure, it's going to be annoying no matter what, but anything 
I can do to ease this pain is helpful, because I do this kind of thing a _lot_ 
in the first few hours of a project. Anytime I see a better organization, 
_now_ is the time, before I've published it to a dev team (let alone to the 
world via Github) and locked myself into an API.

It's also annoyingly redundant and menial. This is something the machine can 
do perfectly well for me, so why not let it?

Also worth mentioning: When you're autoloading everything, anything beyond the 
top-level "require 'yourgem'" is no longer part of your public API. So, any 
naming convention is entirely for your own convenience at this point. I just 
did what Rails did and made it convenient to follow that one naming 
convention. So when I actually use AutoLoader, I don't have exceptions to that 
naming convention, or, really, any reason to.

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

Well, as you demonstrate, that's easily solved. But in my opinion, the other 
downside is that it makes things explicit, when there isn't a good reason for 
them to be.

At this point, AutoLoader isn't about solving the relativity issue, it's about 
DRYing things up a bit.