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:       @gem_home = options[:install_dir]
49: 
50:       Gem::Specification.dirs = @gem_home
51:       Gem.ensure_gem_subdirectories @gem_home
52:       options[:install_dir] = @gem_home # FIX: because we suck and reuse below
53:     end
54: 
55:     options = DEFAULT_OPTIONS.merge options
56: 
57:     @bin_dir             = options[:bin_dir]
58:     @development         = options[:development]
59:     @domain              = options[:domain]
60:     @env_shebang         = options[:env_shebang]
61:     @force               = options[:force]
62:     @format_executable   = options[:format_executable]
63:     @ignore_dependencies = options[:ignore_dependencies]
64:     @prerelease          = options[:prerelease]
65:     @security_policy     = options[:security_policy]
66:     @user_install        = options[:user_install]
67:     @wrappers            = options[:wrappers]
68: 
69:     @installed_gems = []
70: 
71:     @install_dir = options[:install_dir] || Gem.dir
72:     @cache_dir = options[:cache_dir] || @install_dir
73: 
74:     # Set with any errors that SpecFetcher finds while search through
75:     # gemspecs for a dep
76:     @errors = nil
77:   end

Public Instance methods

[Source]

     # File lib/rubygems/dependency_installer.rb, line 159
159:   def add_found_dependencies to_do, dependency_list
160:     seen = {}
161:     dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
162: 
163:     until to_do.empty? do
164:       spec = to_do.shift
165:       next if spec.nil? or seen[spec.name]
166:       seen[spec.name] = true
167: 
168:       deps = spec.runtime_dependencies
169:       deps |= spec.development_dependencies if @development
170: 
171:       deps.each do |dep|
172:         dependencies[dep.name] = dependencies[dep.name].merge dep
173: 
174:         results = find_gems_with_sources(dep).reverse
175: 
176:         results.reject! do |dep_spec,|
177:           to_do.push dep_spec
178: 
179:           # already locally installed
180:           Gem::Specification.any? do |installed_spec|
181:             dep.name == installed_spec.name and
182:               dep.requirement.satisfied_by? installed_spec.version
183:           end
184:         end
185: 
186:         results.each do |dep_spec, source_uri|
187:           @specs_and_sources << [dep_spec, source_uri]
188: 
189:           dependency_list.add dep_spec
190:         end
191:       end
192:     end
193: 
194:     dependency_list.remove_specs_unsatisfied_by dependencies
195:   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 85
 85:   def find_gems_with_sources(dep)
 86:     # Reset the errors
 87:     @errors = nil
 88:     gems_and_sources = []
 89: 
 90:     if @domain == :both or @domain == :local then
 91:       Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file|
 92:         spec = Gem::Format.from_file_by_path(gem_file).spec
 93:         gems_and_sources << [spec, gem_file] if spec.name == dep.name
 94:       end
 95:     end
 96: 
 97:     if @domain == :both or @domain == :remote then
 98:       begin
 99:         # REFACTOR: all = dep.requirement.needs_all?
100:         requirements = dep.requirement.requirements.map do |req, ver|
101:           req
102:         end
103: 
104:         all = !dep.prerelease? &&
105:               # we only need latest if there's one requirement and it is
106:               # guaranteed to match the newest specs
107:               (requirements.length > 1 or
108:                 (requirements.first != ">=" and requirements.first != ">"))
109: 
110:         found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
111: 
112:         gems_and_sources.push(*found)
113: 
114:       rescue Gem::RemoteFetcher::FetchError => e
115:         if Gem.configuration.really_verbose then
116:           say "Error fetching remote data:\t\t#{e.message}"
117:           say "Falling back to local-only install"
118:         end
119:         @domain = :local
120:       end
121:     end
122: 
123:     gems_and_sources.sort_by do |gem, source|
124:       [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
125:     end
126:   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 202
202:   def find_spec_by_name_and_version(gem_name,
203:                                     version = Gem::Requirement.default,
204:                                     prerelease = false)
205:     spec_and_source = nil
206: 
207:     glob = if File::ALT_SEPARATOR then
208:              gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
209:            else
210:              gem_name
211:            end
212: 
213:     local_gems = Dir["#{glob}*"].sort.reverse
214: 
215:     local_gems.each do |gem_file|
216:       next unless gem_file =~ /gem$/
217:       begin
218:         spec = Gem::Format.from_file_by_path(gem_file).spec
219:         spec_and_source = [spec, gem_file]
220:         break
221:       rescue SystemCallError, Gem::Package::FormatError
222:       end
223:     end
224: 
225:     unless spec_and_source 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:       spec_and_source = spec_and_sources.find { |spec, source|
230:         Gem::Platform.match spec.platform
231:       }
232:     end
233: 
234:     if spec_and_source.nil? then
235:       raise Gem::GemNotFoundException.new(
236:         "Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
237:         gem_name, version, @errors)
238:     end
239: 
240:     @specs_and_sources = [spec_and_source]
241:   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 132
132:   def gather_dependencies
133:     specs = @specs_and_sources.map { |spec,_| spec }
134: 
135:     # these gems were listed by the user, always install them
136:     keep_names = specs.map { |spec| spec.full_name }
137: 
138:     dependency_list = Gem::DependencyList.new @development
139:     dependency_list.add(*specs)
140:     to_do = specs.dup
141: 
142:     add_found_dependencies to_do, dependency_list unless @ignore_dependencies
143: 
144:     dependency_list.specs.reject! { |spec|
145:       not keep_names.include?(spec.full_name) and
146:       Gem::Specification.include?(spec)
147:     }
148: 
149:     unless dependency_list.ok? or @ignore_dependencies or @force then
150:       reason = dependency_list.why_not_ok?.map { |k,v|
151:         "#{k} requires #{v.join(", ")}"
152:       }.join("; ")
153:       raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
154:     end
155: 
156:     @gems_to_install = dependency_list.dependency_order.reverse
157:   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 257
257:   def install dep_or_name, version = Gem::Requirement.default
258:     if String === dep_or_name then
259:       find_spec_by_name_and_version dep_or_name, version, @prerelease
260:     else
261:       dep_or_name.prerelease = @prerelease
262:       @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
263:     end
264: 
265:     @installed_gems = []
266: 
267:     gather_dependencies
268: 
269:     last = @gems_to_install.size - 1
270:     @gems_to_install.each_with_index do |spec, index|
271:       next if Gem::Specification.include?(spec) and index != last
272: 
273:       # TODO: make this sorta_verbose so other users can benefit from it
274:       say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
275: 
276:       _, source_uri = @specs_and_sources.assoc spec
277:       begin
278:         local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
279:                                                              @cache_dir
280:       rescue Gem::RemoteFetcher::FetchError
281:         next if @force
282:         raise
283:       end
284: 
285:       inst = Gem::Installer.new local_gem_path,
286:                                 :bin_dir             => @bin_dir,
287:                                 :development         => @development,
288:                                 :env_shebang         => @env_shebang,
289:                                 :force               => @force,
290:                                 :format_executable   => @format_executable,
291:                                 :ignore_dependencies => @ignore_dependencies,
292:                                 :install_dir         => @install_dir,
293:                                 :security_policy     => @security_policy,
294:                                 :user_install        => @user_install,
295:                                 :wrappers            => @wrappers
296: 
297:       spec = inst.install
298: 
299:       @installed_gems << spec
300:     end
301: 
302:     @installed_gems
303:   end

[Validate]