Use ActiveSupport’s #pluralize and #to_sentence.

This commit is contained in:
Markus Reiter 2018-09-17 20:11:11 +02:00
parent c4d418e126
commit 16618d0fc7
21 changed files with 228 additions and 128 deletions

View File

@ -48,7 +48,7 @@ module Cask
end
def audit_languages(languages)
ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.join(", ")}"
ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.to_sentence}"
MacOS.instance_variable_set(:@languages, languages)
audit_cask_instance(CaskLoader.load(cask.sourcefile_path))
ensure

View File

@ -32,7 +32,7 @@ module Cask
rescue TSort::Cyclic
strongly_connected_components = graph.strongly_connected_components.sort_by(&:count)
cyclic_dependencies = strongly_connected_components.last - [cask]
raise CaskCyclicDependencyError.new(cask.token, cyclic_dependencies.join(", "))
raise CaskCyclicDependencyError.new(cask.token, cyclic_dependencies.to_sentence)
end
end
end

View File

@ -182,10 +182,14 @@ module Cask
end
def self.cask_count_for_tap(tap)
Formatter.pluralize(tap.cask_files.count, "cask")
rescue
add_error "Unable to read from Tap: #{tap.path}"
"0"
cask_count = begin
tap.cask_files.count
rescue
add_error "Unable to read from Tap: #{tap.path}"
0
end
"#{cask_count} #{"cask".pluralize(cask_count)}"
end
def self.render_env_var(var)

View File

@ -23,11 +23,9 @@ module Cask
next if (versions = cask.versions).empty?
single = versions.count == 1
puts <<~EOS
#{cask} #{versions.join(", ")} #{single ? "is" : "are"} still installed.
Remove #{single ? "it" : "them all"} with `brew cask uninstall --force #{cask}`.
#{cask} #{versions.to_sentence} #{"is".pluralize(versions.count)} still installed.
Remove #{(versions.count == 1) ? "it" : "them all"} with `brew cask uninstall --force #{cask}`.
EOS
end
end

View File

@ -28,7 +28,7 @@ module Cask
end
ohai "Casks with `auto_updates` or `version :latest` will not be upgraded" if args.empty? && !greedy?
oh1 "Upgrading #{Formatter.pluralize(outdated_casks.length, "outdated package")}, with result:"
oh1 "Upgrading #{outdated_casks.count} #{"outdated package".pluralize(outdated_casks.count)}:"
cask_upgrades = outdated_casks.map do |cask|
if cask.installed_caskfile.nil?
"#{cask.full_name} #{cask.version}"

View File

@ -49,7 +49,7 @@ module Homebrew
if ARGV.named.empty?
if HOMEBREW_CELLAR.exist?
count = Formula.racks.length
puts "#{Formatter.pluralize(count, "keg")}, #{HOMEBREW_CELLAR.abv}"
puts "#{count} #{"keg".pluralize(count)}, #{HOMEBREW_CELLAR.abv}"
end
else
ARGV.named.each_with_index do |f, i|

View File

@ -48,11 +48,11 @@ module Homebrew
pinned_count += 1 if tap.pinned?
private_count += 1 if tap.private?
end
info = Formatter.pluralize(tap_count, "tap").to_s
info = "#{tap_count} #{"tap".pluralize(tap_count)}"
info += ", #{pinned_count} pinned"
info += ", #{private_count} private"
info += ", #{Formatter.pluralize(formula_count, "formula")}"
info += ", #{Formatter.pluralize(command_count, "command")}"
info += ", #{formula_count} #{"formula".pluralize(formula_count)}"
info += ", #{command_count} #{"command".pluralize(command_count)}"
info += ", #{Tap::TAP_DIRECTORY.abv}" if Tap::TAP_DIRECTORY.directory?
puts info
else

View File

@ -66,8 +66,7 @@ module Homebrew
if rack.directory?
versions = rack.subdirs.map(&:basename)
verb = Formatter.pluralize(versions.length, "is", "are")
puts "#{keg.name} #{versions.join(", ")} #{verb} still installed."
puts "#{keg.name} #{versions.to_sentence} #{"is".pluralize(versions.count)} still installed."
puts "Remove all versions with `brew uninstall --force #{keg.name}`."
end
end
@ -119,31 +118,20 @@ module Homebrew
protected
def are(items)
Formatter.pluralize(items.count, "is", "are", show_count: false)
end
def they(items)
Formatter.pluralize(items.count, "it", "they", show_count: false)
end
def list(items)
items.join(", ")
end
def sample_command
"brew uninstall --ignore-dependencies #{ARGV.named.join(" ")}"
end
def are_required_by_deps
"#{are reqs} required by #{list deps}, which #{are deps} currently installed"
"#{"is".pluralize(reqs.count)} required by #{deps.to_sentence}, " \
"which #{"is".pluralize(deps.count)} currently installed"
end
end
class DeveloperDependentsMessage < DependentsMessage
def output
opoo <<~EOS
#{list reqs} #{are_required_by_deps}.
#{reqs.to_sentence} #{are_required_by_deps}.
You can silence this warning with:
#{sample_command}
EOS
@ -153,8 +141,8 @@ module Homebrew
class NondeveloperDependentsMessage < DependentsMessage
def output
ofail <<~EOS
Refusing to uninstall #{list reqs}
because #{they reqs} #{are_required_by_deps}.
Refusing to uninstall #{reqs.to_sentence}
because #{"it".pluralize(reqs.count)} #{are_required_by_deps}.
You can override this and force removal with:
#{sample_command}
EOS

View File

@ -107,8 +107,7 @@ module Homebrew
unless updated_taps.empty?
update_preinstall_header
puts "Updated #{Formatter.pluralize(updated_taps.size, "tap")} " \
"(#{updated_taps.join(", ")})."
puts "Updated #{updated_taps.count} #{"tap".pluralize(updated_taps.count)} (#{updated_taps.to_sentence})."
updated = true
end

View File

@ -63,14 +63,14 @@ module Homebrew
formulae_to_install = outdated.map(&:latest_formula)
if !pinned.empty? && !ARGV.include?("--ignore-pinned")
ofail "Not upgrading #{Formatter.pluralize(pinned.length, "pinned package")}:"
ofail "Not upgrading #{pinned.count} pinned #{"package".pluralize(pinned.count)}:"
puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
if formulae_to_install.empty?
oh1 "No packages to upgrade"
else
oh1 "Upgrading #{Formatter.pluralize(formulae_to_install.length, "outdated package")}, with result:"
oh1 "Upgrading #{formulae_to_install.count} outdated #{"package".pluralize(formulae_to_install.count)}:"
formulae_upgrades = formulae_to_install.map do |f|
if f.optlinked?
"#{f.full_specified_name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
@ -304,7 +304,7 @@ module Homebrew
# Print the pinned dependents.
unless pinned.empty?
ohai "Not upgrading #{Formatter.pluralize(pinned.length, "pinned dependent")}:"
ohai "Not upgrading #{pinned.count} pinned #{"dependent".pluralize(pinned.count)}:"
puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
@ -312,7 +312,7 @@ module Homebrew
if upgradable.empty?
ohai "No dependents to upgrade" if ARGV.verbose?
else
ohai "Upgrading #{Formatter.pluralize(upgradable.length, "dependent")}:"
ohai "Upgrading #{upgradable.count} #{"dependent".pluralize(upgradable.count)}:"
formulae_upgrades = upgradable.map do |f|
if f.optlinked?
"#{f.full_specified_name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
@ -337,7 +337,7 @@ module Homebrew
# Print the pinned dependents.
unless pinned.empty?
onoe "Not reinstalling #{Formatter.pluralize(pinned.length, "broken and outdated, but pinned dependent")}:"
onoe "Not reinstalling #{pinned.count} broken and outdated, but pinned #{"dependent".pluralize(pinned.count)}:"
$stderr.puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
end
@ -345,7 +345,7 @@ module Homebrew
if reinstallable.empty?
ohai "No broken dependents to reinstall" if ARGV.verbose?
else
ohai "Reinstalling #{Formatter.pluralize(reinstallable.length, "broken dependent")} from source:"
ohai "Reinstalling #{reinstallable.count} broken #{"dependent".pluralize(reinstallable.count)} from source:"
puts reinstallable.map(&:full_specified_name).join(", ")
end

View File

@ -186,9 +186,9 @@ module Homebrew
end
total_problems_count = problem_count + new_formula_problem_count
problem_plural = Formatter.pluralize(total_problems_count, "problem")
formula_plural = Formatter.pluralize(formula_count, "formula")
corrected_problem_plural = Formatter.pluralize(corrected_problem_count, "problem")
problem_plural = "#{total_problems_count} #{"problem".pluralize(total_problems_count)}"
formula_plural = "#{formula_count} #{"formula".pluralize(formula_count)}"
corrected_problem_plural = "#{corrected_problem_count} #{"problem".pluralize(corrected_problem_count)}"
errors_summary = "#{problem_plural} in #{formula_plural} detected"
if corrected_problem_count.positive?
errors_summary += ", #{corrected_problem_plural} corrected"

View File

@ -441,18 +441,10 @@ end
# and are being installed on a system without necessary build tools
class BuildToolsError < RuntimeError
def initialize(formulae)
if formulae.length > 1
formula_text = "formulae"
package_text = "binary packages"
else
formula_text = "formula"
package_text = "a binary package"
end
super <<~EOS
The following #{formula_text}:
#{formulae.join(", ")}
cannot be installed as #{package_text} and must be built from source.
The following #{"formula".pluralize(formulae.count)}
#{formulae.to_sentence}
cannot be installed as #{"binary package".pluralize(formulae.count)} and must be built from source.
#{DevelopmentTools.installation_instructions}
EOS
end

View File

@ -537,7 +537,7 @@ class FormulaInstaller
puts "All dependencies for #{formula.full_name} are satisfied."
elsif !deps.empty?
oh1 "Installing dependencies for #{formula.full_name}: " \
"#{deps.map(&:first).map(&Formatter.method(:identifier)).join(", ")}",
"#{deps.map(&:first).map(&Formatter.method(:identifier)).to_sentence}",
truncate: false
deps.each { |dep, options| install_dependency(dep, options) }
end

View File

@ -9,6 +9,18 @@ require_relative "load_path"
require "active_support/core_ext/numeric/time"
require "active_support/core_ext/array/access"
require "active_support/i18n"
require "active_support/inflector/inflections"
I18n.backend.available_locales # Initialize locales so they can be overwritten.
I18n.backend.store_translations :en, support: { array: { last_word_connector: " and " } }
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.irregular "formula", "formulae"
inflect.irregular "is", "are"
inflect.irregular "it", "they"
end
require "config"
require "os"
require "extend/ARGV"

View File

@ -297,7 +297,7 @@ class Tap
link_completions_and_manpages
formatted_contents = Formatter.comma_and(*contents)&.prepend(" ")
formatted_contents = contents.presence&.to_sentence&.dup&.prepend(" ")
puts "Tapped#{formatted_contents} (#{path.abv})." unless quiet
Descriptions.cache_formulae(formula_names)
@ -328,7 +328,7 @@ class Tap
puts "Untapping #{name}..."
abv = path.abv
formatted_contents = Formatter.comma_and(*contents)&.prepend(" ")
formatted_contents = contents.presence&.to_sentence&.dup&.prepend(" ")
unpin if pinned?
Descriptions.uncache_formulae(formula_names)
@ -365,15 +365,15 @@ class Tap
contents = []
if (command_count = command_files.count).positive?
contents << Formatter.pluralize(command_count, "command")
contents << "#{command_count} #{"command".pluralize(command_count)}"
end
if (cask_count = cask_files.count).positive?
contents << Formatter.pluralize(cask_count, "cask")
contents << "#{cask_count} #{"cask".pluralize(cask_count)}"
end
if (formula_count = formula_files.count).positive?
contents << Formatter.pluralize(formula_count, "formula")
contents << "#{formula_count} #{"formula".pluralize(formula_count)}"
end
contents

View File

@ -53,45 +53,4 @@ describe Formatter do
it { is_expected.to eq("\n") }
end
end
describe "::pluralize" do
it "pluralizes words" do
expect(described_class.pluralize(0, "cask")).to eq("0 casks")
expect(described_class.pluralize(1, "cask")).to eq("1 cask")
expect(described_class.pluralize(2, "cask")).to eq("2 casks")
end
it "allows specifying custom plural forms" do
expect(described_class.pluralize(1, "child", "children")).to eq("1 child")
expect(described_class.pluralize(2, "child", "children")).to eq("2 children")
end
it "has plural forms of Homebrew jargon" do
expect(described_class.pluralize(1, "formula")).to eq("1 formula")
expect(described_class.pluralize(2, "formula")).to eq("2 formulae")
end
it "pluralizes the last word of a string" do
expect(described_class.pluralize(1, "new formula")).to eq("1 new formula")
expect(described_class.pluralize(2, "new formula")).to eq("2 new formulae")
end
end
describe "::comma_and" do
it "returns nil if given no arguments" do
expect(described_class.comma_and).to be nil
end
it "returns the input as string if there is only one argument" do
expect(described_class.comma_and(1)).to eq("1")
end
it "concatenates two items with “and”" do
expect(described_class.comma_and(1, 2)).to eq("1 and 2")
end
it "concatenates all items with a comma and appends the last with “and”" do
expect(described_class.comma_and(1, 2, 3)).to eq("1, 2 and 3")
end
end
end

View File

@ -152,13 +152,13 @@ def pretty_duration(s)
if s > 59
m = s / 60
s %= 60
res = Formatter.pluralize(m, "minute")
res = "#{m} #{"minute".pluralize(m)}"
return res if s.zero?
res << " "
end
res << Formatter.pluralize(s, "second")
res << "#{s} #{"second".pluralize(s)}"
end
def interactive_shell(f = nil)

View File

@ -98,28 +98,4 @@ module Formatter
output
end
def pluralize(count, singular, plural = nil, show_count: true)
return (show_count ? "#{count} #{singular}" : singular.to_s) if count == 1
*adjectives, noun = singular.to_s.split(" ")
plural ||= {
"formula" => "formulae",
}.fetch(noun, "#{noun}s")
words = adjectives.push(plural).join(" ")
show_count ? "#{count} #{words}" : words
end
def comma_and(*items)
# TODO: Remove when RuboCop 0.57.3 is released.
# False positive has been fixed and merged, but is not yet in a
# stable release: https://github.com/rubocop-hq/rubocop/pull/6038
*items, last = items.map(&:to_s) # rubocop:disable Lint/ShadowedArgument
return last if items.empty?
"#{items.join(", ")} and #{last}"
end
end

View File

@ -0,0 +1,135 @@
en:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
# Don't forget the nil at the beginning; there's no such thing as a 0th month
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
# Used in date_select and datetime_select.
order:
- year
- month
- day
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " and "
last_word_connector: ", and "
number:
# Used in NumberHelper.number_to_delimited()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: "."
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# If set to true, precision will mean the number of significant digits instead
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
significant: false
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
strip_insignificant_zeros: false
# Used in NumberHelper.number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "$"
# These five are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
significant: false
strip_insignificant_zeros: false
# Used in NumberHelper.number_to_percentage()
percentage:
format:
# These five are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# significant: false
# strip_insignificant_zeros: false
format: "%n%"
# Used in NumberHelper.number_to_rounded()
precision:
format:
# These five are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# significant: false
# strip_insignificant_zeros: false
# Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
human:
format:
# These five are to override number.format and are optional
# separator:
delimiter: ""
precision: 3
significant: true
strip_insignificant_zeros: true
# Used in number_to_human_size()
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
pb: "PB"
eb: "EB"
# Used in NumberHelper.number_to_human()
decimal_units:
format: "%n %u"
# Decimal units output formatting
# By default we will only quantify some of the exponents
# but the commented ones might be defined or overridden
# by the user.
units:
# femto: Quadrillionth
# pico: Trillionth
# nano: Billionth
# micro: Millionth
# mili: Thousandth
# centi: Hundredth
# deci: Tenth
unit: ""
# ten:
# one: Ten
# other: Tens
# hundred: Hundred
thousand: Thousand
million: Million
billion: Billion
trillion: Trillion
quadrillion: Quadrillion

View File

@ -0,0 +1,29 @@
class Hash
def slice(*keep_keys)
h = {}
keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) }
h
end unless Hash.method_defined?(:slice)
def except(*less_keys)
slice(*keys - less_keys)
end unless Hash.method_defined?(:except)
def deep_symbolize_keys
inject({}) { |result, (key, value)|
value = value.deep_symbolize_keys if value.is_a?(Hash)
result[(key.to_sym rescue key) || key] = value
result
}
end unless Hash.method_defined?(:deep_symbolize_keys)
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
MERGER = proc do |key, v1, v2|
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
end
def deep_merge!(data)
merge!(data, &MERGER)
end unless Hash.method_defined?(:deep_merge!)
end

View File

@ -0,0 +1,8 @@
module Kernel
def suppress_warnings
original_verbosity, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = original_verbosity
end
end