# class RDoc::RI::Store

A set of ri data.

The store manages reading and writing ri data for a project (gem, path, etc.) and maintains a cache of methods, classes and ancestors in the store.

The store maintains a cache of its contents for faster lookup. After adding items to the store it must be flushed using save_cache. The cache contains the following structures:

    @cache = {
      :class_methods    => {}, # class name => class methods
      :instance_methods => {}, # class name => instance methods
      :attributes       => {}, # class name => attributes
      :modules          => [], # classes and modules in this store
      :ancestors        => {}, # class name => ancestor names
    }

### Attributes

cache[R]

The contents of the Store

dry_run[RW]

If true this Store will not write any files

encoding[RW]

The encoding of the contents in the Store

path[RW]

Path this store reads or writes

type[RW]

Type of ri datastore this was loaded from. See RDoc::RI::Driver, RDoc::RI::Paths.

### Public Class Methods

new(path, type = nil) click to toggle source

Creates a new Store of `type` that will load or save to `path`

    # File lib/rdoc/ri/store.rb, line 55
    def initialize path, type = nil
      @dry_run  = false
      @type     = type
      @path     = path
      @encoding = nil

      @cache = {
        :ancestors        => {},
        :attributes       => {},
        :class_methods    => {},
        :encoding         => @encoding,
        :instance_methods => {},
        :modules          => [],
      }
    end

### Public Instance Methods

ancestors() click to toggle source

Ancestors cache accessor. Maps a klass name to an Array of its ancestors in this store. If Foo in this store inherits from Object, Kernel won’t be listed (it will be included from ruby’s ri store).

    # File lib/rdoc/ri/store.rb, line 76
    def ancestors
      @cache[:ancestors]
    end

attributes() click to toggle source

Attributes cache accessor. Maps a class to an Array of its attributes.

    # File lib/rdoc/ri/store.rb, line 83
    def attributes
      @cache[:attributes]
    end

cache_path() click to toggle source

Path to the cache file

    # File lib/rdoc/ri/store.rb, line 90
    def cache_path
      File.join @path, 'cache.ri'
    end

class_file(klass_name) click to toggle source

Path to the ri data for `klass_name`

    # File lib/rdoc/ri/store.rb, line 97
    def class_file klass_name
      name = klass_name.split('::').last
      File.join class_path(klass_name), "cdesc-#{name}.ri"
    end

class_methods() click to toggle source

Class methods cache accessor. Maps a class to an Array of its class methods (not full name).

    # File lib/rdoc/ri/store.rb, line 106
    def class_methods
      @cache[:class_methods]
    end

class_path(klass_name) click to toggle source

Path where data for `klass_name` will be stored (methods or class data)

    # File lib/rdoc/ri/store.rb, line 113
    def class_path klass_name
      File.join @path, *klass_name.split('::')
    end

friendly_path() click to toggle source

Friendly rendition of path

    # File lib/rdoc/ri/store.rb, line 136
    def friendly_path
      case type
      when :gem    then
        sep = Regexp.union(*['/', File::ALT_SEPARATOR].compact)
        @path =~ %r#{sep}doc#{sep}(.*?)#{sep}ri$/
        "gem #{$1}"
      when :home   then '~/.ri'
      when :site   then 'ruby site'
      when :system then 'ruby core'
      else @path
      end
    end

instance_methods() click to toggle source

Instance methods cache accessor. Maps a class to an Array of its instance methods (not full name).

    # File lib/rdoc/ri/store.rb, line 157
    def instance_methods
      @cache[:instance_methods]
    end

load_cache() click to toggle source

Loads cache file for this store

    # File lib/rdoc/ri/store.rb, line 164
    def load_cache
      #orig_enc = @encoding

      open cache_path, 'rb' do |io|
        @cache = Marshal.load io.read
      end

      load_enc = @cache[:encoding]

      # TODO this feature will be time-consuming to add:
      # a) Encodings may be incompatible but transcodeable
      # b) Need to warn in the appropriate spots, wherever they may be
      # c) Need to handle cross-cache differences in encodings
      # d) Need to warn when generating into a cache with diffent encodings
      #
      #if orig_enc and load_enc != orig_enc then
      #  warn "Cached encoding #{load_enc} is incompatible with #{orig_enc}\n" \
      #       "from #{path}/cache.ri" unless
      #    Encoding.compatible? orig_enc, load_enc
      #end

      @encoding = load_enc unless @encoding

      @cache
    rescue Errno::ENOENT
    end

load_class(klass_name) click to toggle source

Loads ri data for `klass_name`

    # File lib/rdoc/ri/store.rb, line 194
    def load_class klass_name
      open class_file(klass_name), 'rb' do |io|
        Marshal.load io.read
      end
    end

load_method(klass_name, method_name) click to toggle source

Loads ri data for `method_name` in `klass_name`

    # File lib/rdoc/ri/store.rb, line 203
    def load_method klass_name, method_name
      open method_file(klass_name, method_name), 'rb' do |io|
        Marshal.load io.read
      end
    end

method_file(klass_name, method_name) click to toggle source

Path to the ri data for `method_name` in `klass_name`

    # File lib/rdoc/ri/store.rb, line 212
    def method_file klass_name, method_name
      method_name = method_name.split('::').last
      method_name =~ %r#(.*)/
      method_type = $1 ? 'i' : 'c'
      method_name = $1 if $1

      method_name = if ''.respond_to? :ord then
                      method_name.gsub(%r\W/) { "%%%02x" % $&[0].ord }
                    else
                      method_name.gsub(%r\W/) { "%%%02x" % $&[0] }
                    end

      File.join class_path(klass_name), "#{method_name}-#{method_type}.ri"
    end

modules() click to toggle source

Modules cache accessor. An Array of all the modules (and classes) in the store.

    # File lib/rdoc/ri/store.rb, line 231
    def modules
      @cache[:modules]
    end

save_cache() click to toggle source

Writes the cache file for this store

    # File lib/rdoc/ri/store.rb, line 238
    def save_cache
      clean_cache_collection @cache[:ancestors]
      clean_cache_collection @cache[:attributes]
      clean_cache_collection @cache[:class_methods]
      clean_cache_collection @cache[:instance_methods]

      @cache[:modules].uniq!
      @cache[:modules].sort!
      @cache[:encoding] = @encoding # this gets set twice due to assert_cache

      return if @dry_run

      marshal = Marshal.dump @cache

      open cache_path, 'wb' do |io|
        io.write marshal
      end
    end

save_class(klass) click to toggle source

Writes the ri data for `klass`

    # File lib/rdoc/ri/store.rb, line 260
    def save_class klass
      full_name = klass.full_name

      FileUtils.mkdir_p class_path(full_name) unless @dry_run

      @cache[:modules] << full_name

      path = class_file full_name

      begin
        disk_klass = load_class full_name

        klass = disk_klass.merge klass
      rescue Errno::ENOENT
      end

      # BasicObject has no ancestors
      ancestors = klass.ancestors.compact.map do |ancestor|
        # HACK for classes we don't know about (class X < RuntimeError)
        String === ancestor ? ancestor : ancestor.full_name
      end

      @cache[:ancestors][full_name] ||= []
      @cache[:ancestors][full_name].push(*ancestors)

      attributes = klass.attributes.map do |attribute|
        "#{attribute.definition} #{attribute.name}"
      end

      unless attributes.empty? then
        @cache[:attributes][full_name] ||= []
        @cache[:attributes][full_name].push(*attributes)
      end

      to_delete = []

      unless klass.method_list.empty? then
        @cache[:class_methods][full_name]    ||= []
        @cache[:instance_methods][full_name] ||= []

        class_methods, instance_methods =
          klass.method_list.partition { |meth| meth.singleton }

        class_methods    = class_methods.   map { |method| method.name }
        instance_methods = instance_methods.map { |method| method.name }

        old = @cache[:class_methods][full_name] - class_methods
        to_delete.concat old.map { |method|
          method_file full_name, "#{full_name}::#{method}"
        }

        old = @cache[:instance_methods][full_name] - instance_methods
        to_delete.concat old.map { |method|
          method_file full_name, "#{full_name}##{method}"
        }

        @cache[:class_methods][full_name]    = class_methods
        @cache[:instance_methods][full_name] = instance_methods
      end

      return if @dry_run

      FileUtils.rm_f to_delete

      marshal = Marshal.dump klass

      open path, 'wb' do |io|
        io.write marshal
      end
    end

save_method(klass, method) click to toggle source

Writes the ri data for `method` on `klass`

    # File lib/rdoc/ri/store.rb, line 334
    def save_method klass, method
      full_name = klass.full_name

      FileUtils.mkdir_p class_path(full_name) unless @dry_run

      cache = if method.singleton then
                @cache[:class_methods]
              else
                @cache[:instance_methods]
              end
      cache[full_name] ||= []
      cache[full_name] << method.name

      return if @dry_run

      marshal = Marshal.dump method

      open method_file(full_name, method.full_name), 'wb' do |io|
        io.write marshal
      end
    end