From: Rich Kilmer Date: 2010-04-15T12:36:32+09:00 Subject: [ruby-core:29526] Re: [Bug #3140] gem activation has changed between 1.8 and 1.9 I wrote this original code in gem_prelude. The intent of this was threefold: 1) Make RubyGems packaged files accessible with require/load in 1.9 without having to require 'rubygems' 2) Minimize startup time (don't load the full rubygems library if its not going to be used) 3) Load rubygems proper if need be. In a nutshell, the way I solved this was to push all the highest version of gem's directories on the load path. Thus regular require and load would find those files without require 'rubygems'. This solved 1, 2 (kind of...unless you have a lot of gems) and 3 (within the gem_prelude). The problem with this is it does not honor Gem's dependency management (as Evan outlined above). The way RubyGems normally works is it replaces require. If the original require raises a LoadError it catches it and searches the gem directories for that file in gems, and if it finds it in a gem, it 'activates' that gem. Activation first goes through the list of gems that the gem being activated is dependent on, and activates those gems (cascading until all the required gems are activated) then activates the gem. Activation is just pushing the gem path on a load path and marking that gem as loaded. After this is done, the original require is called again and this time if finds the file since the load path has now been modified. RubyGems is still to heavy IMHO to just load at every startup. To fix this what I am thinking we could do is: A) put the overridden require in the gem_prelude that is currently in 1.8 + loading rubygems (the first time) if the original require does not find the required file I don't really like this because if a LoadError occurs the error will include (in the call stack) the gem_prelude code and it calls require twice (just like the current 1.8 solution) This will solve the startup speed (by lazy loading gems) and make it work without require 'rubygems' I just don't like it. B) port the code that's in the 1.8 rubygems require into the load.c code. This solves the problems too, and could remove the call stack issue. The problems are there will be calls to modules/classes in a standard library (rubygems) in the load.c code. I don't really like this either but to me its better than A. C) implement a 'hook' in load.c where, if a file is not found on the load path (in either require or load) it calls that hook (a Kernel private method). That hook can then modify the load path returning that a path where the file has been found or nil if it was not found. If a path is returned load/require do their thing, otherwise a LoadError is raised. Then gem_prelude would be changed to use this hook. I would prefer C. I know we don't have a lot of time before 1.9.2 but if we get our ducks in a row we can make this work...it does need to be fixed. Best, Rich On Wed, Apr 14, 2010 at 12:41 PM, Evan Phoenix wrote: > Is there any comment on this? This is a big bug in 1.9.2 that we'd like to get fixed as soon as we can, but I need some input on it. > > �- Evan > > On Apr 12, 2010, at 6:16 PM, Evan Phoenix wrote: > >> After a brief discussion with Eric Hodel about this, there are a few questions before we can figure out how to solve this: >> >> 1) Why are all gems being required in gem_prelude in the first place? Eric wasn't sure, we thought that perhaps this was a requirement that ruby-core had. >> >> 2) If all gems are required in gem_prelude, what's the expected behavior for version conflicts? >> >> For example: >> >> gem old_A requires gem B version 1.0 >> gem new_C requires gem B version 2.0 >> >> Because we're requiring them all automatically, we'll get old_A and B version 2.0. This means that old_A will very likely not work at all. >> >> This is not a contrived example, it's very easy for people to have old gems installed. >> >> It's true this can exist in 1.8, but because a user has typically explicitly required some gems, it happens much less often. And in this case, the user can fix the situation themselves by removing the gem requirements in their particular code. >> >> This problem is hit especially often by rails users, because they use a large number of gems. As more users are using rails on 1.9, this problem will get worse as newer versions of gems are released and installed but not everything is upgraded to new versions. >> >> >> My recommendation is that gem_prelude not require any gems and simply provide the functionality to automatically load all of rubygems if it's used. >> >> One addition to gem_prelude would probably be it's custom require so that user can load code from gems directly. >> >> The behavior then will be just as it is in 1.8. >> >> - Evan Phoenix >> >> >> On Apr 12, 2010, at 5:45 PM, Aaron Patterson wrote: >> >>> Bug #3140: gem activation has changed between 1.8 and 1.9 >>> http://redmine.ruby-lang.org/issues/show/3140 >>> >>> Author: Aaron Patterson >>> Status: Open, Priority: Normal >>> Target version: 1.9.2 >>> ruby -v: ruby 1.9.2dev (2010-04-12 trunk 27317) [x86_64-darwin10.2.0] >>> >>> 1.8 will raise gem activation errors where ruby 1.9 will not. >>> >>> To reproduce this bug, first install these gems: >>> >>> $ gem install rubygems-bug-parent >>> $ gem install rubygems-bug-child >>> >>> Your gem list should look like this: >>> >>> $ gem list rubygems-bug >>> >>> *** LOCAL GEMS *** >>> >>> rubygems-bug-child (1.1, 1.0) >>> rubygems-bug-parent (1.0) >>> >>> Then run the following program: >>> >>> � $ ruby -rubygems -e "require 'rubygems-bug-child'; require 'rubygems-bug-parent'" >>> >>> Ruby 1.8 will raise an activation error because of the conflicting versions: >>> >>> Here I am in version 1.1 >>> /Library/Ruby/Site/1.8/rubygems.rb:230:in `activate': can't activate rubygems-bug-child (= 1.0.0, runtime) for ["rubygems-bug-parent-1.0"], already activated rubygems-bug-child-1.1 for [] (Gem::LoadError) >>> � � �from /Library/Ruby/Site/1.8/rubygems.rb:246:in `activate' >>> � � �from /Library/Ruby/Site/1.8/rubygems.rb:245:in `each' >>> � � �from /Library/Ruby/Site/1.8/rubygems.rb:245:in `activate' >>> � � �from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:35:in `require' >>> � � �from -e:1 >>> >>> Ruby 1.9 will continue along using the 1.1 version, and never detect the version conflict: >>> >>> $ ruby -rubygems -e "require 'rubygems-bug-child'; require 'rubygems-bug-parent'" >>> Here I am in version 1.1 >>> >>> >>> ---------------------------------------- >>> http://redmine.ruby-lang.org >>> >>> >> >> >> > > >