Class: FastRI::RiIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/fastri/ri_index.rb

Overview

This class provides the same functionality as RiReader, with some improvements:

  • lower memory consumption

  • ability to handle information from different sources separately.

Some operations can be restricted to a given “scope”, that is, a “RI DB directory”. This allows you to e.g. look for all the instance methods in String defined by a package.

Such operations take a scope argument, which is either an integer which indexes the source in #paths, or a name identifying the source (either “system” or a package name). If scope == nil, the information from all sources is merged.

Defined Under Namespace

Classes: ClassEntry, MethodEntry, TopLevelEntry

Constant Summary collapse

MAGIC =
"FastRI index #{FASTRI_INDEX_FORMAT}"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#pathsObject (readonly)

Returns the value of attribute paths.



199
200
201
# File 'lib/fastri/ri_index.rb', line 199

def paths
  @paths
end

Class Method Details

.new_from_IO(anIO) ⇒ Object



209
210
211
212
213
# File 'lib/fastri/ri_index.rb', line 209

def self.new_from_IO(anIO)
  obj = new
  obj.load(anIO)
  obj
end

.new_from_paths(paths = nil) ⇒ Object



203
204
205
206
207
# File 'lib/fastri/ri_index.rb', line 203

def self.new_from_paths(paths = nil)
  obj = new
  obj.rebuild_index(paths)
  obj
end

Instance Method Details

#all_names(scope = nil) ⇒ Object

Return a list of all classes, modules, and methods.



375
376
377
# File 'lib/fastri/ri_index.rb', line 375

def all_names(scope = nil)
  full_class_names(scope).concat(full_method_names(scope))
end

#dump(anIO) ⇒ Object

Serializes index to the given IO.



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/fastri/ri_index.rb', line 287

def dump(anIO)
  anIO.puts MAGIC
  anIO.puts "Sources:"
  @paths.zip(@gem_names).each{|p,g| anIO.puts "%-30s  %s" % [g, p]}
  anIO.puts "=" * 80
  anIO.puts "Namespaces:"
  anIO.puts @namespace_array
  anIO.puts "=" * 80
  anIO.puts "Methods:"
  anIO.puts @method_array
  anIO.puts "=" * 80
end

#find_class_by_name(full_name, scope = nil) ⇒ Object

Returns the ClassDescription associated to the given full_name.



318
319
320
321
322
# File 'lib/fastri/ri_index.rb', line 318

def find_class_by_name(full_name, scope = nil)
  entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
  return nil unless entry && entry.full_name == full_name
  get_class(entry)
end

#find_method_by_name(full_name, scope = nil) ⇒ Object

Returns the MethodDescription associated to the given full_name. Only the first definition is returned when scope = nil.



326
327
328
329
330
# File 'lib/fastri/ri_index.rb', line 326

def find_method_by_name(full_name, scope = nil)
  entry = get_entry(@method_array, full_name, MethodEntry, scope)
  return nil unless entry && entry.full_name == full_name
  get_method(entry)
end

#find_methods(name, is_class_method, namespaces) ⇒ Object

Returns an array of MethodEntry objects, corresponding to the methods in the ClassEntry objects in the namespaces array.



334
335
336
337
338
339
340
# File 'lib/fastri/ri_index.rb', line 334

def find_methods(name, is_class_method, namespaces)
  result = []
  namespaces.each do |ns|
    result.concat ns.methods_matching(name, is_class_method)
  end
  result
end

#full_class_names(scope = nil) ⇒ Object

Return the names of all classes and modules.



365
366
367
# File 'lib/fastri/ri_index.rb', line 365

def full_class_names(scope = nil)
  all_entries(@namespace_array, scope)
end

#full_method_names(scope = nil) ⇒ Object

Return the names of all methods.



370
371
372
# File 'lib/fastri/ri_index.rb', line 370

def full_method_names(scope = nil)
  all_entries(@method_array, scope)
end

#get_class(class_entry) ⇒ Object

Return a ClassDescription for a given ClassEntry.



350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/fastri/ri_index.rb', line 350

def get_class(class_entry)
  result = nil
  for path in class_entry.path_names
    path = RI::RiWriter.class_desc_path(path, class_entry)
    desc = File.open(path) {|f| RI::Description.deserialize(f) }
    if result
      result.merge_in(desc)
    else
      result = desc
    end
  end
  result
end

#get_class_entry(full_name, scope = nil) ⇒ Object

Returns the ClassEntry associated to the given full_name.



392
393
394
395
396
# File 'lib/fastri/ri_index.rb', line 392

def get_class_entry(full_name, scope = nil)
  entry = get_entry(@namespace_array, full_name, ClassEntry, scope)
  return nil unless entry && entry.full_name == full_name
  entry
end

#get_method(method_entry) ⇒ Object

Return the MethodDescription for a given MethodEntry by deserializing the YAML.



344
345
346
347
# File 'lib/fastri/ri_index.rb', line 344

def get_method(method_entry)
  path = method_entry.path_name
  File.open(path) { |f| RI::Description.deserialize(f) }
end

#get_method_entry(full_name, scope = nil) ⇒ Object

Returns the MethodEntry associated to the given full_name.



399
400
401
402
403
# File 'lib/fastri/ri_index.rb', line 399

def get_method_entry(full_name, scope = nil)
  entry = get_entry(@method_array, full_name, MethodEntry, scope)
  return nil unless entry && entry.full_name == full_name
  entry
end

#load(anIO) ⇒ Object

Load the index from the given IO. It must contain a textual representation generated by #dump.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/fastri/ri_index.rb', line 259

def load(anIO)
  header = anIO.gets    
  raise "Invalid format." unless header.chomp == MAGIC
  anIO.gets  # discard "Sources:"
  paths     = []
  gem_names = []
  until (line = anIO.gets).index("=" * 80) == 0
    gemname, path = line.strip.split(/\s+/)
    paths     << path
    gem_names << gemname
  end
  anIO.gets # discard "Namespaces:"
  namespace_array = []
  until (line = anIO.gets).index("=" * 80) == 0
    namespace_array << line
  end
  anIO.gets # discard "Methods:"
  method_array = []
  until (line = anIO.gets).index("=" * 80) == 0
    method_array << line
  end
  @paths           = paths
  @gem_names       = gem_names
  @namespace_array = namespace_array
  @method_array    = method_array
end

#lookup_namespace_in(target, namespaces) ⇒ Object

Returns an array of ClassEntry objects whose names match target, and which correspond to the namespaces contained in namespaces. namespaces is an array of ClassEntry objects.



309
310
311
312
313
314
315
# File 'lib/fastri/ri_index.rb', line 309

def lookup_namespace_in(target, namespaces)
  result = []
  namespaces.each do |ns|
    result.concat(ns.contained_modules_matching(target))
  end
  result
end

#methods_under(class_entry_or_name, recursive, scope = nil) ⇒ Object

Returns array of MethodEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy.



446
447
448
# File 'lib/fastri/ri_index.rb', line 446

def methods_under(class_entry_or_name, recursive, scope = nil)
  methods_under_matching(class_entry_or_name, //, recursive, scope)
end

#methods_under_matching(class_entry_or_name, regexp, recursive, scope = nil) ⇒ Object

Returns array of MethodEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy whose full_name matches the given regexp.



453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/fastri/ri_index.rb', line 453

def methods_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
  case class_entry_or_name
  when ClassEntry
    full_name = class_entry_or_name.full_name
  else
    full_name = class_entry_or_name
  end
  method_entry = get_entry(@method_array, full_name, MethodEntry)
  return [] unless method_entry
  ret = []
  re1, re2 = matching_regexps_method(full_name)
  (method_entry.index...@method_array.size).each do |i|
    entry = @method_array[i]
    break unless re1 =~ entry
    next if !recursive && re2 !~ entry 
    full_name = entry[/\S+/]
    next unless regexp =~ full_name
    if scope
      sources = method_sources(i)
      if sources.include?(sindex = scope_to_sindex(scope))
        ret << MethodEntry.new(self, full_name, i, sindex)
      end
    else
      ret << MethodEntry.new(self, full_name, i, nil)
    end
  end
  ret
end

#namespaces_under(class_entry_or_name, recursive, scope = nil) ⇒ Object

Returns array of ClassEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy.



407
408
409
# File 'lib/fastri/ri_index.rb', line 407

def namespaces_under(class_entry_or_name, recursive, scope = nil)
  namespaces_under_matching(class_entry_or_name, //, recursive, scope)
end

#namespaces_under_matching(class_entry_or_name, regexp, recursive, scope = nil) ⇒ Object

Returns array of ClassEntry objects under class_entry_or_name (either String or ClassEntry) in the hierarchy whose full_name matches the given regexp.



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/fastri/ri_index.rb', line 414

def namespaces_under_matching(class_entry_or_name, regexp, recursive, scope = nil)
  case class_entry_or_name
  when ClassEntry
    class_entry = class_entry_or_name
  when ""
    class_entry = top_level_namespace(scope)[0]
  else
    class_entry = get_entry(@namespace_array, class_entry_or_name, ClassEntry, scope)
  end
  return [] unless class_entry
  ret = []
  re1, re2 = matching_regexps_namespace(class_entry.full_name)
  (class_entry.index+1...@namespace_array.size).each do |i|
    entry = @namespace_array[i]
    break unless re1 =~ entry
    next if !recursive && re2 !~ entry 
    full_name = entry[/\S+/]
    next unless regexp =~ full_name
    if scope
      sources = namespace_sources(i)
      if sources.include?(sindex = scope_to_sindex(scope))
        ret << ClassEntry.new(self, full_name, i, sindex)
      end
    else
      ret << ClassEntry.new(self, full_name, i, nil)
    end
  end
  ret
end

#num_methodsObject

Returns the number of methods in the index.



382
383
384
# File 'lib/fastri/ri_index.rb', line 382

def num_methods
  @method_array.size
end

#num_namespacesObject

Returns the number of namespaces in the index.



387
388
389
# File 'lib/fastri/ri_index.rb', line 387

def num_namespaces
  @namespace_array.size
end

#rebuild_index(paths = nil) ⇒ Object



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
# File 'lib/fastri/ri_index.rb', line 215

def rebuild_index(paths = nil)
  @paths = paths || RI::Paths::PATH
  @gem_names = paths.map do |p|
    fullp = File.expand_path(p)
    gemname = nil
    begin
      require 'rubygems'
      Gem.path.each do |gempath|
        re = %r!^#{Regexp.escape(File.expand_path(gempath))}/doc/!
        if re =~ fullp
          gemname = fullp.gsub(re,"")[%r{^[^/]+}]
          break
        end
      end
    rescue LoadError
      # no RubyGems, no gems installed, skip it
    end
    gemname ? gemname : "system"
  end
  methods    = Hash.new{|h,k| h[k] = []}
  namespaces = methods.clone 
  @paths.each_with_index do |path, source_index|
    ri_reader = RI::RiReader.new(RI::RiCache.new(path))
    obtain_classes(ri_reader.top_level_namespace.first).each{|name| namespaces[name] << source_index }
    obtain_methods(ri_reader.top_level_namespace.first).each{|name| methods[name] << source_index }
  end
  @method_array = methods.sort_by{|h,k| h}.map do |name, sources|
    "#{name} #{sources.map{|x| x.to_s}.join(' ')}"
  end
  @namespace_array = namespaces.sort_by{|h,k| h}.map do |name, sources|
    "#{name} #{sources.map{|x| x.to_s}.join(' ')}"
  end

=begin
  puts "@method_array: #{@method_array.size}"
  puts "@namespace_array: #{@namespace_array.size}"
  puts @method_array.inject(0){|s,x| s + x.size}
  puts @namespace_array.inject(0){|s,x| s + x.size}
=end
end

#source_paths_for(entry_or_name) ⇒ Object

Returns array of Strings corresponding to the base directories of all the sources fo the given entry_or_name.



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/fastri/ri_index.rb', line 484

def source_paths_for(entry_or_name)
  case entry_or_name
  when ClassEntry
    namespace_sources(entry_or_name.index).map{|i| @paths[i] }
  when MethodEntry
    method_sources(entry_or_name.index).map{|i| @paths[i]}
  when nil
    []
  else
    case entry_or_name
    when /[#.]\S+/
      method_entry = get_entry(@method_array, entry_or_name, MethodEntry, nil)
      source_paths_for(method_entry)
    when ""
      []
    else
      class_entry = get_entry(@namespace_array, entry_or_name, ClassEntry, nil)
      source_paths_for(class_entry)
    end
  end
end

#top_level_namespace(scope = nil) ⇒ Object

Returns an array with the top level namespace.



302
303
304
# File 'lib/fastri/ri_index.rb', line 302

def top_level_namespace(scope = nil)
  [TopLevelEntry.new(self, "", -1, scope ? scope_to_sindex(scope) : nil)]
end