Merge pull request #8332 from MLH-Fellowship/named-args

args: Move named args parsing to separate class
This commit is contained in:
Mike McQuaid 2020-08-17 15:15:26 +01:00 committed by GitHub
commit c565a2130e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 314 additions and 141 deletions

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
require "cli/named_args"
require "ostruct"
module Homebrew
@ -19,7 +20,7 @@ module Homebrew
# Can set these because they will be overwritten by freeze_named_args!
# (whereas other values below will only be overwritten if passed).
self[:named_args] = []
self[:named_args] = NamedArgs.new
self[:remaining] = []
end
@ -28,18 +29,12 @@ module Homebrew
end
def freeze_named_args!(named_args)
# Reset cache values reliant on named_args
@formulae = nil
@formulae_and_casks = nil
@resolved_formulae = nil
@resolved_formulae_casks = nil
@formulae_paths = nil
@casks = nil
@loaded_casks = nil
@kegs = nil
@kegs_casks = nil
self[:named_args] = named_args.freeze
self[:named_args] = NamedArgs.new(
*named_args.freeze,
override_spec: spec(nil),
force_bottle: force_bottle?,
flags: flags_only,
)
end
def freeze_processed_options!(processed_options)
@ -54,7 +49,7 @@ module Homebrew
end
def named
named_args || []
named_args || NamedArgs.new
end
def no_named?
@ -62,102 +57,39 @@ module Homebrew
end
def formulae
require "formula"
@formulae ||= (downcased_unique_named - casks).map do |name|
Formulary.factory(name, spec, force_bottle: force_bottle?, flags: flags_only)
end.uniq(&:name).freeze
named.to_formulae
end
def formulae_and_casks
@formulae_and_casks ||= begin
formulae_and_casks = []
downcased_unique_named.each do |name|
formulae_and_casks << Formulary.factory(name, spec)
rescue FormulaUnavailableError
begin
formulae_and_casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No available formula or cask with the name \"#{name}\""
end
end
formulae_and_casks.freeze
end
named.to_formulae_and_casks
end
def resolved_formulae
require "formula"
@resolved_formulae ||= (downcased_unique_named - casks).map do |name|
Formulary.resolve(name, spec: spec(nil), force_bottle: force_bottle?, flags: flags_only)
end.uniq(&:name).freeze
named.to_resolved_formulae
end
def resolved_formulae_casks
@resolved_formulae_casks ||= begin
resolved_formulae = []
casks = []
downcased_unique_named.each do |name|
resolved_formulae << Formulary.resolve(name, spec: spec(nil),
force_bottle: force_bottle?, flags: flags_only)
rescue FormulaUnavailableError
begin
casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No available formula or cask with the name \"#{name}\""
end
end
[resolved_formulae.freeze, casks.freeze].freeze
end
named.to_resolved_formulae_to_casks
end
def formulae_paths
@formulae_paths ||= (downcased_unique_named - casks).map do |name|
Formulary.path(name)
end.uniq.freeze
named.to_formulae_paths
end
def casks
@casks ||= downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
.freeze
named.homebrew_tap_cask_names
end
def loaded_casks
@loaded_casks ||= downcased_unique_named.map(&Cask::CaskLoader.method(:load)).freeze
named.to_casks
end
def kegs
@kegs ||= downcased_unique_named.map do |name|
resolve_keg name
rescue NoSuchKegError => e
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
$stderr.puts reason
end
raise e
end.freeze
named.to_kegs
end
def kegs_casks
@kegs_casks ||= begin
kegs = []
casks = []
downcased_unique_named.each do |name|
kegs << resolve_keg(name)
rescue NoSuchKegError
begin
casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No installed keg or cask with the name \"#{name}\""
end
end
[kegs.freeze, casks.freeze].freeze
end
named.to_kegs_to_casks
end
def build_stable?
@ -218,17 +150,6 @@ module Homebrew
@cli_args.freeze
end
def downcased_unique_named
# Only lowercase names, not paths, bottle filenames or URLs
named.map do |arg|
if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
arg
else
arg.downcase
end
end.uniq
end
def spec(default = :stable)
if HEAD?
:head
@ -238,50 +159,6 @@ module Homebrew
default
end
end
def resolve_keg(name)
require "keg"
require "formula"
require "missing_formula"
raise UsageError if name.blank?
rack = Formulary.to_rack(name.downcase)
dirs = rack.directory? ? rack.subdirs : []
raise NoSuchKegError, rack.basename if dirs.empty?
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
begin
if opt_prefix.symlink? && opt_prefix.directory?
Keg.new(opt_prefix.resolved_path)
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
Keg.new(linked_keg_ref.resolved_path)
elsif dirs.length == 1
Keg.new(dirs.first)
else
f = if name.include?("/") || File.exist?(name)
Formulary.factory(name)
else
Formulary.from_rack(rack)
end
unless (prefix = f.installed_prefix).directory?
raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions"
end
Keg.new(prefix)
end
rescue FormulaUnavailableError
raise MultipleVersionsInstalledError, <<~EOS
Multiple kegs installed to #{rack}
However we don't know which one you refer to.
Please delete (with rm -rf!) all but one and then try again.
EOS
end
end
end
end
end

View File

@ -0,0 +1,168 @@
# frozen_string_literal: true
require "delegate"
module Homebrew
module CLI
class NamedArgs < SimpleDelegator
def initialize(*args, override_spec: nil, force_bottle: false, flags: [])
@args = args
@override_spec = override_spec
@force_bottle = force_bottle
@flags = flags
__setobj__(@args)
end
def to_formulae
@to_formulae ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
Formulary.factory(name, spec, force_bottle: @force_bottle, flags: @flags)
end.uniq(&:name).freeze
end
def to_formulae_and_casks
@to_formulae_and_casks ||= begin
formulae_and_casks = []
downcased_unique_named.each do |name|
formulae_and_casks << Formulary.factory(name, spec)
rescue FormulaUnavailableError
begin
formulae_and_casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No available formula or cask with the name \"#{name}\""
end
end
formulae_and_casks.freeze
end
end
def to_resolved_formulae
@to_resolved_formulae ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
Formulary.resolve(name, spec: spec(nil), force_bottle: @force_bottle, flags: @flags)
end.uniq(&:name).freeze
end
def to_resolved_formulae_to_casks
@to_resolved_formulae_to_casks ||= begin
resolved_formulae = []
casks = []
downcased_unique_named.each do |name|
resolved_formulae << Formulary.resolve(name, spec: spec(nil), force_bottle: @force_bottle, flags: @flags)
rescue FormulaUnavailableError
begin
casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No available formula or cask with the name \"#{name}\""
end
end
[resolved_formulae.freeze, casks.freeze].freeze
end
end
def to_formulae_paths
@to_formulae_paths ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
Formulary.path(name)
end.uniq.freeze
end
def to_casks
@to_casks ||= downcased_unique_named.map(&Cask::CaskLoader.method(:load)).freeze
end
def to_kegs
@to_kegs ||= downcased_unique_named.map do |name|
resolve_keg name
rescue NoSuchKegError => e
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
$stderr.puts reason
end
raise e
end.freeze
end
def to_kegs_to_casks
@to_kegs_to_casks ||= begin
kegs = []
casks = []
downcased_unique_named.each do |name|
kegs << resolve_keg(name)
rescue NoSuchKegError, FormulaUnavailableError
begin
casks << Cask::CaskLoader.load(name)
rescue Cask::CaskUnavailableError
raise "No installed keg or cask with the name \"#{name}\""
end
end
[kegs.freeze, casks.freeze].freeze
end
end
def homebrew_tap_cask_names
downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
end
private
def downcased_unique_named
# Only lowercase names, not paths, bottle filenames or URLs
map do |arg|
if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
arg
else
arg.downcase
end
end.uniq
end
def spec(default = :stable)
@override_spec || default
end
def resolve_keg(name)
raise UsageError if name.blank?
rack = Formulary.to_rack(name.downcase)
dirs = rack.directory? ? rack.subdirs : []
raise NoSuchKegError, rack.basename if dirs.empty?
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
begin
if opt_prefix.symlink? && opt_prefix.directory?
Keg.new(opt_prefix.resolved_path)
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
Keg.new(linked_keg_ref.resolved_path)
elsif dirs.length == 1
Keg.new(dirs.first)
else
f = if name.include?("/") || File.exist?(name)
Formulary.factory(name)
else
Formulary.from_rack(rack)
end
unless (prefix = f.installed_prefix).directory?
raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions"
end
Keg.new(prefix)
end
rescue FormulaUnavailableError
raise MultipleVersionsInstalledError, <<~EOS
Multiple kegs installed to #{rack}
However we don't know which one you refer to.
Please delete (with rm -rf!) all but one and then try again.
EOS
end
end
end
end
end

View File

@ -0,0 +1,112 @@
# frozen_string_literal: true
require "cli/named_args"
describe Homebrew::CLI::NamedArgs do
let(:foo) do
formula "foo" do
url "https://brew.sh"
version "1.0"
end
end
let(:foo_keg) do
path = (HOMEBREW_CELLAR/"foo/1.0").resolved_path
mkdir_p path
Keg.new(path)
end
let(:bar) do
formula "bar" do
url "https://brew.sh"
version "1.0"
end
end
let(:bar_keg) do
path = (HOMEBREW_CELLAR/"bar/1.0").resolved_path
mkdir_p path
Keg.new(path)
end
let(:baz) do
Cask::CaskLoader.load(+<<~RUBY)
cask "baz" do
version "1.0"
end
RUBY
end
describe "#to_formulae" do
it "returns formulae" do
allow(Formulary).to receive(:loader_for).and_call_original
stub_formula_loader foo
stub_formula_loader bar
expect(described_class.new("foo", "bar").to_formulae).to eq [foo, bar]
end
end
describe "#to_formulae_and_casks" do
it "returns formulae and casks" do
allow(Formulary).to receive(:loader_for).and_call_original
stub_formula_loader foo
stub_cask_loader baz
expect(described_class.new("foo", "baz").to_formulae_and_casks).to eq [foo, baz]
end
end
describe "#to_resolved_formulae" do
it "returns resolved formulae" do
allow(Formulary).to receive(:resolve).and_return(foo, bar)
expect(described_class.new("foo", "bar").to_resolved_formulae).to eq [foo, bar]
end
end
describe "#to_resolved_formulae_to_casks" do
it "returns resolved formulae, as well as casks" do
allow(Formulary).to receive(:resolve).and_call_original
allow(Formulary).to receive(:resolve).with("foo", any_args).and_return foo
stub_cask_loader baz
resolved_formulae, casks = described_class.new("foo", "baz").to_resolved_formulae_to_casks
expect(resolved_formulae).to eq [foo]
expect(casks).to eq [baz]
end
end
describe "#to_casks" do
it "returns casks" do
stub_cask_loader baz
expect(described_class.new("baz").to_casks).to eq [baz]
end
end
describe "#to_kegs" do
it "returns kegs" do
named_args = described_class.new("foo", "bar")
allow(named_args).to receive(:resolve_keg).with("foo").and_return foo_keg
allow(named_args).to receive(:resolve_keg).with("bar").and_return bar_keg
expect(named_args.to_kegs).to eq [foo_keg, bar_keg]
end
end
describe "#to_kegs_to_casks" do
it "returns kegs, as well as casks" do
named_args = described_class.new("foo", "baz")
allow(named_args).to receive(:resolve_keg).and_call_original
allow(named_args).to receive(:resolve_keg).with("foo").and_return foo_keg
stub_cask_loader baz
kegs, casks = named_args.to_kegs_to_casks
expect(kegs).to eq [foo_keg]
expect(casks).to eq [baz]
end
end
end

View File

@ -36,6 +36,7 @@ $LOAD_PATH.push(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/test/suppo
require_relative "../global"
require "test/support/no_seed_progress_formatter"
require "test/support/helper/cask"
require "test/support/helper/fixtures"
require "test/support/helper/formula"
require "test/support/helper/mktmpdir"
@ -86,6 +87,7 @@ RSpec.configure do |config|
config.include(RuboCop::RSpec::ExpectOffense)
config.include(Test::Helper::Cask)
config.include(Test::Helper::Fixtures)
config.include(Test::Helper::Formula)
config.include(Test::Helper::MkTmpDir)

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require "cask/cask_loader"
module Test
module Helper
module Cask
def stub_cask_loader(cask, ref = cask.token)
loader = ::Cask::CaskLoader::FromInstanceLoader.new cask
allow(::Cask::CaskLoader).to receive(:for).with(ref).and_return(loader)
end
end
end
end