Project

General

Profile

Actions

Feature #19889

closed

Let `Kernel.#require` search for files relative to the current working directory for non ./, ../ relative paths

Added by sawa (Tsuyoshi Sawada) 7 months ago. Updated 7 months ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:114796]

Description

My understanding is that ./ and ../ in the given path argument are interpreted relative to:

(1)

  • The current working directory (for load or require)
  • The requiring file's path (for require_relative)

which shows a division of labor between the methods, and seems reasonable. However, when it comes to other relative paths (e.g., foo/bar.rb), they are interpreted relative to:

(2)

  • Paths in $LOAD_PATH (for require)
  • Paths in $LOAD_PATH or the current working directory (for load or require_relative)

For example, given:

  • File some_path/foo/a.rb
  • File some_path/b.rb with content require "foo/a"
  • Current directory at some_path,

running ruby b.rb raises a LoadError, but given:

  • File some_path/foo/a.rb
  • File some_path/b.rb with content require_relative "foo/a"
  • Current directory at some_path,

running ruby b.rb does not raise an error.

The search path in (2) for require is a proper subset of that of load and require_relative. There is no division of labor here; there is only inconvenience for require.

Furthermore, in (1), require (as well as load) is concerned with the current working directory while require_relative is not, but in (2), the relation is reversed: require_relative (as well as load) is concerned with the current working directory while require is not.

This situation is making the specification of require versus require_relative difficult to understand, as well as causing inconvenience.

Proposal: For non-./-or-../ relative paths, I propose to let Kernel.#require search relative to the current working directory if the file is not found relative to the paths in $LOAD_PATH, so that the methods load, require, and require_relative all work the same in this respect.

Actions #1

Updated by sawa (Tsuyoshi Sawada) 7 months ago

  • Description updated (diff)
Actions #2

Updated by sawa (Tsuyoshi Sawada) 7 months ago

  • Description updated (diff)

Updated by nobu (Nobuyoshi Nakada) 7 months ago

  • Status changed from Open to Feedback

sawa (Tsuyoshi Sawada) wrote:

My understanding is that ./ and ../ in the given path argument are interpreted relative to:

(1)

  • The current working directory (for load or require)
  • The requiring file's path (for require_relative)

The former is correct, the latter is wrong.
Always ./ means the current working directory, never the loading file's directory.

Proposal: For non-./-or-../ relative paths, I propose to let Kernel.#require search relative to the current working directory if the file is not found relative to the paths in $LOAD_PATH, so that the methods load, require, and require_relative all work the same in this respect.

You can add "." to $LOAD_PATH, at your own risk.
Historically, $LOAD_PATH had contained "." if $SAFE is 0 old days, but it was totally removed in 2009 for security reasons.
I don't think we will revise it.

Updated by rubyFeedback (robert heiler) 7 months ago

sawa wrote:

This situation is making the specification of require versus require_relative
difficult to understand, as well as causing inconvenience.

I kind of stay(ed) with require and almost never use require_relative. So for me
at the least it is not really an issue. Other ruby devs use require_relative
fine in their gems and code. I guess it is a matter of preference and how you
want to work with extension-code you write in ruby. For me it seemed easier to
think of every extensions as a gem, at the least when possible, and there I
can do require 'name_of_the_gem/first_file_to_load.rb', which then may load
all the other .rb files.

As for ./ and ../ - I kind of try to ignore them completely, and when I get
a listing of a directory content or absolute path, I try to always store
directories, or rather name of directories, with a trailing '/'. My mind just
prefers to see a trailing '/' whenever I work with a directory.

In many of my projects, under base/base.rb, I tend to add a method such as
"def pwd" or "def return_pwd", that is basically Dir.pwd but also ensures
there is one trailing /. This may seem rather pointless, but I kind of like
to use it consistently, and not rely on Dir.pwd directly. I also try to use
File.absolute_path() when it is feasible, even if it may not be absolutely
necessary. It just seems easier for me to rely on such awkward patterns.

On the other hand I (almost) never modify $LOAD_PATH; I think I only do so
for irbrc or so; other than that I try to just let ruby handle $LOAD_PATH
for me. At the least most of the time.

sawa wrote:

Proposal: For non-./-or-../ relative paths, I propose to let Kernel.#require
search relative to the current working directory if the file is not found
relative to the paths in $LOAD_PATH, so that the methods load, require,
and require_relative all work the same in this respect.

I somewhat understand the thought process here, even more so as I myself try
to avoid require_relative. :) But I think, people can use just require() too,
or? For some of the ruby code I use locally, such as, a simple example, the file at strategeme/strategeme.rb, I have:

require_relative 'common.rb'

And this is then a local .rb file I use to power all the other ruby code
that is solely for local use. The rest I tend to publish freely available
on rubygems.org, where I don't need to use require_relative.

Interestingly some ruby gems seem to prefer require_relative, e. g.
webrick:

httpservlet.rb # require_relative 'httpservlet/filehandler'

In such cases I just use require directly rather than require_relative.
But functionality-wise, I think both achieve just about the same?

Updated by vo.x (Vit Ondruch) 7 months ago

I think that the biggest issue is with require_relative and expansion of the relative paths to absolute paths. What it should actually do IMHO is add the current directory, form which the relativeness is derived, to the $LOAD_PATH and use the usual require afterwards.

Updated by vo.x (Vit Ondruch) 7 months ago

In short, this could be the implementation:

def require_relative(path)
  $LOAD_PATH.unshift __dir__
  require path
  $LOAD_PATH.shift
end

Anything else breaks is harmful (talking about issues such as #16978 which is related IMHO).

Updated by vo.x (Vit Ondruch) 7 months ago

Actually this could be even better and more in line with current implementation:

def require_relative(path)
  original_load_path = $LOAD_PATH.dup
  $LOAD_PATH.replace [__dir__]
  require path
  $LOAD_PATH.replace original_load_path
end
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0