| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | require "formula" | 
					
						
							| 
									
										
										
										
											2015-09-09 13:55:19 +08:00
										 |  |  | require "formula_versions" | 
					
						
							| 
									
										
										
										
											2018-06-05 10:55:00 +02:00
										 |  |  | require "search" | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  | require "searchable" | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Descriptions | 
					
						
							| 
									
										
										
										
											2018-06-05 10:55:00 +02:00
										 |  |  |   extend Homebrew::Search | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-09 14:00:26 +08:00
										 |  |  |   CACHE_FILE = HOMEBREW_CACHE + "desc_cache.json" | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   def self.cache | 
					
						
							| 
									
										
										
										
											2016-09-17 15:17:27 +01:00
										 |  |  |     @cache || load_cache | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # If the cache file exists, load it into, and return, a hash; otherwise, | 
					
						
							|  |  |  |   # return nil. | 
					
						
							|  |  |  |   def self.load_cache | 
					
						
							| 
									
										
										
										
											2016-11-20 13:00:01 -05:00
										 |  |  |     @cache = JSON.parse(CACHE_FILE.read) if CACHE_FILE.exist? | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Write the cache to disk after ensuring the existence of the containing | 
					
						
							|  |  |  |   # directory. | 
					
						
							|  |  |  |   def self.save_cache | 
					
						
							|  |  |  |     HOMEBREW_CACHE.mkpath | 
					
						
							| 
									
										
										
										
											2016-11-20 13:00:01 -05:00
										 |  |  |     CACHE_FILE.atomic_write JSON.dump(@cache) | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Create a hash mapping all formulae to their descriptions; | 
					
						
							|  |  |  |   # save it for future use. | 
					
						
							|  |  |  |   def self.generate_cache | 
					
						
							|  |  |  |     @cache = {} | 
					
						
							| 
									
										
										
										
											2015-09-10 13:38:51 +08:00
										 |  |  |     Formula.each do |f| | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |       @cache[f.full_name] = f.desc | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-09-17 15:17:27 +01:00
										 |  |  |     save_cache | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-26 19:20:18 +08:00
										 |  |  |   # Return true if the cache exists, and none of the Taps | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   # repos were updated more recently than it was. | 
					
						
							|  |  |  |   def self.cache_fresh? | 
					
						
							| 
									
										
										
										
											2015-09-09 13:46:08 +08:00
										 |  |  |     return false unless CACHE_FILE.exist? | 
					
						
							| 
									
										
										
										
											2016-04-03 16:28:38 -04:00
										 |  |  |     cache_mtime = File.mtime(CACHE_FILE) | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-09 13:46:08 +08:00
										 |  |  |     Tap.each do |tap| | 
					
						
							|  |  |  |       next unless tap.git? | 
					
						
							| 
									
										
										
										
											2016-02-26 19:20:18 +08:00
										 |  |  |       repo_mtime = File.mtime(tap.path/".git/refs/heads/master") | 
					
						
							| 
									
										
										
										
											2015-09-09 13:46:08 +08:00
										 |  |  |       return false if repo_mtime > cache_mtime | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2015-09-09 13:46:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     true | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Create the cache if it doesn't already exist. | 
					
						
							|  |  |  |   def self.ensure_cache | 
					
						
							| 
									
										
										
										
											2016-09-17 15:17:27 +01:00
										 |  |  |     generate_cache unless cache_fresh? && cache | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Take a {Report}, as generated by cmd/update.rb. | 
					
						
							|  |  |  |   # Unless the cache file exists, do nothing. | 
					
						
							|  |  |  |   # If it does exist, but the Report is empty, just touch the cache file. | 
					
						
							|  |  |  |   # Otherwise, use the report to update the cache. | 
					
						
							|  |  |  |   def self.update_cache(report) | 
					
						
							| 
									
										
										
										
											2016-10-24 17:07:57 +02:00
										 |  |  |     return unless CACHE_FILE.exist? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if report.empty? | 
					
						
							|  |  |  |       FileUtils.touch CACHE_FILE | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       renamings = report.select_formula(:R) | 
					
						
							|  |  |  |       alterations = report.select_formula(:A) + report.select_formula(:M) + | 
					
						
							|  |  |  |                     renamings.map(&:last) | 
					
						
							|  |  |  |       cache_formulae(alterations, save: false) | 
					
						
							|  |  |  |       uncache_formulae(report.select_formula(:D) + | 
					
						
							|  |  |  |                             renamings.map(&:first)) | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Given an array of formula names, add them and their descriptions to the | 
					
						
							|  |  |  |   # cache. Save the updated cache to disk, unless explicitly told not to. | 
					
						
							| 
									
										
										
										
											2016-09-17 15:32:44 +01:00
										 |  |  |   def self.cache_formulae(formula_names, options = { save: true }) | 
					
						
							| 
									
										
										
										
											2016-10-24 17:07:57 +02:00
										 |  |  |     return unless cache | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     formula_names.each do |name| | 
					
						
							|  |  |  |       begin | 
					
						
							| 
									
										
										
										
											2017-10-07 00:31:28 +02:00
										 |  |  |         @cache[name] = Formulary.factory(name).desc | 
					
						
							| 
									
										
										
										
											2016-10-24 17:07:57 +02:00
										 |  |  |       rescue FormulaUnavailableError, *FormulaVersions::IGNORED_EXCEPTIONS | 
					
						
							| 
									
										
										
										
											2017-10-07 00:31:28 +02:00
										 |  |  |         @cache.delete(name) | 
					
						
							| 
									
										
										
										
											2015-09-09 13:55:19 +08:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-10-24 17:07:57 +02:00
										 |  |  |     save_cache if options[:save] | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Given an array of formula names, remove them and their descriptions from | 
					
						
							|  |  |  |   # the cache. Save the updated cache to disk, unless explicitly told not to. | 
					
						
							| 
									
										
										
										
											2016-09-17 15:32:44 +01:00
										 |  |  |   def self.uncache_formulae(formula_names, options = { save: true }) | 
					
						
							| 
									
										
										
										
											2016-10-24 17:07:57 +02:00
										 |  |  |     return unless cache | 
					
						
							|  |  |  |     formula_names.each { |name| @cache.delete(name) } | 
					
						
							|  |  |  |     save_cache if options[:save] | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Given a regex, find all formulae whose specified fields contain a match. | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |   def self.search(string_or_regex, field = :either) | 
					
						
							| 
									
										
										
										
											2016-09-17 15:17:27 +01:00
										 |  |  |     ensure_cache | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |     @cache.extend(Searchable) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     results = case field | 
					
						
							|  |  |  |     when :name | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |       @cache.search(string_or_regex) { |name, _| name } | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     when :desc | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |       @cache.search(string_or_regex) { |_, desc| desc } | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     when :either | 
					
						
							| 
									
										
										
										
											2018-06-05 15:39:09 +02:00
										 |  |  |       @cache.search(string_or_regex) | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     new(results) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Create an actual instance. | 
					
						
							|  |  |  |   def initialize(descriptions) | 
					
						
							|  |  |  |     @descriptions = descriptions | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Take search results -- a hash mapping formula names to descriptions -- and | 
					
						
							|  |  |  |   # print them. | 
					
						
							|  |  |  |   def print | 
					
						
							| 
									
										
										
										
											2016-08-30 21:38:13 +02:00
										 |  |  |     blank = Formatter.warning("[no description]") | 
					
						
							| 
									
										
										
										
											2016-04-11 17:09:40 -04:00
										 |  |  |     @descriptions.keys.sort.each do |full_name| | 
					
						
							|  |  |  |       short_name = short_names[full_name] | 
					
						
							| 
									
										
										
										
											2017-05-29 18:24:52 +01:00
										 |  |  |       printed_name = (short_name_counts[short_name] == 1) ? short_name : full_name | 
					
						
							| 
									
										
										
										
											2016-04-11 17:09:40 -04:00
										 |  |  |       description = @descriptions[full_name] || blank | 
					
						
							| 
									
										
										
										
											2016-08-26 16:04:47 +02:00
										 |  |  |       puts "#{Tty.bold}#{printed_name}:#{Tty.reset} #{description}" | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-04-11 17:09:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def short_names | 
					
						
							|  |  |  |     @short_names ||= Hash[@descriptions.keys.map { |k| [k, k.split("/").last] }] | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def short_name_counts | 
					
						
							|  |  |  |     @short_name_counts ||= | 
					
						
							| 
									
										
										
										
											2016-09-21 14:36:20 +02:00
										 |  |  |       short_names.values.each_with_object(Hash.new(0)) { |name, counts| counts[name] += 1 } | 
					
						
							| 
									
										
										
										
											2016-04-11 17:09:40 -04:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2015-05-05 15:29:01 -07:00
										 |  |  | end |