From 1a25f1ec6dbc86247ca462e44c01065728b98a2c Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 10 Sep 2016 04:24:55 +0200 Subject: [PATCH 01/15] Make `MacOS.language` less opinionated. --- Library/Homebrew/os/mac.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 0b01478253f1c..566f409930bc7 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -41,8 +41,12 @@ def cat version.to_sym end + def languages + @languages ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + end + def language - @language ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").delete(" \n\"()").sub(/,.*/, "") + languages.first end def active_developer_dir From 929c594f41437f3a0a6d13fed2d596a0ba60435b Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 10 Sep 2016 17:15:58 +0200 Subject: [PATCH 02/15] Add test for `MacOS.languages`. --- Library/Homebrew/test/test_os_mac_language.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/test/test_os_mac_language.rb b/Library/Homebrew/test/test_os_mac_language.rb index 2cdd50917da18..709913000bb98 100644 --- a/Library/Homebrew/test/test_os_mac_language.rb +++ b/Library/Homebrew/test/test_os_mac_language.rb @@ -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 From f52116cd2622eff3ef1611e059bab8b96163a596 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 14 Sep 2016 23:11:21 +0200 Subject: [PATCH 03/15] Add `language` stanza to cask DSL. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 26 +++++++++++++++++++++++ Library/Homebrew/cask/lib/hbc/dsl/base.rb | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 83c0bf1fb3316..69ade9a861735 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -64,6 +64,7 @@ class DSL :depends_on, :gpg, :homepage, + :language, :license, :name, :sha256, @@ -98,6 +99,31 @@ def homepage(homepage = nil) @homepage ||= homepage end + def language(*args, &block) + @language ||= {} + + if !args.empty? && block_given? + args.each do |arg| + MacOS.languages.each_with_index do |l, index| + string_or_regex = arg == :default ? %r{^en} : arg + next unless l.match(string_or_regex) + next unless @language[:level].nil? || @language[:level] > index + @language = { + block: block, + level: index, + } + end + end + + if args.include?(:default) + # :default has to be the last language block in order to assign return value of the selected `language` block to `@language` + @language = @language[:block].call + end + else + @language + end + end + def url(*args, &block) url_given = !args.empty? || block_given? return @url unless url_given diff --git a/Library/Homebrew/cask/lib/hbc/dsl/base.rb b/Library/Homebrew/cask/lib/hbc/dsl/base.rb index ccf93dae9a213..20a3cec619baa 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/base.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/base.rb @@ -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) From 65fdfefc99e2308a406e57eb55cd2e32f107717f Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 18 Sep 2016 04:15:28 +0200 Subject: [PATCH 04/15] Add `language_eval` method. --- Library/Homebrew/cask/lib/hbc/cask.rb | 5 ++++- Library/Homebrew/cask/lib/hbc/dsl.rb | 17 +++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index 756b05b83fcb7..1e2056efc705e 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -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| diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 69ade9a861735..a1b3280e66048 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -100,30 +100,31 @@ def homepage(homepage = nil) end def language(*args, &block) - @language ||= {} - if !args.empty? && block_given? args.each do |arg| MacOS.languages.each_with_index do |l, index| string_or_regex = arg == :default ? %r{^en} : arg next unless l.match(string_or_regex) - next unless @language[:level].nil? || @language[:level] > index + next unless @language.nil? || @language[:level].nil? || @language[:level] > index @language = { block: block, level: index, } end end - - if args.include?(:default) - # :default has to be the last language block in order to assign return value of the selected `language` block to `@language` - @language = @language[:block].call - end else + language_eval @language end end + + def language_eval + if @language.is_a?(Hash) && @language.key?(:block) + @language = @language[:block].call + end + end + def url(*args, &block) url_given = !args.empty? || block_given? return @url unless url_given From 87299af22547093a4efb9c4a5e06b55bb51f9ff7 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 21 Sep 2016 21:56:59 +0200 Subject: [PATCH 05/15] Add test for `language` stanza. --- Library/Homebrew/cask/test/cask/dsl_test.rb | 33 +++++++++++++++++++++ Library/Homebrew/os/mac.rb | 6 +++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/cask/test/cask/dsl_test.rb b/Library/Homebrew/cask/test/cask/dsl_test.rb index ccf2f1a24816f..96095ed70b144 100644 --- a/Library/Homebrew/cask/test/cask/dsl_test.rb +++ b/Library/Homebrew/cask/test/cask/dsl_test.rb @@ -122,6 +122,39 @@ end end + describe "language stanza" do + after(:each) do + ENV["HOMEBREW_LANGUAGES"] = nil + end + + it "allows multilingual casks" do + cask = lambda { + Hbc::Cask.new("cask-with-apps") do + language "FIRST_LANGUAGE" do + :first + end + + language %r{SECOND_LANGUAGE} do + :second + end + + language :default do + :default + end + end + } + + ENV["HOMEBREW_LANGUAGES"] = "FIRST_LANGUAGE" + cask.call.language.must_equal :first + + ENV["HOMEBREW_LANGUAGES"] = "SECOND_LANGUAGE" + cask.call.language.must_equal :second + + ENV["HOMEBREW_LANGUAGES"] = "THIRD_LANGUAGE" + cask.call.language.must_equal :default + end + end + describe "app stanza" do it "allows you to specify app stanzas" do cask = Hbc::Cask.new("cask-with-apps") do diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 566f409930bc7..854d39174c409 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -42,7 +42,11 @@ def cat end def languages - @languages ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + if ENV["HOMEBREW_LANGUAGES"] + ENV["HOMEBREW_LANGUAGES"].split(",") + else + @languages ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + end end def language From 1e86c7d3ec8f7d1a82b01646fcc0f72d0f2c6ea8 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 21 Sep 2016 22:13:01 +0200 Subject: [PATCH 06/15] Always fall back to `language :default`. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index a1b3280e66048..624d62a7607e5 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -103,8 +103,7 @@ def language(*args, &block) if !args.empty? && block_given? args.each do |arg| MacOS.languages.each_with_index do |l, index| - string_or_regex = arg == :default ? %r{^en} : arg - next unless l.match(string_or_regex) + next unless arg == :default || l.match(arg) next unless @language.nil? || @language[:level].nil? || @language[:level] > index @language = { block: block, From b703c81ca6640ee2ff3d0489462c672a2aa18c0f Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 21 Sep 2016 22:19:04 +0200 Subject: [PATCH 07/15] Stub `MacOS.languages` instead of setting HOMEBREW_LANGUAGES. --- Library/Homebrew/cask/test/cask/dsl_test.rb | 10 +++------- Library/Homebrew/os/mac.rb | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/cask/test/cask/dsl_test.rb b/Library/Homebrew/cask/test/cask/dsl_test.rb index 96095ed70b144..1e13d49f323a1 100644 --- a/Library/Homebrew/cask/test/cask/dsl_test.rb +++ b/Library/Homebrew/cask/test/cask/dsl_test.rb @@ -123,10 +123,6 @@ end describe "language stanza" do - after(:each) do - ENV["HOMEBREW_LANGUAGES"] = nil - end - it "allows multilingual casks" do cask = lambda { Hbc::Cask.new("cask-with-apps") do @@ -144,13 +140,13 @@ end } - ENV["HOMEBREW_LANGUAGES"] = "FIRST_LANGUAGE" + MacOS.stubs(languages: ["FIRST_LANGUAGE"]) cask.call.language.must_equal :first - ENV["HOMEBREW_LANGUAGES"] = "SECOND_LANGUAGE" + MacOS.stubs(languages: ["SECOND_LANGUAGE"]) cask.call.language.must_equal :second - ENV["HOMEBREW_LANGUAGES"] = "THIRD_LANGUAGE" + MacOS.stubs(languages: ["THIRD_LANGUAGE"]) cask.call.language.must_equal :default end end diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 854d39174c409..1b207a538bd13 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -42,10 +42,10 @@ def cat end def languages - if ENV["HOMEBREW_LANGUAGES"] + @languages ||= if ENV["HOMEBREW_LANGUAGES"] ENV["HOMEBREW_LANGUAGES"].split(",") else - @languages ||= Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) end end From 546a91f78e67939e49df4984562f02b25e68aa72 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 25 Sep 2016 20:43:03 +0200 Subject: [PATCH 08/15] =?UTF-8?q?Add=20`=E2=80=94language`=20option.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Homebrew/cask/lib/hbc/cli.rb | 4 ++++ Library/Homebrew/os/mac.rb | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb index 3f67e131ddc1c..f637ae7af5f72 100644 --- a/Library/Homebrew/cask/lib/hbc/cli.rb +++ b/Library/Homebrew/cask/lib/hbc/cli.rb @@ -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) diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 1b207a538bd13..b2f0515a0a2bd 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -42,11 +42,19 @@ def cat end def languages - @languages ||= if ENV["HOMEBREW_LANGUAGES"] - ENV["HOMEBREW_LANGUAGES"].split(",") - else - Utils.popen_read("defaults", "read", ".GlobalPreferences", "AppleLanguages").scan(/[^ \n"(),]+/) + 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 From bc2d676b6f590d0ddd2987699624f6718384b641 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 25 Sep 2016 22:13:44 +0200 Subject: [PATCH 09/15] Refactor logic to always choose first matched language. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 36 +++++++++++++-------- Library/Homebrew/cask/test/cask/dsl_test.rb | 6 ++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 624d62a7607e5..bae395726f3ca 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -101,27 +101,37 @@ def homepage(homepage = nil) def language(*args, &block) if !args.empty? && block_given? - args.each do |arg| - MacOS.languages.each_with_index do |l, index| - next unless arg == :default || l.match(arg) - next unless @language.nil? || @language[:level].nil? || @language[:level] > index - @language = { - block: block, - level: index, - } - end - end + @language_blocks ||= {} + @language_blocks[args] = block else language_eval @language end end - def language_eval - if @language.is_a?(Hash) && @language.key?(:block) - @language = @language[:block].call + return if instance_variable_defined?(:@language) + + return unless instance_variable_defined?(:@language_blocks) + + default_key = @language_blocks.keys.detect { |key| key.include?(:default) } + + MacOS.languages.each do |language| + @language_blocks.each do |regexes_or_strings, block| + if regexes_or_strings.include?(:default) + regexes_or_strings = regexes_or_strings - [:default] + [%r{^en}] + end + + case language + when *regexes_or_strings + @language = block.call + return + end + end end + + # fallback to :default + @language = default_key.nil? ? nil : @language_blocks[default_key].call end def url(*args, &block) diff --git a/Library/Homebrew/cask/test/cask/dsl_test.rb b/Library/Homebrew/cask/test/cask/dsl_test.rb index 1e13d49f323a1..f6fca3d2d1b9e 100644 --- a/Library/Homebrew/cask/test/cask/dsl_test.rb +++ b/Library/Homebrew/cask/test/cask/dsl_test.rb @@ -148,6 +148,12 @@ MacOS.stubs(languages: ["THIRD_LANGUAGE"]) cask.call.language.must_equal :default + + MacOS.stubs(languages: ["THIRD_LANGUAGE", "SECOND_LANGUAGE", "FIRST_LANGUAGE"]) + cask.call.language.must_equal :second + + MacOS.stubs(languages: ["THIRD_LANGUAGE", "FIRST_LANGUAGE", "SECOND_LANGUAGE"]) + cask.call.language.must_equal :first end end From bc143bb4701eb4f5fdcd19036b9232a32f4e52ae Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 25 Sep 2016 22:47:31 +0200 Subject: [PATCH 10/15] Support `audit` for multilingual casks. --- Library/Homebrew/cask/lib/hbc/auditor.rb | 18 ++++++++++++++++++ Library/Homebrew/cask/lib/hbc/dsl.rb | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/auditor.rb b/Library/Homebrew/cask/lib/hbc/auditor.rb index 6b0c1c4764373..7a6bb608fe9b3 100644 --- a/Library/Homebrew/cask/lib/hbc/auditor.rb +++ b/Library/Homebrew/cask/lib/hbc/auditor.rb @@ -1,6 +1,24 @@ 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.map(&:first).each do |language| + ohai "Auditing language #{language.to_s}" + language = "en-US" if language == :default + MacOS.instance_variable_set(:@languages, [language]) + 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) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index bae395726f3ca..9970f83954766 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -122,8 +122,13 @@ def language_eval regexes_or_strings = regexes_or_strings - [:default] + [%r{^en}] end - case language - when *regexes_or_strings + regexes_or_strings.each do |regex_or_string| + if regex_or_string.class == language.class + next unless regex_or_string == language + else + next unless regex_or_string =~ language + end + @language = block.call return end From b5531e8ec1dab3e3beabf85e7e270738bc214492 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Tue, 27 Sep 2016 22:23:13 +0200 Subject: [PATCH 11/15] Add Locale class to enable easier matching of locales. --- Library/Homebrew/cask/spec/locale_spec.rb | 72 +++++++++++++++++++++++ Library/Homebrew/locale.rb | 68 +++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 Library/Homebrew/cask/spec/locale_spec.rb create mode 100644 Library/Homebrew/locale.rb diff --git a/Library/Homebrew/cask/spec/locale_spec.rb b/Library/Homebrew/cask/spec/locale_spec.rb new file mode 100644 index 0000000000000..98a2de9139469 --- /dev/null +++ b/Library/Homebrew/cask/spec/locale_spec.rb @@ -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 diff --git a/Library/Homebrew/locale.rb b/Library/Homebrew/locale.rb new file mode 100644 index 0000000000000..1aff33dae6c8b --- /dev/null +++ b/Library/Homebrew/locale.rb @@ -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 From 013f33be4488205dd1fdf4f1b798dac58d1b93fe Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 28 Sep 2016 00:48:19 +0200 Subject: [PATCH 12/15] Change language DSL to only allow strings. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 32 ++++------- Library/Homebrew/cask/test/cask/dsl_test.rb | 61 +++++++++++++-------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 9970f83954766..08afe75fc519f 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -1,4 +1,5 @@ require "set" +require "locale" require "hbc/dsl/appcast" require "hbc/dsl/base" @@ -99,9 +100,10 @@ def homepage(homepage = nil) @homepage ||= homepage end - def language(*args, &block) + def language(*args, default: false, &block) if !args.empty? && block_given? @language_blocks ||= {} + @language_blocks.default = block if default @language_blocks[args] = block else language_eval @@ -114,29 +116,15 @@ def language_eval return unless instance_variable_defined?(:@language_blocks) - default_key = @language_blocks.keys.detect { |key| key.include?(:default) } + MacOS.languages.map(&Locale.method(:parse)).any? { |locale| + key = @language_blocks.keys.detect { |strings| + strings.any? { |string| locale.include?(string) } + } - MacOS.languages.each do |language| - @language_blocks.each do |regexes_or_strings, block| - if regexes_or_strings.include?(:default) - regexes_or_strings = regexes_or_strings - [:default] + [%r{^en}] - end + return @language = @language_blocks[key].call unless key.nil? + } - regexes_or_strings.each do |regex_or_string| - if regex_or_string.class == language.class - next unless regex_or_string == language - else - next unless regex_or_string =~ language - end - - @language = block.call - return - end - end - end - - # fallback to :default - @language = default_key.nil? ? nil : @language_blocks[default_key].call + @language = @language_blocks.default.call end def url(*args, &block) diff --git a/Library/Homebrew/cask/test/cask/dsl_test.rb b/Library/Homebrew/cask/test/cask/dsl_test.rb index f6fca3d2d1b9e..053eae1e1dd11 100644 --- a/Library/Homebrew/cask/test/cask/dsl_test.rb +++ b/Library/Homebrew/cask/test/cask/dsl_test.rb @@ -124,36 +124,51 @@ describe "language stanza" do it "allows multilingual casks" do - cask = lambda { + cask = lambda do Hbc::Cask.new("cask-with-apps") do - language "FIRST_LANGUAGE" do - :first + language "zh" do + sha256 "abc123" + "zh-CN" end - language %r{SECOND_LANGUAGE} do - :second + language "en-US", default: true do + sha256 "xyz789" + "en-US" end - language :default do - :default - end + url "https://example.org/#{language}.zip" end - } - - MacOS.stubs(languages: ["FIRST_LANGUAGE"]) - cask.call.language.must_equal :first - - MacOS.stubs(languages: ["SECOND_LANGUAGE"]) - cask.call.language.must_equal :second - - MacOS.stubs(languages: ["THIRD_LANGUAGE"]) - cask.call.language.must_equal :default - - MacOS.stubs(languages: ["THIRD_LANGUAGE", "SECOND_LANGUAGE", "FIRST_LANGUAGE"]) - cask.call.language.must_equal :second + end - MacOS.stubs(languages: ["THIRD_LANGUAGE", "FIRST_LANGUAGE", "SECOND_LANGUAGE"]) - cask.call.language.must_equal :first + 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 From b104e6ff37010232eacc86f442ef4d2cba155aac Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 28 Sep 2016 00:57:19 +0200 Subject: [PATCH 13/15] Raise error if more than one default language if specified. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 08afe75fc519f..a1129a21aeeb1 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -103,8 +103,15 @@ def homepage(homepage = nil) def language(*args, default: false, &block) if !args.empty? && block_given? @language_blocks ||= {} - @language_blocks.default = block if default @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 @language From ef26bf188254671127f9c6b11e1c76585f136629 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 28 Sep 2016 02:22:25 +0200 Subject: [PATCH 14/15] Refactor audit for changed DSL. --- Library/Homebrew/cask/lib/hbc/auditor.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/auditor.rb b/Library/Homebrew/cask/lib/hbc/auditor.rb index 7a6bb608fe9b3..ee1b50938b75b 100644 --- a/Library/Homebrew/cask/lib/hbc/auditor.rb +++ b/Library/Homebrew/cask/lib/hbc/auditor.rb @@ -4,10 +4,9 @@ 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.map(&:first).each do |language| - ohai "Auditing language #{language.to_s}" - language = "en-US" if language == :default - MacOS.instance_variable_set(:@languages, [language]) + 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 From e2b3753fd91c47beeb3227a1c0df4c0dfa6026fc Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 3 Oct 2016 02:34:32 +0200 Subject: [PATCH 15/15] Style changes in `language_eval`. --- Library/Homebrew/cask/lib/hbc/dsl.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index a1129a21aeeb1..8e0a7715af427 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -114,22 +114,25 @@ def language(*args, default: false, &block) @language_blocks.default = block else language_eval - @language end end def language_eval - return if instance_variable_defined?(:@language) + return @language if instance_variable_defined?(:@language) - return unless instance_variable_defined?(:@language_blocks) + if @language_blocks.nil? || @language_blocks.empty? + return @language = nil + end - MacOS.languages.map(&Locale.method(:parse)).any? { |locale| + MacOS.languages.map(&Locale.method(:parse)).each do |locale| key = @language_blocks.keys.detect { |strings| strings.any? { |string| locale.include?(string) } } - return @language = @language_blocks[key].call unless key.nil? - } + next if key.nil? + + return @language = @language_blocks[key].call + end @language = @language_blocks.default.call end