diff --git a/Library/Homebrew/cask/lib/hbc.rb b/Library/Homebrew/cask/lib/hbc.rb index 363879574d..4f22a643d8 100644 --- a/Library/Homebrew/cask/lib/hbc.rb +++ b/Library/Homebrew/cask/lib/hbc.rb @@ -7,7 +7,6 @@ require "hbc/auditor" require "hbc/cache" require "hbc/cask" require "hbc/cask_loader" -require "hbc/without_source" require "hbc/caskroom" require "hbc/checkable" require "hbc/cli" @@ -24,7 +23,6 @@ require "hbc/macos" require "hbc/pkg" require "hbc/qualified_token" require "hbc/scopes" -require "hbc/source" require "hbc/staged" require "hbc/system_command" require "hbc/topological_hash" @@ -45,10 +43,7 @@ module Hbc Caskroom.ensure_caskroom_exists end - def self.load(query) - odebug "Loading Cask definitions" - cask = Source.for_query(query).load - cask.dumpcask - cask + def self.load(ref) + CaskLoader.load(ref) end end diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb index 3fe02d7e15..c392e6b727 100644 --- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb +++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb @@ -1,44 +1,167 @@ module Hbc - class CaskLoader - def self.load_from_file(path) - raise CaskError, "File '#{path}' does not exist" unless path.exist? - raise CaskError, "File '#{path}' is not readable" unless path.readable? - raise CaskError, "File '#{path}' is not a plain file" unless path.file? - - token = path.basename(".rb").to_s - content = IO.read(path).force_encoding("UTF-8") - - new(token, content, path).load - end - - def self.load_from_string(token, content) - new(token, content).load - end - - def load - instance_eval(@content, __FILE__, __LINE__) - rescue CaskError, StandardError, ScriptError => e - e.message.concat(" while loading '#{@token}'") - e.message.concat(" from '#{@path}'") unless @path.nil? - raise e, e.message - end - - private - - def initialize(token, content, path = nil) - @token = token - @content = content - @path = path unless path.nil? - end - - def cask(header_token, &block) - raise CaskTokenDoesNotMatchError.new(@token, header_token) unless @token == header_token - - if @path.nil? - Cask.new(@token, &block) - else - Cask.new(@token, sourcefile_path: @path, &block) + module CaskLoader + class FromContentLoader + def initialize(content) + @content = content end + + def load + instance_eval(@content.force_encoding("UTF-8"), __FILE__, __LINE__) + end + + private + + def cask(header_token, &block) + Cask.new(header_token, &block) + end + end + + class FromPathLoader < FromContentLoader + def self.can_load?(ref) + path = Pathname.new(ref) + path.extname == ".rb" && path.expand_path.exist? + end + + attr_reader :token, :path + + def initialize(path) + path = Pathname.new(path).expand_path + + @token = path.basename(".rb").to_s + @path = path + end + + def load + raise CaskError, "'#{@path}' does not exist." unless @path.exist? + raise CaskError, "'#{@path}' is not readable." unless @path.readable? + raise CaskError, "'#{@path}' is not a file." unless @path.file? + + @content = IO.read(@path) + + super + end + + private + + def cask(header_token, &block) + if @token != header_token + raise CaskTokenDoesNotMatchError.new(@token, header_token) + end + + Cask.new(header_token, sourcefile_path: @path, &block) + end + end + + class FromURILoader < FromPathLoader + def self.can_load?(ref) + !(ref.to_s !~ ::URI.regexp) + end + + def initialize(url) + @url = url + uri = URI(url) + super Hbc.cache/File.basename(uri.path) + end + + def load + Hbc.cache.mkpath + FileUtils.rm_f @path + + begin + ohai "Downloading #{@url}." + curl @url, "-o", @path + rescue ErrorDuringExecution + raise CaskUnavailableError, @url + end + + super + end + end + + class FromTapLoader < FromPathLoader + def self.can_load?(ref) + !(ref.to_s !~ HOMEBREW_TAP_CASK_REGEX) + end + + def initialize(tapped_name) + user, repo, token = tapped_name.split("/", 3).map(&:downcase) + @tap = Tap.fetch(user, repo) + + super @tap.cask_dir/"#{token}.rb" + end + + def load + @tap.install unless @tap.installed? + + super + end + end + + class NullLoader < FromPathLoader + def self.can_load?(*) + true + end + + def initialize(ref) + @token = File.basename(ref, ".rb") + super CaskLoader.default_path(@token) + end + + def load + raise CaskUnavailableError, @token + end + end + + def self.load_from_file(path) + FromPathLoader.new(path).load + end + + def self.load_from_string(content) + FromContentLoader.new(content).load + end + + def self.path(ref) + self.for(ref).path + end + + def self.load(ref) + self.for(ref).load + end + + def self.for(ref) + [ + FromURILoader, + FromTapLoader, + FromPathLoader, + ].each do |loader_class| + return loader_class.new(ref) if loader_class.can_load?(ref) + end + + if FromPathLoader.can_load?(default_path(ref)) + return FromPathLoader.new(default_path(ref)) + end + + possible_tap_casks = tap_paths(ref) + if possible_tap_casks.count == 1 + possible_tap_cask = possible_tap_casks.first + return FromPathLoader.new(possible_tap_cask) + end + + possible_installed_cask = Cask.new(ref) + if possible_installed_cask.installed? + return FromPathLoader.new(possible_installed_cask.installed_caskfile) + end + + NullLoader.new(ref) + end + + def self.default_path(token) + Hbc.default_tap.cask_dir/"#{token.to_s.downcase}.rb" + end + + def self.tap_paths(token) + Tap.map { |t| t.cask_dir/"#{token.to_s.downcase}.rb" } + .select(&:exist?) end end end diff --git a/Library/Homebrew/cask/lib/hbc/locations.rb b/Library/Homebrew/cask/lib/hbc/locations.rb index 80add91dd5..c15cec32bf 100644 --- a/Library/Homebrew/cask/lib/hbc/locations.rb +++ b/Library/Homebrew/cask/lib/hbc/locations.rb @@ -126,15 +126,14 @@ module Hbc end if token_with_tap - user, repo, token = token_with_tap.split("/") + user, repo, token = token_with_tap.split("/", 3) tap = Tap.fetch(user, repo) else token = query_without_extension tap = Hbc.default_tap end - return query_path if tap.cask_dir.nil? - tap.cask_dir.join("#{token}.rb") + CaskLoader.default_path(token) end def tcc_db diff --git a/Library/Homebrew/cask/lib/hbc/scopes.rb b/Library/Homebrew/cask/lib/hbc/scopes.rb index aa870eec55..fb26757358 100644 --- a/Library/Homebrew/cask/lib/hbc/scopes.rb +++ b/Library/Homebrew/cask/lib/hbc/scopes.rb @@ -11,15 +11,15 @@ module Hbc end def all_tapped_cask_dirs - Tap.map(&:cask_dir).compact + Tap.map(&:cask_dir).select(&:directory?) end def all_tokens - Tap.map do |t| + Tap.flat_map do |t| t.cask_files.map do |p| "#{t.name}/#{File.basename(p, ".rb")}" end - end.flatten + end end def installed diff --git a/Library/Homebrew/cask/lib/hbc/source.rb b/Library/Homebrew/cask/lib/hbc/source.rb deleted file mode 100644 index 5f2e9ddfef..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "hbc/source/gone" -require "hbc/source/path_slash_required" -require "hbc/source/path_slash_optional" -require "hbc/source/tapped_qualified" -require "hbc/source/untapped_qualified" -require "hbc/source/tapped" -require "hbc/source/uri" - -module Hbc - module Source - def self.sources - [ - URI, - PathSlashRequired, - TappedQualified, - UntappedQualified, - Tapped, - PathSlashOptional, - Gone, - ] - end - - def self.for_query(query) - odebug "Translating '#{query}' into a valid Cask source" - raise CaskUnavailableError, query if query.to_s =~ /^\s*$/ - source = sources.find do |s| - odebug "Testing source class #{s}" - s.me?(query) - end - raise CaskUnavailableError, query unless source - odebug "Success! Using source class #{source}" - resolved_cask_source = source.new(query) - odebug "Resolved Cask URI or file source to '#{resolved_cask_source}'" - resolved_cask_source - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/gone.rb b/Library/Homebrew/cask/lib/hbc/source/gone.rb deleted file mode 100644 index 1c744d0dbb..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/gone.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Hbc - module Source - class Gone - def self.me?(query) - WithoutSource.new(query).installed? - end - - attr_reader :query - - def initialize(query) - @query = query - end - - def load - WithoutSource.new(query) - end - - def to_s - "" - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/path_base.rb b/Library/Homebrew/cask/lib/hbc/source/path_base.rb deleted file mode 100644 index 4e4bdcf158..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/path_base.rb +++ /dev/null @@ -1,29 +0,0 @@ -require "rubygems" -require "hbc/cask_loader" - -module Hbc - module Source - class PathBase - # derived classes must define method self.me? - - def self.path_for_query(query) - Pathname.new(query).sub(/(\.rb)?$/, ".rb") - end - - attr_reader :path - - def initialize(path) - @path = Pathname.new(path).expand_path - end - - def load - CaskLoader.load_from_file(@path) - end - - def to_s - # stringify to fully-resolved location - @path.to_s - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/path_slash_optional.rb b/Library/Homebrew/cask/lib/hbc/source/path_slash_optional.rb deleted file mode 100644 index d96a41130d..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/path_slash_optional.rb +++ /dev/null @@ -1,12 +0,0 @@ -require "hbc/source/path_base" - -module Hbc - module Source - class PathSlashOptional < PathBase - def self.me?(query) - path = path_for_query(query) - path.exist? - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/path_slash_required.rb b/Library/Homebrew/cask/lib/hbc/source/path_slash_required.rb deleted file mode 100644 index 2753b17109..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/path_slash_required.rb +++ /dev/null @@ -1,12 +0,0 @@ -require "hbc/source/path_base" - -module Hbc - module Source - class PathSlashRequired < PathBase - def self.me?(query) - path = path_for_query(query) - path.to_s.include?("/") && path.exist? - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/tapped.rb b/Library/Homebrew/cask/lib/hbc/source/tapped.rb deleted file mode 100644 index c1f5f95bca..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/tapped.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Hbc - module Source - class Tapped - def self.me?(query) - Hbc.path(query).exist? - end - - attr_reader :token - - def initialize(token) - @token = token - end - - def load - PathSlashOptional.new(Hbc.path(token)).load - end - - def to_s - # stringify to fully-resolved location - Hbc.path(token).expand_path.to_s - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/tapped_qualified.rb b/Library/Homebrew/cask/lib/hbc/source/tapped_qualified.rb deleted file mode 100644 index 2db6ddbca5..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/tapped_qualified.rb +++ /dev/null @@ -1,21 +0,0 @@ -require "hbc/source/tapped" - -module Hbc - module Source - class TappedQualified < Tapped - def self.me?(query) - return if (tap = tap_for_query(query)).nil? - - tap.installed? && Hbc.path(query).exist? - end - - def self.tap_for_query(query) - qualified_token = QualifiedToken.parse(query) - return if qualified_token.nil? - - user, repo = qualified_token[0..1] - Tap.fetch(user, repo) - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/untapped_qualified.rb b/Library/Homebrew/cask/lib/hbc/source/untapped_qualified.rb deleted file mode 100644 index 698cc46ce6..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/untapped_qualified.rb +++ /dev/null @@ -1,14 +0,0 @@ -require "hbc/source/tapped_qualified" - -module Hbc - module Source - class UntappedQualified < TappedQualified - def self.me?(query) - return if (tap = tap_for_query(query)).nil? - - tap.install - tap.installed? && Hbc.path(query).exist? - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/source/uri.rb b/Library/Homebrew/cask/lib/hbc/source/uri.rb deleted file mode 100644 index 09fab4bd0e..0000000000 --- a/Library/Homebrew/cask/lib/hbc/source/uri.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Hbc - module Source - class URI - def self.me?(query) - !(query.to_s =~ ::URI.regexp).nil? - end - - attr_reader :uri - - def initialize(uri) - @uri = uri - end - - def load - Hbc.cache.mkpath - path = Hbc.cache.join(File.basename(uri)) - ohai "Downloading #{uri}" - odebug "Download target -> #{path}" - begin - curl(uri, "-o", path.to_s) - rescue ErrorDuringExecution - raise CaskUnavailableError, uri - end - PathSlashOptional.new(path).load - end - - def to_s - uri.to_s - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/without_source.rb b/Library/Homebrew/cask/lib/hbc/without_source.rb deleted file mode 100644 index 69131d5c70..0000000000 --- a/Library/Homebrew/cask/lib/hbc/without_source.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Hbc - class WithoutSource < Cask - # Override from `Hbc::DSL` because we don't have a cask source file to work - # with, so we don't know the cask's `version`. - def staged_path - (caskroom_path.children - [metadata_master_container_path]).first - end - - def to_s - "#{token} (!)" - end - - def installed? - caskroom_path.exist? - end - end -end diff --git a/Library/Homebrew/cmd/irb.rb b/Library/Homebrew/cmd/irb.rb index 5561845e72..cba8a5b82b 100644 --- a/Library/Homebrew/cmd/irb.rb +++ b/Library/Homebrew/cmd/irb.rb @@ -19,6 +19,11 @@ class String end end +def cask + $LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib") + require "hbc" +end + module Homebrew module_function diff --git a/Library/Homebrew/compat/hbc/cask_loader.rb b/Library/Homebrew/compat/hbc/cask_loader.rb index a7c84e3ce8..e6cb65b4f2 100644 --- a/Library/Homebrew/compat/hbc/cask_loader.rb +++ b/Library/Homebrew/compat/hbc/cask_loader.rb @@ -12,7 +12,11 @@ module CaskLoaderCompatibilityLayer end module Hbc - class CaskLoader - prepend CaskLoaderCompatibilityLayer + module CaskLoader + class FromContentLoader; end + + class FromPathLoader < FromContentLoader + prepend CaskLoaderCompatibilityLayer + end end end diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index 56e06834d9..ba55e3cf66 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -285,7 +285,7 @@ class Tap # path to the directory of all {Formula} files for this {Tap}. def formula_dir - @formula_dir ||= potential_formula_dirs.detect(&:directory?) + @formula_dir ||= potential_formula_dirs.detect(&:directory?) || path/"Formula" end def potential_formula_dirs @@ -294,12 +294,12 @@ class Tap # path to the directory of all {Cask} files for this {Tap}. def cask_dir - @cask_dir ||= [path/"Casks"].detect(&:directory?) + @cask_dir ||= path/"Casks" end # an array of all {Formula} files of this {Tap}. def formula_files - @formula_files ||= if formula_dir + @formula_files ||= if formula_dir.directory? formula_dir.children.select(&method(:formula_file?)) else [] @@ -308,7 +308,7 @@ class Tap # an array of all {Cask} files of this {Tap}. def cask_files - @cask_files ||= if cask_dir + @cask_files ||= if cask_dir.directory? cask_dir.children.select(&method(:cask_file?)) else [] diff --git a/Library/Homebrew/test/cask/cli/list_spec.rb b/Library/Homebrew/test/cask/cli/list_spec.rb index e367e95887..b53eb169ac 100644 --- a/Library/Homebrew/test/cask/cli/list_spec.rb +++ b/Library/Homebrew/test/cask/cli/list_spec.rb @@ -40,23 +40,6 @@ describe Hbc::CLI::List, :cask do end end - describe "when Casks have been renamed" do - let(:caskroom_path) { Hbc.caskroom.join("ive-been-renamed") } - let(:staged_path) { caskroom_path.join("latest") } - - before do - staged_path.mkpath - end - - it "lists installed Casks without backing ruby files (due to renames or otherwise)" do - expect { - Hbc::CLI::List.run - }.to output(<<-EOS.undent).to_stdout - ive-been-renamed (!) - EOS - end - end - describe "given a set of installed Casks" do let(:caffeine) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") } let(:transmission) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") }