New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using require_relative in the Rails codebase #29638
Conversation
5f223ad
to
05bdd4e
Compare
Related to #29417 |
Superlative patch Akira. I am personally positive about using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm not thrilled about how these look... for the same reason I'd use a link to /users/bob
from /users/fred
, I guess: symmetry, avoidance of incidental dependency on filenames, etc
It does seem worthwhile despite my aesthetic concerns, though. 👍
@@ -43,7 +43,7 @@ module ActiveSupport | |||
# | |||
# To access this class outside of Rails, require the core extension with: | |||
# | |||
# require "active_support/core_ext/hash/indifferent_access" | |||
# require_relative "core_ext/hash/indifferent_access" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! Good catch! Fixed.
05bdd4e
to
f851e1f
Compare
@matthewd share the aesthetic concerns, the symmetry breaks, does not look as nice. I believe we see it similarly, as a trade-off, sacrifice a bit aesthetics for practical reasons. Regarding the dependency on file names, I believe in practice it is less than one could think. If you move a file, you'll generally need to revise require calls, either normal or relative. Moving a file to another directory under |
I did have one thought on a different aesthetic trade-off: Taking -require "action_dispatch/http/request"
-require "action_dispatch/middleware/exception_wrapper"
-require "action_dispatch/routing/inspector"
+require_relative "../http/request"
+require_relative "exception_wrapper"
+require_relative "../routing/inspector" We could: -require "action_dispatch/http/request"
-require "action_dispatch/middleware/exception_wrapper"
-require "action_dispatch/routing/inspector"
+require_relative "../../action_dispatch/http/request"
+require_relative "../../action_dispatch/middleware/exception_wrapper"
+require_relative "../../action_dispatch/routing/inspector" It restores [most of] the symmetry, by using a few needless levels of directory traversal -- but retains the speed of avoiding the load path search. I'm not sure how I feel about it, but thought I'd throw it out there. |
I think we could merge this patch, it is huge and we agree it looks in principle like a good change. Then, we can perhaps revise details here and there if we wish, or make a second pass with some uniform pattern if we prefer it. I have done a systematic pass over all the changes and they seem correct. The alternative going always up to the root of the component following parent directories might be interesting to explore, though this patch makes a normal usage of Alternatively, I was thinking that maybe we could just visually sort the lines. From external to internal so to speak. First @amatsuda thanks again for this work ❤️ . |
I wonder if Ruby issue 10222 will manifest itself more often with this change. Will people feel concerned about elevated warnings numbers? |
@pointlessone thanks for the pointer, that has to be kept in mind. Let's see what's the experience in practice. |
For the archives, this was fixed in 4816e82. |
@pointlessone wrote:
It is manifesting itself for me right now on a Jenkins continuos integration server, where /var/lib/jenkins is a symlink to /home/jenkins Aside from the numerous warnings resulting from requiring the same file twice, this makes it impossible to load
Notice how it alternates between loading rails files from EDIT My current workaround is to configure every CI job to use a custom path for its gems so that the gems in use aren't installed in a symlinked directory:
|
I have been thinking about this and I am not totally sure what is the right thing to do. Rails uses From the point of view of Rails team, we have introduced something that in practice has that consequence regardless of the source. Files should not be evaluated twice, period. So, the mathematician in me leans towards the former, and the pragmatist in me leans just slightly towards the latter. |
Well, "introduced" may not be exact. Potentially this could be already there, any So it is mostly a black & white situation I believe. Either we go with the first option, or else we ban |
Yeah, given that it's apparently a nontrivial bug in a relatively (😛) new feature, it does seem like we should avoid it for now. If it were fixed upstream and headed for release on all supported series, it might be reasonable to just expect symlink users to upgrade... but as the bug seems stalled, I think symlinks are too common for us to dismiss as Not Our Problem. |
I don't think double load is fatal. In most cases it's just a bunch of constants redefined/classes reopened. It may be breaking in some pathological cases when some crazy (and broken) metaprogramming is involved, though. I'd hope Rails being affected can motivate someone to fix it on MRI side. |
We could go with That spelling does do The Right Thing, doesn't it? |
I don't think that's any different to Consider when a file first required through a symlimked path. Then, through |
Oh! I misunderstood the problem... Does that suggest we could work around this by getting rubygems to realpath before it requires? Alternatively, we could define our own |
@matthewd Please take a look at MRI issue 10222 for broader context. |
@amatsuda you're a Ruby committer, which is your point of view? |
@fxn @matthewd @pointlessone @yskkin We discussed about this at the developer meetings and RubyKaigi. We concluded this to be So, current Rails master should work as expected with Ruby 2.5+, but this problem remains until we drop Ruby < 2.5 support (or we overwrite I'm kind of happy that we could find and fix Ruby's bug through this challenge, but I'm sorry for messing up some edge users' environment. I'll soon revert my changes. |
@amatsuda great, thanks! Do you have any sense of whether that's likely to be backported? For a workaround, as we specify that the gem's "main" file is to always be required first, I wonder if we could update |
Since Rails requires Ruby version 2.5.0 or later on the master, we could continue working on this patch. |
@bogdanvlviv I don't see a problem with attempting a 2019 version of this — just give @amatsuda credit for the commits if you use these as the base. |
OK, I'll work on this again, maybe after 6.0.0 stable release :) |
@amatsuda I've been already working on this https://github.com/bogdanvlviv/rails/commits/require_relative_2019. I had to ask you whether i could work on this before I started (since you are the author of this PR). Do you mind if I finish work on this? |
@amatsuda still interested in updating this or would you rather pass the buck to @bogdanlviv? Bogdan are you interested in continuing or would you rather pass to someone else? |
Thanks @bogdanvlviv for your offer, but this might perhaps easier and more reliable for me than anyone else to do because I preserve the script that I used for generating this patch. |
👍. Actually, that's why I was asking. Using a script for this definitely will be safer and less time-consuming than doing this by hands. |
Oh, maybe I'll PR this "require active_support/all everywhere" first, so we can cut some |
Summary
This PR replaces 773
Kernel#require
calls toKernel#require_relative
for speed.Details
Ruby has a faster variation of
Kernel#require
namedKernel#require_relative
since its version 1.9. It's considered faster because it simply directly loads the specified file instead of scanning through all $LOAD_PATH entries.https://github.com/ruby/ruby/blob/24171da2226cf82bdd46b691d56ccffafb6b30b4/load.c#L838-L846
Also,
require_relative
is not overridden by any 3rd party libraries such as RubyGems, Bundler, Polyglot, ActiveSupport, etc, unlikerequire
. So we can bypass evil hacks from these gems by callingrequire_relative
.However, this feature is very rarely used in the Rails codebase.
git grep
shows only 79 occurrences in the whole repo, and most of them seems to be tests, bin/*, version.rb, etc.So there're big room for improvements here. And this approach is much easier than adding yet another hack on
Kernel#require
.Benchmark
This patch reduces 1,010 Ruby level methods calls when starting up a vanilla Rails app.
In terms of speed, reduced time was about 6..7% (only 0.1 second) in average on a vanilla app on my local machine. It may be different if the app has longer $LOAD_PATH.