diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index e5de938d2c..dbd31fadef 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -9,6 +9,7 @@ require 'patches' require 'compilers' require 'build_environment' require 'build_options' +require 'formulary' class Formula @@ -320,6 +321,7 @@ class Formula python(:allowed_major_versions => [3], &block) end + # Generates a formula's ruby class name from a formula's name def self.class_s name # remove invalid characters and then camelcase it name.capitalize.gsub(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase } \ @@ -394,99 +396,7 @@ class Formula end 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 - - # 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 - - unless Object.const_defined? self.class_s(name) - HOMEBREW_CACHE_FORMULA.mkpath - FileUtils.rm path, :force => true - curl url, '-o', path - 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 - 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 - else - name = name_without_version - end - path = Formula.path(name) - install_type = :from_local_bottle - else - name = Formula.canonical_name(name) - - 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 - - # 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$/ - - 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 - end - end - - klass_name = self.class_s(name) - unless Object.const_defined? klass_name - puts "#{$0}: loading #{path}" if ARGV.debug? - require path - 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 - end - - if install_type == :from_local_bottle - formula = klass.new(name) - formula.downloader.local_bottle_path = bottle_filename - return formula - 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) + Formulary.factory name end def tap diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb new file mode 100644 index 0000000000..6cdbe95887 --- /dev/null +++ b/Library/Homebrew/formulary.rb @@ -0,0 +1,106 @@ +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 + + # 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 + + unless Object.const_defined? Formula.class_s(name) + HOMEBREW_CACHE_FORMULA.mkpath + FileUtils.rm path, :force => true + curl url, '-o', path + 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 + 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 + else + name = name_without_version + end + path = Formula.path(name) + install_type = :from_local_bottle + else + name = Formula.canonical_name(name) + + 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 + + # 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$/ + + 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 + end + end + + klass_name = Formula.class_s(name) + unless Object.const_defined? klass_name + puts "#{$0}: loading #{path}" if ARGV.debug? + require path + 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 + end + + if install_type == :from_local_bottle + formula = klass.new(name) + formula.downloader.local_bottle_path = bottle_filename + return formula + 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) + end +end