Hello all,

I've run into a behavior with autoload and $LOAD_PATH that is inconsistent
across Ruby versions and implementations, and would like clarification on what
the correct behavior should be.

Consider the two source files below:

(root)/foo.rb
-------------

    $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)) + '/./foo')

    module Foo
      autoload :Bar, 'bar'
    end

    Foo::Bar

(root)/foo/bar.rb
-----------------

    module Foo; class Bar; end; end

An executable, expanded version of this example, along with some annotations, is
available at https://github.com/yipdw/autoload-testcase.

The above code will unshift a path resembling /a/./foo into $LOAD_PATH.  This
path is absolute -- it addresses an unambiguous location in the filesystem --
but it is not expanded.

This example is a bit silly, but this can happen in less contrived situations,
such as loading libraries using

    File.join(File.dirname(__FILE__), '../a/b')

.

(I know that File.join(File.dirname(__FILE__), ...) has issues, but that doesn't
mean it's illegal Ruby code.)

Here are run results for the autoload-testcase example under a variety of Ruby
implementations and versions[*]:

    | Implementation                       | Result            |
    +--------------------------------------+-------------------+
    | Ruby 1.8.7-p330                      | resolves Foo::Bar |
    | Ruby 1.9.2-p0                        | raises NameError  |
    | Ruby 1.9.2-p136                      | raises NameError  |
    | Rubinius 1.2.0                       | resolves Foo::Bar |
    | JRuby 1.5.6, 1.8 compatibility mode  | resolves Foo::Bar |
    | JRuby 1.5.6, 1.9 compatibility mode  | resolves Foo::Bar |

A run log is available in the autoload-testcase repository.

So I guess my question can be stated as: does it make sense to permit absolute
paths having . and .. in $LOAD_PATH?

$LOAD_PATH can already contain relative paths; additionally, . and .. are
properly expanded for relative paths.  Furthermore, most shells will expand .
and .. in absolute paths.  Therefore, I think the answer is "yes", but I would
like input on this, especially as I haven't seen this issue raised anywhere
else.  (If it has been raised before, please let me know that, too.)

Thanks,

- David

---

[*] The exceptions thrown by Ruby 1.9.2-p0 and 1.9.2-p136 appear to be triggered
in part by the algorithm of rb_get_expanded_load_path(void).

In r30603 (latest revision as of this writing) the following is present in
rb_get_expanded_load_path at load.c:51-66:

    for (i = 0; i < RARRAY_LEN(load_path); ++i) {
	VALUE str = rb_check_string_type(RARRAY_PTR(load_path)[i]);
	if (NIL_P(str) || !rb_is_absolute_path(RSTRING_PTR(str)))
	    goto relative_path_found;
    }
    return load_path;

    relative_path_found:
      ary = rb_ary_new2(RARRAY_LEN(load_path));
      for (i = 0; i < RARRAY_LEN(load_path); ++i) {
          VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
          rb_str_freeze(path);
          rb_ary_push(ary, path);
      }
      rb_obj_freeze(ary);
      return ary;


On non-DOSISH systems, rb_is_absolute_path expands to

    int
    rb_is_absolute_path(const char *path)
    {
      if (path[0] == '/') return 1;

      return 0;
    }

Therefore, if there are no paths present in $LOAD_PATH for which
rb_is_absolute_path returns false, paths such as /a/./b will not be expanded to
/a/b by rb_get_expanded_load_path.  I haven't yet studied the code long enough
to precisely figure out how that behavior interacts with the autoload mechanism
and produces the name resolution failure, but it does seem to play a major role.