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.from_gems_in 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 142
142:   def add_found_dependencies to_do, dependency_list
143:     seen = {}
144: 
145:     until to_do.empty? do
146:       spec = to_do.shift
147:       next if spec.nil? or seen[spec.name]
148:       seen[spec.name] = true
149: 
150:       deps = spec.runtime_dependencies
151:       deps |= spec.development_dependencies if @development
152: 
153:       deps.each do |dep|
154:         results = find_gems_with_sources(dep).reverse
155: 
156:         # FIX: throw in everything that satisfies, and let
157:         # FIX: dependencylist reduce to the chosen few
158:         results.reject! do |dep_spec,|
159:           to_do.push dep_spec
160: 
161:           # already locally installed
162:           @source_index.any? do |_, installed_spec|
163:             dep.name == installed_spec.name and
164:               dep.requirement.satisfied_by? installed_spec.version
165:           end
166:         end
167: 
168:         results.each do |dep_spec, source_uri|
169:           next if seen[dep_spec.name]
170:           @specs_and_sources << [dep_spec, source_uri]
171: 
172:           # FIX: this is the bug
173:           dependency_list.add dep_spec
174:         end
175:       end
176:     end
177:   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 184
184:   def find_spec_by_name_and_version(gem_name,
185:                                     version = Gem::Requirement.default,
186:                                     prerelease = false)
187:     spec_and_source = nil
188: 
189:     glob = if File::ALT_SEPARATOR then
190:              gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
191:            else
192:              gem_name
193:            end
194: 
195:     local_gems = Dir["#{glob}*"].sort.reverse
196: 
197:     unless local_gems.empty? then
198:       local_gems.each do |gem_file|
199:         next unless gem_file =~ /gem$/
200:         begin
201:           spec = Gem::Format.from_file_by_path(gem_file).spec
202:           spec_and_source = [spec, gem_file]
203:           break
204:         rescue SystemCallError, Gem::Package::FormatError
205:         end
206:       end
207:     end
208: 
209:     if spec_and_source.nil? then
210:       dep = Gem::Dependency.new gem_name, version
211:       dep.prerelease = true if prerelease
212:       spec_and_sources = find_gems_with_sources(dep).reverse
213: 
214:       spec_and_source = spec_and_sources.find { |spec, source|
215:         Gem::Platform.match spec.platform
216:       }
217:     end
218: 
219:     if spec_and_source.nil? then
220:       raise Gem::GemNotFoundException.new(
221:         "Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
222:         gem_name, version, @errors)
223:     end
224: 
225:     @specs_and_sources = [spec_and_source]
226:   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:     dependency_list = Gem::DependencyList.new @development
134:     dependency_list.add(*specs)
135:     to_do = specs.dup
136: 
137:     add_found_dependencies to_do, dependency_list unless @ignore_dependencies
138: 
139:     @gems_to_install = dependency_list.dependency_order.reverse
140:   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 242
242:   def install dep_or_name, version = Gem::Requirement.default
243:     if String === dep_or_name then
244:       find_spec_by_name_and_version dep_or_name, version, @prerelease
245:     else
246:       dep_or_name.prerelease = @prerelease
247:       @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
248:     end
249: 
250:     @installed_gems = []
251: 
252:     gather_dependencies
253: 
254:     @gems_to_install.each do |spec|
255:       last = spec == @gems_to_install.last
256:       # HACK is this test for full_name acceptable?
257:       next if @source_index.any? { |n,_| n == spec.full_name } and not last
258: 
259:       # TODO: make this sorta_verbose so other users can benefit from it
260:       say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
261: 
262:       _, source_uri = @specs_and_sources.assoc spec
263:       begin
264:         local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
265:                                                              @cache_dir
266:       rescue Gem::RemoteFetcher::FetchError
267:         next if @force
268:         raise
269:       end
270: 
271:       inst = Gem::Installer.new local_gem_path,
272:                                 :bin_dir             => @bin_dir,
273:                                 :development         => @development,
274:                                 :env_shebang         => @env_shebang,
275:                                 :force               => @force,
276:                                 :format_executable   => @format_executable,
277:                                 :ignore_dependencies => @ignore_dependencies,
278:                                 :install_dir         => @install_dir,
279:                                 :security_policy     => @security_policy,
280:                                 :source_index        => @source_index,
281:                                 :user_install        => @user_install,
282:                                 :wrappers            => @wrappers
283: 
284:       spec = inst.install
285: 
286:       @installed_gems << spec
287:     end
288: 
289:     @installed_gems
290:   end

[Validate]