Module: Gem::QuickLoader

Included in:
Gem
Defined in:
lib/faster_rubygems/my_gem_prelude.rb,
lib/faster_rubygems/prelude_bin_path.rb,
lib/faster_rubygems/prelude_cached_load.rb,
lib/faster_rubygems/prelude_create_cache.rb

Defined Under Namespace

Modules: PreludeRequire

Constant Summary collapse

GemPaths =
{}
GemVersions =
{}
GemsActivated =
{}
AllCaches =

if the gem dir doesn’t exist, don’t count it against us

Gem.path.select{|path| File.exist?(path)}.map{|path|
   cache_name = path + '/.faster_rubygems_cache'
   if File.exist?(cache_name)
      File.open(cache_name, 'rb') do |f|
        [path, Marshal.load(f)]
      end
   else
     $stderr.puts 'faster_rubygems: a cache file does not exist! reverting to full load path ' + cache_name
     nil
   end
}

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



348
349
350
351
352
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 348

def method_missing(method, *args, &block)
  QuickLoader.load_full_rubygems_library
  super unless Gem.respond_to?(method)
  Gem.send(method, *args, &block)
end

Class Method Details

.fake_rubygems_as_loadedObject



179
180
181
182
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 179

def self.fake_rubygems_as_loaded
  path = path_to_full_rubygems_library
  $" << path unless $".include?(path)
end

.load_full_rubygems_libraryObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 148

def self.load_full_rubygems_library
  puts 'faster_rubygems: loading full rubygems',caller if $VERBOSE
  if $DEBUG || $VERBOSE
    $stderr.puts 'warning, loading full rubygems'
  end
  return if @loaded_full_rubygems_library

  @loaded_full_rubygems_library = true

  class << Gem
    undef_method(*Gem::GEM_PRELUDE_METHODS)
    undef_method :const_missing
    undef_method :method_missing
  end

  Kernel.module_eval do
    undef_method :gem if method_defined? :gem
  end

  $".delete path_to_full_rubygems_library
  if path = $".detect {|path2| path2 =~ Regexp.new('/rubygems.rb$')}
    raise LoadError, "another rubygems is already loaded from #{path}"
  end
  require 'rubygems'
  begin
    require 'rubygems.rb.bak' # just in case
  rescue ::LoadError
    # ok
  end
end

.path_to_full_rubygems_libraryObject



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 184

def self.path_to_full_rubygems_library
  # null rubylibprefix may mean 'you have loaded the other rubygems already somehow...' hmm
  prefix = (RbConfig::CONFIG["rubylibprefix"] ||  RbConfig::CONFIG['rubylibdir'].split('/')[0..-2].join('/'))
  installed_path = File.join(prefix, Gem::ConfigMap[:ruby_version])
  if $:.include?(installed_path)
    return File.join(installed_path, 'rubygems.rb')
  else # e.g., on test-all
    $:.each do |dir|
      if File.exist?( path = File.join(dir, 'rubygems.rb') )
        return path
      end
    end
    raise LoadError, 'rubygems.rb'
  end
end

.removeObject



142
143
144
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 142

def self.remove
  $stderr.puts 'warning--faster_rubygems doesnt quite yet implement remove yet, for 1.9.2--it will probably work without it though...'  if $VERBOSE || $DEBUG
end

Instance Method Details

#bin_path(gem_name, exec_name = nil, *version_requirements) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/faster_rubygems/prelude_bin_path.rb', line 4

def bin_path(gem_name, exec_name = nil, *version_requirements)
  unless GemPaths.has_key?(gem_name) then
    raise Gem::LoadError, "Could not find RubyGem #{gem_name} (>= 0)\n"
  end

  version_match = false
  if version_requirements.empty?
    version_match = true
  else
    if version_requirements.length > 1 then
      version_match = false
    else

      requirement, version = version_requirements[0].split
      requirement.strip!

      if loaded_version = GemVersions[gem_name] then
        case requirement
        when ">", ">=" then
          if  (loaded_version <=> Gem.integers_for(version)) >= 0
            version_match = true
          end
        when "~>" then
          required_version = Gem.integers_for version

          if loaded_version.first == required_version.first
            version_match = true
          end
        end
      end
    end
  end

  if version_match && exec_name
    full_path = GemPaths[gem_name] + '/bin/' + exec_name
    if File.exist? full_path
      return full_path
    end
  end
  QuickLoader.load_full_rubygems_library
  bin_path(gem_name, exec_name, *version_requirements)
end

#calculate_all_highest_version_gems(load_them_into_the_require_path) ⇒ Object



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 284

def calculate_all_highest_version_gems load_them_into_the_require_path
  start = Time.now if $VERBOSE
  Gem.path.each do |path|
    gems_directory = File.join(path, "gems")

    if File.exist?(gems_directory) then
      Dir.entries(gems_directory).each do |gem_directory_name|
        next if gem_directory_name == "." || gem_directory_name == ".."

        next unless gem_name = gem_directory_name[/(.*)-(.*)/, 1]
        new_version = integers_for($2)
        current_version = GemVersions[gem_name]

        if !current_version or (current_version <=> new_version) < 0 then
          GemVersions[gem_name] = new_version
          GemPaths[gem_name] = File.join(gems_directory, gem_directory_name) # TODO lazy load these, too...why not?
        end
      end
    end
  end
  puts "faster_rubygems: took " + (Time.now - start).to_s + "s to scan the dirs for versions" if $VERBOSE
  # TODO don't require iterating over all these, since you can extrapolate them from the cache files...

  return unless load_them_into_the_require_path
  
  
  # load all lib dirs into $:...
  require_paths = []

  GemPaths.each_value do |path|
    if File.exist?(file = File.join(path, ".require_paths")) then
      paths = File.read(file).split.map do |require_path|
        File.join path, require_path
      end

      require_paths.concat paths
    else
    # bin shouldn't be necessary...
    # require_paths << file if File.exist?(file = File.join(path, "bin"))
      require_paths << file if File.exist?(file = File.join(path, "lib"))
    end
  end

  # "tag" the first require_path inserted into the $LOAD_PATH to enable
  # indexing correctly with rubygems proper when it inserts an explicitly
  # gem version
  unless require_paths.empty? then
    require_paths.first.instance_variable_set(:@gem_prelude_index, true)
  end
  # gem directories must come after -I and ENV['RUBYLIB']
  $:[$:.find{|e|e.instance_variable_defined?(:@gem_prelude_index)}||-1,0] = require_paths
end

#const_missing(constant) ⇒ Object



337
338
339
340
341
342
343
344
345
346
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 337

def const_missing(constant)
  puts 'gem_prelude: const missing', constant if $VERBOSE || $DEBUG
  QuickLoader.load_full_rubygems_library

  if Gem.const_defined?(constant) then
    Gem.const_get constant
  else
    super
  end
end

#create_cache(gems_paths) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/faster_rubygems/prelude_create_cache.rb', line 5

def create_cache gems_paths
  puts 'faster_rubygems: creating caches'
  gems_paths.each do |path|
    gem_versions = {}
    gem_paths = {}
    gems_directory = File.join(path, "gems")
    if File.exist?(gems_directory) then
      Dir.entries(gems_directory).each do |gem_directory_name|
        next if gem_directory_name == "." || gem_directory_name == ".."

        next unless gem_name = gem_directory_name[/(.*)-(.*)/, 1]
        new_version = integers_for($2)
        current_version = gem_versions[gem_name]

        if !current_version or (current_version <=> new_version) < 0 then
          gem_versions[gem_name] = new_version
          gem_paths[gem_name] = File.join(gems_directory, gem_directory_name)
        end
      end
    end
    gem_paths_with_contents = {}
    # strip out directories, and the gem-d.d.d prefix
    gem_paths.each{|k, v| 
      
      files = Dir[v + '/**/*.{rb,so,bundle}'].select{|f| 
        !File.directory? f
      }.map{ |full_name| 
        full_name.sub(v + '/', '')
        full_name.split('/')[-1].split('.')[0] # just a of a.b.c.rb, for now
      }
      
      hash_of_files = {}
      files.each{|small_filename|
        hash_of_files[small_filename] = true
      }
      gem_paths_with_contents[k] = hash_of_files
    }
    
    cache_path = path + '/.faster_rubygems_cache'
    print '.'
    puts cache_path if $VERBOSE
    $stdout.flush
    # accomodate for those not running as sudo...
    if File.writable? path
      File.open(cache_path, 'wb') do |f|
        f.write Marshal.dump(gem_paths_with_contents)            
      end
    else
      $stderr.puts "warning, unable to write cache to:" + cache_path
    end
  end

end

#create_cache_for_all!Object



59
60
61
62
# File 'lib/faster_rubygems/prelude_create_cache.rb', line 59

def create_cache_for_all!
  puts 'recreating all faster_rubygems caches'
  create_cache Gem.path
end

#integers_for(gem_version) ⇒ Object

1.9.1’s gem_prelude doesn’t seem to have this for some reason…



48
49
50
51
52
53
# File 'lib/faster_rubygems/prelude_bin_path.rb', line 48

def integers_for(gem_version)
  numbers = gem_version.split(".").collect {|n| n.to_i}
  numbers.pop while numbers.last == 0
  numbers << 0 if numbers.empty?
  numbers
end

#push_all_gems_that_might_match_and_reload_files(lib, error) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/faster_rubygems/prelude_cached_load.rb', line 18

def push_all_gems_that_might_match_and_reload_files lib, error
  sub_lib = lib.gsub("\\", '/').split('/')[-1].split('.')[0]
  success = false
  raise if AllCaches.empty? # should never be empty...
  AllCaches.each{|path, gem_list|
    for gem_name, long_file_list in gem_list
      if long_file_list[sub_lib]
        puts 'activating' + gem_name + ' ' + sub_lib.to_s if $DEBUG
        if gem(gem_name)
          puts 'gem activated ' + gem_name + ' ' + sub_lib if $VERBOSE || $DEBUG
          success = true
        end
        puts 'done activeating' + gem_name + ' ' + sub_lib if $DEBUG
      end

    end
  }
  success
end

#push_gem_version_on_load_path(gem_name, *version_requirements) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/faster_rubygems/my_gem_prelude.rb', line 204

def push_gem_version_on_load_path(gem_name, *version_requirements)
  if version_requirements.empty?
    unless path = GemPaths[gem_name] then
      puts "Could not find RubyGem #{gem_name} (>= 0)\n" if $VERBOSE || $DEBUG
      raise Gem::LoadError, "Could not find RubyGem #{gem_name} (>= 0)\n"
    end
    # highest version gems *not* already active
    if !AllCaches.empty?
      # then we are using the caches, and the stuff isn't preloaded yet
      # copied and pasted...
            require_paths = []
            if GemsActivated[gem_name]
              return false
            else
              GemsActivated[gem_name] = true
            end
            if File.exist?(file = File.join(path, ".require_paths")) then
              paths = File.read(file).split.map do |require_path|
                File.join path, require_path
              end
              
              require_paths.concat paths
            else
              # bin shouldn't be necessary...
              # require_paths << file if File.exist?(file = File.join(path, "bin"))
              require_paths << file if File.exist?(file = File.join(path, "lib"))
            end

              # "tag" the first require_path inserted into the $LOAD_PATH to enable
              # indexing correctly with rubygems proper when it inserts an explicitly
              # gem version
              unless require_paths.empty? then
                require_paths.first.instance_variable_set(:@gem_prelude_index, true)
              end
              # gem directories must come after -I and ENV['RUBYLIB']
             # TODO  puts $:.index{|e|e.instance_variable_defined?(:@gem_prelude_index)}
             # $:[$:.index{|e|e.instance_variable_defined?(:@gem_prelude_index)}||-1,0] = require_paths
              $:[0,0] = require_paths
              return true
    end
    # normal flow when not using caches--it's already on the path
    return false
  else
    if version_requirements.length > 1 then
      QuickLoader.load_full_rubygems_library
      return gem(gem_name, *version_requirements)
    end

    requirement, version = version_requirements[0].split
    requirement.strip!
    # accomodate for gem 'xxx', '2.3.8'
    if !version
      version = requirement
      requirement = '='
    end

    if loaded_version = GemVersions[gem_name] then
      case requirement
      when ">", ">=", '=' then
        return false if
          (loaded_version <=> Gem.integers_for(version)) >= 0
      when "~>" then
        required_version = Gem.integers_for version

        return false if loaded_version.first == required_version.first
      end
    end

    QuickLoader.load_full_rubygems_library
    gem gem_name, *version_requirements
  end
end