Class: Gem::SourceIndex

Inherits:
Object
  • Object
show all
Extended by:
Forwardable, UserInteraction
Includes:
Enumerable, UserInteraction
Defined in:
lib/rubygems/source_index.rb

Overview

The SourceIndex object indexes all the gems available from a particular source (e.g. a list of gem directories, or a remote source). A SourceIndex maps a gem full name to a gem specification.

NOTE

The class used to be named Cache, but that became confusing when cached source fetchers where introduced. The constant Gem::Cache is an alias for this class to allow old YAMLized source index objects to load properly.

Constant Summary collapse

INCREMENTAL_THRESHHOLD =
50

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DefaultUserInteraction

#ui, ui, #ui=, ui=, #use_ui, use_ui

Constructor Details

#initialize(specifications = {}) ⇒ SourceIndex

Constructs a source index instance from the provided specifications

specifications
Hash

hash of [Gem name, Gem::Specification] pairs



118
119
120
# File 'lib/rubygems/source_index.rb', line 118

def initialize(specifications={})
  @gems = specifications
end

Class Method Details

.from_gems_in(*spec_dirs) ⇒ Object

Factory method to construct a source index instance for a

given path.
spec_dirs

List of directories to search for specifications. Each directory should have a “specifications” subdirectory containing the gem specifications.

return

SourceIndex instance



80
81
82
# File 'lib/rubygems/source_index.rb', line 80

def from_gems_in(*spec_dirs)
  self.new.load_gems_in(*spec_dirs)
end

.from_installed_gems(*deprecated) ⇒ Object

Factory method to construct a source index instance for a given path.

deprecated

If supplied, from_installed_gems will act just like from_gems_in. This argument is deprecated and is provided just for backwards compatibility, and should not generally be used.

return

SourceIndex instance



51
52
53
54
55
56
57
# File 'lib/rubygems/source_index.rb', line 51

def from_installed_gems(*deprecated)
  if deprecated.empty?
    from_gems_in(*installed_spec_directories)
  else
    from_gems_in(*deprecated)
  end
end

.installed_spec_directoriesObject

Return a list of directories in the current gem path that contain specifications.

return

List of directory paths (all ending in “../specifications”).



65
66
67
# File 'lib/rubygems/source_index.rb', line 65

def installed_spec_directories
  Gem.path.collect { |dir| File.join(dir, "specifications") }
end

.load_specification(file_name) ⇒ Object

Load a specification from a file (eval’d Ruby code)

file_name
String

The .gemspec file

return

Specification instance or nil if an error occurs



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rubygems/source_index.rb', line 89

def load_specification(file_name)
  begin
    spec_code = File.read(file_name).untaint
    gemspec = eval(spec_code)
    if gemspec.is_a?(Gem::Specification)
      gemspec.loaded_from = file_name
      return gemspec
    end
    alert_warning "File '#{file_name}' does not evaluate to a gem specification"
  rescue SyntaxError => e
    alert_warning e
    alert_warning spec_code
  rescue Exception => e
    alert_warning(e.inspect.to_s + "\n" + spec_code)
    alert_warning "Invalid .gemspec format in '#{file_name}'"
  end
  return nil
end

Instance Method Details

#add_spec(gem_spec) ⇒ Object

Add a gem specification to the source index.



152
153
154
# File 'lib/rubygems/source_index.rb', line 152

def add_spec(gem_spec)
  @gems[gem_spec.full_name] = gem_spec
end

#each(&block) ⇒ Object

Iterate over the specifications in the source index.

&block
yields gem.full_name, Gem::Specification


165
166
167
# File 'lib/rubygems/source_index.rb', line 165

def each(&block)
  @gems.each(&block)
end

#find_name(gem_name, version_requirement = Version::Requirement.new(">= 0")) ⇒ Object

Find a gem by an exact match on the short name.



188
189
190
# File 'lib/rubygems/source_index.rb', line 188

def find_name(gem_name, version_requirement=Version::Requirement.new(">= 0"))
  search(/^#{gem_name}$/, version_requirement)
end

#gem_signature(gem_full_name) ⇒ Object

The signature for the given gem specification.



181
182
183
# File 'lib/rubygems/source_index.rb', line 181

def gem_signature(gem_full_name)
  Digest::SHA256.new(@gems[gem_full_name].to_yaml).to_s
end

#index_signatureObject

The signature for the source index. Changes in the signature indicate a change in the index.



176
177
178
# File 'lib/rubygems/source_index.rb', line 176

def index_signature
  Digest::SHA256.new(@gems.keys.sort.join(',')).to_s
end

#latest_specsObject

Returns a Hash of name => Specification of the latest versions of each gem in this index.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rubygems/source_index.rb', line 136

def latest_specs
  thin = {}

  each do |full_name, spec|
    name = spec.name
    if thin.has_key? name then
      thin[name] = spec if spec.version > thin[name].version
    else
      thin[name] = spec
    end
  end

  thin
end

#load_gems_in(*spec_dirs) ⇒ Object

Reconstruct the source index from the list of source directories.



124
125
126
127
128
129
130
131
132
# File 'lib/rubygems/source_index.rb', line 124

def load_gems_in(*spec_dirs)
  @gems.clear
  specs = Dir.glob File.join("{#{spec_dirs.join(',')}}", "*.gemspec")
  specs.each do |file_name|
    gemspec = self.class.load_specification(file_name.untaint)
    add_spec(gemspec) if gemspec
  end
  self
end

#outdatedObject

Returns an Array of Gem::Specifications that are not up to date.



226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/rubygems/source_index.rb', line 226

def outdated
  remotes = Gem::SourceInfoCache.search(//)
  outdateds = []
  latest_specs.each do |_, local|
    name = local.name
    remote = remotes.select  { |spec| spec.name == name }.
                     sort_by { |spec| spec.version }.
                     last
    outdateds << name if remote and local.version < remote.version
  end
  outdateds
end

#refresh!Object

Refresh the source index from the local file system.

return

Returns a pointer to itself.



220
221
222
# File 'lib/rubygems/source_index.rb', line 220

def refresh!
  load_gems_in(self.class.installed_spec_directories)
end

#remove_spec(full_name) ⇒ Object

Remove a gem specification named full_name.



157
158
159
# File 'lib/rubygems/source_index.rb', line 157

def remove_spec(full_name)
  @gems.delete(full_name)
end

#search(gem_pattern, version_requirement = Version::Requirement.new(">= 0")) ⇒ Object

Search for a gem by short name pattern and optional version

gem_name
String

a partial for the (short) name of the gem, or

Regex

a pattern to match against the short name

version_requirement
String | default=Version::Requirement.new(“>= 0”)

version to

find

return
Array

list of Gem::Specification objects in sorted (version)

order. Empty if not found.



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rubygems/source_index.rb', line 204

def search(gem_pattern, version_requirement=Version::Requirement.new(">= 0"))
  gem_pattern = /#{ gem_pattern }/i if String === gem_pattern
  version_requirement = Gem::Version::Requirement.create(version_requirement)
  result = []
  @gems.each do |full_spec_name, spec|
    next unless spec.name =~ gem_pattern
    result << spec if version_requirement.satisfied_by?(spec.version)
  end
  result = result.sort
  result
end

#specification(full_name) ⇒ Object

The gem specification given a full gem spec name.



170
171
172
# File 'lib/rubygems/source_index.rb', line 170

def specification(full_name)
  @gems[full_name]
end

#update(source_uri) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/rubygems/source_index.rb', line 239

def update(source_uri)
  use_incremental = false

  begin
    gem_names = fetch_quick_index source_uri
    remove_extra gem_names
    missing_gems = find_missing gem_names
    use_incremental = missing_gems.size <= INCREMENTAL_THRESHHOLD
  rescue Gem::OperationNotSupportedError => ex
    use_incremental = false
  end

  if use_incremental then
    update_with_missing source_uri, missing_gems
  else
    new_index = fetch_bulk_index source_uri
    @gems.replace new_index.gems
  end

  self
end