Fix `brew cask alfred` for Yosemite/Alfred 2.4

Closes #5820
References: #6059
This commit is contained in:
Roland Walker 2014-09-01 12:11:54 -04:00
parent 3a3e4945c3
commit 0f664ca78a
2 changed files with 153 additions and 88 deletions

View File

@ -10,8 +10,12 @@ class Cask::CLI::Alfred < Cask::CLI::Base
'~/Library/Mobile Documents/',
'~/Library/PreferencePanes'
]
DOMAIN = "com.runningwithcrayons.Alfred-Preferences"
KEY = "features.defaultresults.scope"
PRIMARY_DOMAIN = 'com.runningwithcrayons.Alfred-Preferences'
PRIMARY_SCOPES_KEY = 'features.defaultresults.scope'
# http://www.alfredforum.com/topic/4810-alfred%E2%80%99s-scope-can-no-longer-be-changed-programatically/?p=29434
LOCALPREFS_SCOPES_FILEGLOB = '~/Library/Application Support/Alfred 2/Alfred.alfredpreferences/preferences/local/*/features/defaultresults/prefs.plist'
LOCALPREFS_SCOPES_KEY = 'scope'
def self.run(*args)
subcommand = args.first
@ -23,9 +27,9 @@ class Cask::CLI::Alfred < Cask::CLI::Base
end
case subcommand
when "link" then link
when "unlink" then unlink
when "status" then status
when 'link' then link
when 'unlink' then unlink
when 'status' then status
else
usage
end
@ -36,34 +40,36 @@ class Cask::CLI::Alfred < Cask::CLI::Base
end
def self.assert_installed
if !alfred_installed?
opoo "Could not find Alfred 2 preferences, Alfred 2 is probably not installed."
unless alfred_2_installed?
opoo "Could not find Alfred 2 preferences. Alfred 2 is probably not installed."
end
alfred_installed?
if alfred_localprefs? and localprefs_scopes_files.size < 1
raise CaskError.new "Alfred 2.4+ detected, but local preferences file not found. Try running Alfred first."
end
alfred_2_installed?
end
def self.link
return unless assert_installed
if linked?
opoo "Alfred is already linked to homebrew-cask."
else
odebug 'Linking Alfred scopes'
save_alfred_scopes(alfred_scopes << Cask.caskroom)
ohai "Successfully linked Alfred to homebrew-cask."
opoo "Alfred appears to be already linked. Updating defaults anyway."
end
odebug 'Linking Alfred scopes'
# this silly ternary operation is for precise compat with Ruby 1.8
save_alfred_scopes( alfred_scopes.include?(Cask.caskroom.to_s) ? alfred_scopes : alfred_scopes + [ Cask.caskroom.to_s ])
ohai "Successfully linked Alfred to homebrew-cask."
end
def self.unlink
return unless assert_installed
if !linked?
opoo "Alfred is already unlinked from homebrew-cask."
else
odebug 'Unlinking Alfred scopes'
save_alfred_scopes(alfred_scopes.reject { |x| x == Cask.caskroom.to_s })
ohai "Successfully unlinked Alfred from homebrew-cask."
unless linked?
opoo "Alfred appears to be already unlinked. Updating defaults anyway."
end
odebug 'Unlinking Alfred scopes'
save_alfred_scopes(alfred_scopes.reject { |x| x == Cask.caskroom.to_s })
ohai "Successfully unlinked Alfred from homebrew-cask."
end
def self.status
@ -74,14 +80,38 @@ class Cask::CLI::Alfred < Cask::CLI::Base
else
ohai "Alfred is not linked to homebrew-cask."
end
if alfred_localprefs? and localprefs_scopes_files.size > 1
opoo "Multiple local preference files detected. 'brew cask alfred status' may be inaccurate."
end
end
def self.save_alfred_scopes(scopes)
alfred_preference(KEY, "(#{scopes.map { |s| "'#{s}'" }.join(",")})")
alfred_preference(PRIMARY_SCOPES_KEY, %Q{(#{scopes.map { |s| "'#{s}'" }.join(',')})})
end
def self.alfred_installed?
alfred_preference('version') =~ /^2\.[0-9]/
def self.alfred_version
begin
# reset the version every time when running under the test harness
# todo: I don't know the right way to detect that; using const_get
Cask.const_get(:FakeSystemCommand)
@alfred_version = nil
rescue
end
return @alfred_version if @alfred_version
v = alfred_primary_preference('version')
@alfred_version = begin
Gem::Version.new(v).to_s.to_f
rescue
0
end
end
def self.alfred_2_installed?
alfred_version >= 2.0
end
def self.alfred_localprefs?
alfred_version >= 2.4
end
def self.linked?
@ -98,24 +128,53 @@ class Cask::CLI::Alfred < Cask::CLI::Base
SCOPE_REGEXP = /^\s*"(.*)",?$/
def self.alfred_scopes
scopes = alfred_preference(KEY).split("\n").map do |line|
matchdata = line.match(SCOPE_REGEXP)
matchdata ? matchdata.captures.first : nil
scopes = alfred_preference(PRIMARY_SCOPES_KEY).split("\n").map do |line|
$1 if line.match(SCOPE_REGEXP)
end.compact
scopes.empty? ? DEFAULT_SCOPES : scopes
end
def self.localprefs_scopes_files
# local prefs file is used in Alfred 2.4 and above for Yosemite compatibility
Pathname.glob(Pathname.new(LOCALPREFS_SCOPES_FILEGLOB).expand_path)
end
def self.alfred_preference(key, value=nil)
if value
odebug 'Writing Alfred preferences'
@system_command.run('/usr/bin/defaults', :args => ['write', DOMAIN, key, %Q(#{value})])
if alfred_localprefs? and key == PRIMARY_SCOPES_KEY
alfred_file_preference(localprefs_scopes_files, LOCALPREFS_SCOPES_KEY, value)
else
odebug 'Reading Alfred preferences'
@system_command.run('/usr/bin/defaults', :args => ['read', DOMAIN, key])
alfred_primary_preference(key, value)
end
end
def self.alfred_primary_preference(key, value=nil)
if value
odebug 'Writing Alfred primary preferences'
@system_command.run('/usr/bin/defaults', :args => ['write', PRIMARY_DOMAIN, key, value.to_s])
else
odebug 'Reading Alfred primary preferences'
@system_command.run('/usr/bin/defaults', :args => ['read', PRIMARY_DOMAIN, key])
end
end
def self.alfred_file_preference(files, key, value=nil)
unless files and files.length > 0
raise CaskError.new "Required 'files' argument empty"
end
Array(files).map do |file|
if value
odebug 'Writing Alfred local preferences'
@system_command.run('/usr/bin/defaults', :args => ['write', file, key, value.to_s])
else
odebug 'Reading Alfred local preferences'
@system_command.run('/usr/bin/defaults', :args => ['read', file, key])
end
# hack/limitation: if there happen to be multiple local preference
# files, we only return the value from the first one.
end.first
end
def self.usage
ohai 'brew cask alfred', <<-ALFREDHELP.undent
manages integration with Alfred; allows applications installed with

View File

@ -1,14 +1,43 @@
require 'test_helper'
def fake_alfred_preference(key, response)
# slightly different format for read/write
scope = (Cask::CLI::Alfred::DEFAULT_SCOPES).map { |s| %Q{"#{s}"} }
DEFAULT_READ_SCOPE = %Q{(\n#{scope.join(",\n ")}\n)}
scope = (Cask::CLI::Alfred::DEFAULT_SCOPES + [Cask.caskroom.to_s]).map { |s| %Q{"#{s}"} }
ALTERED_READ_SCOPE = %Q{(\n#{scope.join(",\n ")}\n)}
scope = (Cask::CLI::Alfred::DEFAULT_SCOPES).map { |s| %Q{'#{s}'} }
DEFAULT_WRITE_SCOPE = %Q{(#{scope.join(',')})}
scope = (Cask::CLI::Alfred::DEFAULT_SCOPES + [Cask.caskroom.to_s]).map { |s| %Q{'#{s}'} }
ALTERED_WRITE_SCOPE = %Q{(#{scope.join(',')})}
def fake_alfred_read_primary_preference(key, response)
Cask::FakeSystemCommand.stubs_command(['/usr/bin/defaults', 'read', 'com.runningwithcrayons.Alfred-Preferences', key], response)
end
def fake_alfred_write_primary_preference(key, value)
Cask::FakeSystemCommand.stubs_command(['/usr/bin/defaults', 'write', 'com.runningwithcrayons.Alfred-Preferences', key, value])
end
def fake_alfred_read_local_preference(key, response)
local_files = Pathname.glob(Pathname.new(Cask::CLI::Alfred::LOCALPREFS_SCOPES_FILEGLOB).expand_path)
local_files.each do |file|
Cask::FakeSystemCommand.stubs_command(['/usr/bin/defaults', 'read', file, key], response)
end
end
def fake_alfred_write_local_preference(key, value)
local_files = Pathname.glob(Pathname.new(Cask::CLI::Alfred::LOCALPREFS_SCOPES_FILEGLOB).expand_path)
local_files.each do |file|
Cask::FakeSystemCommand.stubs_command(['/usr/bin/defaults', 'write', file, key, value])
end
end
def fake_alfred_installed(installed=true)
if installed
fake_alfred_preference 'version', '2.0.3'
fake_alfred_read_primary_preference 'version', '2.0.3'
else
fake_alfred_preference 'version', <<-VERSION.undent
fake_alfred_read_primary_preference 'version', <<-VERSION.undent
2013-05-11 13:32:51.086 defaults[5072:707]
The domain/default pair of (com.runningwithcrayons.Alfred-Preferences, version) does not exist
VERSION
@ -19,22 +48,17 @@ describe Cask::CLI::Alfred do
describe "status" do
it "properly reports when alfred is not installed" do
fake_alfred_installed(false)
fake_alfred_read_local_preference('scope', DEFAULT_READ_SCOPE)
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('status', Cask::FakeSystemCommand)
}, "Warning: Could not find Alfred 2 preferences, Alfred 2 is probably not installed.")
}, "Warning: Could not find Alfred 2 preferences. Alfred 2 is probably not installed.")
end
it "properly reports when alfred is installed but unlinked" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
(
"/Applications",
"/Library/PreferencePanes",
"/System/Library/PreferencePanes"
)
SCOPE_RESPONSE
fake_alfred_read_primary_preference 'features.defaultresults.scope', DEFAULT_READ_SCOPE
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('status', Cask::FakeSystemCommand)
@ -48,38 +72,28 @@ describe Cask::CLI::Alfred do
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('link', Cask::FakeSystemCommand)
}, "Warning: Could not find Alfred 2 preferences, Alfred 2 is probably not installed.")
}, "Warning: Could not find Alfred 2 preferences. Alfred 2 is probably not installed.")
end
it "warns when alfred is already linked" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
(
"/Applications",
"/Library/PreferencePanes",
"#{Cask.caskroom}",
"/System/Library/PreferencePanes"
)
SCOPE_RESPONSE
fake_alfred_read_primary_preference 'features.defaultresults.scope', ALTERED_READ_SCOPE
fake_alfred_read_local_preference 'scope', ALTERED_READ_SCOPE
fake_alfred_write_primary_preference 'features.defaultresults.scope', ALTERED_WRITE_SCOPE
fake_alfred_write_local_preference 'scope', ALTERED_WRITE_SCOPE
# todo: the message text is out of expected order because of mixing STDERR/STDOUT
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('link', Cask::FakeSystemCommand)
}, "Warning: Alfred is already linked to homebrew-cask.")
}, "==> Successfully linked Alfred to homebrew-cask.\nWarning: Alfred appears to be already linked. Updating defaults anyway.")
end
it "links when it needs to" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
(
"/Applications",
"/Library/PreferencePanes",
"/System/Library/PreferencePanes"
)
SCOPE_RESPONSE
Cask::FakeSystemCommand.stubs_command(
['/usr/bin/defaults', 'write', 'com.runningwithcrayons.Alfred-Preferences', 'features.defaultresults.scope', %Q{('/Applications','/Library/PreferencePanes','/System/Library/PreferencePanes','#{Cask.caskroom}')}]
)
fake_alfred_read_primary_preference 'features.defaultresults.scope', DEFAULT_READ_SCOPE
fake_alfred_read_local_preference 'scope', DEFAULT_READ_SCOPE
fake_alfred_write_primary_preference 'features.defaultresults.scope', ALTERED_WRITE_SCOPE
fake_alfred_write_local_preference 'scope', ALTERED_WRITE_SCOPE
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('link', Cask::FakeSystemCommand)
@ -88,15 +102,17 @@ describe Cask::CLI::Alfred do
it "links with default scopes if the preference hasn't been customized" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
fake_alfred_read_primary_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
2013-05-11 13:32:51.086 defaults[5072:707]
The domain/default pair of (com.runningwithcrayons.Alfred-Preferences, features.defaultresults.scope) does not exist
SCOPE_RESPONSE
fake_alfred_read_local_preference 'scope', <<-SCOPE_RESPONSE.undent
2013-05-11 13:32:51.086 defaults[5072:707]
The domain/default pair of (<file>, scope) does not exist
SCOPE_RESPONSE
expected_scopes = (Cask::CLI::Alfred::DEFAULT_SCOPES + [Cask.caskroom]).map { |s| "'#{s}'" }
Cask::FakeSystemCommand.stubs_command(
['/usr/bin/defaults', 'write', 'com.runningwithcrayons.Alfred-Preferences', 'features.defaultresults.scope', %Q{(#{expected_scopes.join(',')})}]
)
fake_alfred_write_primary_preference 'features.defaultresults.scope', ALTERED_WRITE_SCOPE
fake_alfred_write_local_preference 'scope', ALTERED_WRITE_SCOPE
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('link', Cask::FakeSystemCommand)
@ -110,38 +126,28 @@ describe Cask::CLI::Alfred do
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('unlink', Cask::FakeSystemCommand)
}, "Warning: Could not find Alfred 2 preferences, Alfred 2 is probably not installed.")
}, "Warning: Could not find Alfred 2 preferences. Alfred 2 is probably not installed.")
end
it "warns when alfred is already unlinked" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
(
"/Applications",
"/Library/PreferencePanes",
"/System/Library/PreferencePanes"
)
SCOPE_RESPONSE
fake_alfred_read_primary_preference 'features.defaultresults.scope', DEFAULT_READ_SCOPE
fake_alfred_read_local_preference 'scope', DEFAULT_READ_SCOPE
fake_alfred_write_primary_preference 'features.defaultresults.scope', DEFAULT_WRITE_SCOPE
fake_alfred_write_local_preference 'scope', DEFAULT_WRITE_SCOPE
# todo: the message text is out of expected order because of mixing STDERR/STDOUT
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('unlink', Cask::FakeSystemCommand)
}, "Warning: Alfred is already unlinked from homebrew-cask.")
}, "==> Successfully unlinked Alfred from homebrew-cask.\nWarning: Alfred appears to be already unlinked. Updating defaults anyway.")
end
it "unlinks when it needs to" do
fake_alfred_installed(true)
fake_alfred_preference 'features.defaultresults.scope', <<-SCOPE_RESPONSE.undent
(
"/Applications",
"/Library/PreferencePanes",
"#{Cask.caskroom}",
"/System/Library/PreferencePanes"
)
SCOPE_RESPONSE
Cask::FakeSystemCommand.stubs_command(
['/usr/bin/defaults', 'write', 'com.runningwithcrayons.Alfred-Preferences', 'features.defaultresults.scope', %Q{('/Applications','/Library/PreferencePanes','/System/Library/PreferencePanes')}]
)
fake_alfred_read_primary_preference 'features.defaultresults.scope', ALTERED_READ_SCOPE
fake_alfred_read_local_preference 'scope', ALTERED_READ_SCOPE
fake_alfred_write_primary_preference 'features.defaultresults.scope', DEFAULT_WRITE_SCOPE
fake_alfred_write_local_preference 'scope', DEFAULT_WRITE_SCOPE
TestHelper.must_output(self, lambda {
Cask::CLI::Alfred.run('unlink', Cask::FakeSystemCommand)