Class Gem::DependencyInstaller
In: lib/rubygems/dependency_installer.rb
Parent: Object

Installs a gem along with all its dependencies from local and remote gems.

Methods

Included Modules

Gem::UserInteraction

Constants

DEFAULT_OPTIONS = { :env_shebang => false, :domain => :both, # HACK dup :force => false, :format_executable => false, # HACK dup :ignore_dependencies => false, :prerelease => false, :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low? :wrappers => true, }

Attributes

gems_to_install  [R] 
installed_gems  [R] 

Public Class methods

Creates a new installer instance.

Options are:

:cache_dir:Alternate repository path to store .gem files in.
:domain::local, :remote, or :both. :local only searches gems in the current directory. :remote searches only gems in Gem::sources. :both searches both.
:env_shebang:See Gem::Installer::new.
:force:See Gem::Installer#install.
:format_executable:See Gem::Installer#initialize.
:ignore_dependencies:Don‘t install any dependencies.
:install_dir:See Gem::Installer#install.
:prerelease:Allow prerelease versions. See install.
:security_policy:See Gem::Installer::new and Gem::Security.
:user_install:See Gem::Installer.new
:wrappers:See Gem::Installer::new

[Source]

    # File lib/rubygems/dependency_installer.rb, line 46
46:   def initialize(options = {})
47:     if options[:install_dir] then
48:       spec_dir = options[:install_dir], 'specifications'
49:       @source_index = Gem::SourceIndex.new [spec_dir]
50:     else
51:       @source_index = Gem.source_index
52:     end
53: 
54:     options = DEFAULT_OPTIONS.merge options
55: 
56:     @bin_dir = options[:bin_dir]
57:     @development = options[:development]
58:     @domain = options[:domain]
59:     @env_shebang = options[:env_shebang]
60:     @force = options[:force]
61:     @format_executable = options[:format_executable]
62:     @ignore_dependencies = options[:ignore_dependencies]
63:     @prerelease = options[:prerelease]
64:     @security_policy = options[:security_policy]
65:     @user_install = options[:user_install]
66:     @wrappers = options[:wrappers]
67: 
68:     @installed_gems = []
69: 
70:     @install_dir = options[:install_dir] || Gem.dir
71:     @cache_dir = options[:cache_dir] || @install_dir
72: 
73:     # Set with any errors that SpecFetcher finds while search through
74:     # gemspecs for a dep
75:     @errors = nil
76:   end

Public Instance methods

[Source]

     # File lib/rubygems/dependency_installer.rb, line 157
157:   def add_found_dependencies to_do, dependency_list
158:     seen = {}
159:     dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
160: 
161:     until to_do.empty? do
162:       spec = to_do.shift
163:       next if spec.nil? or seen[spec.name]
164:       seen[spec.name] = true
165: 
166:       deps = spec.runtime_dependencies
167:       deps |= spec.development_dependencies if @development
168: 
169:       deps.each do |dep|
170:         dependencies[dep.name] = dependencies[dep.name].merge dep
171: 
172:         results = find_gems_with_sources(dep).reverse
173: 
174:         results.reject! do |dep_spec,|
175:           to_do.push dep_spec
176: 
177:           # already locally installed
178:           @source_index.any? do |_, installed_spec|
179:             dep.name == installed_spec.name and
180:               dep.requirement.satisfied_by? installed_spec.version
181:           end
182:         end
183: 
184:         results.each do |dep_spec, source_uri|
185:           @specs_and_sources << [dep_spec, source_uri]
186: 
187:           dependency_list.add dep_spec
188:         end
189:       end
190:     end
191: 
192:     dependency_list.remove_specs_unsatisfied_by dependencies
193:   end

Returns a list of pairs of gemspecs and source_uris that match Gem::Dependency dep from both local (Dir.pwd) and remote (Gem.sources) sources. Gems are sorted with newer gems preferred over older gems, and local gems preferred over remote gems.

[Source]

     # File lib/rubygems/dependency_installer.rb, line 84
 84:   def find_gems_with_sources(dep)
 85:     # Reset the errors
 86:     @errors = nil
 87:     gems_and_sources = []
 88: 
 89:     if @domain == :both or @domain == :local then
 90:       Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file|
 91:         spec = Gem::Format.from_file_by_path(gem_file).spec
 92:         gems_and_sources << [spec, gem_file] if spec.name == dep.name
 93:       end
 94:     end
 95: 
 96:     if @domain == :both or @domain == :remote then
 97:       begin
 98:         requirements = dep.requirement.requirements.map do |req, ver|
 99:           req
100:         end
101: 
102:         all = !dep.prerelease? &&
103:               # we only need latest if there's one requirement and it is
104:               # guaranteed to match the newest specs
105:               (requirements.length > 1 or
106:                 (requirements.first != ">=" and requirements.first != ">"))
107: 
108:         found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
109: 
110:         gems_and_sources.push(*found)
111: 
112:       rescue Gem::RemoteFetcher::FetchError => e
113:         if Gem.configuration.really_verbose then
114:           say "Error fetching remote data:\t\t#{e.message}"
115:           say "Falling back to local-only install"
116:         end
117:         @domain = :local
118:       end
119:     end
120: 
121:     gems_and_sources.sort_by do |gem, source|
122:       [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
123:     end
124:   end

Finds a spec and the source_uri it came from for gem gem_name and version. Returns an Array of specs and sources required for installation of the gem.

[Source]

     # File lib/rubygems/dependency_installer.rb, line 200
200:   def find_spec_by_name_and_version(gem_name,
201:                                     version = Gem::Requirement.default,
202:                                     prerelease = false)
203:     spec_and_source = nil
204: 
205:     glob = if File::ALT_SEPARATOR then
206:              gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
207:            else
208:              gem_name
209:            end
210: 
211:     local_gems = Dir["#{glob}*"].sort.reverse
212: 
213:     unless local_gems.empty? then
214:       local_gems.each do |gem_file|
215:         next unless gem_file =~ /gem$/
216:         begin
217:           spec = Gem::Format.from_file_by_path(gem_file).spec
218:           spec_and_source = [spec, gem_file]
219:           break
220:         rescue SystemCallError, Gem::Package::FormatError
221:         end
222:       end
223:     end
224: 
225:     if spec_and_source.nil? then
226:       dep = Gem::Dependency.new gem_name, version
227:       dep.prerelease = true if prerelease
228:       spec_and_sources = find_gems_with_sources(dep).reverse
229: 
230:       spec_and_source = spec_and_sources.find { |spec, source|
231:         Gem::Platform.match spec.platform
232:       }
233:     end
234: 
235:     if spec_and_source.nil? then
236:       raise Gem::GemNotFoundException.new(
237:         "Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
238:         gem_name, version, @errors)
239:     end
240: 
241:     @specs_and_sources = [spec_and_source]
242:   end

Gathers all dependencies necessary for the installation from local and remote sources unless the ignore_dependencies was given.

[Source]

     # File lib/rubygems/dependency_installer.rb, line 130
130:   def gather_dependencies
131:     specs = @specs_and_sources.map { |spec,_| spec }
132: 
133:     # these gems were listed by the user, always install them
134:     keep_names = specs.map { |spec| spec.full_name }
135: 
136:     dependency_list = Gem::DependencyList.new @development
137:     dependency_list.add(*specs)
138:     to_do = specs.dup
139: 
140:     add_found_dependencies to_do, dependency_list unless @ignore_dependencies
141: 
142:     dependency_list.specs.reject! { |spec|
143:       ! keep_names.include? spec.full_name and
144:         @source_index.any? { |n,_| n == spec.full_name }
145:     }
146: 
147:     unless dependency_list.ok? or @ignore_dependencies or @force then
148:       reason = dependency_list.why_not_ok?.map { |k,v|
149:         "#{k} requires #{v.join(", ")}"
150:       }.join("; ")
151:       raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
152:     end
153: 
154:     @gems_to_install = dependency_list.dependency_order.reverse
155:   end

Installs the gem dep_or_name and all its dependencies. Returns an Array of installed gem specifications.

If the +:prerelease+ option is set and there is a prerelease for dep_or_name the prerelease version will be installed.

Unless explicitly specified as a prerelease dependency, prerelease gems that dep_or_name depend on will not be installed.

If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed separately.

[Source]

     # File lib/rubygems/dependency_installer.rb, line 258
258:   def install dep_or_name, version = Gem::Requirement.default
259:     if String === dep_or_name then
260:       find_spec_by_name_and_version dep_or_name, version, @prerelease
261:     else
262:       dep_or_name.prerelease = @prerelease
263:       @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
264:     end
265: 
266:     @installed_gems = []
267: 
268:     gather_dependencies
269: 
270:     @gems_to_install.each do |spec|
271:       last = spec == @gems_to_install.last
272:       next if @source_index.any? { |n,_| n == spec.full_name } and not last
273: 
274:       # TODO: make this sorta_verbose so other users can benefit from it
275:       say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
276: 
277:       _, source_uri = @specs_and_sources.assoc spec
278:       begin
279:         local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
280:                                                              @cache_dir
281:       rescue Gem::RemoteFetcher::FetchError
282:         next if @force
283:         raise
284:       end
285: 
286:       inst = Gem::Installer.new local_gem_path,
287:                                 :bin_dir             => @bin_dir,
288:                                 :development         => @development,
289:                                 :env_shebang         => @env_shebang,
290:                                 :force               => @force,
291:                                 :format_executable   => @format_executable,
292:                                 :ignore_dependencies => @ignore_dependencies,
293:                                 :install_dir         => @install_dir,
294:                                 :security_policy     => @security_policy,
295:                                 :source_index        => @source_index,
296:                                 :user_install        => @user_install,
297:                                 :wrappers            => @wrappers
298: 
299:       spec = inst.install
300: 
301:       @installed_gems << spec
302:     end
303: 
304:     @installed_gems
305:   end

[Validate]