1require 'rubygems/command' 2require 'rubygems/local_remote_options' 3require 'rubygems/version_option' 4 5class Gem::Commands::DependencyCommand < Gem::Command 6 7 include Gem::LocalRemoteOptions 8 include Gem::VersionOption 9 10 def initialize 11 super 'dependency', 12 'Show the dependencies of an installed gem', 13 :version => Gem::Requirement.default, :domain => :local 14 15 add_version_option 16 add_platform_option 17 add_prerelease_option 18 19 add_option('-R', '--[no-]reverse-dependencies', 20 'Include reverse dependencies in the output') do 21 |value, options| 22 options[:reverse_dependencies] = value 23 end 24 25 add_option('-p', '--pipe', 26 "Pipe Format (name --version ver)") do |value, options| 27 options[:pipe_format] = value 28 end 29 30 add_local_remote_options 31 end 32 33 def arguments # :nodoc: 34 "GEMNAME name of gem to show dependencies for" 35 end 36 37 def defaults_str # :nodoc: 38 "--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies" 39 end 40 41 def usage # :nodoc: 42 "#{program_name} GEMNAME" 43 end 44 45 def execute 46 if options[:reverse_dependencies] and remote? and not local? then 47 alert_error 'Only reverse dependencies for local gems are supported.' 48 terminate_interaction 1 49 end 50 51 options[:args] << '' if options[:args].empty? 52 53 pattern = if options[:args].length == 1 and 54 options[:args].first =~ /\A\/(.*)\/(i)?\z/m then 55 flags = $2 ? Regexp::IGNORECASE : nil 56 Regexp.new $1, flags 57 else 58 /\A#{Regexp.union(*options[:args])}/ 59 end 60 61 # TODO: deprecate for real damnit 62 dependency = Gem::Deprecate.skip_during { 63 Gem::Dependency.new pattern, options[:version] 64 } 65 dependency.prerelease = options[:prerelease] 66 67 specs = [] 68 69 specs.concat dependency.matching_specs if local? 70 71 if remote? and not options[:reverse_dependencies] then 72 fetcher = Gem::SpecFetcher.fetcher 73 74 ss, _ = fetcher.spec_for_dependency dependency 75 76 ss.each { |s,o| specs << s } 77 end 78 79 if specs.empty? then 80 patterns = options[:args].join ',' 81 say "No gems found matching #{patterns} (#{options[:version]})" if 82 Gem.configuration.verbose 83 84 terminate_interaction 1 85 end 86 87 specs = specs.uniq.sort 88 89 reverse = Hash.new { |h, k| h[k] = [] } 90 91 if options[:reverse_dependencies] then 92 specs.each do |spec| 93 reverse[spec.full_name] = find_reverse_dependencies spec 94 end 95 end 96 97 if options[:pipe_format] then 98 specs.each do |spec| 99 unless spec.dependencies.empty? 100 spec.dependencies.sort_by { |dep| dep.name }.each do |dep| 101 say "#{dep.name} --version '#{dep.requirement}'" 102 end 103 end 104 end 105 else 106 response = '' 107 108 specs.each do |spec| 109 response << print_dependencies(spec) 110 unless reverse[spec.full_name].empty? then 111 response << " Used by\n" 112 reverse[spec.full_name].each do |sp, dep| 113 response << " #{sp} (#{dep})\n" 114 end 115 end 116 response << "\n" 117 end 118 119 say response 120 end 121 end 122 123 def print_dependencies(spec, level = 0) 124 response = '' 125 response << ' ' * level + "Gem #{spec.full_name}\n" 126 unless spec.dependencies.empty? then 127 spec.dependencies.sort_by { |dep| dep.name }.each do |dep| 128 response << ' ' * level + " #{dep}\n" 129 end 130 end 131 response 132 end 133 134 ## 135 # Returns an Array of [specification, dep] that are satisfied by +spec+. 136 137 def find_reverse_dependencies(spec) 138 result = [] 139 140 Gem::Specification.each do |sp| 141 sp.dependencies.each do |dep| 142 dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep 143 144 if spec.name == dep.name and 145 dep.requirement.satisfied_by?(spec.version) then 146 result << [sp.full_name, dep] 147 end 148 end 149 end 150 151 result 152 end 153 154end 155 156