Feature #4969

Subtle issue with require

Added by Thomas Sawyer almost 3 years ago. Updated over 1 year ago.

[ruby-core:37757]
Status:Rejected
Priority:Normal
Assignee:-
Category:-
Target version:-

Description

If I have a library with same name as Ruby standard library in load path (as an example):

lib/abbrev.rb

There is conflict with loading. Ok, I work around:

require 'rbconfig'

# Notice that rubylibdir takes precendence.
LOCATIONS = ::RbConfig::CONFIG.values_at(
'rubylibdir', 'archdir', 'sitelibdir', 'sitearchdir'
)

#
def requireruby(file)
LOCATIONS.each do |loadpath|
if path = lib
find(loadpath, file)
return path
end
end

raise LoadError, "no such file to load -- #{fname}"

end

private

SUFFIXES = ['.rb', '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar']

# Given a +loadpath+, a file's relative path, +relname+, and 
# options hash, determine a matching file exists. Unless +:load+
# option is +true+, this will check for each viable Ruby suffix.
# If a match is found the full path to the file is returned,
# otherwise +nil+.
def lib_find(loadpath, relname)
  if SUFFIXES.include?(File.extname(relname))
    abspath = File.join(loadpath, relname)
    File.exist?(abspath) ? abspath : nil
  else
    SUFFIXES.each do |ext|
      abspath = File.join(loadpath, relname + ext)
      return abspath if File.exist?(abspath)
    end
  end
  nil
end

Now I can do:

require 'abbrev'
require_ruby 'abbrev'

And it works fine. But, if I do:

require_ruby 'abbrev'
require 'abbrev'

The second is not loaded because somehow it seems to confuse it for the first in $LOADED_FEATURES.

I realize this is a very subtle issue and not likely to effect most people, but it presents a big problem for some of my work (e.g. wedge gem)

How is Ruby confusing the two? Can it be fixed? Or is there at least a work around?

History

#1 Updated by Thomas Sawyer almost 3 years ago

Note: ignore the lib_find documentation about the load option, I removed the option hash to simplify this example.

#2 Updated by Aaron Patterson almost 3 years ago

  • ruby -v changed from ruby 1.9.3dev (2011-07-03 trunk 32372) [x86_64-linux] to -

On Mon, Jul 04, 2011 at 04:19:42AM +0900, Thomas Sawyer wrote:

[snip]

How is Ruby confusing the two? Can it be fixed? Or is there at least a work around?

I'm not sure about the code you presented above, but you should be able
to ensure your gem is consulted before stdlib by doing "gem 'whatever'".

Possibly you could just do:

gem 'wedge'
require 'abbrev'

Though, I would just avoid conflicting filenames. Maybe have a
'wedge/abbrev'.

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

#3 Updated by Thomas Sawyer almost 3 years ago

@Aaron Yea, the problem isn't with loading a file of wedge. It has to do with what wedge does. The code I presented is a slightly simplified "wedge" in the project itself. The wedge gem is a lot like polyglot, but works a bit differently. And was originally created to handle the issue of loading ruby/gem files while by-passing any possible name conflicts. Of course it can be use for other things too, but that's what my current use case is.

I am going to do some more in-depth research on this so hopefully I can come back with more specific details on what's causing the problem. Please let me know if you have any ideas. Thanks.

#4 Updated by Yui NARUSE over 2 years ago

  • File deleted (noname)

#5 Updated by Yui NARUSE over 2 years ago

  • Status changed from Open to Feedback

#6 Updated by Nobuyoshi Nakada over 2 years ago

Your require_ruby seems to load nothing. Missed to paste?

Anyway, the behavior does not seem a bug.
It might be a feature request, though I'm not sure what you are suggesting.

#7 Updated by Thomas Sawyer over 2 years ago

Okay, I finally got around to digging into this a bit more. The issue can be seen from this simple example.

Given a local directory containing:

fixture/
abbrev.rb

Then:

$ irb

$LOADPATH.unshift('./fixture')
=> ["./fixture", "/usr/local/lib/ruby/gems/1.9.1/gems/wirble-0.1.3/lib", "/usr/local/lib/ruby/site
ruby/1.9.1", "/usr/local/lib/ruby/siteruby/1.9.1/x8664-linux", "/usr/local/lib/ruby/siteruby", "/usr/local/lib/ruby/vendorruby/1.9.1", "/usr/local/lib/ruby/vendorruby/1.9.1/x8664-linux", "/usr/local/lib/ruby/vendorruby", "/usr/local/lib/ruby/1.9.1", "/usr/local/lib/ruby/1.9.1/x8664-linux"]
require '/usr/local/lib/ruby/1.9.1/abbrev.rb'
=> true
Abbrev
=> Abbrev
require 'abbrev'
=> false

Even though we loaded the standard abbrev.rb file using an absolute path, Ruby thinks that the subsequent require is for the same file. But it is not b/c the files in the 'fixture' directory should be taking precedence over the other location since it is earlier in the $LOAD_PATH.

#8 Updated by Jon Forums over 2 years ago

what happens when you put $LOADED_FEATURES.reject! { |i| i =~/abbrev/ } before the last require

#9 Updated by Thomas Sawyer over 2 years ago

Then it works.

$LOADED_FEATURES.reject! { |i| i =~/abbrev/ }
require 'abbrev'
true

#10 Updated by Thomas Sawyer over 2 years ago

I've gone ahead and released the loadable gem. You can read about it at http://github.com/rubyworks/loadable. I know there has been some talk here about creating a more flexible load path. loadable works by adding load hook objects to the $LOADERS global variable. Load hooks are any object that responds to #call, which loads/requires the file, and #each, that iterates over all loadable files. It also extends #require and #load to accept an options hash to allow loader configurations.

The Ruby Loader that it comes with can be used to isolate loading from Ruby's standard library. But this issue (#4969) prevents it form fully working.

So, what's the word on this?

#11 Updated by Yura Sokolov over 2 years ago

Why not put abbrev.rb into lib/wedge, and then call require 'wedge/abbrev' ? I thought it is standard way.

#12 Updated by Thomas Sawyer over 2 years ago

hi, abbrev.rb is not part of wedge. I just used 'abbrev.rb' as an easy to understand example of the potential problem. Wedge (which has been renamed to 'Loadable' in the latest release), has a "load wedge" that makes it possible to circumvents any possible name clashes. Now it would be nice if everyone followed proper practice and thus avoided all possible clashes, but that is often not the case, like it or not. This can be easily seen from this very partial list, http://github.com/rubyworks/loadable/blob/master/INFRACTIONS.md

Even so, the issue I've run into isn't this per se.* I wrote Loadable to deal with it. The problem is that I can't make Loadable work as it should b/c of the way in which Ruby is handling feature caching. See point #7 of this discussion for what I mean. Thanks.

*Though IMO it would be a good idea for Ruby to deal with this.

#13 Updated by Nobuyoshi Nakada about 2 years ago

  • Tracker changed from Bug to Feature
  • Status changed from Feedback to Rejected

If you still need this, please make the issue clear and refine the proposal, then reopen this or file a new ticket.

#14 Updated by Thomas Sawyer over 1 year ago

I just tried this out on Ruby v1.9.3-p327. And it seems to have been fixed! Yea!

So you can change the status of this from Rejected to Closed (if you'd like to be precise).

Thanks!

Also available in: Atom PDF