Project

General

Profile

Actions

Feature #8781

closed

Use require_relative() instead of require() if possible

Added by ko1 (Koichi Sasada) over 11 years ago. Updated about 6 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:56569]

Description

I wrote a attached small script rrc.rb, stand for "RequireRelativeChecker".

This small script points out that require() can be replaced with require_relative().

"Detecting replace-able require()" algorithm is easy (and not perfect):
(1) If loaded file is at sub (or same) directory of requiring file.
(2) If requiring file foo.rb is at $LOAD_PATH, then check only foo/*.
See attached script for details.

This is a part of output.

/home/ko1/tmp/trunk/lib/ruby/2.1.0/cgi.rb:294: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/cgi/core.rb.
/home/ko1/tmp/trunk/lib/ruby/2.1.0/cgi.rb:295: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/cgi/cookie.rb.
/home/ko1/tmp/trunk/lib/ruby/2.1.0/date.rb:4: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/date/format.rb.
/home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http.rb:1541: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http/exceptions.rb.
/home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http.rb:1543: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http/header.rb.
/home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http.rb:1545: WARNING: Use require_relative() to require /home/ko1/tmp/trunk/lib/ruby/2.1.0/net/http/generic_request.rb.

(all of warnings are attached)

How about to replace require() with require_relative() if it is possible?

Advantage:

  • require_relative() is faster than require() especially with many gems.
  • Easy to detect which file is loaded.

Disadvantage (incompatibility)

  • We can't replace loading file with $LOAD_PATH trick.
    (But I believe nobody expect such behavior)

(I also recommend other gem authors to use require_relative)

Any comments?


Files

rrc.rb (841 Bytes) rrc.rb ko1 (Koichi Sasada), 08/12/2013 03:35 PM
log (145 KB) log ko1 (Koichi Sasada), 08/12/2013 03:35 PM

Related issues 2 (0 open2 closed)

Related to Ruby master - Feature #12973: [RFC] net/http: use require_relative to reduce syscallsClosednormalperson (Eric Wong)Actions
Has duplicate Ruby master - Bug #15206: require_relative in std_libClosedActions

Updated by Eregon (Benoit Daloze) over 11 years ago

Strongly agreed, and it would ensure a whole library is loaded, not by accident a part of it and some other files which happen to have the same structure above in $LOAD_PATH.

Updated by steveklabnik (Steve Klabnik) over 11 years ago

Isn't require_relative generally considered worse because it makes your file structure more brittle? From a code design standpoint, not from a speed standpoint.

Updated by trans (Thomas Sawyer) over 11 years ago

require_relative makes sense for "packages", i.e. where you have a main file that loads a bunch of subordinate files. In other words, the subordinate files could have all been in the main file, but are split out for better organization. So the brittleness you speak of is not an issue here. A much worse brittleness that require_relative helps prevent is when two libraries clobber each other by using the same absolute library path --it shouldn't happen in a well structured project, but people don't always follow the proper conventions.

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 11 years ago

Steve, from my point of view, using require_relative helps us to structure our projects rather than the opposite. That's actually the main reason for my +1, not the speed improvements one.

It helps us avoiding issues that might happen by using a different $LOAD_PATH in some scenarios.

Updated by drbrain (Eric Hodel) over 11 years ago

Following an IRC conversation with Koichi I don't believe this bug proposes to eliminate require_relative outright, instead it is for investigating if require_relative should be used more in the standard library and in gems. The optimization is useful for libraries with many files as it eliminates repeated $LOAD_PATH lookups.

Regarding the $LOAD_PATH trick restriction of require_relative:

For a gem it is not overly restrictive. I typically replace whole libraries as it is easier and more convenient that replacing just one file.

  1. gem unpack foo or git clone git@...:foo
  2. edit files
  3. ruby -I foo/lib …

Is easier than

  1. mkdir new/path/to/dir
  2. cp {old,new}/path/to/dir/foo.rb
  3. edit file
  4. ruby -Ilib …

For the second case, a need to edit multiple files and possibly create a patch makes the first option much more convenient..

For files in the standard library, replacing a file loaded by require_relative that is not part of a gem is more difficult. To alter net/http/request.rb loaded by require_relative you must duplicate the tree of files that require_relative it in order to use the $LOAD_PATH trick. I see adding features of the standard library as default gems a workaround for this restriction.

How is require_relative more brittle that require? If a file is removed from a gem it can be loaded from the wrong path (via -I if in a gem or vice versa). Using require_relative the error is immediate and obvious. It seems to eliminate this class of error entirely.

I would like to note that autoload can't benefit from this optimization as it always uses $LOAD_PATH. Koichi suggested adding a relative: true keyword argument to support this.

Updated by ko1 (Koichi Sasada) over 11 years ago

(2013/08/13 2:25), drbrain (Eric Hodel) wrote:

For files in the standard library, replacing a file loaded by require_relative that is not part of a gem is more difficult. To alter net/http/request.rb loaded by require_relative you must duplicate the tree of files that require_relative it in order to use the $LOAD_PATH trick. I see adding features of the standard library as default gems a workaround for this restriction.

I think this proposal depends on that how many people want to do such a
replacement.

Before reading comments, I had believed that there are no case to
replace only a file such as lib/net/http/request.rb required from
lib/net/http.rb. It depends on a version strictly and we can add/modify
behavior by monkey patching. However, I'm not a heavy user of Ruby :p.

If there are many such cases, I need to withdraw this proposal. (without
Eric's "default gem" proposal)

How is require_relative more brittle that require? If a file is
removed from a gem it can be loaded from the wrong path (via -I if in a
gem or vice versa). Using require_relative the error is immediate and
obvious. It seems to eliminate this class of error entirely.

+1 from design perspective.

--
// SASADA Koichi at atdot dot net

Updated by drbrain (Eric Hodel) over 11 years ago

Even if a switch from require to require_relative is unsuitable for the standard library, we certainly can update the documentation for require and require_relative with recommended use and caveats.

Updated by Eregon (Benoit Daloze) over 11 years ago

ko1 (Koichi Sasada) wrote:

(2013/08/13 2:25), drbrain (Eric Hodel) wrote:

For files in the standard library, replacing a file loaded by require_relative that is not part of a gem is more difficult. To alter net/http/request.rb loaded by require_relative you must duplicate the tree of files that require_relative it in order to use the $LOAD_PATH trick. I see adding features of the standard library as default gems a workaround for this restriction.

I think this proposal depends on that how many people want to do such a
replacement.

I doubt many users do this.

For my part I always track the project under some version control and edit the file directly
(except for the smallest change when the editor undo feature is enough).

Making the structure is probably even easier with
cp -R lib/net edited than
mkdir -p edited/net/http && cp lib/net/http/request.rb edited/net/http.

Updated by ko1 (Koichi Sasada) over 11 years ago

(2013/08/12 15:35), ko1 (Koichi Sasada) wrote:

Advantage:

  • require_relative() is faster than require() especially with many gems.

FYI: with Akira (Matsuda-san), we compare performance of require and
require_relative to load thousands of files.

Without Bundler, require_relative is good performance.
However, with Bundler, require_relative does not help so much.
Maybe most of Ruby user == Rails programmer use Bundler.

So we can concentrate to discuss about "which is a good design?".

--
// SASADA Koichi at atdot dot net

Updated by trans (Thomas Sawyer) over 11 years ago

On Tue, Aug 13, 2013 at 6:14 AM, SASADA Koichi wrote:

(2013/08/12 15:35), ko1 (Koichi Sasada) wrote:

Advantage:

  • require_relative() is faster than require() especially with many gems.

FYI: with Akira (Matsuda-san), we compare performance of require and
require_relative to load thousands of files.

Without Bundler, require_relative is good performance.
However, with Bundler, require_relative does not help so much.
Maybe most of Ruby user == Rails programmer use Bundler.

Does anyone know why performance degrades with Bundler?

Also, I have wondered before about require_relative performance. In my
tests it seemed like it was slower than it should be. That was about a year
ago. Has any work been done to optimize it --is there room to optimize it?

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 11 years ago

Actually, to be completely honest, I use none of them in my Rails projects since they don't play well in development mode with auto-reloading. That's why most people using Rails nowadays will use require_dependency (from active_support/dependencies) or won't require anything and follow Rails locations conventions and let Rails handle their requires automatically...

Updated by ko1 (Koichi Sasada) over 11 years ago

(2013/08/13 19:25), Trans wrote:

FYI: with Akira (Matsuda-san), we compare performance of require and
require_relative to load thousands of files.

Without Bundler, require_relative is good performance.
However, with Bundler, require_relative does not help so much.
Maybe most of Ruby user == Rails programmer use Bundler.

Does anyone know why performance degrades with Bundler?

Also, I have wondered before about require_relative performance. In my
tests it seemed like it was slower than it should be. That was about a
year ago. Has any work been done to optimize it --is there room to
optimize it?

I'm sorry, it was a bad description.

This is not a degration of "require_relative()" with bundler.
Performance improvement of "require()" with Bundler.

Test do:
(1) require some gems
(2-1) require many files
(2-2) require_relative many files

We compared (2-1) and (2-2).

                require() require_relative()

without Bundler about 7sec about 1~2sec
with Bundler about 1~2sec about 1~2sec (* prepare a Gemfile)

test code:

require 'bundler/setup' # with Gemfile or not
require 'haml'
require 'psych'
require 'atomic'
require 'RedCloth'
require 'rspec'

N = 5_000

unless File.exist?("test-files")
Dir.mkdir("test-files")
N.times{|i|
open("test-files/test#{i}.rb", 'w')
}
end

$: << Dir.pwd + "/test-files"
p [$:.size, $:]

N.times{|i|

require_relative "test-files/test#{i}"

require "test#{i}"
}

--
// SASADA Koichi at atdot dot net

Updated by tenderlovemaking (Aaron Patterson) over 11 years ago

On Tue, Aug 13, 2013 at 07:38:01AM +0900, SASADA Koichi wrote:

(2013/08/13 2:25), drbrain (Eric Hodel) wrote:

For files in the standard library, replacing a file loaded by require_relative that is not part of a gem is more difficult. To alter net/http/request.rb loaded by require_relative you must duplicate the tree of files that require_relative it in order to use the $LOAD_PATH trick. I see adding features of the standard library as default gems a workaround for this restriction.

I think this proposal depends on that how many people want to do such a
replacement.

Before reading comments, I had believed that there are no case to
replace only a file such as lib/net/http/request.rb required from
lib/net/http.rb. It depends on a version strictly and we can add/modify
behavior by monkey patching. However, I'm not a heavy user of Ruby :p.

If there are many such cases, I need to withdraw this proposal. (without
Eric's "default gem" proposal)

How is require_relative more brittle that require? If a file is
removed from a gem it can be loaded from the wrong path (via -I if in a
gem or vice versa). Using require_relative the error is immediate and
obvious. It seems to eliminate this class of error entirely.

+1 from design perspective.

If you move the file, then all calls to require_relative in that file
must be changed. If you had just used require, the file can be moved
without changes.

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by ko1 (Koichi Sasada) over 11 years ago

(2013/08/16 14:21), Aaron Patterson wrote:

If you move the file, then all calls to require_relative in that file
must be changed. If you had just used require, the file can be moved
without changes.

Which case?

For example, there are files:
foo.rb
foo/bar/a.rb

And foo.rb has "require 'foo/bar/a'".

If you move foo/bar/a.rb to foo/bar/b.rb, then you need to rewrite to
"require 'foo/bar/b'".

If you move foo/bar/a.rb to foo/baz/a.rb, then you need to rewrite to
"require 'foo/baz/a'".

--
// SASADA Koichi at atdot dot net

Updated by tenderlovemaking (Aaron Patterson) over 11 years ago

On Fri, Aug 16, 2013 at 03:00:59PM +0900, SASADA Koichi wrote:

(2013/08/16 14:21), Aaron Patterson wrote:

If you move the file, then all calls to require_relative in that file
must be changed. If you had just used require, the file can be moved
without changes.

Which case?

For example, there are files:
foo.rb
foo/bar/a.rb

And foo.rb has "require 'foo/bar/a'".

If you move foo/bar/a.rb to foo/bar/b.rb, then you need to rewrite to
"require 'foo/bar/b'".

If you move foo/bar/a.rb to foo/baz/a.rb, then you need to rewrite to
"require 'foo/baz/a'".

Yes, you changed the files that depend on the source for 'foo/bar/a',
but 'foo/bar/a.rb' itself did not change.

If something you depend on changes location, then yes, you should
change your code.

Here is an example with require_relative:

$ mkdir -p lib/foo/bar
$ touch lib/foo.rb
$ echo "require_relative '../../foo'" > lib/foo/bar/baz.rb
$ ruby -I lib -r'foo/bar/baz' -e 0
$ mv lib/foo/bar/baz.rb lib/foo/
$ ruby -I lib -r'foo/baz' -e 0
/Users/aaron/git/example/lib/foo/baz.rb:1:in require_relative': cannot load such file -- /Users/aaron/git/example/foo (LoadError) from /Users/aaron/git/example/lib/foo/baz.rb:1:in <top (required)>'
from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require' from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require'
$

In the above example, "foo.rb" did not change location, yet I have to
change the call to require_relative.

foo/bar/baz.rb depends on foo.rb. foo.rb did not change locations, so
why do I have to change my code?

Let's take the same example, "foo/bar/baz.rb" that depends on "foo.rb",
but instead use require:

$ mkdir -p lib/foo/bar
$ echo "require 'foo'" > lib/foo/bar/baz.rb
$ touch lib/foo.rb
$ ruby -I lib -r'foo/bar/baz' -e 0
$ mv lib/foo/bar/baz.rb lib/foo/
$ ruby -I lib -r'foo/baz' -e 0
$

My dependency, "foo.rb", did not change locations, so I don't need to
change my code. "foo/bar/baz.rb" works as-is, no matter where it is on
the filesystem. :-)

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 11 years ago

Since we were discussing the design perspective, you just demonstrated how the design of using require can become much worse than using require_relative. When you're reading code you can be sure where to look for the files being loaded as expected while when using require you will always have to check $LOAD_PATH to be sure. I don't really think using require to allow such hacks is a good enough reason to favor require instead of require_relative... After all, Ruby already allows you to override code by monkey patching. Why would you need to override a full file?

Updated by steveklabnik (Steve Klabnik) over 11 years ago

Thank you, Aaron, that was what I was trying to say, but put much better. <3

Updated by trans (Thomas Sawyer) over 11 years ago

$ echo "require_relative '../../foo'" > lib/foo/bar/baz.rb

Seriously? That is not a real use case. Proper use of require_relative is downward, not upward.

Updated by tenderlovemaking (Aaron Patterson) over 11 years ago

On Sat, Aug 17, 2013 at 07:17:50AM +0900, trans (Thomas Sawyer) wrote:

Issue #8781 has been updated by trans (Thomas Sawyer).

$ echo "require_relative '../../foo'" > lib/foo/bar/baz.rb

Seriously? That is not a real use case. Proper use of require_relative is downward, not upward.

sigh

First, it is a real use case (as in, people actually use it in real
projects):

[aaron@higgins ruby (trunk)]$ git grep require_relative | grep '..' | wc -l
45
[aaron@higgins ruby (trunk)]$

If Ruby's source code isn't enough examples for you, try a GitHub seach:

https://github.com/search?q=require_relative+..&type=Code&ref=searchresults

It's not up to you what is "proper" use.

Second, my point remains valid whether you go down or up. Let's do
another example.

"foo.rb" depends on "foo/bar/baz.rb".

$ mkdir -p lib/foo/bar
$ touch lib/foo/bar/baz.rb
$ echo "require_relative 'foo/bar/baz'" > lib/foo.rb
$ ruby -I lib -rfoo -e 0
$ mv lib/foo.rb lib/foo/
$ ruby -I lib -rfoo/foo -e 0
/Users/aaron/git/example/lib/foo/foo.rb:1:in require_relative': cannot load such file -- /Users/aaron/git/example/lib/foo/foo/bar/baz (LoadError) from /Users/aaron/git/example/lib/foo/foo.rb:1:in <top (required)>'
from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require' from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require'
$

If we move "foo.rb", we still have to change the call to
"require_relative" even though the file we depend on did not change.

Here is the same example just using require:

$ mkdir -p lib/foo/bar
$ echo "require 'foo/bar/baz'" > lib/foo.rb
$ touch lib/foo/bar/baz.rb
$ ruby -I lib -rfoo -e 0
$ mv lib/foo.rb lib/foo/
$ ruby -I lib -rfoo/foo -e 0
$

Again, "foo.rb" is completely independent of the filesystem. The files
it depends on did not change, so it did not have to change.

I am uncertain how to make the coupling between "require_relative" and
the filesystem more clear than this.

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by tenderlovemaking (Aaron Patterson) over 11 years ago

On Fri, Aug 16, 2013 at 09:35:04AM -0300, Rodrigo Rosenfeld Rosas wrote:

Em 16-08-2013 03:24, Aaron Patterson escreveu:

On Fri, Aug 16, 2013 at 03:00:59PM +0900, SASADA Koichi wrote:

(2013/08/16 14:21), Aaron Patterson wrote:

If you move the file, then all calls to require_relative in that file
must be changed. If you had just used require, the file can be moved
without changes.
Which case?

For example, there are files:
foo.rb
foo/bar/a.rb

And foo.rb has "require 'foo/bar/a'".

If you move foo/bar/a.rb to foo/bar/b.rb, then you need to rewrite to
"require 'foo/bar/b'".

If you move foo/bar/a.rb to foo/baz/a.rb, then you need to rewrite to
"require 'foo/baz/a'".
Yes, you changed the files that depend on the source for 'foo/bar/a',
but 'foo/bar/a.rb' itself did not change.

If something you depend on changes location, then yes, you should
change your code.

Here is an example with require_relative:

$ mkdir -p lib/foo/bar
$ touch lib/foo.rb
$ echo "require_relative '../../foo'" > lib/foo/bar/baz.rb
$ ruby -I lib -r'foo/bar/baz' -e 0
$ mv lib/foo/bar/baz.rb lib/foo/
$ ruby -I lib -r'foo/baz' -e 0
/Users/aaron/git/example/lib/foo/baz.rb:1:in require_relative': cannot load such file -- /Users/aaron/git/example/foo (LoadError) from /Users/aaron/git/example/lib/foo/baz.rb:1:in <top (required)>'
from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require' from /Users/aaron/.rbenv/versions/2.0.0-p247/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in require'
$

In the above example, "foo.rb" did not change location, yet I have to
change the call to require_relative.

foo/bar/baz.rb depends on foo.rb. foo.rb did not change locations, so
why do I have to change my code?

Let's take the same example, "foo/bar/baz.rb" that depends on "foo.rb",
but instead use require:

$ mkdir -p lib/foo/bar
$ echo "require 'foo'" > lib/foo/bar/baz.rb
$ touch lib/foo.rb
$ ruby -I lib -r'foo/bar/baz' -e 0
$ mv lib/foo/bar/baz.rb lib/foo/
$ ruby -I lib -r'foo/baz' -e 0
$

My dependency, "foo.rb", did not change locations, so I don't need to
change my code. "foo/bar/baz.rb" works as-is, no matter where it is on
the filesystem. :-)

Since we were discussing the design perspective, you just
demonstrated how the design of using require can become much worse
than using require_relative. When you're reading code you can be
sure where to look for the files being loaded as expected while when
using require you will always have to check $LOAD_PATH to be sure.

Use $LOADED_FEATURES:

[aaron@higgins example]$ touch foo.rb
[aaron@higgins example]$ irb -I .
irb(main):001:0> x = $LOADED_FEATURES.dup; nil
=> nil
irb(main):002:0> require 'foo'
=> true
irb(main):003:0> $LOADED_FEATURES - x
=> ["/Users/aaron/git/example/foo.rb"]
irb(main):004:0>

I don't really think using require to allow such hacks is a good
enough reason to favor require instead of require_relative... After
all, Ruby already allows you to override code by monkey patching.
Why would you need to override a full file?

Simply requiring a file will execute code. There are times (especially
in tests) where you do not want ANY of the code to be executed (maybe it
connects to a database, or network, etc).

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by trans (Thomas Sawyer) over 11 years ago

But it doesn't matter if it is downward, b/c it is relative to the internal structure. If I move something around in my internal structure, that I might have alter a require statement is to be expected. In your example, if we move foo/bar/baz we'll still break a require, regardless of where foo.rb resides. Worse still, if an external program had depended on the location of foo.rb, moving foo.rb would break their program. In practice, the foo.rb of your example rarely moves, by its very design. It represents a component and the things under foo/ are its subcomponents. Some code in the file might move but not the file itself.

I feel like you are making something of a strawman argument against using require_relative b/c it is doing exactly what require_relative is supposed to do. Meanwhile ignoring its benefits. I really don't understand the objection to it. Used judiciously (better than "properly"?) it works quite well.

As for "proper" use, two dollars to a donut most of those uses of ../ relative requires will be in test scripts. I don't even have to look. That's okay b/c it's localized to building the project. I'd still recommend removing it, for example adding test directory to the $LOAD_PATH when running tests. The usage I am talking about is within lib where we are structuring our programs from top level structures to low level structures. It's just the common pattern that we've all used:

module MyApp -> myapp/
class Foo -> foo.rb and foo/
class Bar -> foo/bar.rb

So in foo.rb we require 'foo/bar.rb' not the other way around. That's all I mean by "proper".

Hey, let me make one other interesting point. I would be totally supportive of not using require_relative --requiring relative to the current file, if we could require relative to the current library's base directory. The load system I created actually has this feature. I think it is really the most "proper" solution of all, but it requires that Ruby have some notion of a library as having a base directory. (To clarify, in your example code lib/foo/ is the base directory of the library.)

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

(13/08/17 13:13), Aaron Patterson wrote:

First, it is a real use case (as in, people actually use it in real
projects):

[aaron@higgins ruby (trunk)]$ git grep require_relative | grep '..' | wc -l
45
[aaron@higgins ruby (trunk)]$

It's only in test directory, and almost is test/ruby/envutil.rb.

I agree that require_relative fits something but may not other.
That example tells us that envutil.rb shouldn't be bound in test/ruby.

Again, "foo.rb" is completely independent of the filesystem. The files
it depends on did not change, so it did not have to change.

I am uncertain how to make the coupling between "require_relative" and
the filesystem more clear than this.

But I don't think your point enough to prohibit to use require_relative.
Why move a file but not edit it?
An author can use it when one thinks it is useful, it's the author's choice.
This proposal is just a proposal, but not mandatory.

--
Nobu Nakada

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 11 years ago

Em 17-08-2013 01:16, Aaron Patterson escreveu:

...

I don't really think using require to allow such hacks is a good
enough reason to favor require instead of require_relative... After
all, Ruby already allows you to override code by monkey patching.
Why would you need to override a full file?
Simply requiring a file will execute code. There are times (especially
in tests) where you do not want ANY of the code to be executed (maybe it
connects to a database, or network, etc).

Now I finally understand how people are using this trick. I believe this
is mostly used for mocking domain classes (Rails models, for instance)
when using ActiveRecord or Sequel, given their metaprogramming tricks on
initialization to fetch the tables meta-data.

But I don't see how this ticket would affect such environments. If I
understand correctly, what Koichi proposed is to replace require with
require_relative on Ruby stdlibs and core classes only. Do you see any
reason you'd like to execute those files? I believe they won't touch any
database or network so they shouldn't slow down any tests, right?

Cheers,
Rodrigo.

Updated by tenderlovemaking (Aaron Patterson) over 11 years ago

On Sat, Aug 17, 2013 at 09:00:34PM +0900, Nobuyoshi Nakada wrote:

(13/08/17 13:13), Aaron Patterson wrote:

First, it is a real use case (as in, people actually use it in real
projects):

[aaron@higgins ruby (trunk)]$ git grep require_relative | grep '..' | wc -l
45
[aaron@higgins ruby (trunk)]$

It's only in test directory, and almost is test/ruby/envutil.rb.

I agree that require_relative fits something but may not other.
That example tells us that envutil.rb shouldn't be bound in test/ruby.

Again, "foo.rb" is completely independent of the filesystem. The files
it depends on did not change, so it did not have to change.

I am uncertain how to make the coupling between "require_relative" and
the filesystem more clear than this.

But I don't think your point enough to prohibit to use require_relative.

I don't think it should be prohibited. I am pointing out why I don't
use it (namely LOAD_PATH hacking).

Why move a file but not edit it?

I am using "file moving" as a way to demonstrate how require_relative
couples your file to its location on the file system. require does
not have this coupling.

An author can use it when one thinks it is useful, it's the author's choice.
This proposal is just a proposal, but not mandatory.

Yes. I think it's fine, I just don't want to use it. Are there no more
ways to speed up require?

--
Aaron Patterson
http://tenderlovemaking.com/

Updated by matsuda (Akira Matsuda) over 11 years ago

FYI: For those of you who wants to estimate how a "real use case" looks
like, here I made tons of require_relatives on Rails
https://github.com/amatsuda/rails/compare/require_relative
Here I replaced all requires to require_relative.
I confirmed it actually works, and as Ko1 said, we confirmed that it's not
at all faster than the current require version, because Bundler already
made require faster in a different way.

Yes. I think it's fine, I just don't want to use it. Are there no more
ways to speed up require?

  1. Use Bundler

  2. Avoid scanning through all installed gems before falling back to the
    original_require:
    https://github.com/ruby/ruby/blob/c106d19/lib/rubygems/core_ext/kernel_require.rb#L41
    This is the reason every require call performs significantly slower than
    require_relative.

  3. Reduce number of installed gems in your box...

On Sun, Aug 18, 2013 at 5:58 AM, Aaron Patterson
wrote:

On Sat, Aug 17, 2013 at 09:00:34PM +0900, Nobuyoshi Nakada wrote:

(13/08/17 13:13), Aaron Patterson wrote:

First, it is a real use case (as in, people actually use it in real
projects):

[aaron@higgins ruby (trunk)]$ git grep require_relative | grep '..'
| wc -l
45
[aaron@higgins ruby (trunk)]$

It's only in test directory, and almost is test/ruby/envutil.rb.

I agree that require_relative fits something but may not other.
That example tells us that envutil.rb shouldn't be bound in test/ruby.

Again, "foo.rb" is completely independent of the filesystem. The files
it depends on did not change, so it did not have to change.

I am uncertain how to make the coupling between "require_relative" and
the filesystem more clear than this.

But I don't think your point enough to prohibit to use require_relative.

I don't think it should be prohibited. I am pointing out why I don't
use it (namely LOAD_PATH hacking).

Why move a file but not edit it?

I am using "file moving" as a way to demonstrate how require_relative
couples your file to its location on the file system. require does
not have this coupling.

An author can use it when one thinks it is useful, it's the author's
choice.
This proposal is just a proposal, but not mandatory.

Yes. I think it's fine, I just don't want to use it. Are there no more
ways to speed up require?

--
Aaron Patterson
http://tenderlovemaking.com/

--
Akira Matsuda

Updated by duerst (Martin Dürst) over 11 years ago

On 2013/08/18 5:58, Aaron Patterson wrote:

I am using "file moving" as a way to demonstrate how require_relative
couples your file to its location on the file system. require does
not have this coupling.

On the other hand, 'require' has a tighter coupling to the file name
than 'reqire_relative'. It's no problem to have two different files of
the same name with 'require_relative', but it won't work with 'require'.

This reminds me of absolute vs. relative links in Web pages. Both have
their advantages, but none of them is perfect.

Regards, Martin.

Updated by ko1 (Koichi Sasada) about 11 years ago

  • Target version changed from 2.1.0 to 2.6

No conclusion.

Actions #29

Updated by shyouhei (Shyouhei Urabe) almost 8 years ago

  • Related to Feature #12973: [RFC] net/http: use require_relative to reduce syscalls added

Updated by shevegen (Robert A. Heiler) over 7 years ago

Martin Dürst wrote back then:

On the other hand, 'require' has a tighter coupling to the file
name than 'reqire_relative'.

Both have a similar requirement - you need to pass in the name of
the file at question.

Both also require some information to the directory tree that you
are operating on; with require_relative being relative to the
.rb file that it is being used.

If the target .rb file changes, then the files that make such
a call onto that file also have to be changed - I think that this
is a bigger limitation altogether, as I am unaware of any more
sophisticated way in ruby to load up per-project code. (I also
do not fully understand the python import way; perhaps they
use a more sophisticated way, I have no idea.)

I am thinking about proposing a different way altogether; e. g.
a way where we could describe the individual .rb files in a
centralized manner, and access them; and then let ruby figure
out the best way to call them. But ... I have not really been
able to come up with any variant that could be great. It's
actually quite difficult.

Perhaps I can share my use case here. I lately rewrote two of my
largest projects in ruby, in total about 300.rb files or so (that's
large for me!).

Since I also split up the logic, and some of the code was years
old, this was not so easy; I had to rename files, I had to change
their positions and of course, due to the hardcoded names and
file paths still, I had to change all of that one by one. I also
had to build-up in a way to avoid circular dependency warnings.

All in all, that was a lot of time that I spent largely doing
re-shuffling of files (and of course, splitting the logic up,
fixing other problems and code style etc...).

My idea here was to be able to use shortcut identifiers for
(certain) files; or perhaps even all files.

For example, say I have files called:

project_base_dir.rb
project_yaml_dir.rb

What these files do is specify the path to the base directory
and the path to the yaml directory (I tend to have a yaml/
directory for many of my larger projects).

I would usually require them like so, if the name of the project
is "foobar" and top-namespace is Foobar, usually module Foobar:

require 'foobar/project_base_dir.rb' # I like to add the extension name

I was thinking of an alternative way instead, something such as:

require_in_project :project_base_dir

The above could be kept track of in a .yml file, where such dependencies
and file structures could be used. For that base directory file, it is
not a big deal, but I often have to add in more base .rb files, and
end up with lots of require lines which have hardcoded path entries
there.

To clarify the above require_in_project line:

The symbol identifier :project_base_dir would of course still
point to a .rb file in question that is hardcoded (information
contained within the project yaml file), so it would point to
foobar/project_base_dir.rb - but the advantage would be that
we can always use such identifiers in the whole project, and
then change this only at ONE point, that is, the specific
yaml file that keeps track of these files. This would be a hybrid
approach by the way - people could still use require, load and
require_relative just fine, but they may also decide to use
this require_in_project() call (do not focus too much on the
name, it is just an example; rather than require_in_project
another name could be used - my point is just to focus on
the functionality here primarily).

I'd wish there was a way to just use identifiers like that instead,
because then I can always keep track of them in a SINGLE file alone,
rather than having to make modifications in all .rb files.

Not sure I was able to explain what I may want ... it's actually a bit
difficult to describe since it is more complex ... sorry for off-topic,
I just wanted to add a bit to this, in particular since perhaps we
can have some more powerful and sophisticated way in ruby 3.x to
deal with larger projects - should we really be required to have
all hardcoded file paths, in every .rb file, like oldschool C when
it comes to a project that already has a full "namespace", such
as in the example given above by "module Foobar"? Surely ruby could
infer some information from a common structure, and I really mean
common basic structures that people often tend to use here ... name
of the gem == toplevel namespace constant, usually a module, due to
being able to use "include NamespaceHere" lateron.

I hope we can get some more progress before ruby 3.x; I am worried
that after ruby 3.x we may again have to wait for a longer time
before doing changes that could perhaps help simplify project
management of larger ruby projects. It's no big deal if a project
is small but ... larger gems are actually another thing. I guess
there is a reason why the rails people built their own way, they
probably have to deal with a very large code base.

Actions #31

Updated by naruse (Yui NARUSE) almost 7 years ago

  • Target version deleted (2.6)
Actions #32

Updated by shyouhei (Shyouhei Urabe) about 6 years ago

  • Has duplicate Bug #15206: require_relative in std_lib added
Actions #33

Updated by marcandre (Marc-Andre Lafortune) about 6 years ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0