1#-- 2# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. 3# All rights reserved. 4# See LICENSE.txt for permissions. 5#++ 6 7require 'monitor' 8 9module Kernel 10 11 RUBYGEMS_ACTIVATION_MONITOR = Monitor.new # :nodoc: 12 13 if defined?(gem_original_require) then 14 # Ruby ships with a custom_require, override its require 15 remove_method :require 16 else 17 ## 18 # The Kernel#require from before RubyGems was loaded. 19 20 alias gem_original_require require 21 private :gem_original_require 22 end 23 24 ## 25 # When RubyGems is required, Kernel#require is replaced with our own which 26 # is capable of loading gems on demand. 27 # 28 # When you call <tt>require 'x'</tt>, this is what happens: 29 # * If the file can be loaded from the existing Ruby loadpath, it 30 # is. 31 # * Otherwise, installed gems are searched for a file that matches. 32 # If it's found in gem 'y', that gem is activated (added to the 33 # loadpath). 34 # 35 # The normal <tt>require</tt> functionality of returning false if 36 # that file has already been loaded is preserved. 37 38 def require path 39 RUBYGEMS_ACTIVATION_MONITOR.enter 40 41 path = path.to_path if path.respond_to? :to_path 42 43 spec = Gem.find_unresolved_default_spec(path) 44 if spec 45 Gem.remove_unresolved_default_spec(spec) 46 gem(spec.name) 47 end 48 49 # If there are no unresolved deps, then we can use just try 50 # normal require handle loading a gem from the rescue below. 51 52 if Gem::Specification.unresolved_deps.empty? then 53 begin 54 RUBYGEMS_ACTIVATION_MONITOR.exit 55 return gem_original_require(path) 56 ensure 57 RUBYGEMS_ACTIVATION_MONITOR.enter 58 end 59 end 60 61 # If +path+ is for a gem that has already been loaded, don't 62 # bother trying to find it in an unresolved gem, just go straight 63 # to normal require. 64 #-- 65 # TODO request access to the C implementation of this to speed up RubyGems 66 67 spec = Gem::Specification.find { |s| 68 s.activated? and s.contains_requirable_file? path 69 } 70 71 begin 72 RUBYGEMS_ACTIVATION_MONITOR.exit 73 return gem_original_require(path) 74 ensure 75 RUBYGEMS_ACTIVATION_MONITOR.enter 76 end if spec 77 78 # Attempt to find +path+ in any unresolved gems... 79 80 found_specs = Gem::Specification.find_in_unresolved path 81 82 # If there are no directly unresolved gems, then try and find +path+ 83 # in any gems that are available via the currently unresolved gems. 84 # For example, given: 85 # 86 # a => b => c => d 87 # 88 # If a and b are currently active with c being unresolved and d.rb is 89 # requested, then find_in_unresolved_tree will find d.rb in d because 90 # it's a dependency of c. 91 # 92 if found_specs.empty? then 93 found_specs = Gem::Specification.find_in_unresolved_tree path 94 95 found_specs.each do |found_spec| 96 found_spec.activate 97 end 98 99 # We found +path+ directly in an unresolved gem. Now we figure out, of 100 # the possible found specs, which one we should activate. 101 else 102 103 # Check that all the found specs are just different 104 # versions of the same gem 105 names = found_specs.map(&:name).uniq 106 107 if names.size > 1 then 108 raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}" 109 end 110 111 # Ok, now find a gem that has no conflicts, starting 112 # at the highest version. 113 valid = found_specs.select { |s| s.conflicts.empty? }.last 114 115 unless valid then 116 le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate" 117 le.name = names.first 118 raise le 119 end 120 121 valid.activate 122 end 123 124 begin 125 RUBYGEMS_ACTIVATION_MONITOR.exit 126 return gem_original_require(path) 127 ensure 128 RUBYGEMS_ACTIVATION_MONITOR.enter 129 end 130 rescue LoadError => load_error 131 if load_error.message.start_with?("Could not find") or 132 (load_error.message.end_with?(path) and Gem.try_activate(path)) then 133 begin 134 RUBYGEMS_ACTIVATION_MONITOR.exit 135 return gem_original_require(path) 136 ensure 137 RUBYGEMS_ACTIVATION_MONITOR.enter 138 end 139 end 140 141 raise load_error 142 ensure 143 RUBYGEMS_ACTIVATION_MONITOR.exit 144 end 145 146 private :require 147 148end 149 150