From c1d5a2264e7747344b53f2bcb413fc9c1a4c6ed6 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 04:39:18 -0500 Subject: [PATCH 1/8] Load casks as instances of Hbc::Cask rather than subclasses --- lib/hbc/cask.rb | 19 +- lib/hbc/cli.rb | 13 +- lib/hbc/cli/info.rb | 2 +- lib/hbc/dsl.rb | 541 ++++++++++++++++++------------------ lib/hbc/exceptions.rb | 5 +- lib/hbc/source/path_base.rb | 104 +++---- lib/hbc/without_source.rb | 11 - 7 files changed, 324 insertions(+), 371 deletions(-) diff --git a/lib/hbc/cask.rb b/lib/hbc/cask.rb index 940536d892d..fb3b3d43953 100644 --- a/lib/hbc/cask.rb +++ b/lib/hbc/cask.rb @@ -1,17 +1,20 @@ +require 'forwardable' + require 'hbc/dsl' class Hbc::Cask - include Hbc::DSL - - def self.token - # todo removeme: prepending KlassPrefix is transitional as we move away from representing Casks as classes - self.name.sub(/^KlassPrefix/,'').gsub(/([a-zA-Z\d])([A-Z])/,'\1-\2').gsub(/([a-zA-Z\d])([A-Z])/,'\1-\2').downcase - end + extend Forwardable attr_reader :token, :sourcefile_path - def initialize(sourcefile_path=nil) + def initialize(token, sourcefile_path=nil, dsl=nil, &block) + @token = token @sourcefile_path = sourcefile_path - @token = self.class.token + @dsl = dsl || Hbc::DSL.new(@token) + @dsl.instance_eval(&block) if block_given? + end + + Hbc::DSL::DSL_METHODS.each do |method_name| + define_method(method_name) { @dsl.send(method_name) } end METADATA_SUBDIR = '.metadata' diff --git a/lib/hbc/cli.rb b/lib/hbc/cli.rb index 51dae6506cd..4a1de6ca6b2 100644 --- a/lib/hbc/cli.rb +++ b/lib/hbc/cli.rb @@ -115,14 +115,15 @@ class Hbc::CLI command = lookup_command(command_string) run_command(command, *rest) rescue Hbc::CaskError, Hbc::CaskSha256MismatchError => e - onoe e - $stderr.puts Hbc::Utils.error_message_with_suggestions if e.is_a?(Hbc::CaskHeaderParseError) - $stderr.puts e.backtrace if Hbc.debug + msg = e.message + msg << e.backtrace.join("\n") if Hbc.debug + onoe msg exit 1 rescue StandardError, ScriptError, NoMemoryError => e - onoe e - $stderr.puts Hbc::Utils.error_message_with_suggestions - $stderr.puts e.backtrace + msg = e.message + msg << Hbc::Utils.error_message_with_suggestions + msg << e.backtrace.join("\n") + onoe msg exit 1 end diff --git a/lib/hbc/cli/info.rb b/lib/hbc/cli/info.rb index 1bb9298a8e4..1207c2f34eb 100644 --- a/lib/hbc/cli/info.rb +++ b/lib/hbc/cli/info.rb @@ -58,7 +58,7 @@ PURPOSE def self.artifact_info(cask) retval = '' - Hbc::DSL::ClassMethods.ordinary_artifact_types.each do |type| + Hbc::DSL::ORDINARY_ARTIFACT_TYPES.each do |type| if cask.artifacts[type].length > 0 retval = "#{Tty.blue.bold}==>#{Tty.reset.bold} Contents#{Tty.reset}\n" unless retval.length > 0 cask.artifacts[type].each do |artifact| diff --git a/lib/hbc/dsl.rb b/lib/hbc/dsl.rb index 0705f37271a..3fa8404027b 100644 --- a/lib/hbc/dsl.rb +++ b/lib/hbc/dsl.rb @@ -1,6 +1,6 @@ require 'set' -module Hbc::DSL; end +class Hbc::DSL; end require 'hbc/dsl/appcast' require 'hbc/dsl/base' @@ -16,312 +16,297 @@ require 'hbc/dsl/uninstall_postflight' require 'hbc/dsl/uninstall_preflight' require 'hbc/dsl/version' -module Hbc::DSL - def self.included(base) - base.extend(ClassMethods) +class Hbc::DSL + ORDINARY_ARTIFACT_TYPES = [ + :app, + :artifact, + :audio_unit_plugin, + :binary, + :colorpicker, + :font, + :input_method, + :internet_plugin, + :pkg, + :prefpane, + :qlplugin, + :screen_saver, + :service, + :stage_only, + :suite, + :vst_plugin + ] + + ACTIVATABLE_ARTIFACT_TYPES = [:installer, *ORDINARY_ARTIFACT_TYPES] - [:stage_only] + + SPECIAL_ARTIFACT_TYPES = [ + :uninstall, + :zap, + ] + + ARTIFACT_BLOCK_TYPES = [ + :preflight, + :postflight, + :uninstall_preflight, + :uninstall_postflight, + ] + + DSL_METHODS = Set.new [ + :accessibility_access, + :appcast, + :artifacts, + :auto_updates, + :caskroom_path, + :caveats, + :conflicts_with, + :container, + :depends_on, + :full_name, + :gpg, + :homepage, + :license, + :name, + :sha256, + :staged_path, + :url, + :version, + *ORDINARY_ARTIFACT_TYPES, + *ACTIVATABLE_ARTIFACT_TYPES, + *SPECIAL_ARTIFACT_TYPES, + *ARTIFACT_BLOCK_TYPES + ] + + attr_reader :token + def initialize(token) + @token = token end - def full_name; self.class.full_name; end + # A quite fragile shim to allow "full_name" be exposed as simply "name" + # in the DSL. We detect the difference with the already-existing "name" + # method by arity, and use "full_name" exclusively in backend code. + def name(*args) + if args.empty? + super + else + self.full_name(args) + end + end - def homepage; self.class.homepage; end + def full_name(_full_name=nil) + @full_name ||= [] + if _full_name + @full_name.concat(Array(*_full_name)) + end + @full_name + end - def url; self.class.url; end + def homepage(homepage=nil) + if @homepage and !homepage.nil? + raise Hbc::CaskInvalidError.new(self.token, "'homepage' stanza may only appear once") + end + @homepage ||= homepage + end - def appcast; self.class.appcast; end + def url(*args) + return @url if args.empty? + if @url and !args.empty? + raise Hbc::CaskInvalidError.new(self.token, "'url' stanza may only appear once") + end + @url ||= begin + Hbc::URL.new(*args) + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, "'url' stanza failed with: #{e}") + end + end - def gpg; self.class.gpg; end + def appcast(*args) + return @appcast if args.empty? + if @appcast and !args.empty? + raise Hbc::CaskInvalidError.new(self.token, "'appcast' stanza may only appear once") + end + @appcast ||= begin + Hbc::DSL::Appcast.new(*args) unless args.empty? + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + end - def version; self.class.version; end + def gpg(*args) + return @gpg if args.empty? + if @gpg and !args.empty? + raise Hbc::CaskInvalidError.new(self.token, "'gpg' stanza may only appear once") + end + @gpg ||= begin + Hbc::DSL::Gpg.new(*args) unless args.empty? + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + end - def license; self.class.license; end + def container(*args) + return @container if args.empty? + if @container and !args.empty? + # todo: remove this constraint, and instead merge multiple container stanzas + raise Hbc::CaskInvalidError.new(self.token, "'container' stanza may only appear once") + end + @container ||= begin + Hbc::DSL::Container.new(*args) unless args.empty? + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + # todo: remove this backward-compatibility section after removing nested_container + if @container and @container.nested + artifacts[:nested_container] << @container.nested + end + @container + end - def depends_on; self.class.depends_on; end + SYMBOLIC_VERSIONS = Set.new [ + :latest, + ] - def conflicts_with; self.class.conflicts_with; end + def version(arg=nil) + if arg.nil? + return @version + elsif @version + raise Hbc::CaskInvalidError.new(self.token, "'version' stanza may only appear once") + elsif !arg.is_a?(String) and !SYMBOLIC_VERSIONS.include?(arg) + raise Hbc::CaskInvalidError.new(self.token, "invalid 'version' value: '#{arg.inspect}'") + end + @version ||= Hbc::DSL::Version.new(arg) + end - def container; self.class.container; end + SYMBOLIC_SHA256S = Set.new [ + :no_check, + ] - def sha256; self.class.sha256; end + def sha256(arg=nil) + if arg.nil? + return @sha256 + elsif @sha256 + raise Hbc::CaskInvalidError.new(self.token, "'sha256' stanza may only appear once") + elsif !arg.is_a?(String) and !SYMBOLIC_SHA256S.include?(arg) + raise Hbc::CaskInvalidError.new(self.token, "invalid 'sha256' value: '#{arg.inspect}'") + end + @sha256 ||= arg + end - def artifacts; self.class.artifacts; end + def license(arg=nil) + return @license if arg.nil? + if @license and !arg.nil? + raise Hbc::CaskInvalidError.new(self.token, "'license' stanza may only appear once") + end + @license ||= begin + Hbc::DSL::License.new(arg) unless arg.nil? + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + end - def caskroom_path; self.class.caskroom_path; end + # depends_on uses a load method so that multiple stanzas can be merged + def depends_on(*args) + return @depends_on if args.empty? + @depends_on ||= Hbc::DSL::DependsOn.new() + begin + @depends_on.load(*args) unless args.empty? + rescue RuntimeError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + @depends_on + end - def staged_path; self.class.staged_path; end + def conflicts_with(*args) + if @conflicts_with and !args.empty? + # todo: remove this constraint, and instead merge multiple conflicts_with stanzas + raise Hbc::CaskInvalidError.new(self.token, "'conflicts_with' stanza may only appear once") + end + return @conflicts_with if args.empty? + @conflicts_with ||= begin + Hbc::DSL::ConflictsWith.new(*args) unless args.empty? + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) + end + end - def caveats; self.class.caveats; end + def artifacts + @artifacts ||= Hash.new { |hash, key| hash[key] = Set.new } + end - def accessibility_access; self.class.accessibility_access; end + def caskroom_path + @caskroom_path ||= Hbc.caskroom.join(self.token) + end - def auto_updates; self.class.auto_updates; end + def staged_path + return @staged_path if @staged_path + cask_version = self.version || :unknown + @staged_path = self.caskroom_path.join(cask_version.to_s) + end - module ClassMethods + def caveats(*string, &block) + @caveats ||= [] + if block_given? + @caveats << Hbc::Caveats.new(block) + elsif string.any? + @caveats << string.map{ |s| s.to_s.sub(/[\r\n \t]*\Z/, "\n\n") } + else + # accessor + @caveats + end + end - # A quite fragile shim to allow "full_name" be exposed as simply "name" - # in the DSL. We detect the difference with the already-existing "name" - # method by arity, and use "full_name" exclusively in backend code. - def name(*args) - if args.empty? - super - else - self.full_name(args) + def accessibility_access(accessibility_access=nil) + if @accessibility_access and !accessibility_access.nil? + raise Hbc::CaskInvalidError.new(self.token, "'accessibility_access' stanza may only appear once") + end + @accessibility_access ||= accessibility_access + end + + def auto_updates(auto_updates=nil) + if @auto_updates and !auto_updates.nil? + raise Hbc::CaskInvalidError.new(self.token, "'auto_updates' stanza may only appear once") + end + @auto_updates ||= auto_updates + end + + ORDINARY_ARTIFACT_TYPES.each do |type| + define_method(type) do |*args| + if type == :stage_only and args != [true] + raise Hbc::CaskInvalidError.new(self.token, "'stage_only' takes a single argument: true") + end + artifacts[type] << args + if artifacts.key?(:stage_only) and + artifacts.keys.count > 1 and + ! (artifacts.keys & ACTIVATABLE_ARTIFACT_TYPES).empty? + raise Hbc::CaskInvalidError.new(self.token, "'stage_only' must be the only activatable artifact") end end + end - def full_name(_full_name=nil) - @full_name ||= [] - if _full_name - @full_name.concat(Array(*_full_name)) - end - @full_name + def installer(*args) + if args.empty? + return artifacts[:installer] end - - def homepage(homepage=nil) - if @homepage and !homepage.nil? - raise Hbc::CaskInvalidError.new(self.token, "'homepage' stanza may only appear once") - end - @homepage ||= homepage + begin + artifacts[:installer] << Hbc::DSL::Installer.new(*args) + raise "'stage_only' must be the only activatable artifact" if artifacts.key?(:stage_only) + rescue StandardError => e + raise Hbc::CaskInvalidError.new(self.token, e) end + end - def url(*args) - return @url if args.empty? - if @url and !args.empty? - raise Hbc::CaskInvalidError.new(self.token, "'url' stanza may only appear once") - end - @url ||= begin - Hbc::URL.new(*args) - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, "'url' stanza failed with: #{e}") - end + SPECIAL_ARTIFACT_TYPES.each do |type| + define_method(type) do |*args| + artifacts[type].merge(args) end + end - def appcast(*args) - return @appcast if args.empty? - if @appcast and !args.empty? - raise Hbc::CaskInvalidError.new(self.token, "'appcast' stanza may only appear once") - end - @appcast ||= begin - Hbc::DSL::Appcast.new(*args) unless args.empty? - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end + ARTIFACT_BLOCK_TYPES.each do |type| + define_method(type) do |&block| + artifacts[type] << block end + end - def gpg(*args) - return @gpg if args.empty? - if @gpg and !args.empty? - raise Hbc::CaskInvalidError.new(self.token, "'gpg' stanza may only appear once") - end - @gpg ||= begin - Hbc::DSL::Gpg.new(*args) unless args.empty? - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - end - - def container(*args) - return @container if args.empty? - if @container and !args.empty? - # todo: remove this constraint, and instead merge multiple container stanzas - raise Hbc::CaskInvalidError.new(self.token, "'container' stanza may only appear once") - end - @container ||= begin - Hbc::DSL::Container.new(*args) unless args.empty? - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - # todo: remove this backward-compatibility section after removing nested_container - if @container and @container.nested - artifacts[:nested_container] << @container.nested - end - @container - end - - SYMBOLIC_VERSIONS = Set.new [ - :latest, - ] - - def version(arg=nil) - if arg.nil? - return @version - elsif @version - raise Hbc::CaskInvalidError.new(self.token, "'version' stanza may only appear once") - elsif !arg.is_a?(String) and !SYMBOLIC_VERSIONS.include?(arg) - raise Hbc::CaskInvalidError.new(self.token, "invalid 'version' value: '#{arg.inspect}'") - end - @version ||= Hbc::DSL::Version.new(arg) - end - - SYMBOLIC_SHA256S = Set.new [ - :no_check, - ] - - def sha256(arg=nil) - if arg.nil? - return @sha256 - elsif @sha256 - raise Hbc::CaskInvalidError.new(self.token, "'sha256' stanza may only appear once") - elsif !arg.is_a?(String) and !SYMBOLIC_SHA256S.include?(arg) - raise Hbc::CaskInvalidError.new(self.token, "invalid 'sha256' value: '#{arg.inspect}'") - end - @sha256 ||= arg - end - - def license(arg=nil) - return @license if arg.nil? - if @license and !arg.nil? - raise Hbc::CaskInvalidError.new(self.token, "'license' stanza may only appear once") - end - @license ||= begin - Hbc::DSL::License.new(arg) unless arg.nil? - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - end - - # depends_on uses a load method so that multiple stanzas can be merged - def depends_on(*args) - return @depends_on if args.empty? - @depends_on ||= Hbc::DSL::DependsOn.new() - begin - @depends_on.load(*args) unless args.empty? - rescue RuntimeError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - @depends_on - end - - def conflicts_with(*args) - if @conflicts_with and !args.empty? - # todo: remove this constraint, and instead merge multiple conflicts_with stanzas - raise Hbc::CaskInvalidError.new(self.token, "'conflicts_with' stanza may only appear once") - end - return @conflicts_with if args.empty? - @conflicts_with ||= begin - Hbc::DSL::ConflictsWith.new(*args) unless args.empty? - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - end - - def artifacts - @artifacts ||= Hash.new { |hash, key| hash[key] = Set.new } - end - - def caskroom_path - @caskroom_path ||= Hbc.caskroom.join(self.token) - end - - def staged_path - return @staged_path if @staged_path - cask_version = self.version || :unknown - @staged_path = self.caskroom_path.join(cask_version.to_s) - end - - def caveats(*string, &block) - @caveats ||= [] - if block_given? - @caveats << Hbc::Caveats.new(block) - elsif string.any? - @caveats << string.map{ |s| s.to_s.sub(/[\r\n \t]*\Z/, "\n\n") } - else - # accessor - @caveats - end - end - - def accessibility_access(accessibility_access=nil) - if @accessibility_access and !accessibility_access.nil? - raise Hbc::CaskInvalidError.new(self.token, "'accessibility_access' stanza may only appear once") - end - @accessibility_access ||= accessibility_access - end - - def auto_updates(auto_updates=nil) - if @auto_updates and !auto_updates.nil? - raise Hbc::CaskInvalidError.new(self.token, "'auto_updates' stanza may only appear once") - end - @auto_updates ||= auto_updates - end - - def self.ordinary_artifact_types - @@ordinary_artifact_types ||= [ - :app, - :suite, - :artifact, - :prefpane, - :qlplugin, - :font, - :service, - :colorpicker, - :binary, - :input_method, - :internet_plugin, - :audio_unit_plugin, - :vst_plugin, - :screen_saver, - :pkg, - :stage_only, - ] - end - - def self.activatable_artifact_types - @@activatable_artifact_types ||= [:installer, *ordinary_artifact_types] - [:stage_only] - end - - ordinary_artifact_types.each do |type| - define_method(type) do |*args| - if type == :stage_only and args != [true] - raise Hbc::CaskInvalidError.new(self.token, "'stage_only' takes a single argument: true") - end - artifacts[type] << args - if artifacts.key?(:stage_only) and - artifacts.keys.count > 1 and - ! (artifacts.keys & Hbc::DSL::ClassMethods.activatable_artifact_types).empty? - raise Hbc::CaskInvalidError.new(self.token, "'stage_only' must be the only activatable artifact") - end - end - end - - def installer(*args) - if args.empty? - return artifacts[:installer] - end - begin - artifacts[:installer] << Hbc::DSL::Installer.new(*args) - raise "'stage_only' must be the only activatable artifact" if artifacts.key?(:stage_only) - rescue StandardError => e - raise Hbc::CaskInvalidError.new(self.token, e) - end - end - - SPECIAL_ARTIFACT_TYPES = [ - :uninstall, - :zap, - ] - - SPECIAL_ARTIFACT_TYPES.each do |type| - define_method(type) do |*args| - artifacts[type].merge(args) - end - end - - ARTIFACT_BLOCK_TYPES = [ - :preflight, - :postflight, - :uninstall_preflight, - :uninstall_postflight, - ] - - ARTIFACT_BLOCK_TYPES.each do |type| - define_method(type) do |&block| - artifacts[type] << block - end - end - - def method_missing(method, *args) - Hbc::Utils.method_missing_message(method, self.token) - return nil - end + def method_missing(method, *args) + Hbc::Utils.method_missing_message(method, self.token) + return nil end end diff --git a/lib/hbc/exceptions.rb b/lib/hbc/exceptions.rb index d957830e235..84486148341 100644 --- a/lib/hbc/exceptions.rb +++ b/lib/hbc/exceptions.rb @@ -126,7 +126,10 @@ class Hbc::CaskInvalidError < Hbc::CaskError end end -class Hbc::CaskHeaderParseError < Hbc::CaskInvalidError +class Hbc::CaskTokenDoesNotMatchError < Hbc::CaskInvalidError + def initialize(token, header_token) + super(token, "Bad header line: '#{header_token}' does not match file name") + end end class Hbc::CaskSha256MissingError < ArgumentError diff --git a/lib/hbc/source/path_base.rb b/lib/hbc/source/path_base.rb index dd732db10e1..d0623921913 100644 --- a/lib/hbc/source/path_base.rb +++ b/lib/hbc/source/path_base.rb @@ -20,76 +20,48 @@ class Hbc::Source::PathBase raise Hbc::CaskError.new "File '#{path}' does not exist" unless path.exist? raise Hbc::CaskError.new "File '#{path}' is not readable" unless path.readable? raise Hbc::CaskError.new "File '#{path}' is not a plain file" unless path.file? - begin - - # transitional hack: convert first lines of the new form - # - # cask 'google-chrome' do - # - # to the old form - # - # class GoogleChrome < Hbc (class GoogleChrome < Cask) - # - # limitation: does not support Ruby extended quoting such as %Q{} - # - # todo: in the future, this can be pared down to an "eval" - - # read Cask - cask_contents = File.open(path, 'rb') do |handle| - contents = handle.read - if defined?(Encoding) - contents.force_encoding('UTF-8') - else - contents - end - end - - # munge text - cask_contents.sub!(%r{\A(\s*\#[^\n]*\n)+}, ''); - if %r{\A\s*(test_)?cask\s+(?::v[\d_]+(test)?\s+=>\s+)?([\'\"])(\S+?)\3(?:\s*,\s*|\s+)do\s*\n}.match(cask_contents) - is_test = $1 || $2 - header_token = $4 - superclass_name = is_test ? 'Hbc::TestCask' : 'Hbc::Cask' - cask_contents.sub!(%r{\A[^\n]+\n}, "class #{cask_class_name} < #{superclass_name}\n") - if header_token != cask_token - raise Hbc::CaskInvalidError.new(cask_token, "Bad header line: '#{header_token}' does not match file name") - end - else - raise Hbc::CaskHeaderParseError.new(cask_token, "Bad header line: parse failed") - end - - # simulate "require" - begin - Object.const_get(cask_class_name) - rescue NameError - eval(cask_contents, TOPLEVEL_BINDING) - end - - rescue Hbc::CaskError, StandardError, ScriptError => e - # bug: e.message.concat doesn't work with Hbc::CaskError exceptions - e.message.concat(" while loading '#{path}'") - raise e - end - begin - Object.const_get(cask_class_name).new(path) - rescue Hbc::CaskError, StandardError, ScriptError => e - # bug: e.message.concat doesn't work with Hbc::CaskError exceptions - e.message.concat(" while instantiating '#{cask_class_name}' from '#{path}'") - raise e - end - end - - def cask_token - path.basename.to_s.sub(/\.rb/, '') - end - - def cask_class_name - # todo removeme: prepending KlassPrefix is transitional as we move away from representing Casks as classes - 'KlassPrefix'.concat cask_token.split('-').map(&:capitalize).join + load_cask end def to_s # stringify to fully-resolved location path.to_s end + + private + + def load_cask + instance_eval(cask_contents, __FILE__, __LINE__) + rescue Hbc::CaskError, StandardError, ScriptError => e + # bug: e.message.concat doesn't work with Hbc::CaskError exceptions + raise e, e.message.concat(" while loading '#{path}'") + end + + def cask_contents + File.open(path, 'rb') do |handle| + contents = handle.read + if defined?(Encoding) + contents.force_encoding('UTF-8') + else + contents + end + end + end + + def cask(header_token, &block) + build_cask(Hbc::Cask, header_token, &block) + end + + def test_cask(header_token, &block) + build_cask(Hbc::TestCask, header_token, &block) + end + + def build_cask(cask_class, header_token, &block) + raise Hbc::CaskTokenDoesNotMatchError.new(cask_token, header_token) unless cask_token == header_token + cask_class.new(cask_token, path, &block) + end + + def cask_token + path.basename.to_s.sub(/\.rb/, '') + end end diff --git a/lib/hbc/without_source.rb b/lib/hbc/without_source.rb index 489c06504cb..dac6861da02 100644 --- a/lib/hbc/without_source.rb +++ b/lib/hbc/without_source.rb @@ -1,15 +1,4 @@ class Hbc::WithoutSource < Hbc::Cask - def initialize(sourcefile_path=nil) - @sourcefile_path = sourcefile_path - @token = sourcefile_path - end - - # Override from `Hbc::DSL` because `@token` is set to the constructor argument - # instead of `self.class.token` as in `Hbc::Cask`. - def caskroom_path - Hbc.caskroom.join(token) - end - # 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 From 37112e90c35c26a68ae01d02eb51dd18986e5921 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 04:42:20 -0500 Subject: [PATCH 2/8] Fix casks with Utils modules --- Casks/ax88179.rb | 8 ++++---- Casks/ax88772.rb | 8 ++++---- Casks/mcs783x.rb | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Casks/ax88179.rb b/Casks/ax88179.rb index 0d4524fa430..88d04e542f4 100644 --- a/Casks/ax88179.rb +++ b/Casks/ax88179.rb @@ -3,17 +3,17 @@ cask 'ax88179' do sha256 '50b9754649cb9f67a1a911ff07e49c3fc5ac931c49f28dc3235adb95d31e3323' module Utils - def self.basename - "AX88179_178A_Macintosh_10.6_to_10.11_Driver_Installer_v#{Module.nesting.last.version}" + def self.basename(version) + "AX88179_178A_Macintosh_10.6_to_10.11_Driver_Installer_v#{version}" end end - url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename}.zip" + url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename(version)}.zip" name 'AX88179' homepage 'http://www.asix.com.tw/products.php?op=pItemdetail&PItemID=131;71;112&PLine=71' license :gratis - container :nested => "#{Utils.basename}/AX88179_178A.dmg" + container :nested => "#{Utils.basename(version)}/AX88179_178A.dmg" pkg "AX88179_178A_v#{version[0..-10]}.pkg" diff --git a/Casks/ax88772.rb b/Casks/ax88772.rb index 64699af708e..5234e9b465d 100644 --- a/Casks/ax88772.rb +++ b/Casks/ax88772.rb @@ -3,17 +3,17 @@ cask 'ax88772' do sha256 'cc336a77ed35ab6b9972f76fb2a4c77650072c2844fd1632a1875b035a311c6f' module Utils - def self.basename - "AX88772C_772B_772A_760_772_Macintosh_10.5_to_10.11_Driver_Installer_v#{Module.nesting.last.version}" + def self.basename(version) + "AX88772C_772B_772A_760_772_Macintosh_10.5_to_10.11_Driver_Installer_v#{version}" end end - url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename}.zip" + url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename(version)}.zip" name 'AX88772' homepage 'http://www.asix.com.tw/products.php?op=pItemdetail&PItemID=86;71;101&PLine=71' license :unknown # TODO: change license and remove this comment; ':unknown' is a machine-generated placeholder - container :nested => "#{Utils.basename}/AX88772.dmg" + container :nested => "#{Utils.basename(version)}/AX88772.dmg" pkg "AX88772_v#{version.major_minor_patch}.pkg" diff --git a/Casks/mcs783x.rb b/Casks/mcs783x.rb index fe92fe3ba2f..b7007d71413 100644 --- a/Casks/mcs783x.rb +++ b/Casks/mcs783x.rb @@ -3,17 +3,17 @@ cask 'mcs783x' do sha256 'd86bdf6107cec7d3990f6967a5be782f7945cb722f22789cb04051514ba87a10' module Utils - def self.basename - "MCS783x_Mac_OSX_10.5_to_10.10_driver_v#{Module.nesting.last.version}" + def self.basename(version) + "MCS783x_Mac_OSX_10.5_to_10.10_driver_v#{version}" end end - url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename}.zip" + url "http://www.asix.com.tw/FrootAttach/driver/#{Utils.basename(version)}.zip" name 'ASIX MCS7830/7832 USB to Ethernet Controller Driver' homepage 'http://www.asix.com.tw/products.php?op=pItemdetail&PItemID=108;71;101&PLine=71' license :unknown # TODO: change license and remove this comment; ':unknown' is a machine-generated placeholder - container :nested => "#{Utils.basename}/MCS7830_v#{version.major_minor_patch}.dmg" + container :nested => "#{Utils.basename(version)}/MCS7830_v#{version.major_minor_patch}.dmg" pkg "MCS7830 v#{version.major_minor_patch}.pkg" From 99c96c0fc1b4a4e489da06f11265c1965081c547 Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 04:45:37 -0500 Subject: [PATCH 3/8] Fix tests broken by changing casks from classes to instances --- lib/hbc/cli/base.rb | 2 +- spec/cask/audit_spec.rb | 5 +- spec/cask/cli_spec.rb | 64 +++++++++----------- spec/cask/download_strategy_spec.rb | 2 +- spec/spec_helper.rb | 2 + test/cask/artifact/alt_target_test.rb | 7 +-- test/cask/artifact/app_test.rb | 7 +-- test/cask/artifact/postflight_block_test.rb | 8 +-- test/cask/artifact/preflight_block_test.rb | 8 +-- test/cask/artifact/two_apps_correct_test.rb | 7 +-- test/cask/container/naked_test.rb | 4 +- test/cask/dsl_test.rb | 66 ++++++++------------- test/cask_test.rb | 43 +++----------- test/test_helper.rb | 8 +-- 14 files changed, 83 insertions(+), 150 deletions(-) diff --git a/lib/hbc/cli/base.rb b/lib/hbc/cli/base.rb index e44ae3af64a..9a0a3ddae1d 100644 --- a/lib/hbc/cli/base.rb +++ b/lib/hbc/cli/base.rb @@ -8,7 +8,7 @@ class Hbc::CLI::Base end def self.cask_tokens_from(args) - args.reject { |a| a.chars.first == '-' } + args.reject { |a| a.empty? || a.chars.first == '-' } end def self.help diff --git a/spec/cask/audit_spec.rb b/spec/cask/audit_spec.rb index 0e98c961d0f..7dfc05e1579 100644 --- a/spec/cask/audit_spec.rb +++ b/spec/cask/audit_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Hbc::Audit do include AuditMatchers - let(:cask) { Hbc::Cask.new } + let(:cask) { instance_double(Hbc::Cask) } let(:download) { false } let(:audit) { Hbc::Audit.new(cask, download) } @@ -125,7 +125,8 @@ describe Hbc::Audit do end describe "audit of downloads" do - let(:cask) { Hbc::Cask.new } + let(:cask_token) { 'with-binary' } + let(:cask) { Hbc.load(cask_token) } let(:download) { instance_double(Hbc::Download) } let(:verify) { class_double(Hbc::Verify).as_stubbed_const } let(:error_msg) { "Download Failed" } diff --git a/spec/cask/cli_spec.rb b/spec/cask/cli_spec.rb index 7ada825fdf7..59e9fba32d4 100644 --- a/spec/cask/cli_spec.rb +++ b/spec/cask/cli_spec.rb @@ -15,48 +15,42 @@ describe Hbc::CLI do ]) end - describe ".process" do + context ".process" do let(:noop_command) { double('CLI::Noop') } - before { - allow(Hbc::CLI).to receive(:lookup_command) { noop_command } - allow(noop_command).to receive(:run) - } - it "respects the env variable when choosing what appdir to create" do - EnvHelper.with_env_var('HOMEBREW_CASK_OPTS', "--appdir=/custom/appdir") do - allow(Hbc).to receive(:init) { - expect(Hbc.appdir.to_s).to eq('/custom/appdir') - } - Hbc::CLI.process('noop') - end + before do + allow(Hbc).to receive(:init) + allow(described_class).to receive(:lookup_command).with('noop').and_return(noop_command) + allow(noop_command).to receive(:run) end - # todo: merely invoking init causes an attempt to create the caskroom directory - # - # it "respects the ENV variable when choosing a non-default Caskroom location" do - # EnvHelper.with_env_var 'HOMEBREW_CASK_OPTS', "--caskroom=/custom/caskdir" do - # allow(Hbc).to receive(:init) { - # expect(Hbc.caskroom.to_s).to eq('/custom/caskdir') - # } - # Hbc::CLI.process('noop') - # end - # end - - it "exits with a status of 1 when something goes wrong" do - Hbc::CLI.expects(:exit).with(1) - Hbc::CLI.expects(:lookup_command).raises(Hbc::CaskError) - allow(Hbc).to receive(:init) { - shutup { - Hbc::CLI.process('noop') - } - } + around do |example| + shutup { example.run } end it "passes `--version` along to the subcommand" do - expect(Hbc::CLI).to receive(:run_command).with(noop_command, '--version') - shutup { - Hbc::CLI.process(['noop', '--version']) - } + expect(described_class).to receive(:run_command).with(noop_command, '--version') + described_class.process(%w[noop --version]) + end + + it "respects the env variable when choosing what appdir to create" do + EnvHelper.with_env_var('HOMEBREW_CASK_OPTS', "--appdir=/custom/appdir") do + expect(Hbc).to receive(:appdir=).with(Pathname('/custom/appdir')) + described_class.process('noop') + end + end + + it "respects the env variable when choosing a non-default Caskroom location" do + EnvHelper.with_env_var 'HOMEBREW_CASK_OPTS', "--caskroom=/custom/caskdir" do + expect(Hbc).to receive(:caskroom=).with(Pathname('/custom/caskdir')) + described_class.process('noop') + end + end + + it "exits with a status of 1 when something goes wrong" do + allow(described_class).to receive(:lookup_command).and_raise(Hbc::CaskError) + expect(described_class).to receive(:exit).with(1) + described_class.process('noop') end end end diff --git a/spec/cask/download_strategy_spec.rb b/spec/cask/download_strategy_spec.rb index e9ebedc73cf..314fd942520 100644 --- a/spec/cask/download_strategy_spec.rb +++ b/spec/cask/download_strategy_spec.rb @@ -4,7 +4,7 @@ describe 'download strategies' do let(:url) { 'http://example.com/cask.dmg' } let(:url_options) { Hash.new } let(:cask) { - class_double(Hbc::Cask, + instance_double(Hbc::Cask, :token => 'some-cask', :url => Hbc::URL.new(url, url_options), :version => '1.2.3.4' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a38cb59c4ff..6c96c4fe04b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +require 'pathname' + if ENV['COVERAGE'] require 'coveralls' Coveralls.wear_merged! diff --git a/test/cask/artifact/alt_target_test.rb b/test/cask/artifact/alt_target_test.rb index 4da6b220ce7..3cb1cfdd313 100644 --- a/test/cask/artifact/alt_target_test.rb +++ b/test/cask/artifact/alt_target_test.rb @@ -33,8 +33,7 @@ describe Hbc::Artifact::App do end it "works with an application in a subdir" do - AltSubDirCask = Class.new(Hbc::Cask) - AltSubDirCask.class_eval do + subdir_cask = Hbc::Cask.new('subdir') do url TestHelper.local_binary_url('caffeine.zip') homepage 'http://example.com/local-caffeine' version '1.2.3' @@ -43,9 +42,7 @@ describe Hbc::Artifact::App do end begin - subdir_cask = AltSubDirCask.new.tap do |cask| - TestHelper.install_without_artifacts(cask) - end + TestHelper.install_without_artifacts(subdir_cask) appsubdir = subdir_cask.staged_path.join('subdir').tap(&:mkpath) FileUtils.mv(subdir_cask.staged_path.join('Caffeine.app'), appsubdir) diff --git a/test/cask/artifact/app_test.rb b/test/cask/artifact/app_test.rb index 158d6b31ca1..0697484e551 100644 --- a/test/cask/artifact/app_test.rb +++ b/test/cask/artifact/app_test.rb @@ -19,8 +19,7 @@ describe Hbc::Artifact::App do end it "works with an application in a subdir" do - SubDirCask = Class.new(Hbc::Cask) - SubDirCask.class_eval do + subdir_cask = Hbc::Cask.new('subdir') do url TestHelper.local_binary_url('caffeine.zip') homepage 'http://example.com/local-caffeine' version '1.2.3' @@ -29,9 +28,7 @@ describe Hbc::Artifact::App do end begin - subdir_cask = SubDirCask.new.tap do |cask| - TestHelper.install_without_artifacts(cask) - end + TestHelper.install_without_artifacts(subdir_cask) appsubdir = subdir_cask.staged_path.join('subdir').tap(&:mkpath) FileUtils.mv(subdir_cask.staged_path.join('Caffeine.app'), appsubdir) diff --git a/test/cask/artifact/postflight_block_test.rb b/test/cask/artifact/postflight_block_test.rb index 88584ed1789..341fc2e3a17 100644 --- a/test/cask/artifact/postflight_block_test.rb +++ b/test/cask/artifact/postflight_block_test.rb @@ -6,15 +6,13 @@ describe Hbc::Artifact::PostflightBlock do called = false yielded_arg = nil - CaskWithPostflight = Class.new(Hbc::Cask) - CaskWithPostflight.class_eval do + cask = Hbc::Cask.new('with-postflight') do postflight do |c| called = true yielded_arg = c end end - cask = CaskWithPostflight.new Hbc::Artifact::PostflightBlock.new(cask).install_phase called.must_equal true @@ -27,15 +25,13 @@ describe Hbc::Artifact::PostflightBlock do called = false yielded_arg = nil - CaskWithUninstallPostflight = Class.new(Hbc::Cask) - CaskWithUninstallPostflight.class_eval do + cask = Hbc::Cask.new('with-uninstall-postflight') do uninstall_postflight do |c| called = true yielded_arg = c end end - cask = CaskWithUninstallPostflight.new Hbc::Artifact::PostflightBlock.new(cask).uninstall_phase called.must_equal true diff --git a/test/cask/artifact/preflight_block_test.rb b/test/cask/artifact/preflight_block_test.rb index 1d3c6b223bd..9d1c89310f1 100644 --- a/test/cask/artifact/preflight_block_test.rb +++ b/test/cask/artifact/preflight_block_test.rb @@ -6,15 +6,13 @@ describe Hbc::Artifact::PreflightBlock do called = false yielded_arg = nil - CaskWithPreflight = Class.new(Hbc::Cask) - CaskWithPreflight.class_eval do + cask = Hbc::Cask.new('with-preflight') do preflight do |c| called = true yielded_arg = c end end - cask = CaskWithPreflight.new Hbc::Artifact::PreflightBlock.new(cask).install_phase called.must_equal true @@ -27,15 +25,13 @@ describe Hbc::Artifact::PreflightBlock do called = false yielded_arg = nil - CaskWithUninstallPreflight = Class.new(Hbc::Cask) - CaskWithUninstallPreflight.class_eval do + cask = Hbc::Cask.new('with-uninstall-preflight') do uninstall_preflight do |c| called = true yielded_arg = c end end - cask = CaskWithUninstallPreflight.new Hbc::Artifact::PreflightBlock.new(cask).uninstall_phase called.must_equal true diff --git a/test/cask/artifact/two_apps_correct_test.rb b/test/cask/artifact/two_apps_correct_test.rb index b0e359d7deb..7b29093d2f2 100644 --- a/test/cask/artifact/two_apps_correct_test.rb +++ b/test/cask/artifact/two_apps_correct_test.rb @@ -20,8 +20,7 @@ describe Hbc::Artifact::App do end it "works with an application in a subdir" do - AltSubDirTwoAppsCask = Class.new(Hbc::Cask) - AltSubDirTwoAppsCask.class_eval do + subdir_cask = Hbc::Cask.new('alt-subdir-two-apps') do url TestHelper.local_binary_url('caffeine.zip') homepage 'http://example.com/local-caffeine' version '1.2.3' @@ -31,9 +30,7 @@ describe Hbc::Artifact::App do end begin - subdir_cask = AltSubDirTwoAppsCask.new.tap do |cask| - TestHelper.install_without_artifacts(cask) - end + TestHelper.install_without_artifacts(subdir_cask) appsubdir = subdir_cask.staged_path.join('subdir').tap(&:mkpath) FileUtils.mv(subdir_cask.staged_path.join('Caffeine.app'), appsubdir) diff --git a/test/cask/container/naked_test.rb b/test/cask/container/naked_test.rb index 4530249b62e..ef3addba4b2 100644 --- a/test/cask/container/naked_test.rb +++ b/test/cask/container/naked_test.rb @@ -2,13 +2,11 @@ require 'test_helper' describe Hbc::Container::Naked do it "saves files with spaces in them from uris with encoded spaces" do - SpaceyCask = Class.new(Hbc::Cask) - SpaceyCask.class_eval do + cask = Hbc::Cask.new('spacey') do url 'http://example.com/kevin%20spacey.pkg' version '1.2' end - cask = SpaceyCask.new path = '/tmp/downloads/kevin-spacey-1.2.pkg' expected_destination = cask.staged_path.join('kevin spacey.pkg') expected_command = ['/usr/bin/ditto', '--', path, expected_destination] diff --git a/test/cask/dsl_test.rb b/test/cask/dsl_test.rb index c6db86cf396..a7c75e13225 100644 --- a/test/cask/dsl_test.rb +++ b/test/cask/dsl_test.rb @@ -9,12 +9,11 @@ describe Hbc::DSL do end describe "when a Cask includes an unknown method" do - UnexpectedMethodCask = Class.new(Hbc::Cask) attempt_unknown_method = nil before do attempt_unknown_method = lambda { - UnexpectedMethodCask.class_eval do + Hbc::Cask.new('unexpected-method-cask') do future_feature :not_yet_on_your_machine end } @@ -48,14 +47,13 @@ describe Hbc::DSL do it "requires a valid header format" do err = lambda { invalid_cask = Hbc.load('invalid/invalid-header-format') - }.must_raise(Hbc::CaskHeaderParseError) - err.message.must_include 'Bad header line: parse failed' + }.must_raise(SyntaxError) end it "requires the header token to match the file name" do err = lambda { invalid_cask = Hbc.load('invalid/invalid-header-token-mismatch') - }.must_raise(Hbc::CaskInvalidError) + }.must_raise(Hbc::CaskTokenDoesNotMatchError) err.message.must_include 'Bad header line:' err.message.must_include 'does not match file name' end @@ -70,36 +68,33 @@ describe Hbc::DSL do describe "name stanza" do it "lets you set the full name via a name stanza" do - NameCask = Class.new(Hbc::Cask) - NameCask.class_eval do + cask = Hbc::Cask.new('name-cask') do name 'Proper Name' end - instance = NameCask.new - instance.full_name.must_equal [ + + cask.full_name.must_equal [ 'Proper Name', ] end it "Accepts an array value to the name stanza" do - ArrayNameCask = Class.new(Hbc::Cask) - ArrayNameCask.class_eval do + cask = Hbc::Cask.new('array-name-cask') do name ['Proper Name', 'Alternate Name'] end - instance = ArrayNameCask.new - instance.full_name.must_equal [ + + cask.full_name.must_equal [ 'Proper Name', 'Alternate Name', ] end it "Accepts multiple name stanzas" do - MultiNameCask = Class.new(Hbc::Cask) - MultiNameCask.class_eval do + cask = Hbc::Cask.new('multi-name-cask') do name 'Proper Name' name 'Alternate Name' end - instance = MultiNameCask.new - instance.full_name.must_equal [ + + cask.full_name.must_equal [ 'Proper Name', 'Alternate Name', ] @@ -108,67 +103,56 @@ describe Hbc::DSL do describe "sha256 stanza" do it "lets you set checksum via sha256" do - ChecksumCask = Class.new(Hbc::Cask) - ChecksumCask.class_eval do + cask = Hbc::Cask.new('checksum-cask') do sha256 'imasha2' end - instance = ChecksumCask.new - instance.sha256.must_equal 'imasha2' + + cask.sha256.must_equal 'imasha2' end end describe "app stanza" do it "allows you to specify app stanzas" do - CaskWithApps = Class.new(Hbc::Cask) - CaskWithApps.class_eval do + cask = Hbc::Cask.new('cask-with-apps') do app 'Foo.app' app 'Bar.app' end - instance = CaskWithApps.new - Array(instance.artifacts[:app]).must_equal [['Foo.app'], ['Bar.app']] + Array(cask.artifacts[:app]).must_equal [['Foo.app'], ['Bar.app']] end it "allow app stanzas to be empty" do - CaskWithNoApps = Class.new(Hbc::Cask) - - instance = CaskWithNoApps.new - Array(instance.artifacts[:app]).must_equal %w[] + cask = Hbc::Cask.new('cask-with-no-apps') + Array(cask.artifacts[:app]).must_equal %w[] end end describe "caveats stanza" do it "allows caveats to be specified via a method define" do - PlainCask = Class.new(Hbc::Cask) + cask = Hbc::Cask.new('plain-cask') - instance = PlainCask.new + cask.caveats.must_be :empty? - instance.caveats.must_be :empty? - - CaskWithCaveats = Class.new(Hbc::Cask) - CaskWithCaveats.class_eval do + cask = Hbc::Cask.new('cask-with-caveats') do def caveats; <<-EOS.undent When you install this Cask, you probably want to know this. EOS end end - instance = CaskWithCaveats.new - instance.caveats.must_equal "When you install this Cask, you probably want to know this.\n" + cask.caveats.must_equal "When you install this Cask, you probably want to know this.\n" end end describe "pkg stanza" do it "allows installable pkgs to be specified" do - CaskWithPkgs = Class.new(Hbc::Cask) - CaskWithPkgs.class_eval do + cask = Hbc::Cask.new('cask-with-pkgs') do pkg 'Foo.pkg' pkg 'Bar.pkg' end - instance = CaskWithPkgs.new - Array(instance.artifacts[:pkg]).must_equal [['Foo.pkg'], ['Bar.pkg']] + Array(cask.artifacts[:pkg]).must_equal [['Foo.pkg'], ['Bar.pkg']] end end diff --git a/test/cask_test.rb b/test/cask_test.rb index 260ba3946b0..06998535ef0 100644 --- a/test/cask_test.rb +++ b/test/cask_test.rb @@ -5,35 +5,23 @@ describe "Cask" do it "returns an instance of the Cask for the given token" do c = Hbc.load("adium") c.must_be_kind_of(Hbc::Cask) - c.must_be_instance_of(KlassPrefixAdium) + c.token.must_equal('adium') end it "returns an instance of the Cask from a specific file location" do - # defensive constant cleanup is required because Cask - # classes may already be loaded due to audit test - begin - Object.class_eval{remove_const :KlassPrefixDia} - rescue - end location = File.expand_path('./Casks/dia.rb') c = Hbc.load(location) c.must_be_kind_of(Hbc::Cask) - c.must_be_instance_of(KlassPrefixDia) - Object.class_eval{remove_const :KlassPrefixDia} + c.token.must_equal('dia') end it "returns an instance of the Cask from a url" do - begin - Object.class_eval{remove_const :KlassPrefixDia} - rescue - end url = "file://" + File.expand_path('./Casks/dia.rb') c = shutup do Hbc.load(url) end c.must_be_kind_of(Hbc::Cask) - c.must_be_instance_of(KlassPrefixDia) - Object.class_eval{remove_const :KlassPrefixDia} + c.token.must_equal('dia') end it "raises an error when failing to download a Cask from a url" do @@ -46,19 +34,14 @@ describe "Cask" do end it "returns an instance of the Cask from a relative file location" do - begin - Object.class_eval{remove_const :KlassPrefixBbedit} - rescue - end c = Hbc.load("./Casks/bbedit.rb") c.must_be_kind_of(Hbc::Cask) - c.must_be_instance_of(KlassPrefixBbedit) - Object.class_eval{remove_const :KlassPrefixBbedit} + c.token.must_equal('bbedit') end it "uses exact match when loading by token" do - Hbc.load('test-opera').must_be_instance_of(KlassPrefixTestOpera) - Hbc.load('test-opera-mail').must_be_instance_of(KlassPrefixTestOperaMail) + Hbc.load('test-opera').token.must_equal('test-opera') + Hbc.load('test-opera-mail').token.must_equal('test-opera-mail') end it "raises an error when attempting to load a Cask that doesn't exist" do @@ -72,19 +55,7 @@ describe "Cask" do it "returns a token for every Cask" do all_cask_tokens = Hbc.all_tokens all_cask_tokens.count.must_be :>, 20 - all_cask_tokens.each { |cask| cask.must_be_kind_of String } - end - end - - describe "token" do - it "converts a class constant to a token-style dashed string" do - KlassPrefixPascalCasedConstant = Class.new(Hbc::Cask) - KlassPrefixPascalCasedConstant.token.must_equal 'pascal-cased-constant' - end - - it "properly dasherizes constants with single letters in the middle" do - KlassPrefixGamesXChange = Class.new(Hbc::Cask) - KlassPrefixGamesXChange.token.must_equal 'games-x-change' + all_cask_tokens.each { |token| token.must_be_kind_of String } end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 50b20ec0ab2..3a832eac8bd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,6 @@ require 'bundler' require 'bundler/setup' +require 'pathname' if ENV['COVERAGE'] require 'coveralls' @@ -18,11 +19,10 @@ lib_path = brew_cask_path.join('lib') $:.push(lib_path) # todo: removeme, this is transitional -require "#{brew_cask_path}/spec/support/kernel_at_exit_hacks" -require "#{brew_cask_path}/spec/support/homebrew_testing_environment" -include HomebrewTestingEnvironment +require 'vendor/homebrew-fork/testing_env' # force some environment variables +ENV['HOMEBREW_NO_EMOJI'] = '1' ENV['HOMEBREW_CASK_OPTS'] = nil # todo temporary, copied from old Homebrew, this method is now moved inside a class @@ -87,7 +87,7 @@ class TestHelper end def self.test_cask - Hbc.load('basic-cask') + @test_cask ||= Hbc.load('basic-cask') end def self.fake_fetcher From 2edb8af7d41680a1c2d2e9f1ade4a13b7851fc4c Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 04:52:48 -0500 Subject: [PATCH 4/8] Minor optimization to speed up recurring calls to Hbc.installed --- lib/hbc/scopes.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/hbc/scopes.rb b/lib/hbc/scopes.rb index d0489738fb1..0c0e44b4fcc 100644 --- a/lib/hbc/scopes.rb +++ b/lib/hbc/scopes.rb @@ -41,6 +41,7 @@ module Hbc::Scopes end def installed + @installed ||= {} installed_cask_dirs = Pathname.glob(caskroom.join("*")) # Hbc.load has some DWIM which is slow. Optimize here # by spoon-feeding Hbc.load fully-qualified paths. @@ -52,9 +53,9 @@ module Hbc::Scopes tap_dir.join("#{cask_token}.rb").exist? end if path_to_cask - Hbc.load(path_to_cask.join("#{cask_token}.rb")) + @installed[cask_token] ||= Hbc.load(path_to_cask.join("#{cask_token}.rb")) else - Hbc.load(cask_token) + @installed[cask_token] ||= Hbc.load(cask_token) end end end From ca8b390443051a261eeb824dc98e276586d94fac Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 05:24:40 -0500 Subject: [PATCH 5/8] Replace `full_name` with `name` Casks are no longer classes, so there's no conflict for `name` anymore --- lib/hbc/audit.rb | 2 +- lib/hbc/cask.rb | 6 ++---- lib/hbc/cli/info.rb | 6 +++--- lib/hbc/cli/internal_stanza.rb | 1 - lib/hbc/dsl.rb | 20 +++----------------- test/cask/dsl_test.rb | 22 +++++++++++----------- 6 files changed, 20 insertions(+), 37 deletions(-) diff --git a/lib/hbc/audit.rb b/lib/hbc/audit.rb index b541c6819f5..5d4427f38d5 100644 --- a/lib/hbc/audit.rb +++ b/lib/hbc/audit.rb @@ -37,7 +37,7 @@ class Hbc::Audit add_error "a #{sym} stanza is required" unless cask.send(sym) end add_error 'a license stanza is required (:unknown is OK)' unless cask.license - add_error 'at least one name stanza is required' if cask.full_name.empty? + add_error 'at least one name stanza is required' if cask.name.empty? # todo: specific DSL knowledge should not be spread around in various files like this # todo: nested_container should not still be a pseudo-artifact at this point installable_artifacts = cask.artifacts.reject{ |k,v| [:uninstall, :zap, :nested_container].include?(k)} diff --git a/lib/hbc/cask.rb b/lib/hbc/cask.rb index fb3b3d43953..e2dc85233e2 100644 --- a/lib/hbc/cask.rb +++ b/lib/hbc/cask.rb @@ -77,7 +77,7 @@ class Hbc::Cask odebug "Cask instance dumps in YAML:" odebug "Cask instance toplevel:", self.to_yaml [ - :full_name, + :name, :homepage, :url, :appcast, @@ -93,9 +93,7 @@ class Hbc::Cask :accessibility_access, :auto_updates ].each do |method| - printable_method = method.to_s - printable_method = "name" if printable_method == "full_name" - odebug "Cask instance method '#{printable_method}':", self.send(method).to_yaml + odebug "Cask instance method '#{method}':", self.send(method).to_yaml end end end diff --git a/lib/hbc/cli/info.rb b/lib/hbc/cli/info.rb index 1207c2f34eb..8b26850d5e9 100644 --- a/lib/hbc/cli/info.rb +++ b/lib/hbc/cli/info.rb @@ -23,7 +23,7 @@ class Hbc::CLI::Info < Hbc::CLI::Base # todo completely reformat the info report <<-PURPOSE #{cask}: #{cask.version} -#{formatted_full_name(cask) } +#{formatted_name(cask) } #{cask.homepage or 'No Homepage'} #{installation} #{github_info(cask) or 'No GitHub URL'} @@ -31,9 +31,9 @@ class Hbc::CLI::Info < Hbc::CLI::Base PURPOSE end - def self.formatted_full_name(cask) + def self.formatted_name(cask) # todo transitional: make name a required stanza, and then stop substituting cask.token here - cask.full_name.empty? ? cask.token : cask.full_name.join(', ') + cask.name.empty? ? cask.token : cask.name.join(', ') end def self.github_info(cask) diff --git a/lib/hbc/cli/internal_stanza.rb b/lib/hbc/cli/internal_stanza.rb index e1fd9f740cb..3540ea99461 100644 --- a/lib/hbc/cli/internal_stanza.rb +++ b/lib/hbc/cli/internal_stanza.rb @@ -71,7 +71,6 @@ class Hbc::CLI::InternalStanza < Hbc::CLI::InternalUseBase def self.print_stanzas(stanza, format=nil, table=nil, quiet=nil, *cask_tokens) count = 0 - stanza = :full_name if stanza == :name if ARTIFACTS.include?(stanza) artifact_name = stanza stanza = :artifacts diff --git a/lib/hbc/dsl.rb b/lib/hbc/dsl.rb index 3fa8404027b..a0a58d84ad4 100644 --- a/lib/hbc/dsl.rb +++ b/lib/hbc/dsl.rb @@ -60,7 +60,6 @@ class Hbc::DSL :conflicts_with, :container, :depends_on, - :full_name, :gpg, :homepage, :license, @@ -80,23 +79,10 @@ class Hbc::DSL @token = token end - # A quite fragile shim to allow "full_name" be exposed as simply "name" - # in the DSL. We detect the difference with the already-existing "name" - # method by arity, and use "full_name" exclusively in backend code. def name(*args) - if args.empty? - super - else - self.full_name(args) - end - end - - def full_name(_full_name=nil) - @full_name ||= [] - if _full_name - @full_name.concat(Array(*_full_name)) - end - @full_name + @name ||= [] + return @name if args.empty? + @name.concat(args.flatten) end def homepage(homepage=nil) diff --git a/test/cask/dsl_test.rb b/test/cask/dsl_test.rb index a7c75e13225..9db7f60ad18 100644 --- a/test/cask/dsl_test.rb +++ b/test/cask/dsl_test.rb @@ -72,9 +72,9 @@ describe Hbc::DSL do name 'Proper Name' end - cask.full_name.must_equal [ - 'Proper Name', - ] + cask.name.must_equal [ + 'Proper Name', + ] end it "Accepts an array value to the name stanza" do @@ -82,10 +82,10 @@ describe Hbc::DSL do name ['Proper Name', 'Alternate Name'] end - cask.full_name.must_equal [ - 'Proper Name', - 'Alternate Name', - ] + cask.name.must_equal [ + 'Proper Name', + 'Alternate Name', + ] end it "Accepts multiple name stanzas" do @@ -94,10 +94,10 @@ describe Hbc::DSL do name 'Alternate Name' end - cask.full_name.must_equal [ - 'Proper Name', - 'Alternate Name', - ] + cask.name.must_equal [ + 'Proper Name', + 'Alternate Name', + ] end end From 9f57c7ff9a28d6f5614ff69bbe7d3c8f140caeaf Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Wed, 6 Jan 2016 05:48:58 -0500 Subject: [PATCH 6/8] Use keyword arguments in Hbc::Cask constructor --- lib/hbc/cask.rb | 2 +- lib/hbc/source/path_base.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/hbc/cask.rb b/lib/hbc/cask.rb index e2dc85233e2..88f90d23eb1 100644 --- a/lib/hbc/cask.rb +++ b/lib/hbc/cask.rb @@ -6,7 +6,7 @@ class Hbc::Cask extend Forwardable attr_reader :token, :sourcefile_path - def initialize(token, sourcefile_path=nil, dsl=nil, &block) + def initialize(token, sourcefile_path: nil, dsl: nil, &block) @token = token @sourcefile_path = sourcefile_path @dsl = dsl || Hbc::DSL.new(@token) diff --git a/lib/hbc/source/path_base.rb b/lib/hbc/source/path_base.rb index d0623921913..eac17951b73 100644 --- a/lib/hbc/source/path_base.rb +++ b/lib/hbc/source/path_base.rb @@ -58,7 +58,7 @@ class Hbc::Source::PathBase def build_cask(cask_class, header_token, &block) raise Hbc::CaskTokenDoesNotMatchError.new(cask_token, header_token) unless cask_token == header_token - cask_class.new(cask_token, path, &block) + cask_class.new(cask_token, sourcefile_path: path, &block) end def cask_token From 8ee120b1e686f93d137a1db2fbfde4c4d51fb8ba Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Thu, 7 Jan 2016 23:44:45 -0500 Subject: [PATCH 7/8] Fix explanatory comment in Scopes.all_tokens --- lib/hbc/scopes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hbc/scopes.rb b/lib/hbc/scopes.rb index 0c0e44b4fcc..e2e9ad18662 100644 --- a/lib/hbc/scopes.rb +++ b/lib/hbc/scopes.rb @@ -35,7 +35,7 @@ module Hbc::Scopes c = c.split('/').last 4 # => ["caskroom", "example-tap", "Casks", "example"] c.delete_at(-2) - # => ["example-tap", "example"] + # => ["caskroom", "example-tap", "example"] c = c.join '/' } end From 2d70f2c78473695c06028c07159c835b5157d1ed Mon Sep 17 00:00:00 2001 From: Josh Hagins Date: Thu, 7 Jan 2016 23:45:40 -0500 Subject: [PATCH 8/8] Memoize casks in Scopes.all for speed --- lib/hbc/scopes.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/hbc/scopes.rb b/lib/hbc/scopes.rb index e2e9ad18662..05683dc1473 100644 --- a/lib/hbc/scopes.rb +++ b/lib/hbc/scopes.rb @@ -5,7 +5,8 @@ module Hbc::Scopes module ClassMethods def all - all_tokens.map { |c| self.load c } + @all_casks ||= {} + all_tokens.map { |t| @all_casks[t] ||= self.load(t) } end def all_tapped_cask_dirs