Skip to content

Commit

Permalink
Merge pull request #906 from reitermarkus/os-language
Browse files Browse the repository at this point in the history
Make `MacOS.language` less opinionated and add `language` stanza.
  • Loading branch information
reitermarkus authored Oct 3, 2016
2 parents 7d31a70 + e2b3753 commit 35ee283
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 4 deletions.
17 changes: 17 additions & 0 deletions Library/Homebrew/cask/lib/hbc/auditor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
module Hbc
class Auditor
def self.audit(cask, audit_download: false, check_token_conflicts: false)
saved_languages = MacOS.instance_variable_get(:@languages)

if languages_blocks = cask.instance_variable_get(:@dsl).instance_variable_get(:@language_blocks)
languages_blocks.keys.each do |languages|
ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.join(", ")}"
MacOS.instance_variable_set(:@languages, languages)
audit_cask_instance(Hbc.load(cask.sourcefile_path), audit_download, check_token_conflicts)
CLI::Cleanup.run(cask.token) if audit_download
end
else
audit_cask_instance(cask, audit_download, check_token_conflicts)
end
ensure
MacOS.instance_variable_set(:@languages, saved_languages)
end

def self.audit_cask_instance(cask, audit_download, check_token_conflicts)
download = audit_download && Download.new(cask)
audit = Audit.new(cask, download: download,
check_token_conflicts: check_token_conflicts)
Expand Down
5 changes: 4 additions & 1 deletion Library/Homebrew/cask/lib/hbc/cask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ def initialize(token, sourcefile_path: nil, dsl: nil, &block)
@token = token
@sourcefile_path = sourcefile_path
@dsl = dsl || DSL.new(@token)
@dsl.instance_eval(&block) if block_given?
if block_given?
@dsl.instance_eval(&block)
@dsl.language_eval
end
end

DSL::DSL_METHODS.each do |method_name|
Expand Down
4 changes: 4 additions & 0 deletions Library/Homebrew/cask/lib/hbc/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ def self.nice_listing(cask_list)
def self.parser
# If you modify these arguments, please update USAGE.md
@parser ||= OptionParser.new do |opts|
opts.on("--language STRING") do
# handled in OS::Mac
end

OPTIONS.each do |option, method|
opts.on("#{option}" "PATH", Pathname) do |path|
Hbc.public_send(method, path)
Expand Down
39 changes: 39 additions & 0 deletions Library/Homebrew/cask/lib/hbc/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "set"
require "locale"

require "hbc/dsl/appcast"
require "hbc/dsl/base"
Expand Down Expand Up @@ -64,6 +65,7 @@ class DSL
:depends_on,
:gpg,
:homepage,
:language,
:license,
:name,
:sha256,
Expand Down Expand Up @@ -98,6 +100,43 @@ def homepage(homepage = nil)
@homepage ||= homepage
end

def language(*args, default: false, &block)
if !args.empty? && block_given?
@language_blocks ||= {}
@language_blocks[args] = block

return unless default

unless @language_blocks.default.nil?
raise CaskInvalidError.new(token, "Only one default language may be defined")
end

@language_blocks.default = block
else
language_eval
end
end

def language_eval
return @language if instance_variable_defined?(:@language)

if @language_blocks.nil? || @language_blocks.empty?
return @language = nil
end

MacOS.languages.map(&Locale.method(:parse)).each do |locale|
key = @language_blocks.keys.detect { |strings|
strings.any? { |string| locale.include?(string) }
}

next if key.nil?

return @language = @language_blocks[key].call
end

@language = @language_blocks.default.call
end

def url(*args, &block)
url_given = !args.empty? || block_given?
return @url unless url_given
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/lib/hbc/dsl/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def initialize(cask, command = SystemCommand)
@command = command
end

def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir
def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir, :language

def system_command(executable, options = {})
@command.run!(executable, options)
Expand Down
72 changes: 72 additions & 0 deletions Library/Homebrew/cask/spec/locale_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "spec_helper"
require "locale"

describe Locale do
describe "::parse" do
it "parses a string in the correct format" do
expect(described_class.parse("zh")).to eql(described_class.new("zh", nil, nil))
expect(described_class.parse("zh-CN")).to eql(described_class.new("zh", "CN", nil))
expect(described_class.parse("zh-Hans")).to eql(described_class.new("zh", nil, "Hans"))
expect(described_class.parse("zh-CN-Hans")).to eql(described_class.new("zh", "CN", "Hans"))
end

context "raises a ParserError when given" do
it "an empty string" do
expect{ described_class.parse("") }.to raise_error(Locale::ParserError)
end

it "a string in a wrong format" do
expect { described_class.parse("zh_CN_Hans") }.to raise_error(Locale::ParserError)
expect { described_class.parse("zhCNHans") }.to raise_error(Locale::ParserError)
expect { described_class.parse("zh-CN_Hans") }.to raise_error(Locale::ParserError)
expect { described_class.parse("zhCN") }.to raise_error(Locale::ParserError)
expect { described_class.parse("zh_Hans") }.to raise_error(Locale::ParserError)
end
end
end

describe "::new" do
it "raises an ArgumentError when all arguments are nil" do
expect { described_class.new(nil, nil, nil) }.to raise_error(ArgumentError)
end

it "raises a ParserError when one of the arguments does not match the locale format" do
expect { described_class.new("ZH", nil, nil) }.to raise_error(Locale::ParserError)
expect { described_class.new(nil, "cn", nil) }.to raise_error(Locale::ParserError)
expect { described_class.new(nil, nil, "hans") }.to raise_error(Locale::ParserError)
end
end

subject { described_class.new("zh", "CN", "Hans") }

describe "#include?" do
it { is_expected.to include("zh") }
it { is_expected.to include("zh-CN") }
it { is_expected.to include("CN") }
it { is_expected.to include("CN-Hans") }
it { is_expected.to include("Hans") }
it { is_expected.to include("zh-CN-Hans") }
end

describe "#eql?" do
subject { described_class.new("zh", "CN", "Hans") }

context "all parts match" do
it { is_expected.to eql("zh-CN-Hans") }
it { is_expected.to eql(subject) }
end

context "only some parts match" do
it { is_expected.to_not eql("zh") }
it { is_expected.to_not eql("zh-CN") }
it { is_expected.to_not eql("CN") }
it { is_expected.to_not eql("CN-Hans") }
it { is_expected.to_not eql("Hans") }
end

it "does not raise if 'other' cannot be parsed" do
expect { subject.eql?("zh_CN_Hans") }.not_to raise_error
expect(subject.eql?("zh_CN_Hans")).to be false
end
end
end
50 changes: 50 additions & 0 deletions Library/Homebrew/cask/test/cask/dsl_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,56 @@
end
end

describe "language stanza" do
it "allows multilingual casks" do
cask = lambda do
Hbc::Cask.new("cask-with-apps") do
language "zh" do
sha256 "abc123"
"zh-CN"
end

language "en-US", default: true do
sha256 "xyz789"
"en-US"
end

url "https://example.org/#{language}.zip"
end
end

MacOS.stubs(languages: ["zh"])
cask.call.language.must_equal "zh-CN"
cask.call.sha256.must_equal "abc123"
cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip"

MacOS.stubs(languages: ["zh-XX"])
cask.call.language.must_equal "zh-CN"
cask.call.sha256.must_equal "abc123"
cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip"

MacOS.stubs(languages: ["en"])
cask.call.language.must_equal "en-US"
cask.call.sha256.must_equal "xyz789"
cask.call.url.to_s.must_equal "https://example.org/en-US.zip"

MacOS.stubs(languages: ["xx-XX"])
cask.call.language.must_equal "en-US"
cask.call.sha256.must_equal "xyz789"
cask.call.url.to_s.must_equal "https://example.org/en-US.zip"

MacOS.stubs(languages: ["xx-XX", "zh", "en"])
cask.call.language.must_equal "zh-CN"
cask.call.sha256.must_equal "abc123"
cask.call.url.to_s.must_equal "https://example.org/zh-CN.zip"

MacOS.stubs(languages: ["xx-XX", "en-US", "zh"])
cask.call.language.must_equal "en-US"
cask.call.sha256.must_equal "xyz789"
cask.call.url.to_s.must_equal "https://example.org/en-US.zip"
end
end

describe "app stanza" do
it "allows you to specify app stanzas" do
cask = Hbc::Cask.new("cask-with-apps") do
Expand Down
68 changes: 68 additions & 0 deletions Library/Homebrew/locale.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class Locale
class ParserError < ::RuntimeError
end

LANGUAGE_REGEX = /(?:[a-z]{2})/
REGION_REGEX = /(?:[A-Z]{2})/
SCRIPT_REGEX = /(?:[A-Z][a-z]{3})/

LOCALE_REGEX = /^(#{LANGUAGE_REGEX})?(?:(?:^|-)(#{REGION_REGEX}))?(?:(?:^|-)(#{SCRIPT_REGEX}))?$/

def self.parse(string)
language, region, script = string.to_s.scan(LOCALE_REGEX)[0]

if language.nil? && region.nil? && script.nil?
raise ParserError, "'#{string}' cannot be parsed to a #{self.class}"
end

new(language, region, script)
end

attr_reader :language, :region, :script

def initialize(language, region, script)
if language.nil? && region.nil? && script.nil?
raise ArgumentError, "#{self.class} cannot be empty"
end

{
language: language,
region: region,
script: script,
}.each do |key, value|
next if value.nil?

regex = self.class.const_get("#{key.upcase}_REGEX")
raise ParserError, "'#{value}' does not match #{regex}" unless value =~ regex
instance_variable_set(:"@#{key}", value)
end

self
end

def include?(other)
other = self.class.parse(other) unless other.is_a?(self.class)

[:language, :region, :script].all? { |var|
if other.public_send(var).nil?
true
else
public_send(var) == other.public_send(var)
end
}
end

def eql?(other)
other = self.class.parse(other) unless other.is_a?(self.class)
[:language, :region, :script].all? { |var|
public_send(var) == other.public_send(var)
}
rescue ParserError
false
end
alias == eql?

def to_s
[@language, @region, @script].compact.join("-")
end
end
18 changes: 17 additions & 1 deletion Library/Homebrew/os/mac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,24 @@ def cat
version.to_sym
end

def languages
return @languages unless @languages.nil?

@languages = Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/)

if ENV["HOMEBREW_LANGUAGES"]
@languages = ENV["HOMEBREW_LANGUAGES"].split(",") + @languages
end

if ARGV.value("language")
@languages = ARGV.value("language").split(",") + @languages
end

@languages = @languages.uniq
end

def language
@language ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").delete(" \n\"()").sub(/,.*/, "")
languages.first
end

def active_developer_dir
Expand Down
10 changes: 9 additions & 1 deletion Library/Homebrew/test/test_os_mac_language.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
require "os/mac"

class OSMacLanguageTests < Homebrew::TestCase
LANGUAGE_REGEX = /\A[a-z]{2}(-[A-Z]{2})?(-[A-Z][a-z]{3})?\Z/

def test_languages_format
OS::Mac.languages.each do |language|
assert_match LANGUAGE_REGEX, language
end
end

def test_language_format
assert_match(/\A[a-z]{2}(-[A-Z]{2})?\Z/, OS::Mac.language)
assert_match LANGUAGE_REGEX, OS::Mac.language
end
end

0 comments on commit 35ee283

Please sign in to comment.