1require 'rubygems/command' 2require 'rubygems/local_remote_options' 3require 'rubygems/spec_fetcher' 4require 'rubygems/version_option' 5require 'rubygems/text' 6 7class Gem::Commands::QueryCommand < Gem::Command 8 9 include Gem::Text 10 include Gem::LocalRemoteOptions 11 include Gem::VersionOption 12 13 def initialize(name = 'query', 14 summary = 'Query gem information in local or remote repositories') 15 super name, summary, 16 :name => //, :domain => :local, :details => false, :versions => true, 17 :installed => nil, :version => Gem::Requirement.default 18 19 add_option('-i', '--[no-]installed', 20 'Check for installed gem') do |value, options| 21 options[:installed] = value 22 end 23 24 add_option('-I', 'Equivalent to --no-installed') do |value, options| 25 options[:installed] = false 26 end 27 28 add_version_option command, "for use with --installed" 29 30 add_option('-n', '--name-matches REGEXP', 31 'Name of gem(s) to query on matches the', 32 'provided REGEXP') do |value, options| 33 options[:name] = /#{value}/i 34 end 35 36 add_option('-d', '--[no-]details', 37 'Display detailed information of gem(s)') do |value, options| 38 options[:details] = value 39 end 40 41 add_option( '--[no-]versions', 42 'Display only gem names') do |value, options| 43 options[:versions] = value 44 options[:details] = false unless value 45 end 46 47 add_option('-a', '--all', 48 'Display all gem versions') do |value, options| 49 options[:all] = value 50 end 51 52 add_option( '--[no-]prerelease', 53 'Display prerelease versions') do |value, options| 54 options[:prerelease] = value 55 end 56 57 add_local_remote_options 58 end 59 60 def defaults_str # :nodoc: 61 "--local --name-matches // --no-details --versions --no-installed" 62 end 63 64 def execute 65 exit_code = 0 66 67 name = options[:name] 68 prerelease = options[:prerelease] 69 70 unless options[:installed].nil? then 71 if name.source.empty? then 72 alert_error "You must specify a gem name" 73 exit_code |= 4 74 else 75 installed = installed? name, options[:version] 76 installed = !installed unless options[:installed] 77 78 if installed then 79 say "true" 80 else 81 say "false" 82 exit_code |= 1 83 end 84 end 85 86 terminate_interaction exit_code 87 end 88 89 req = Gem::Requirement.default 90 # TODO: deprecate for real 91 dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req } 92 dep.prerelease = prerelease 93 94 if local? then 95 if prerelease and not both? then 96 alert_warning "prereleases are always shown locally" 97 end 98 99 if ui.outs.tty? or both? then 100 say 101 say "*** LOCAL GEMS ***" 102 say 103 end 104 105 specs = Gem::Specification.find_all { |s| 106 s.name =~ name and req =~ s.version 107 } 108 109 spec_tuples = specs.map do |spec| 110 [spec.name_tuple, spec] 111 end 112 113 output_query_results spec_tuples 114 end 115 116 if remote? then 117 if ui.outs.tty? or both? then 118 say 119 say "*** REMOTE GEMS ***" 120 say 121 end 122 123 fetcher = Gem::SpecFetcher.fetcher 124 125 type = if options[:all] 126 if options[:prerelease] 127 :complete 128 else 129 :released 130 end 131 elsif options[:prerelease] 132 :prerelease 133 else 134 :latest 135 end 136 137 if options[:name].source.empty? 138 spec_tuples = fetcher.detect(type) { true } 139 else 140 spec_tuples = fetcher.detect(type) do |name_tuple| 141 options[:name] === name_tuple.name 142 end 143 end 144 145 output_query_results spec_tuples 146 end 147 end 148 149 private 150 151 ## 152 # Check if gem +name+ version +version+ is installed. 153 154 def installed?(name, req = Gem::Requirement.default) 155 Gem::Specification.any? { |s| s.name =~ name and req =~ s.version } 156 end 157 158 def output_query_results(spec_tuples) 159 output = [] 160 versions = Hash.new { |h,name| h[name] = [] } 161 162 spec_tuples.each do |spec_tuple, source| 163 versions[spec_tuple.name] << [spec_tuple, source] 164 end 165 166 versions = versions.sort_by do |(n,_),_| 167 n.downcase 168 end 169 170 output_versions output, versions 171 172 say output.join(options[:details] ? "\n\n" : "\n") 173 end 174 175 def output_versions output, versions 176 versions.each do |gem_name, matching_tuples| 177 matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse 178 179 platforms = Hash.new { |h,version| h[version] = [] } 180 181 matching_tuples.each do |n, _| 182 platforms[n.version] << n.platform if n.platform 183 end 184 185 seen = {} 186 187 matching_tuples.delete_if do |n,_| 188 if seen[n.version] then 189 true 190 else 191 seen[n.version] = true 192 false 193 end 194 end 195 196 output << make_entry(matching_tuples, platforms) 197 end 198 end 199 200 def entry_details entry, detail_tuple, specs, platforms 201 return unless options[:details] 202 203 name_tuple, spec = detail_tuple 204 205 spec = spec.fetch_spec name_tuple unless Gem::Specification === spec 206 207 entry << "\n" 208 209 spec_platforms entry, platforms 210 spec_authors entry, spec 211 spec_homepage entry, spec 212 spec_license entry, spec 213 spec_loaded_from entry, spec, specs 214 spec_summary entry, spec 215 end 216 217 def entry_versions entry, name_tuples, platforms 218 return unless options[:versions] 219 220 list = 221 if platforms.empty? or options[:details] then 222 name_tuples.map { |n| n.version }.uniq 223 else 224 platforms.sort.reverse.map do |version, pls| 225 if pls == [Gem::Platform::RUBY] then 226 version 227 else 228 ruby = pls.delete Gem::Platform::RUBY 229 platform_list = [ruby, *pls.sort].compact 230 "#{version} #{platform_list.join ' '}" 231 end 232 end 233 end 234 235 entry << " (#{list.join ', '})" 236 end 237 238 def make_entry entry_tuples, platforms 239 detail_tuple = entry_tuples.first 240 241 name_tuples, specs = entry_tuples.flatten.partition do |item| 242 Gem::NameTuple === item 243 end 244 245 entry = [name_tuples.first.name] 246 247 entry_versions entry, name_tuples, platforms 248 entry_details entry, detail_tuple, specs, platforms 249 250 entry.join 251 end 252 253 def spec_authors entry, spec 254 authors = "Author#{spec.authors.length > 1 ? 's' : ''}: " 255 authors << spec.authors.join(', ') 256 entry << format_text(authors, 68, 4) 257 end 258 259 def spec_homepage entry, spec 260 return if spec.homepage.nil? or spec.homepage.empty? 261 262 entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4) 263 end 264 265 def spec_license entry, spec 266 return if spec.license.nil? or spec.license.empty? 267 268 licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: " 269 licenses << spec.licenses.join(', ') 270 entry << "\n" << format_text(licenses, 68, 4) 271 end 272 273 def spec_loaded_from entry, spec, specs 274 return unless spec.loaded_from 275 276 if specs.length == 1 then 277 default = spec.default_gem? ? ' (default)' : nil 278 entry << "\n" << " Installed at#{default}: #{spec.base_dir}" 279 else 280 label = 'Installed at' 281 specs.each do |s| 282 version = s.version.to_s 283 version << ', default' if s.default_gem? 284 entry << "\n" << " #{label} (#{version}): #{s.base_dir}" 285 label = ' ' * label.length 286 end 287 end 288 end 289 290 def spec_platforms entry, platforms 291 non_ruby = platforms.any? do |_, pls| 292 pls.any? { |pl| pl != Gem::Platform::RUBY } 293 end 294 295 return unless non_ruby 296 297 if platforms.length == 1 then 298 title = platforms.values.length == 1 ? 'Platform' : 'Platforms' 299 entry << " #{title}: #{platforms.values.sort.join ', '}\n" 300 else 301 entry << " Platforms:\n" 302 platforms.sort_by do |version,| 303 version 304 end.each do |version, pls| 305 label = " #{version}: " 306 data = format_text pls.sort.join(', '), 68, label.length 307 data[0, label.length] = label 308 entry << data << "\n" 309 end 310 end 311 end 312 313 def spec_summary entry, spec 314 entry << "\n\n" << format_text(spec.summary, 68, 4) 315 end 316 317end 318 319