diff options
| author | Adam Vandenberg | 2013-06-18 10:11:06 -0700 |
|---|---|---|
| committer | Adam Vandenberg | 2013-06-29 16:44:45 -0700 |
| commit | c2a5e3608ca435a969f62b2fa28722c3a1a106f0 (patch) | |
| tree | 06e184d54685c5825e7d2229a84e5494f1a71522 /Library | |
| parent | 099a62c95bb957ec676157533f06d621e75e5e85 (diff) | |
| download | brew-c2a5e3608ca435a969f62b2fa28722c3a1a106f0.tar.bz2 | |
Use Formula Loaders
Diffstat (limited to 'Library')
| -rw-r--r-- | Library/Homebrew/formulary.rb | 230 |
1 files changed, 151 insertions, 79 deletions
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 6cdbe9588..eb86a1fb8 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -1,106 +1,178 @@ +# The Formulary is responsible for creating instances +# of Formula. class Formulary - # Return a Formula instance for the given `name`. - # `name` may be: - # * a Formula instance, in which case it is returned - # TODO: is this code path used? - # * a Pathname to a local formula - # * a string containing a formula pathname - # * a string containing a formula URL - # * a string containing a formula name - # * a string containing a local bottle reference - def self.factory name - # If an instance of Formula is passed, just return it - return name if name.kind_of? Formula - # Otherwise, convert to String in case a Pathname comes in - name = name.to_s + def self.formula_class_defined? formula_name + Object.const_defined?(Formula.class_s(formula_name)) + end - # If a URL is passed, download to the cache and install - if name =~ %r[(https?|ftp)://] - url = name - name = Pathname.new(name).basename - path = HOMEBREW_CACHE_FORMULA+name - name = name.basename(".rb").to_s + def self.get_formula_class formula_name + Object.const_get(Formula.class_s(formula_name)) + end - unless Object.const_defined? Formula.class_s(name) - HOMEBREW_CACHE_FORMULA.mkpath - FileUtils.rm path, :force => true - curl url, '-o', path + # A FormulaLoader returns instances of formulae. + # Subclasses implement loaders for particular sources of formulae. + class FormulaLoader + # The formula's name + attr_reader :name + # The formula's ruby file's path or filename + attr_reader :path + + # Gets the formula instance. + # Subclasses must define this. + def get_formula; end + + # Return the Class for this formula, `require`-ing it if + # it has not been parsed before. + def klass + unless Formulary.formula_class_defined? name + puts "#{$0}: loading #{path}" if ARGV.debug? + begin + require path.to_s + rescue NoMethodError + # This is a programming error in an existing formula, and should not + # have a "no such formula" message. + raise + rescue LoadError, NameError + # TODO - show exception details + raise FormulaUnavailableError.new(name) + end end + klass = Formulary.get_formula_class(name) + if (klass == Formula) || !klass.ancestors.include?(Formula) + raise FormulaUnavailableError.new(name) + end + klass + end + end - install_type = :from_url - elsif name.match bottle_regex - bottle_filename = Pathname(name).realpath - version = Version.parse(bottle_filename).to_s - bottle_basename = bottle_filename.basename.to_s + # Loads formulae from bottles. + class BottleLoader < FormulaLoader + def initialize bottle_name + @bottle_filename = Pathname(bottle_name).realpath + version = Version.parse(@bottle_filename).to_s + bottle_basename = @bottle_filename.basename.to_s name_without_version = bottle_basename.rpartition("-#{version}").first if name_without_version.empty? if ARGV.homebrew_developer? opoo "Add a new version regex to version.rb to parse this filename." end + @name = name else - name = name_without_version + @name = name_without_version end - path = Formula.path(name) - install_type = :from_local_bottle - else - name = Formula.canonical_name(name) + @path = Formula.path(@name) + end - if name =~ %r{^(\w+)/(\w+)/([^/])+$} - # name appears to be a tapped formula, so we don't munge it - # in order to provide a useful error message when require fails. - path = Pathname.new(name) - elsif name.include? "/" - # If name was a path or mapped to a cached formula + def get_formula + formula = klass.new(name) + formula.downloader.local_bottle_path = @bottle_filename + return formula + end + end - # require allows filenames to drop the .rb extension, but everything else - # in our codebase will require an exact and fullpath. - name = "#{name}.rb" unless name =~ /\.rb$/ + # Loads formulae from Homebrew's provided Library + class StandardLoader < FormulaLoader + def initialize name + @name = name + @path = Formula.path(name) + end - path = Pathname.new(name) - name = path.stem - install_type = :from_path - else - # For names, map to the path and then require - path = Formula.path(name) - install_type = :from_name + def get_formula + return klass.new(name) + end + end + + # Loads formulae from disk using a path + class FromPathLoader < FormulaLoader + def initialize path + # require allows filenames to drop the .rb extension, but everything else + # in our codebase will require an exact and fullpath. + path = "#{name}.rb" unless path =~ /\.rb$/ + + @path = Pathname.new(path) + @name = @path.stem + end + + def get_formula + klass.new(name, path.to_s) + end + end + + # Loads formulae from URLs. + class FromUrlLoader < FormulaLoader + attr_reader :url + + def initialize url + @url = url + @path = (HOMEBREW_CACHE_FORMULA/(File.basename(url))) + @name = File.basename(url, '.rb') + end + + # Downloads the formula's .rb file + def fetch + unless Formulary.formula_class_defined? name + HOMEBREW_CACHE_FORMULA.mkpath + FileUtils.rm path.to_s, :force => true + curl url, '-o', path.to_s end end - klass_name = Formula.class_s(name) - unless Object.const_defined? klass_name - puts "#{$0}: loading #{path}" if ARGV.debug? - require path + def get_formula + return klass.new(name, path.to_s) end + end - begin - klass = Object.const_get klass_name - rescue NameError - # TODO really this text should be encoded into the exception - # and only shown if the UI deems it correct to show it - onoe "class \"#{klass_name}\" expected but not found in #{name}.rb" - puts "Double-check the name of the class in that formula." - raise LoadError + # Loads tapped formulae. + class TapLoader < FormulaLoader + def initialize tapped_name + @name = tapped_name + @path = Pathname.new(tapped_name) end - if install_type == :from_local_bottle - formula = klass.new(name) - formula.downloader.local_bottle_path = bottle_filename - return formula + def get_formula + klass.new(tapped_name, path.to_s) + end + end + + # Return a Formula instance for the given reference. + # `ref` may be: + # * a Formula instance, in which case it is returned + # TODO: is this code path used? + # * a Pathname to a local formula + # * a string containing a formula pathname + # * a string containing a formula URL + # * a string containing a formula name + # * a string containing a local bottle reference + def self.factory ref + # If an instance of Formula is passed, just return it + return ref if ref.kind_of? Formula + + # Otherwise, convert to String in case a Pathname comes in + # TODO - do we call with a Pathname instead of a string anywhere? + ref = ref.to_s + + # If a URL is passed, download to the cache and install + if ref =~ %r[(https?|ftp)://] + f = FromUrlLoader.new(ref) + f.fetch + elsif ref =~ Pathname::BOTTLE_EXTNAME_RX + f = BottleLoader.new(ref) + else + name_or_path = Formula.canonical_name(ref) + if name_or_path =~ %r{^(\w+)/(\w+)/([^/])+$} + # name appears to be a tapped formula, so we don't munge it + # in order to provide a useful error message when require fails. + f = TapLoader.new(name_or_path) + elsif name_or_path.include? "/" + # If name was a path or mapped to a cached formula + f = FromPathLoader.new(name_or_path) + else + # For names, map to the path and then require + f = StandardLoader.new(name_or_path) + end end - raise NameError if !klass.ancestors.include? Formula - raise NameError if klass == Formula - - return klass.new(name) if install_type == :from_name - return klass.new(name, path.to_s) - rescue NoMethodError - # This is a programming error in an existing formula, and should not - # have a "no such formula" message. - raise - rescue LoadError, NameError - # Catch NameError so that things that are invalid symbols still get - # a useful error message. - raise FormulaUnavailableError.new(name) + f.get_formula end end |
