lib/faster_require.rb in faster_require-0.7.0 vs lib/faster_require.rb in faster_require-0.7.2

- old
+ new

@@ -1,288 +1,295 @@ -require 'rbconfig' -#require 'rubygems' -#require 'ruby-debug' - -module FastRequire - $FAST_REQUIRE_DEBUG ||= $DEBUG # can set via $DEBUG, or on its own. - - def self.setup - @@dir = File.expand_path('~/.ruby_faster_require_cache') - Dir.mkdir @@dir unless File.directory?(@@dir) - - parts = [File.basename($0), RUBY_VERSION, RUBY_PLATFORM, File.basename(Dir.pwd), Dir.pwd, File.dirname($0), File.expand_path(File.dirname($0))].map{|part| sanitize(part)} - loc_name = (parts.map{|part| part[0..5]} + parts).join('-')[0..75] # try to be unique, but short... - @@loc = @@dir + '/' + loc_name - end - - def self.sanitize filename - filename.gsub(/[\/:]/, '_') - end - - FastRequire.setup - - def self.load filename - @@require_locs = Marshal.restore( File.open(filename, 'rb') {|f| f.read}) rescue {} - end - - if File.exist?(@@loc) - FastRequire.load @@loc - else - @@require_locs = {} - end - - @@already_loaded = {} - - # try to see where this file was loaded from, from $: - # partial_name might be abc.rb, or might be abc - # partial_name might be a full path, too - def self.guess_discover partial_name, add_dot_rb = false - - # test for full path first - # unfortunately it has to be a full separate test - # for windoze sake, as drive letter could be different than slapping a '/' on the dir to test list... - tests = [partial_name] - - if add_dot_rb - tests << partial_name + '.rb' - tests << partial_name + '.' + RbConfig::CONFIG['DLEXT'] - end - - tests.each{|b| - # assume that .rb.rb is...valid...? - if File.file?(b) && ((b[-3..-1] == '.rb') || (b[-3..-1] == '.' + RbConfig::CONFIG['DLEXT'])) - return File.expand_path(b) - end - } - - for dir in $: - if File.file?(b = (dir + '/' + partial_name)) - # make sure we require a file that has the right suffix... - if (b[-3..-1] == '.rb') || (b[-3..-1] == '.' + RbConfig::CONFIG['DLEXT']) - return File.expand_path(b) - end - - end - end - - if add_dot_rb && (partial_name[-3..-1] != '.rb') && (partial_name[-3..-1] != '.' + RbConfig::CONFIG['DLEXT']) - guess_discover(partial_name + '.rb') || guess_discover(partial_name + '.') - else - nil - end - end - - $LOADED_FEATURES.each{|already_loaded| - # in 1.8 they might be partial paths - # in 1.9, they might be non collapsed paths - # so we have to sanitize them here... - # XXXX File.exist? is a bit too loose, here... - if File.exist?(already_loaded) - key = File.expand_path(already_loaded) - else - key = FastRequire.guess_discover(already_loaded) || already_loaded - end - @@already_loaded[key] = true - } - - @@already_loaded[File.expand_path(__FILE__)] = true # this file itself isn't in loaded features, yet, but very soon will be.. - # a special case--I hope... - - # also disallow re- $0 - @@require_locs[$0] = File.expand_path($0) # so when we run into it on a require, we will skip it... - @@already_loaded[File.expand_path($0)] = true - - # XXXX within a very long depth to require fast_require, - # require 'a' => 'b' => 'c' => 'd' & fast_require - # then - # => 'b.rb' - # it works always - - def self.already_loaded - @@already_loaded - end - - def self.require_locs - @@require_locs - end - - def self.dir - @@dir - end - - at_exit { - FastRequire.default_save - } - - def self.default_save - self.save @@loc - end - - def self.save to_file - File.open(to_file, 'wb'){|f| f.write Marshal.dump(@@require_locs)} - end - - def self.clear_all! - require 'fileutils' - FileUtils.rm_rf @@dir if File.exist? @@dir - @@require_locs.clear - setup - end - - private - def last_caller - caller[-2] - end - - IN_PROCESS = [] - ALL_IN_PROCESS = [] - @@count = 0 - public - - def require_cached lib - lib = lib.to_s # might not be zactly 1.9 compat... to_path ?? - ALL_IN_PROCESS << [lib, @@count += 1] - begin - p 'doing require ' + lib + ' from ' + caller[-1] if $FAST_REQUIRE_DEBUG - if known_loc = @@require_locs[lib] - if @@already_loaded[known_loc] - p 'already loaded ' + known_loc + ' ' + lib if $FAST_REQUIRE_DEBUG - return false - end - @@already_loaded[known_loc] = true - if known_loc =~ /\.#{RbConfig::CONFIG['DLEXT']}$/ - puts 'doing original_non_cached_require on .so full path ' + known_loc if $FAST_REQUIRE_DEBUG - original_non_cached_require known_loc # not much we can do there...too bad...well at least we pass it a full path though :P - else - unless $LOADED_FEATURES.include? known_loc - if known_loc =~ /rubygems.rb$/ - puts 'requiring rubygems ' + lib if $FAST_REQUIRE_DEBUG - original_non_cached_require(lib) # revert to normal require so rubygems doesn't freak out when it finds itself already in $LOADED_FEATURES with rubygems > 1.6 :P - else - IN_PROCESS << known_loc - begin - if $FAST_REQUIRE_DEBUG - puts 'doing cached loc eval on ' + lib + '=>' + known_loc + " with stack:" + IN_PROCESS.join(' ') - end - $LOADED_FEATURES << known_loc - # fakely add the load path, too, so that autoload for the same file/path in gems will work <sigh> [rspec2] - no_suffix_full_path = known_loc.gsub(/\.[^.]+$/, '') - no_suffix_lib = lib.gsub(/\.[^.]+$/, '') - libs_path = no_suffix_full_path.gsub(no_suffix_lib, '') - libs_path = File.expand_path(libs_path) # strip off trailing '/' - $: << libs_path unless $:.index(libs_path) - # try some more autoload conivings...so that it won't attempt to autoload if it runs into it later... - relative_full_path = known_loc.sub(libs_path, '')[1..-1] - # $LOADED_FEATURES << relative_full_path.gsub('.rb', '') # don't think you need this one - $LOADED_FEATURES << relative_full_path # add in with .rb, too. - - # load(known_loc, false) # too slow - eval(File.open(known_loc, 'rb') {|f| f.read}, TOPLEVEL_BINDING, known_loc) # note the 'rb' here--this means it's reading .rb files as binary, which *typically* works...maybe unnecessary though? - ensure - raise 'unexpected' unless IN_PROCESS.pop == known_loc - end - # --if it breaks re-save the offending file in binary mode, or file an issue on the tracker... - return true - end - else - puts 'ignoring already loaded [circular require?] ' + known_loc + ' ' + lib if $FAST_REQUIRE_DEBUG - end - end - else - # we don't know the location--let Ruby's original require do the heavy lifting for us here - old = $LOADED_FEATURES.dup - if(original_non_cached_require(lib)) - # debugger might land here the first time you run a script and it doesn't have a require - # cached yet... - new = $LOADED_FEATURES - old - found = new.last - - # incredibly, in 1.8.x, this doesn't always get set to a full path. - if RUBY_VERSION < '1.9' - if !File.file?(found) - # discover the full path. - dir = $:.find{|path| File.file?(path + '/' + found)} - return true unless dir # give up, case jruby socket.jar "mysterious" - found = dir + '/' + found - end - found = File.expand_path(found); - end - puts 'found new loc:' + lib + '=>' + found if $FAST_REQUIRE_DEBUG - @@require_locs[lib] = found - @@already_loaded[found] = true - return true - else - - # this is expected if it's for libraries required before faster_require was [like rbconfig] - # raise 'actually expected' + lib if RUBY_VERSION >= '1.9.0' - puts 'already loaded, apparently [require returned false], trying to discover how it was redundant... ' + lib if $FAST_REQUIRE_DEBUG - # this probably was something like - # the first pass was require 'regdeferred' - # now it's a different require 'regdeferred.rb' - # which fails (or vice versa) - # so figure out why - # calc location, expand, map back - where_found = FastRequire.guess_discover(lib, true) - if where_found - puts 'inferred lib loc:' + lib + '=>' + where_found if $FAST_REQUIRE_DEBUG - @@require_locs[lib] = where_found - # unfortunately if it's our first pass - # and we are in the middle of a "real" require - # that is circular - # then $LOADED_FEATURES or (AFAIK) nothing will have been set - # for us to be able to assert that - # so...I think we'll end up - # just fudging for a bit - # raise 'not found' unless @@already_loaded[where_found] # should have already been set...I think... - else - if $FAST_REQUIRE_DEBUG - # happens for enumerator XXXX - puts 'unable to infer ' + lib + ' location' if $FAST_REQUIRE_DEBUG - @@already_loaded[found] = true # so hacky... - end - end - return false # XXXX test all these return values - end - end - ensure - raise 'huh' unless ALL_IN_PROCESS.pop[0] == lib - end - end - -end - -module Kernel - - if(defined?(@already_using_faster_require)) - raise 'loading twice not allowed...we should never get here!' - end - @already_using_faster_require = true - # overwrite old require... - include FastRequire - if defined?(gem_original_require) - class << self - alias :original_remove_method :remove_method - - def remove_method method # I think this actually might be needed <sigh> - if method.to_s == 'require' - #p 'not removing old require, since that\'s ours now' - else - original_remove_method method - end - end - - end - - # unused? - # def remove_method method - # p 'in mine2' - # end - - # similarly overwrite this one...I guess...1.9.x...rubygems uses this as its default...I think... - alias :original_non_cached_require :gem_original_require - alias :gem_original_require :require_cached - else - alias :original_non_cached_require :require - alias :require :require_cached +require 'rbconfig' + +module FastRequire + $FAST_REQUIRE_DEBUG ||= $DEBUG # can set via $DEBUG, or on its own. + + def self.setup + begin + @@dir = File.expand_path('~/.ruby_faster_require_cache') + rescue ArgumentError => e # like couldn't find HOME environment or the like + whoami = `whoami`.strip + if File.directory?(home = "/home/#{whoami}") # assume writable :P + @@dir = home + '/.ruby_faster_require_cache' + else + raise e + end + end + + Dir.mkdir @@dir unless File.directory?(@@dir) + + parts = [File.basename($0), RUBY_VERSION, RUBY_PLATFORM, File.basename(Dir.pwd), Dir.pwd, File.dirname($0), File.expand_path(File.dirname($0))].map{|part| sanitize(part)} + loc_name = (parts.map{|part| part[0..5]} + parts).join('-')[0..75] # try to be unique, but short... + @@loc = @@dir + '/' + loc_name end - -end \ No newline at end of file + + def self.sanitize filename + filename.gsub(/[\/:]/, '_') + end + + FastRequire.setup + + def self.load filename + @@require_locs = Marshal.restore( File.open(filename, 'rb') {|f| f.read}) rescue {} + end + + if File.exist?(@@loc) + FastRequire.load @@loc + else + @@require_locs = {} + end + + @@already_loaded = {} + + # try to see where this file was loaded from, from $: + # partial_name might be abc.rb, or might be abc + # partial_name might be a full path, too + def self.guess_discover partial_name, add_dot_rb = false + + # test for full path first + # unfortunately it has to be a full separate test + # for windoze sake, as drive letter could be different than slapping a '/' on the dir to test list... + tests = [partial_name] + + if add_dot_rb + tests << partial_name + '.rb' + tests << partial_name + '.' + RbConfig::CONFIG['DLEXT'] + end + + tests.each{|b| + # assume that .rb.rb is...valid...? + if File.file?(b) && ((b[-3..-1] == '.rb') || (b[-3..-1] == '.' + RbConfig::CONFIG['DLEXT'])) + return File.expand_path(b) + end + } + + for dir in $: + if File.file?(b = (dir + '/' + partial_name)) + # make sure we require a file that has the right suffix... + if (b[-3..-1] == '.rb') || (b[-3..-1] == '.' + RbConfig::CONFIG['DLEXT']) + return File.expand_path(b) + end + + end + end + + if add_dot_rb && (partial_name[-3..-1] != '.rb') && (partial_name[-3..-1] != '.' + RbConfig::CONFIG['DLEXT']) + guess_discover(partial_name + '.rb') || guess_discover(partial_name + '.') + else + nil + end + end + + $LOADED_FEATURES.each{|already_loaded| + # in 1.8 they might be partial paths + # in 1.9, they might be non collapsed paths + # so we have to sanitize them here... + # XXXX File.exist? is a bit too loose, here... + if File.exist?(already_loaded) + key = File.expand_path(already_loaded) + else + key = FastRequire.guess_discover(already_loaded) || already_loaded + end + @@already_loaded[key] = true + } + + @@already_loaded[File.expand_path(__FILE__)] = true # this file itself isn't in loaded features, yet, but very soon will be.. + # a special case--I hope... + + # also disallow re- $0 + @@require_locs[$0] = File.expand_path($0) # so when we run into it on a require, we will skip it... + @@already_loaded[File.expand_path($0)] = true + + # XXXX within a very long depth to require fast_require, + # require 'a' => 'b' => 'c' => 'd' & fast_require + # then + # => 'b.rb' + # it works always + + def self.already_loaded + @@already_loaded + end + + def self.require_locs + @@require_locs + end + + def self.dir + @@dir + end + + at_exit { + FastRequire.default_save + } + + def self.default_save + self.save @@loc + end + + def self.save to_file + File.open(to_file, 'wb'){|f| f.write Marshal.dump(@@require_locs)} + end + + def self.clear_all! + require 'fileutils' + FileUtils.rm_rf @@dir if File.exist? @@dir + @@require_locs.clear + setup + end + + private + def last_caller + caller[-2] + end + + IN_PROCESS = [] + ALL_IN_PROCESS = [] + @@count = 0 + public + + def require_cached lib + lib = lib.to_s # might not be zactly 1.9 compat... to_path ?? + ALL_IN_PROCESS << [lib, @@count += 1] + begin + p 'doing require ' + lib + ' from ' + caller[-1] if $FAST_REQUIRE_DEBUG + if known_loc = @@require_locs[lib] + if @@already_loaded[known_loc] + p 'already loaded ' + known_loc + ' ' + lib if $FAST_REQUIRE_DEBUG + return false + end + @@already_loaded[known_loc] = true + if known_loc =~ /\.#{RbConfig::CONFIG['DLEXT']}$/ + puts 'doing original_non_cached_require on .so full path ' + known_loc if $FAST_REQUIRE_DEBUG + original_non_cached_require known_loc # not much we can do there...too bad...well at least we pass it a full path though :P + else + unless $LOADED_FEATURES.include? known_loc + if known_loc =~ /rubygems.rb$/ + puts 'requiring rubygems ' + lib if $FAST_REQUIRE_DEBUG + original_non_cached_require(lib) # revert to normal require so rubygems doesn't freak out when it finds itself already in $LOADED_FEATURES with rubygems > 1.6 :P + else + IN_PROCESS << known_loc + begin + if $FAST_REQUIRE_DEBUG + puts 'doing cached loc eval on ' + lib + '=>' + known_loc + " with stack:" + IN_PROCESS.join(' ') + end + $LOADED_FEATURES << known_loc + # fakely add the load path, too, so that autoload for the same file/path in gems will work <sigh> [rspec2] + no_suffix_full_path = known_loc.gsub(/\.[^.]+$/, '') + no_suffix_lib = lib.gsub(/\.[^.]+$/, '') + libs_path = no_suffix_full_path.gsub(no_suffix_lib, '') + libs_path = File.expand_path(libs_path) # strip off trailing '/' + $: << libs_path unless $:.index(libs_path) + # try some more autoload conivings...so that it won't attempt to autoload if it runs into it later... + relative_full_path = known_loc.sub(libs_path, '')[1..-1] + # $LOADED_FEATURES << relative_full_path.gsub('.rb', '') # don't think you need this one + $LOADED_FEATURES << relative_full_path # add in with .rb, too. + + # load(known_loc, false) # too slow + eval(File.open(known_loc, 'rb') {|f| f.read}, TOPLEVEL_BINDING, known_loc) # note the 'rb' here--this means it's reading .rb files as binary, which *typically* works...maybe unnecessary though? + ensure + raise 'unexpected' unless IN_PROCESS.pop == known_loc + end + # --if it breaks re-save the offending file in binary mode, or file an issue on the tracker... + return true + end + else + puts 'ignoring already loaded [circular require?] ' + known_loc + ' ' + lib if $FAST_REQUIRE_DEBUG + end + end + else + # we don't know the location--let Ruby's original require do the heavy lifting for us here + old = $LOADED_FEATURES.dup + if(original_non_cached_require(lib)) + # debugger might land here the first time you run a script and it doesn't have a require + # cached yet... + new = $LOADED_FEATURES - old + found = new.last + + # incredibly, in 1.8.x, this doesn't always get set to a full path. + if RUBY_VERSION < '1.9' + if !File.file?(found) + # discover the full path. + dir = $:.find{|path| File.file?(path + '/' + found)} + return true unless dir # give up, case jruby socket.jar "mysterious" + found = dir + '/' + found + end + found = File.expand_path(found); + end + puts 'found new loc:' + lib + '=>' + found if $FAST_REQUIRE_DEBUG + @@require_locs[lib] = found + @@already_loaded[found] = true + return true + else + + # this is expected if it's for libraries required before faster_require was [like rbconfig] + # raise 'actually expected' + lib if RUBY_VERSION >= '1.9.0' + puts 'already loaded, apparently [require returned false], trying to discover how it was redundant... ' + lib if $FAST_REQUIRE_DEBUG + # this probably was something like + # the first pass was require 'regdeferred' + # now it's a different require 'regdeferred.rb' + # which fails (or vice versa) + # so figure out why + # calc location, expand, map back + where_found = FastRequire.guess_discover(lib, true) + if where_found + puts 'inferred lib loc:' + lib + '=>' + where_found if $FAST_REQUIRE_DEBUG + @@require_locs[lib] = where_found + # unfortunately if it's our first pass + # and we are in the middle of a "real" require + # that is circular + # then $LOADED_FEATURES or (AFAIK) nothing will have been set + # for us to be able to assert that + # so...I think we'll end up + # just fudging for a bit + # raise 'not found' unless @@already_loaded[where_found] # should have already been set...I think... + else + if $FAST_REQUIRE_DEBUG + # happens for enumerator XXXX + puts 'unable to infer ' + lib + ' location' if $FAST_REQUIRE_DEBUG + @@already_loaded[found] = true # so hacky... + end + end + return false # XXXX test all these return values + end + end + ensure + raise 'huh' unless ALL_IN_PROCESS.pop[0] == lib + end + end + +end + +module Kernel + + if(defined?(@already_using_faster_require)) + raise 'loading twice not allowed...we should never get here!' + end + @already_using_faster_require = true + # overwrite old require... + include FastRequire + if defined?(gem_original_require) + class << self + alias :original_remove_method :remove_method + + def remove_method method # I think this actually might be needed <sigh> + if method.to_s == 'require' + #p 'not removing old require, since that\'s ours now' + else + original_remove_method method + end + end + + end + + # unused? + # def remove_method method + # p 'in mine2' + # end + + # similarly overwrite this one...I guess...1.9.x...rubygems uses this as its default...I think... + alias :original_non_cached_require :gem_original_require + alias :gem_original_require :require_cached + else + alias :original_non_cached_require :require + alias :require :require_cached + end +end