Warn about undocumented non-private APIs.

This commit is contained in:
Markus Reiter 2024-04-26 20:55:51 +02:00
parent 7c0b989740
commit caf87c0336
No known key found for this signature in database
GPG Key ID: 245293B51702655B
108 changed files with 1530 additions and 731 deletions

View File

@ -70,6 +70,11 @@ Style/Documentation:
- version.rb - version.rb
- tap.rb - tap.rb
Homebrew/NegateInclude:
Exclude:
# YARD runs stand-alone.
- yard/docstring_parser.rb
Style/DocumentationMethod: Style/DocumentationMethod:
Include: Include:
- "formula.rb" - "formula.rb"

View File

@ -18,6 +18,7 @@ end
group :doc, optional: true do group :doc, optional: true do
gem "yard", require: false gem "yard", require: false
gem "yard-sorbet", require: false gem "yard-sorbet", require: false
gem "redcarpet", require: false
end end
group :ast, optional: true do group :ast, optional: true do
gem "rubocop-ast", require: false gem "rubocop-ast", require: false

View File

@ -60,7 +60,6 @@ class PATH
@paths.join(File::PATH_SEPARATOR) @paths.join(File::PATH_SEPARATOR)
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = to_str def to_s = to_str

View File

@ -39,8 +39,8 @@ module Homebrew
sig { returns(Pathname) } sig { returns(Pathname) }
def self.gh_executable def self.gh_executable
# NOTE: We disable HOMEBREW_VERIFY_ATTESTATIONS when installing `gh` itself, # NOTE: We disable HOMEBREW_VERIFY_ATTESTATIONS when installing `gh` itself,
# to prevent a cycle during bootstrapping. This can eventually be resolved # to prevent a cycle during bootstrapping. This can eventually be resolved
# by vendoring a pure-Ruby Sigstore verifier client. # by vendoring a pure-Ruby Sigstore verifier client.
@gh_executable ||= T.let(with_env("HOMEBREW_VERIFY_ATTESTATIONS" => nil) do @gh_executable ||= T.let(with_env("HOMEBREW_VERIFY_ATTESTATIONS" => nil) do
ensure_executable!("gh") ensure_executable!("gh")
end, T.nilable(Pathname)) end, T.nilable(Pathname))

View File

@ -265,7 +265,7 @@ EOS
fi fi
} }
# NOTE: the members of the array in the second arg must not have spaces! # NOTE: The members of the array in the second arg must not have spaces!
check-array-membership() { check-array-membership() {
local item=$1 local item=$1
shift shift

View File

@ -1,6 +1,6 @@
# Note: that we use a non-standard config file name to reduce # NOTE: We use a non-standard config file name to reduce name clashes with
# name clashes with other IRB config files like `.irbrc`. # other IRB config files like `.irbrc`.
# Note #2: This doesn't work with system Ruby for some reason. # NOTE: This doesn't work with system Ruby for some reason.
require 'irb/completion' require 'irb/completion'

View File

@ -9,16 +9,26 @@ class BuildOptions
end end
# True if a {Formula} is being built with a specific option. # True if a {Formula} is being built with a specific option.
# <pre>args << "--i-want-spam" if build.with? "spam"
# #
# ### Examples
#
# ```ruby
# args << "--i-want-spam" if build.with? "spam"
# ```
#
# ```ruby
# args << "--qt-gui" if build.with? "qt" # "--with-qt" ==> build.with? "qt" # args << "--qt-gui" if build.with? "qt" # "--with-qt" ==> build.with? "qt"
# ```
# #
# # If a formula presents a user with a choice, but the choice must be fulfilled: # If a formula presents a user with a choice, but the choice must be fulfilled:
#
# ```ruby
# if build.with? "example2" # if build.with? "example2"
# args << "--with-example2" # args << "--with-example2"
# else # else
# args << "--with-example1" # args << "--with-example1"
# end</pre> # end
# ```
def with?(val) def with?(val)
option_names = val.respond_to?(:option_names) ? val.option_names : [val] option_names = val.respond_to?(:option_names) ? val.option_names : [val]
@ -34,7 +44,12 @@ class BuildOptions
end end
# True if a {Formula} is being built without a specific option. # True if a {Formula} is being built without a specific option.
# <pre>args << "--no-spam-plz" if build.without? "spam"</pre> #
# ### Example
#
# ```ruby
# args << "--no-spam-plz" if build.without? "spam"
# ```
def without?(val) def without?(val)
!with?(val) !with?(val)
end end
@ -45,19 +60,33 @@ class BuildOptions
end end
# True if a {Formula} is being built with {Formula.head} instead of {Formula.stable}. # True if a {Formula} is being built with {Formula.head} instead of {Formula.stable}.
# <pre>args << "--some-new-stuff" if build.head?</pre> #
# <pre># If there are multiple conditional arguments use a block instead of lines. # ### Examples
#
# ```ruby
# args << "--some-new-stuff" if build.head?
# ```
#
# If there are multiple conditional arguments use a block instead of lines.
#
# ```ruby
# if build.head? # if build.head?
# args << "--i-want-pizza" # args << "--i-want-pizza"
# args << "--and-a-cold-beer" if build.with? "cold-beer" # args << "--and-a-cold-beer" if build.with? "cold-beer"
# end</pre> # end
# ```
def head? def head?
include? "HEAD" include? "HEAD"
end end
# True if a {Formula} is being built with {Formula.stable} instead of {Formula.head}. # True if a {Formula} is being built with {Formula.stable} instead of {Formula.head}.
# This is the default. # This is the default.
# <pre>args << "--some-beta" if build.head?</pre> #
# ### Example
#
# ```ruby
# args << "--some-beta" if build.head?
# ```
def stable? def stable?
!head? !head?
end end

View File

@ -147,7 +147,6 @@ module Cask
cask.config cask.config
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"#{summarize} (#{self.class.english_name})" "#{summarize} (#{self.class.english_name})"

View File

@ -78,7 +78,8 @@ module Cask
@allow_reassignment = allow_reassignment @allow_reassignment = allow_reassignment
@loaded_from_api = loaded_from_api @loaded_from_api = loaded_from_api
@loader = loader @loader = loader
# Sorbet has trouble with bound procs assigned to ivars: https://github.com/sorbet/sorbet/issues/6843 # Sorbet has trouble with bound procs assigned to instance variables:
# https://github.com/sorbet/sorbet/issues/6843
instance_variable_set(:@block, block) instance_variable_set(:@block, block)
@default_config = config || Config.new @default_config = config || Config.new
@ -323,11 +324,9 @@ module Cask
end end
# @api public # @api public
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = token def to_s = token
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<Cask #{token}#{sourcefile_path&.to_s&.prepend(" ")}>" "#<Cask #{token}#{sourcefile_path&.to_s&.prepend(" ")}>"

View File

@ -112,6 +112,16 @@ module Cask
@token = cask.token @token = cask.token
end end
# Specifies the cask's name.
#
# NOTE: Multiple names can be specified.
#
# ### Example
#
# ```ruby
# name "Visual Studio Code"
# ```
#
# @api public # @api public
def name(*args) def name(*args)
@name ||= [] @name ||= []
@ -120,6 +130,14 @@ module Cask
@name.concat(args.flatten) @name.concat(args.flatten)
end end
# Describes the cask.
#
# ### Example
#
# ```ruby
# desc "Open-source code editor"
# ```
#
# @api public # @api public
def desc(description = nil) def desc(description = nil)
set_unique_stanza(:desc, description.nil?) { description } set_unique_stanza(:desc, description.nil?) { description }
@ -146,6 +164,14 @@ module Cask
raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}") raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}")
end end
# Sets the cask's homepage.
#
# ### Example
#
# ```ruby
# homepage "https://code.visualstudio.com/"
# ```
#
# @api public # @api public
def homepage(homepage = nil) def homepage(homepage = nil)
set_unique_stanza(:homepage, homepage.nil?) { homepage } set_unique_stanza(:homepage, homepage.nil?) { homepage }
@ -201,6 +227,14 @@ module Cask
@language_blocks.keys.flatten @language_blocks.keys.flatten
end end
# Sets the cask's download URL.
#
# ### Example
#
# ```ruby
# url "https://update.code.visualstudio.com/#{version}/#{arch}/stable"
# ```
#
# @api public # @api public
def url(*args, **options, &block) def url(*args, **options, &block)
caller_location = T.must(caller_locations).fetch(0) caller_location = T.must(caller_locations).fetch(0)
@ -214,6 +248,8 @@ module Cask
end end
end end
# Sets the cask's appcast URL.
#
# @api public # @api public
def appcast(*args, **kwargs) def appcast(*args, **kwargs)
set_unique_stanza(:appcast, args.empty? && kwargs.empty?) do set_unique_stanza(:appcast, args.empty? && kwargs.empty?) do
@ -222,6 +258,22 @@ module Cask
end end
end end
# Sets the cask's container type or nested container path.
#
# ### Examples
#
# The container is a nested disk image:
#
# ```ruby
# container nested: "orca-#{version}.dmg"
# ```
#
# The container should not be unarchived:
#
# ```ruby
# container type: :naked
# ```
#
# @api public # @api public
def container(**kwargs) def container(**kwargs)
set_unique_stanza(:container, kwargs.empty?) do set_unique_stanza(:container, kwargs.empty?) do
@ -229,6 +281,15 @@ module Cask
end end
end end
# Sets the cask's version.
#
# ### Example
#
# ```ruby
# version "1.88.1"
# ```
#
# @see DSL::Version
# @api public # @api public
def version(arg = nil) def version(arg = nil)
set_unique_stanza(:version, arg.nil?) do set_unique_stanza(:version, arg.nil?) do
@ -240,6 +301,23 @@ module Cask
end end
end end
# Sets the cask's download checksum.
#
# ### Example
#
# For universal or single-architecture downloads:
#
# ```ruby
# sha256 "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca"
# ```
#
# For architecture-dependent downloads:
#
# ```ruby
# sha256 arm: "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca",
# intel: "b3c1c2442480a0219b9e05cf91d03385858c20f04b764ec08a3fa83d1b27e7b2"
# ```
#
# @api public # @api public
def sha256(arg = nil, arm: nil, intel: nil) def sha256(arg = nil, arm: nil, intel: nil)
should_return = arg.nil? && arm.nil? && intel.nil? should_return = arg.nil? && arm.nil? && intel.nil?
@ -259,6 +337,14 @@ module Cask
end end
end end
# Sets the cask's architecture strings.
#
# ### Example
#
# ```ruby
# arch arm: "darwin-arm64", intel: "darwin"
# ```
#
# @api public # @api public
def arch(arm: nil, intel: nil) def arch(arm: nil, intel: nil)
should_return = arm.nil? && intel.nil? should_return = arm.nil? && intel.nil?
@ -270,7 +356,10 @@ module Cask
end end
end end
# `depends_on` uses a load method so that multiple stanzas can be merged. # Declare dependencies and requirements for a cask.
#
# NOTE: Multiple dependencies can be specified.
#
# @api public # @api public
def depends_on(**kwargs) def depends_on(**kwargs)
@depends_on ||= DSL::DependsOn.new @depends_on ||= DSL::DependsOn.new
@ -284,9 +373,11 @@ module Cask
@depends_on @depends_on
end end
# Declare conflicts that keep a cask from installing or working correctly.
#
# @api public # @api public
def conflicts_with(**kwargs) def conflicts_with(**kwargs)
# TODO: remove this constraint, and instead merge multiple conflicts_with stanzas # TODO: Remove this constraint and instead merge multiple `conflicts_with` stanzas
set_unique_stanza(:conflicts_with, kwargs.empty?) { DSL::ConflictsWith.new(**kwargs) } set_unique_stanza(:conflicts_with, kwargs.empty?) { DSL::ConflictsWith.new(**kwargs) }
end end
@ -298,6 +389,8 @@ module Cask
cask.caskroom_path cask.caskroom_path
end end
# The staged location for this cask, including version number.
#
# @api public # @api public
def staged_path def staged_path
return @staged_path if @staged_path return @staged_path if @staged_path
@ -306,6 +399,8 @@ module Cask
@staged_path = caskroom_path.join(cask_version.to_s) @staged_path = caskroom_path.join(cask_version.to_s)
end end
# Provide the user with cask-specific information at install time.
#
# @api public # @api public
def caveats(*strings, &block) def caveats(*strings, &block)
@caveats ||= DSL::Caveats.new(cask) @caveats ||= DSL::Caveats.new(cask)
@ -326,11 +421,15 @@ module Cask
@caveats&.discontinued? == true @caveats&.discontinued? == true
end end
# Asserts that the cask artifacts auto-update.
#
# @api public # @api public
def auto_updates(auto_updates = nil) def auto_updates(auto_updates = nil)
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates } set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
end end
# Automatically fetch the latest version of a cask from changelogs.
#
# @api public # @api public
def livecheck(&block) def livecheck(&block)
@livecheck ||= Livecheck.new(cask) @livecheck ||= Livecheck.new(cask)
@ -344,6 +443,10 @@ module Cask
@livecheck.instance_eval(&block) @livecheck.instance_eval(&block)
end end
# Declare that a cask is no longer functional or supported.
#
# NOTE: A warning will be shown when trying to install this cask.
#
# @api public # @api public
def deprecate!(date:, because:) def deprecate!(date:, because:)
@deprecation_date = Date.parse(date) @deprecation_date = Date.parse(date)
@ -353,6 +456,10 @@ module Cask
@deprecated = true @deprecated = true
end end
# Declare that a cask is no longer functional or supported.
#
# NOTE: An error will be thrown when trying to install this cask.
#
# @api public # @api public
def disable!(date:, because:) def disable!(date:, because:)
@disable_date = Date.parse(date) @disable_date = Date.parse(date)
@ -405,6 +512,8 @@ module Cask
true true
end end
# The directory `app`s are installed into.
#
# @api public # @api public
def appdir def appdir
return HOMEBREW_CASK_APPDIR_PLACEHOLDER if Cask.generating_hash? return HOMEBREW_CASK_APPDIR_PLACEHOLDER if Cask.generating_hash?

View File

@ -37,7 +37,6 @@ module Cask
private_class_method :caveat private_class_method :caveat
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
(@custom_caveats + @built_in_caveats.values).join("\n") (@custom_caveats + @built_in_caveats.values).join("\n")

View File

@ -27,7 +27,6 @@ module Cask
pairs.to_yaml pairs.to_yaml
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = pairs.inspect def to_s = pairs.inspect
end end

View File

@ -9,8 +9,8 @@ module Cask
class Postflight < Base class Postflight < Base
include Staged include Staged
def suppress_move_to_applications(_options = {}) def suppress_move_to_applications(**_options)
odisabled "Cask::DSL#suppress_move_to_applications" odisabled "`Cask::DSL::Postflight#suppress_move_to_applications`"
end end
end end
end end

View File

@ -94,70 +94,94 @@ module Cask
to_s == "latest" to_s == "latest"
end end
# The major version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def major def major
version { slice(MAJOR_MINOR_PATCH_REGEX, 1) } version { slice(MAJOR_MINOR_PATCH_REGEX, 1) }
end end
# The minor version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def minor def minor
version { slice(MAJOR_MINOR_PATCH_REGEX, 2) } version { slice(MAJOR_MINOR_PATCH_REGEX, 2) }
end end
# The patch version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def patch def patch
version { slice(MAJOR_MINOR_PATCH_REGEX, 3) } version { slice(MAJOR_MINOR_PATCH_REGEX, 3) }
end end
# The major and minor version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def major_minor def major_minor
version { [major, minor].reject(&:empty?).join(".") } version { [major, minor].reject(&:empty?).join(".") }
end end
# The major, minor and patch version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def major_minor_patch def major_minor_patch
version { [major, minor, patch].reject(&:empty?).join(".") } version { [major, minor, patch].reject(&:empty?).join(".") }
end end
# The minor and patch version.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def minor_patch def minor_patch
version { [minor, patch].reject(&:empty?).join(".") } version { [minor, patch].reject(&:empty?).join(".") }
end end
# The comma separated values of the version as array.
#
# @api public # @api public
sig { returns(T::Array[Version]) } # Only top-level T.self_type is supported https://sorbet.org/docs/self-type sig { returns(T::Array[Version]) } # Only top-level T.self_type is supported https://sorbet.org/docs/self-type
def csv def csv
split(",").map { self.class.new(_1) } split(",").map { self.class.new(_1) }
end end
# The version part before the first comma.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def before_comma def before_comma
version { split(",", 2).first } version { split(",", 2).first }
end end
# The version part after the first comma.
#
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def after_comma def after_comma
version { split(",", 2).second } version { split(",", 2).second }
end end
# The version without any dividers.
#
# @see DIVIDER_REGEX
# @api public # @api public
sig { returns(T.self_type) } sig { returns(T.self_type) }
def no_dividers def no_dividers
version { gsub(DIVIDER_REGEX, "") } version { gsub(DIVIDER_REGEX, "") }
end end
# The version with the given record separator removed from the end.
#
# @see String#chomp
# @api public # @api public
sig { params(separator: T.nilable(String)).returns(T.self_type) } sig { params(separator: String).returns(T.self_type) }
def chomp(separator = nil) def chomp(separator = T.unsafe(nil))
version { to_s.chomp(T.unsafe(separator)) } version { to_s.chomp(separator) }
end end
private private

View File

@ -13,7 +13,6 @@ module Cask
@errors = errors @errors = errors
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
<<~EOS <<~EOS
@ -41,7 +40,6 @@ module Cask
# Error when a cask is not installed. # Error when a cask is not installed.
class CaskNotInstalledError < AbstractCaskErrorWithToken class CaskNotInstalledError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' is not installed." "Cask '#{token}' is not installed."
@ -57,7 +55,6 @@ module Cask
@message = message @message = message
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' has been #{message}" "Cask '#{token}' has been #{message}"
@ -73,7 +70,6 @@ module Cask
@conflicting_cask = conflicting_cask @conflicting_cask = conflicting_cask
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' conflicts with '#{conflicting_cask}'." "Cask '#{token}' conflicts with '#{conflicting_cask}'."
@ -82,7 +78,6 @@ module Cask
# Error when a cask is not available. # Error when a cask is not available.
class CaskUnavailableError < AbstractCaskErrorWithToken class CaskUnavailableError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' is unavailable#{reason.empty? ? "." : ": #{reason}"}" "Cask '#{token}' is unavailable#{reason.empty? ? "." : ": #{reason}"}"
@ -91,7 +86,6 @@ module Cask
# Error when a cask is unreadable. # Error when a cask is unreadable.
class CaskUnreadableError < CaskUnavailableError class CaskUnreadableError < CaskUnavailableError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' is unreadable#{reason.empty? ? "." : ": #{reason}"}" "Cask '#{token}' is unreadable#{reason.empty? ? "." : ": #{reason}"}"
@ -107,7 +101,6 @@ module Cask
@tap = tap @tap = tap
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = super s = super
@ -142,7 +135,6 @@ module Cask
# Error when a cask already exists. # Error when a cask already exists.
class CaskAlreadyCreatedError < AbstractCaskErrorWithToken class CaskAlreadyCreatedError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
%Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew edit --cask #{token}")} to edit it.) %Q(Cask '#{token}' already exists. Run #{Formatter.identifier("brew edit --cask #{token}")} to edit it.)
@ -151,7 +143,6 @@ module Cask
# Error when there is a cyclic cask dependency. # Error when there is a cyclic cask dependency.
class CaskCyclicDependencyError < AbstractCaskErrorWithToken class CaskCyclicDependencyError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' includes cyclic dependencies on other Casks#{reason.empty? ? "." : ": #{reason}"}" "Cask '#{token}' includes cyclic dependencies on other Casks#{reason.empty? ? "." : ": #{reason}"}"
@ -160,7 +151,6 @@ module Cask
# Error when a cask depends on itself. # Error when a cask depends on itself.
class CaskSelfReferencingDependencyError < CaskCyclicDependencyError class CaskSelfReferencingDependencyError < CaskCyclicDependencyError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' depends on itself." "Cask '#{token}' depends on itself."
@ -169,7 +159,6 @@ module Cask
# Error when no cask is specified. # Error when no cask is specified.
class CaskUnspecifiedError < CaskError class CaskUnspecifiedError < CaskError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"This command requires a Cask token." "This command requires a Cask token."
@ -178,7 +167,6 @@ module Cask
# Error when a cask is invalid. # Error when a cask is invalid.
class CaskInvalidError < AbstractCaskErrorWithToken class CaskInvalidError < AbstractCaskErrorWithToken
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"Cask '#{token}' definition is invalid#{reason.empty? ? "." : ": #{reason}"}" "Cask '#{token}' definition is invalid#{reason.empty? ? "." : ": #{reason}"}"
@ -203,7 +191,6 @@ module Cask
@reason = reason @reason = reason
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = +"Failed to quarantine #{path}." s = +"Failed to quarantine #{path}."
@ -220,7 +207,6 @@ module Cask
# Error while propagating quarantine information to subdirectories. # Error while propagating quarantine information to subdirectories.
class CaskQuarantinePropagationError < CaskQuarantineError class CaskQuarantinePropagationError < CaskQuarantineError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = +"Failed to quarantine one or more files within #{path}." s = +"Failed to quarantine one or more files within #{path}."
@ -237,7 +223,6 @@ module Cask
# Error while removing quarantine information. # Error while removing quarantine information.
class CaskQuarantineReleaseError < CaskQuarantineError class CaskQuarantineReleaseError < CaskQuarantineError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = +"Failed to release #{path} from quarantine." s = +"Failed to release #{path} from quarantine."

View File

@ -17,22 +17,32 @@ module Cask
extend Forwardable extend Forwardable
def_delegators :uri, :path, :scheme, :to_s def_delegators :uri, :path, :scheme, :to_s
# @api public
sig { sig {
params( params(
uri: T.any(URI::Generic, String), uri: T.any(URI::Generic, String),
# @api public
verified: T.nilable(String), verified: T.nilable(String),
# @api public
using: T.any(Class, Symbol, NilClass), using: T.any(Class, Symbol, NilClass),
# @api public
tag: T.nilable(String), tag: T.nilable(String),
# @api public
branch: T.nilable(String), branch: T.nilable(String),
# @api public
revisions: T.nilable(T::Array[String]), revisions: T.nilable(T::Array[String]),
# @api public
revision: T.nilable(String), revision: T.nilable(String),
# @api public
trust_cert: T.nilable(T::Boolean), trust_cert: T.nilable(T::Boolean),
# @api public
cookies: T.nilable(T::Hash[String, String]), cookies: T.nilable(T::Hash[String, String]),
referer: T.nilable(T.any(URI::Generic, String)), referer: T.nilable(T.any(URI::Generic, String)),
# @api public
header: T.nilable(T.any(String, T::Array[String])), header: T.nilable(T.any(String, T::Array[String])),
user_agent: T.nilable(T.any(Symbol, String)), user_agent: T.nilable(T.any(Symbol, String)),
# @api public
data: T.nilable(T::Hash[String, String]), data: T.nilable(T::Hash[String, String]),
# @api public
only_path: T.nilable(String), only_path: T.nilable(String),
).void ).void
} }
@ -77,8 +87,19 @@ module Cask
end end
class BlockDSL class BlockDSL
# To access URL associated with page contents. # Allow accessing the URL associated with page contents.
module PageWithURL module PageWithURL
# Get the URL of the fetched page.
#
# ### Example
#
# ```ruby
# url "https://example.org/download" do |page|
# file = page[/href="([^"]+.dmg)"/, 1]
# URL.join(page.url, file)
# end
# ```
#
# @api public # @api public
sig { returns(URI::Generic) } sig { returns(URI::Generic) }
attr_accessor :url attr_accessor :url
@ -87,12 +108,12 @@ module Cask
sig { sig {
params( params(
uri: T.nilable(T.any(URI::Generic, String)), uri: T.nilable(T.any(URI::Generic, String)),
dsl: T.nilable(::Cask::DSL), dsl: ::Cask::DSL,
block: T.proc.params(arg0: T.all(String, PageWithURL)) block: T.proc.params(arg0: T.all(String, PageWithURL))
.returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash])), .returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash])),
).void ).void
} }
def initialize(uri, dsl: nil, &block) def initialize(uri, dsl:, &block)
@uri = uri @uri = uri
@dsl = dsl @dsl = dsl
@block = block @block = block
@ -114,6 +135,8 @@ module Cask
end end
end end
# Allows calling a nested `url` stanza in a `url do` block.
#
# @api public # @api public
sig { sig {
params( params(
@ -123,10 +146,12 @@ module Cask
).returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash])) ).returns(T.any(T.any(URI::Generic, String), [T.any(URI::Generic, String), Hash]))
} }
def url(uri, &block) def url(uri, &block)
self.class.new(uri, &block).call self.class.new(uri, dsl: @dsl, &block).call
end end
private :url private :url
# This allows calling DSL methods from inside a `url` block.
#
# @api public # @api public
def method_missing(method, *args, &block) def method_missing(method, *args, &block)
if @dsl.respond_to?(method) if @dsl.respond_to?(method)
@ -135,10 +160,12 @@ module Cask
super super
end end
end end
private :method_missing
def respond_to_missing?(method, include_all) def respond_to_missing?(method, include_all)
@dsl.respond_to?(method, include_all) || super @dsl.respond_to?(method, include_all) || super
end end
private :respond_to_missing?
end end
sig { sig {
@ -187,7 +214,7 @@ module Cask
super( super(
if block if block
LazyObject.new do LazyObject.new do
uri2, options = *BlockDSL.new(uri, dsl:, &block).call uri2, options = *BlockDSL.new(uri, dsl: T.must(dsl), &block).call
options ||= {} options ||= {}
DSL.new(uri2, **options) DSL.new(uri2, **options)
end end

View File

@ -68,9 +68,9 @@ module Cask
unless tried_permissions unless tried_permissions
print_stderr = Context.current.debug? || Context.current.verbose? print_stderr = Context.current.debug? || Context.current.verbose?
# TODO: Better handling for the case where path is a symlink. # TODO: Better handling for the case where path is a symlink.
# The -h and -R flags cannot be combined, and behavior is # The `-h` and `-R` flags cannot be combined and behavior is
# dependent on whether the file argument has a trailing # dependent on whether the file argument has a trailing
# slash. This should do the right thing, but is fragile. # slash. This should do the right thing, but is fragile.
command.run("/usr/bin/chflags", command.run("/usr/bin/chflags",
print_stderr:, print_stderr:,
args: command_args + ["--", "000", path]) args: command_args + ["--", "000", path])
@ -87,7 +87,7 @@ module Cask
unless tried_ownership unless tried_ownership
# in case of ownership problems # in case of ownership problems
# TODO: Further examine files to see if ownership is the problem # TODO: Further examine files to see if ownership is the problem
# before using sudo+chown # before using `sudo` and `chown`.
ohai "Using sudo to gain ownership of path '#{path}'" ohai "Using sudo to gain ownership of path '#{path}'"
command.run("/usr/sbin/chown", command.run("/usr/sbin/chown",
args: command_args + ["--", User.current, path], args: command_args + ["--", User.current, path],

View File

@ -756,7 +756,7 @@ class ReporterHub
def dump(auto_update: false) def dump(auto_update: false)
report_all = ENV["HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"].present? report_all = ENV["HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"].present?
if report_all && !Homebrew::EnvConfig.no_install_from_api? if report_all && !Homebrew::EnvConfig.no_install_from_api?
odisabled "HOMEBREW_UPDATE_REPORT_ALL_FORMULAE" odisabled "`HOMEBREW_UPDATE_REPORT_ALL_FORMULAE`"
opoo "This will not report all formulae because Homebrew cannot get this data from the API." opoo "This will not report all formulae because Homebrew cannot get this data from the API."
report_all = false report_all = false
end end

View File

@ -65,7 +65,6 @@ class CompilerFailure
type == compiler.type && version_matched type == compiler.type && version_matched
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{type} #{version}>" "#<#{self.class.name}: #{type} #{version}>"

View File

@ -22,7 +22,6 @@ class CxxStdlib
type.to_s.gsub(/cxx$/, "c++") type.to_s.gsub(/cxx$/, "c++")
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{compiler} #{type}>" "#<#{self.class.name}: #{compiler} #{type}>"

View File

@ -35,7 +35,6 @@ class Dependencies < SimpleDelegator
self.class.new(*__getobj__.reject { |dep| dep.uses_from_macos? && dep.use_macos_install? }) self.class.new(*__getobj__.reject { |dep| dep.uses_from_macos? && dep.use_macos_install? })
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{__getobj__}>" "#<#{self.class.name}: #{__getobj__}>"
@ -62,7 +61,6 @@ class Requirements < SimpleDelegator
self self
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: {#{__getobj__.to_a.join(", ")}}>" "#<#{self.class.name}: {#{__getobj__.to_a.join(", ")}}>"

View File

@ -4,6 +4,8 @@
require "dependable" require "dependable"
# A dependency on another Homebrew formula. # A dependency on another Homebrew formula.
#
# @api internal
class Dependency class Dependency
extend Forwardable extend Forwardable
include Dependable include Dependable
@ -94,11 +96,9 @@ class Dependency
false false
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = name def to_s = name
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{name.inspect} #{tags.inspect}>" "#<#{self.class.name}: #{name.inspect} #{tags.inspect}>"
@ -115,6 +115,8 @@ class Dependency
# the list. # the list.
# The default filter, which is applied when a block is not given, omits # The default filter, which is applied when a block is not given, omits
# optionals and recommends based on what the dependent has asked for # optionals and recommends based on what the dependent has asked for
#
# @api internal
def expand(dependent, deps = dependent.deps, cache_key: nil, &block) def expand(dependent, deps = dependent.deps, cache_key: nil, &block)
# Keep track dependencies to avoid infinite cyclic dependency recursion. # Keep track dependencies to avoid infinite cyclic dependency recursion.
@expand_stack ||= [] @expand_stack ||= []
@ -181,6 +183,8 @@ class Dependency
end end
# Keep a dependency, but prune its dependencies. # Keep a dependency, but prune its dependencies.
#
# @api internal
sig { void } sig { void }
def keep_but_prune_recursive_deps def keep_but_prune_recursive_deps
throw(:action, :keep_but_prune_recursive_deps) throw(:action, :keep_but_prune_recursive_deps)
@ -282,7 +286,6 @@ class UsesFromMacOSDependency < Dependency
self.class.new(formula.full_name.to_s, tags, bounds:) self.class.new(formula.full_name.to_s, tags, bounds:)
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{name.inspect} #{tags.inspect} #{bounds.inspect}>" "#<#{self.class.name}: #{name.inspect} #{tags.inspect} #{bounds.inspect}>"

View File

@ -98,7 +98,7 @@ module Homebrew
end end
odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil? odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil?
else else
# Search in the root directory of <repo> as well as recursively in all of its subdirectories # Search in the root directory of `repo` as well as recursively in all of its subdirectories.
files = Dir[repo/"{,**/}"].filter_map do |dir| files = Dir[repo/"{,**/}"].filter_map do |dir|
Pathname.glob("#{dir}/#{name}.rb").find(&:file?) Pathname.glob("#{dir}/#{name}.rb").find(&:file?)
end end

View File

@ -20,16 +20,18 @@ module Homebrew
def run def run
Homebrew.install_bundler_gems!(groups: ["doc"]) Homebrew.install_bundler_gems!(groups: ["doc"])
HOMEBREW_LIBRARY_PATH.cd do HOMEBREW_LIBRARY_PATH.cd do |dir|
no_api_args = if args.only_public? no_api_args = if args.only_public?
["--hide-api", "private", "--hide-api", "internal"] ["--hide-api", "private", "--hide-api", "internal"]
else else
[] []
end end
system "bundle", "exec", "yard", "doc", "--output", "doc", *no_api_args output_dir = dir/"doc"
exec_browser "file://#{HOMEBREW_LIBRARY_PATH}/doc/index.html" if args.open? safe_system "bundle", "exec", "yard", "doc", "--fail-on-warning", *no_api_args, "--output", output_dir
exec_browser "file://#{output_dir}/index.html" if args.open?
end end
end end
end end

View File

@ -77,11 +77,9 @@ class AbstractDownloadStrategy
end end
# Disable any output during downloading. # Disable any output during downloading.
#
# @deprecated
sig { void } sig { void }
def shutup! def shutup!
odeprecated "AbstractDownloadStrategy#shutup!", "AbstractDownloadStrategy#quiet!" odeprecated "`AbstractDownloadStrategy#shutup!`", "`AbstractDownloadStrategy#quiet!`"
quiet! quiet!
end end
@ -125,7 +123,6 @@ class AbstractDownloadStrategy
end end
private :chdir private :chdir
# @!attribute [r] source_modified_time
# Returns the most recent modified time for all files in the current working directory after stage. # Returns the most recent modified time for all files in the current working directory after stage.
# #
# @api public # @api public
@ -236,9 +233,7 @@ class VCSDownloadStrategy < AbstractDownloadStrategy
version.respond_to?(:head?) && version.head? version.respond_to?(:head?) && version.head?
end end
# @!attribute [r] last_commit # Return the most recent modified timestamp.
# Return last commit's unique identifier for the repository.
# Return most recent modified timestamp unless overridden.
# #
# @api public # @api public
sig { returns(String) } sig { returns(String) }
@ -733,7 +728,8 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
super super
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -747,7 +743,8 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
Time.parse time Time.parse time
end end
# @see VCSDownloadStrategy#last_commit # Return last commit's unique identifier for the repository.
#
# @api public # @api public
sig { returns(String) } sig { returns(String) }
def last_commit def last_commit
@ -847,7 +844,8 @@ class GitDownloadStrategy < VCSDownloadStrategy
@ref ||= "master" @ref ||= "master"
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -855,7 +853,8 @@ class GitDownloadStrategy < VCSDownloadStrategy
Time.parse(out) Time.parse(out)
end end
# @see VCSDownloadStrategy#last_commit # Return last commit's unique identifier for the repository.
#
# @api public # @api public
sig { returns(String) } sig { returns(String) }
def last_commit def last_commit
@ -1165,7 +1164,8 @@ class CVSDownloadStrategy < VCSDownloadStrategy
end end
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -1240,7 +1240,8 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^hg://}, "") @url = @url.sub(%r{^hg://}, "")
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -1250,7 +1251,8 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
Time.parse(out) Time.parse(out)
end end
# @see VCSDownloadStrategy#last_commit # Return last commit's unique identifier for the repository.
#
# @api public # @api public
sig { returns(String) } sig { returns(String) }
def last_commit def last_commit
@ -1327,7 +1329,8 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^bzr://}, "") @url = @url.sub(%r{^bzr://}, "")
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -1338,7 +1341,8 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
Time.parse(timestamp) Time.parse(timestamp)
end end
# @see VCSDownloadStrategy#last_commit # Return last commit's unique identifier for the repository.
#
# @api public # @api public
sig { returns(String) } sig { returns(String) }
def last_commit def last_commit
@ -1390,7 +1394,8 @@ class FossilDownloadStrategy < VCSDownloadStrategy
@url = @url.sub(%r{^fossil://}, "") @url = @url.sub(%r{^fossil://}, "")
end end
# @see AbstractDownloadStrategy#source_modified_time # Returns the most recent modified time for all files in the current working directory after stage.
#
# @api public # @api public
sig { returns(Time) } sig { returns(Time) }
def source_modified_time def source_modified_time
@ -1398,7 +1403,8 @@ class FossilDownloadStrategy < VCSDownloadStrategy
Time.parse(out[/^uuid: +\h+ (.+)$/, 1]) Time.parse(out[/^uuid: +\h+ (.+)$/, 1])
end end
# @see VCSDownloadStrategy#last_commit # Return last commit's unique identifier for the repository.
#
# @api public # @api public
sig { returns(String) } sig { returns(String) }
def last_commit def last_commit

View File

@ -16,7 +16,6 @@ class UsageError < RuntimeError
@reason = reason @reason = reason
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = "Invalid usage" s = "Invalid usage"
@ -109,7 +108,6 @@ class FormulaOrCaskUnavailableError < RuntimeError
"Did you mean #{similar_formula_names.to_sentence two_words_connector: " or ", last_word_connector: " or "}?" "Did you mean #{similar_formula_names.to_sentence two_words_connector: " or ", last_word_connector: " or "}?"
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = "No available formula or cask with the name \"#{name}\". #{did_you_mean}".strip s = "No available formula or cask with the name \"#{name}\". #{did_you_mean}".strip
@ -129,7 +127,6 @@ class TapFormulaOrCaskUnavailableError < FormulaOrCaskUnavailableError
@tap = tap @tap = tap
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = super s = super
@ -149,7 +146,6 @@ class FormulaUnavailableError < FormulaOrCaskUnavailableError
" (dependency of #{dependent})" if dependent && dependent != name " (dependency of #{dependent})" if dependent && dependent != name
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"No available formula with the name \"#{name}\"#{dependent_s}. #{did_you_mean}".strip "No available formula with the name \"#{name}\"#{dependent_s}. #{did_you_mean}".strip
@ -160,7 +156,6 @@ end
module FormulaClassUnavailableErrorModule module FormulaClassUnavailableErrorModule
attr_reader :path, :class_name, :class_list attr_reader :path, :class_name, :class_list
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = super s = super
@ -204,7 +199,6 @@ end
module FormulaUnreadableErrorModule module FormulaUnreadableErrorModule
attr_reader :formula_error attr_reader :formula_error
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"#{name}: " + formula_error.to_s "#{name}: " + formula_error.to_s
@ -233,7 +227,6 @@ class TapFormulaUnavailableError < FormulaUnavailableError
super "#{tap}/#{name}" super "#{tap}/#{name}"
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = super s = super
@ -572,7 +565,7 @@ class UnbottledError < RuntimeError
end end
end end
# Raised by Homebrew.install, Homebrew.reinstall, and Homebrew.upgrade # Raised by `Homebrew.install`, `Homebrew.reinstall` and `Homebrew.upgrade`
# if the user passes any flags/environment that would case a bottle-only # if the user passes any flags/environment that would case a bottle-only
# installation on a system without build tools to fail. # installation on a system without build tools to fail.
class BuildFlagsError < RuntimeError class BuildFlagsError < RuntimeError

View File

@ -1,5 +1,6 @@
# typed: strict # typed: strict
# @!visibility private
module EnvMethods module EnvMethods
include Kernel include Kernel
@ -39,6 +40,7 @@ module EnvActivation
include Superenv include Superenv
end end
# @!visibility private
class Sorbet class Sorbet
module Private module Private
module Static module Static

View File

@ -4,6 +4,7 @@ module SharedEnvExtension
include EnvMethods include EnvMethods
end end
# @!visibility private
class Sorbet class Sorbet
module Private module Private
module Static module Static

View File

@ -2,59 +2,72 @@
# frozen_string_literal: true # frozen_string_literal: true
class Array class Array
# Equal to <tt>self[1]</tt>. # Equal to `self[1]`.
# #
# %w( a b c d e ).second # => "b" # ### Example
#
# ```ruby
# %w( a b c d e ).second # => "b"
# ```
def second = self[1] def second = self[1]
# Equal to <tt>self[2]</tt>. # Equal to `self[2]`.
# #
# %w( a b c d e ).third # => "c" # ### Example
#
# ```ruby
# %w( a b c d e ).third # => "c"
# ```
def third = self[2] def third = self[2]
# Equal to <tt>self[3]</tt>. # Equal to `self[3]`.
# #
# %w( a b c d e ).fourth # => "d" # ### Example
#
# ```ruby
# %w( a b c d e ).fourth # => "d"
# ```
def fourth = self[3] def fourth = self[3]
# Equal to <tt>self[4]</tt>. # Equal to `self[4]`.
# #
# %w( a b c d e ).fifth # => "e" # ### Example
#
# ```ruby
# %w( a b c d e ).fifth # => "e"
# ```
def fifth = self[4] def fifth = self[4]
# Converts the array to a comma-separated sentence where the last element is # Converts the array to a comma-separated sentence where the last element is
# joined by the connector word. # joined by the connector word.
# #
# You can pass the following kwargs to change the default behavior: # ### Examples
# #
# * <tt>:words_connector</tt> - The sign or word used to join all but the last # ```ruby
# element in arrays with three or more elements (default: ", "). # [].to_sentence # => ""
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element # ['one'].to_sentence # => "one"
# in arrays with three or more elements (default: ", and "). # ['one', 'two'].to_sentence # => "one and two"
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements # ['one', 'two', 'three'].to_sentence # => "one, two and three"
# in arrays with two elements (default: " and "). # ['one', 'two'].to_sentence(two_words_connector: '-')
# # => "one-two"
# ```
# #
# ==== Examples # ```
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
# ```
# #
# [].to_sentence # => ""
# ['one'].to_sentence # => "one"
# ['one', 'two'].to_sentence # => "one and two"
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
# ['one', 'two'].to_sentence(two_words_connector: '-')
# # => "one-two"
#
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
# # => "one or two or at least three"
# @see https://github.com/rails/rails/blob/v7.0.4.2/activesupport/lib/active_support/core_ext/array/conversions.rb#L8-L84 # @see https://github.com/rails/rails/blob/v7.0.4.2/activesupport/lib/active_support/core_ext/array/conversions.rb#L8-L84
# ActiveSupport Array#to_sentence monkey-patch # ActiveSupport Array#to_sentence monkey-patch
# #
#
# Copyright (c) David Heinemeier Hansson # Copyright (c) David Heinemeier Hansson
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including # "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, # without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to # distribute, sublicense and/or sell copies of the Software and to
# permit persons to whom the Software is furnished to do so, subject to # permit persons to whom the Software is furnished to do so, subject to
# the following conditions: # the following conditions:
# #
@ -68,6 +81,14 @@ class Array
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#
# @param [String] words_connector The sign or word used to join all but the last
# element in arrays with three or more elements (default: `", "`).
# @param [String] last_word_connector The sign or word used to join the last element
# in arrays with three or more elements (default: `" and "`).
# @param [String] two_words_connector The sign or word used to join the elements
# in arrays with two elements (default: `" and "`).
sig { params(words_connector: String, two_words_connector: String, last_word_connector: String).returns(String) } sig { params(words_connector: String, two_words_connector: String, last_word_connector: String).returns(String) }
def to_sentence(words_connector: ", ", two_words_connector: " and ", last_word_connector: " and ") def to_sentence(words_connector: ", ", two_words_connector: " and ", last_word_connector: " and ")
case length case length

View File

@ -3,15 +3,20 @@
class Object class Object
# An object is blank if it's false, empty, or a whitespace string. # An object is blank if it's false, empty, or a whitespace string.
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
# #
# This simplifies # For example, `nil`, `''`, `' '`, `[]`, `{}` and `false` are all blank.
# #
# !address || address.empty? # ### Example
# #
# to # ```ruby
# !address || address.empty?
# ```
# #
# address.blank? # can be simplified to
#
# ```ruby
# address.blank?
# ```
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def blank? def blank?
respond_to?(:empty?) ? !!T.unsafe(self).empty? : false respond_to?(:empty?) ? !!T.unsafe(self).empty? : false
@ -21,20 +26,23 @@ class Object
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def present? = !blank? def present? = !blank?
# Returns the receiver if it's present otherwise returns +nil+. # Returns the receiver if it's present, otherwise returns `nil`.
# <tt>object.presence</tt> is equivalent to
# #
# object.present? ? object : nil # `object.presence` is equivalent to `object.present? ? object : nil`.
# #
# For example, something like # ### Example
# #
# state = params[:state] if params[:state].present? # ```ruby
# country = params[:country] if params[:country].present? # state = params[:state] if params[:state].present?
# region = state || country || 'US' # country = params[:country] if params[:country].present?
# region = state || country || 'US'
# ```
# #
# becomes # can be simplified to
# #
# region = params[:state].presence || params[:country].presence || 'US' # ```ruby
# region = params[:state].presence || params[:country].presence || 'US'
# ```
sig { returns(T.nilable(T.self_type)) } sig { returns(T.nilable(T.self_type)) }
def presence def presence
self if present? self if present?
@ -42,9 +50,11 @@ class Object
end end
class NilClass class NilClass
# +nil+ is blank: # `nil` is blank:
# #
# nil.blank? # => true # ```ruby
# nil.blank? # => true
# ```
sig { returns(TrueClass) } sig { returns(TrueClass) }
def blank? = true def blank? = true
@ -53,9 +63,11 @@ class NilClass
end end
class FalseClass class FalseClass
# +false+ is blank: # `false` is blank:
# #
# false.blank? # => true # ```ruby
# false.blank? # => true
# ```
sig { returns(TrueClass) } sig { returns(TrueClass) }
def blank? = true def blank? = true
@ -64,9 +76,11 @@ class FalseClass
end end
class TrueClass class TrueClass
# +true+ is not blank: # `true` is not blank:
# #
# true.blank? # => false # ```ruby
# true.blank? # => false
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def blank? = false def blank? = false
@ -77,11 +91,12 @@ end
class Array class Array
# An array is blank if it's empty: # An array is blank if it's empty:
# #
# [].blank? # => true # ```ruby
# [1,2,3].blank? # => false # [].blank? # => true
# # [1,2,3].blank? # => false
# @return [true, false] # ```
alias blank? empty? sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def present? = !empty? # :nodoc: def present? = !empty? # :nodoc:
@ -90,11 +105,13 @@ end
class Hash class Hash
# A hash is blank if it's empty: # A hash is blank if it's empty:
# #
# {}.blank? # => true
# { key: 'value' }.blank? # => false
# #
# @return [true, false] # ```ruby
alias blank? empty? # {}.blank? # => true
# { key: 'value' }.blank? # => false
# ```
sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def present? = !empty? # :nodoc: def present? = !empty? # :nodoc:
@ -103,9 +120,12 @@ end
class Symbol class Symbol
# A Symbol is blank if it's empty: # A Symbol is blank if it's empty:
# #
# :''.blank? # => true # ```ruby
# :symbol.blank? # => false # :''.blank? # => true
alias blank? empty? # :symbol.blank? # => false
# ```
sig { returns(T::Boolean) }
def blank? = empty?
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def present? = !empty? # :nodoc: def present? = !empty? # :nodoc:
@ -122,14 +142,18 @@ class String
# A string is blank if it's empty or contains whitespaces only: # A string is blank if it's empty or contains whitespaces only:
# #
# ''.blank? # => true # ```ruby
# ' '.blank? # => true # ''.blank? # => true
# "\t\n\r".blank? # => true # ' '.blank? # => true
# ' blah '.blank? # => false # "\t\n\r".blank? # => true
# ' blah '.blank? # => false
# ```
# #
# Unicode whitespace is supported: # Unicode whitespace is supported:
# #
# "\u00a0".blank? # => true # ```ruby
# "\u00a0".blank? # => true
# ```
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def blank? def blank?
# The regexp that matches blank strings is expensive. For the case of empty # The regexp that matches blank strings is expensive. For the case of empty
@ -150,8 +174,10 @@ end
class Numeric # :nodoc: class Numeric # :nodoc:
# No number is blank: # No number is blank:
# #
# 1.blank? # => false # ```ruby
# 0.blank? # => false # 1.blank? # => false
# 0.blank? # => false
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def blank? = false def blank? = false
@ -162,7 +188,9 @@ end
class Time # :nodoc: class Time # :nodoc:
# No Time is blank: # No Time is blank:
# #
# Time.now.blank? # => false # ```ruby
# Time.now.blank? # => false
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def blank? = false def blank? = false

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true # frozen_string_literal: true
module Enumerable module Enumerable
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the # The negative of the {Enumerable#include?}. Returns `true` if the
# collection does not include the object. # collection does not include the object.
sig { params(object: T.untyped).returns(T::Boolean) } sig { params(object: T.untyped).returns(T::Boolean) }
def exclude?(object) = !include?(object) def exclude?(object) = !include?(object)
@ -10,21 +10,29 @@ module Enumerable
# Returns a new +Array+ without the blank items. # Returns a new +Array+ without the blank items.
# Uses Object#blank? for determining if an item is blank. # Uses Object#blank? for determining if an item is blank.
# #
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank # ### Examples
# # => [1, 2, true]
# #
# Set.new([nil, "", 1, false]).compact_blank # ```
# # => [1] # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
# # => [1, 2, true]
# ```
# #
# When called on a +Hash+, returns a new +Hash+ without the blank values. # ```ruby
# Set.new([nil, "", 1, false]).compact_blank
# # => [1]
# ```
# #
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank # When called on a {Hash}, returns a new {Hash} without the blank values.
# # => { b: 1, f: true } #
# ```ruby
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# # => { b: 1, f: true }
# ```
sig { returns(T.self_type) } sig { returns(T.self_type) }
def compact_blank = T.unsafe(self).reject(&:blank?) def compact_blank = T.unsafe(self).reject(&:blank?)
end end
class Hash class Hash
# Hash#reject has its own definition, so this needs one too. # {Hash#reject} has its own definition, so this needs one too.
def compact_blank = reject { |_k, v| T.unsafe(v).blank? } def compact_blank = reject { |_k, v| T.unsafe(v).blank? }
end end

View File

@ -2,25 +2,31 @@
# frozen_string_literal: true # frozen_string_literal: true
class Hash class Hash
# Returns a new hash with +self+ and +other_hash+ merged recursively. # Returns a new hash with `self` and `other_hash` merged recursively.
# #
# h1 = { a: true, b: { c: [1, 2, 3] } } # ### Examples
# h2 = { a: false, b: { x: [3, 4, 5] } }
# #
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } } # ```ruby
# h1 = { a: true, b: { c: [1, 2, 3] } }
# h2 = { a: false, b: { x: [3, 4, 5] } }
#
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
# ```
# #
# Like with Hash#merge in the standard library, a block can be provided # Like with Hash#merge in the standard library, a block can be provided
# to merge values: # to merge values:
# #
# h1 = { a: 100, b: 200, c: { c1: 100 } } # ```ruby
# h2 = { b: 250, c: { c1: 200 } } # h1 = { a: 100, b: 200, c: { c1: 100 } }
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val } # h2 = { b: 250, c: { c1: 200 } }
# # => { a: 100, b: 450, c: { c1: 300 } } # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# # => { a: 100, b: 450, c: { c1: 300 } }
# ```
def deep_merge(other_hash, &block) def deep_merge(other_hash, &block)
dup.deep_merge!(other_hash, &block) dup.deep_merge!(other_hash, &block)
end end
# Same as +deep_merge+, but modifies +self+. # Same as {#deep_merge}, but modifies `self`.
def deep_merge!(other_hash, &block) def deep_merge!(other_hash, &block)
merge!(other_hash) do |key, this_val, other_val| merge!(other_hash) do |key, this_val, other_val|
if T.unsafe(this_val).is_a?(Hash) && other_val.is_a?(Hash) if T.unsafe(this_val).is_a?(Hash) && other_val.is_a?(Hash)

View File

@ -6,11 +6,14 @@ class Hash
# This includes the values from the root hash and from all # This includes the values from the root hash and from all
# nested hashes and arrays. # nested hashes and arrays.
# #
# @example # ### Example
# hash = { person: { name: 'Rob', age: '28' } }
# #
# hash.deep_transform_values{ |value| value.to_s.upcase } # ```ruby
# # => {person: {name: "ROB", age: "28"}} # hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_transform_values{ |value| value.to_s.upcase }
# # => {person: {name: "ROB", age: "28"}}
# ```
def deep_transform_values(&block) = _deep_transform_values_in_object(self, &block) def deep_transform_values(&block) = _deep_transform_values_in_object(self, &block)
# Destructively converts all values by using the block operation. # Destructively converts all values by using the block operation.

View File

@ -5,14 +5,18 @@ class Hash
# Validates all keys in a hash match `*valid_keys`, raising # Validates all keys in a hash match `*valid_keys`, raising
# `ArgumentError` on a mismatch. # `ArgumentError` on a mismatch.
# #
# Note that keys are treated differently than HashWithIndifferentAccess, # Note that keys are treated differently than `HashWithIndifferentAccess`,
# meaning that string and symbol keys will not match. # meaning that string and symbol keys will not match.
# #
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # ### Example#
# # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age" #
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # ```ruby
# # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'" # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age)
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing # # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age')
# # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
# ```
sig { params(valid_keys: T.untyped).void } sig { params(valid_keys: T.untyped).void }
def assert_valid_keys(*valid_keys) def assert_valid_keys(*valid_keys)
valid_keys.flatten! valid_keys.flatten!
@ -28,10 +32,14 @@ class Hash
# This includes the keys from the root hash and from all # This includes the keys from the root hash and from all
# nested hashes and arrays. # nested hashes and arrays.
# #
# hash = { person: { name: 'Rob', age: '28' } } # ### Example
# #
# hash.deep_transform_keys{ |key| key.to_s.upcase } # ```ruby
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}} # hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_transform_keys{ |key| key.to_s.upcase }
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
# ```
def deep_transform_keys(&block) = _deep_transform_keys_in_object(self, &block) def deep_transform_keys(&block) = _deep_transform_keys_in_object(self, &block)
# Destructively converts all keys by using the block operation. # Destructively converts all keys by using the block operation.
@ -43,10 +51,14 @@ class Hash
# This includes the keys from the root hash and from all # This includes the keys from the root hash and from all
# nested hashes and arrays. # nested hashes and arrays.
# #
# hash = { person: { name: 'Rob', age: '28' } } # ### Example
# #
# hash.deep_stringify_keys # ```ruby
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}} # hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_stringify_keys
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
# ```
def deep_stringify_keys = T.unsafe(self).deep_transform_keys(&:to_s) def deep_stringify_keys = T.unsafe(self).deep_transform_keys(&:to_s)
# Destructively converts all keys to strings. # Destructively converts all keys to strings.
@ -55,13 +67,17 @@ class Hash
def deep_stringify_keys! = T.unsafe(self).deep_transform_keys!(&:to_s) def deep_stringify_keys! = T.unsafe(self).deep_transform_keys!(&:to_s)
# Returns a new hash with all keys converted to symbols, as long as # Returns a new hash with all keys converted to symbols, as long as
# they respond to +to_sym+. This includes the keys from the root hash # they respond to `to_sym`. This includes the keys from the root hash
# and from all nested hashes and arrays. # and from all nested hashes and arrays.
# #
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } } # ### Example
# #
# hash.deep_symbolize_keys # ```ruby
# # => {:person=>{:name=>"Rob", :age=>"28"}} # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
#
# hash.deep_symbolize_keys
# # => {:person=>{:name=>"Rob", :age=>"28"}}
# ```
def deep_symbolize_keys def deep_symbolize_keys
deep_transform_keys do |key| deep_transform_keys do |key|
T.unsafe(key).to_sym T.unsafe(key).to_sym
@ -71,7 +87,7 @@ class Hash
end end
# Destructively converts all keys to symbols, as long as they respond # Destructively converts all keys to symbols, as long as they respond
# to +to_sym+. This includes the keys from the root hash and from all # to `to_sym`. This includes the keys from the root hash and from all
# nested hashes and arrays. # nested hashes and arrays.
def deep_symbolize_keys! def deep_symbolize_keys!
deep_transform_keys! do |key| deep_transform_keys! do |key|
@ -102,7 +118,7 @@ class Hash
def _deep_transform_keys_in_object!(object, &block) def _deep_transform_keys_in_object!(object, &block)
case object case object
when Hash when Hash
# We can't use `each_key` here because we're updating the hash in-place # We can't use `each_key` here because we're updating the hash in-place.
object.keys.each do |key| # rubocop:disable Style/HashEachMethods object.keys.each do |key| # rubocop:disable Style/HashEachMethods
value = object.delete(key) value = object.delete(key)
object[yield(key)] = _deep_transform_keys_in_object!(value, &block) object[yield(key)] = _deep_transform_keys_in_object!(value, &block)

View File

@ -493,14 +493,20 @@ module Kernel
end end
# Calls the given block with the passed environment variables # Calls the given block with the passed environment variables
# added to ENV, then restores ENV afterwards. # added to `ENV`, then restores `ENV` afterwards.
# <pre>with_env(PATH: "/bin") do #
# system "echo $PATH" # NOTE: This method is **not** thread-safe other threads
# end</pre> # which happen to be scheduled during the block will also
# see these environment variables.
#
# ### Example
#
# ```ruby
# with_env(PATH: "/bin") do
# system "echo $PATH"
# end
# ```
# #
# @note This method is *not* thread-safe - other threads
# which happen to be scheduled during the block will also
# see these environment variables.
# @api public # @api public
def with_env(hash) def with_env(hash)
old_values = {} old_values = {}

View File

@ -1,24 +1,30 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
#--
# Most objects are cloneable, but not all. For example you can't dup methods: # Most objects are cloneable, but not all. For example you can't dup methods:
# #
# method(:puts).dup # => TypeError: allocator undefined for Method # ```ruby
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```
# #
# Classes may signal their instances are not duplicable removing +dup+/+clone+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
# or raising exceptions from them. So, to dup an arbitrary object you normally # or raising exceptions from them. So, to dup an arbitrary object you normally
# use an optimistic approach and are ready to catch an exception, say: # use an optimistic approach and are ready to catch an exception, say:
# #
# arbitrary_object.dup rescue object # ```ruby
# arbitrary_object.dup rescue object
# ```
# #
# Rails dups objects in a few critical spots where they are not that arbitrary. # Rails dups objects in a few critical spots where they are not that arbitrary.
# That rescue is very expensive (like 40 times slower than a predicate), and it # That `rescue` is very expensive (like 40 times slower than a predicate) and it
# is often triggered. # is often triggered.
# #
# That's why we hardcode the following cases and check duplicable? instead of # That's why we hardcode the following cases and check duplicable? instead of
# using that rescue idiom. # using that rescue idiom.
#++ # rubocop:disable Layout/EmptyLines
# rubocop:enable Layout/EmptyLines
class Object class Object
# Can you safely dup this object? # Can you safely dup this object?
# #
@ -31,8 +37,10 @@ end
class Method class Method
# Methods are not duplicable: # Methods are not duplicable:
# #
# method(:puts).duplicable? # => false # ```ruby
# method(:puts).dup # => TypeError: allocator undefined for Method # method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def duplicable? = false def duplicable? = false
end end
@ -40,8 +48,10 @@ end
class UnboundMethod class UnboundMethod
# Unbound methods are not duplicable: # Unbound methods are not duplicable:
# #
# method(:puts).unbind.duplicable? # => false # ```ruby
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod # method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def duplicable? = false def duplicable? = false
end end
@ -51,7 +61,9 @@ require "singleton"
module Singleton module Singleton
# Singleton instances are not duplicable: # Singleton instances are not duplicable:
# #
# Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton # ```ruby
# Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
# ```
sig { returns(FalseClass) } sig { returns(FalseClass) }
def duplicable? = false def duplicable? = false
end end

View File

@ -16,7 +16,7 @@ module Homebrew
return unless @args.cask? return unless @args.cask?
# NOTE: We don't raise an error here because we don't want # NOTE: We don't raise an error here because we don't want
# to print the help page or a stack trace. # to print the help page or a stack trace.
odie "Invalid `--cask` usage: Casks do not work on Linux" odie "Invalid `--cask` usage: Casks do not work on Linux"
end end
end end

View File

@ -27,12 +27,11 @@ module Stdenv
append "LDFLAGS", "-Wl,-headerpad_max_install_names" append "LDFLAGS", "-Wl,-headerpad_max_install_names"
# sed is strict, and errors out when it encounters files with # `sed` is strict and errors out when it encounters files with mixed character sets.
# mixed character sets
delete("LC_ALL") delete("LC_ALL")
self["LC_CTYPE"] = "C" self["LC_CTYPE"] = "C"
# Add lib and include etc. from the current macosxsdk to compiler flags: # Add `lib` and `include` etc. from the current `macosxsdk` to compiler flags:
macosxsdk(formula: @formula, testing_formula:) macosxsdk(formula: @formula, testing_formula:)
return unless MacOS::Xcode.without_clt? return unless MacOS::Xcode.without_clt?
@ -42,8 +41,8 @@ module Stdenv
end end
def remove_macosxsdk(version = nil) def remove_macosxsdk(version = nil)
# Clear all lib and include dirs from CFLAGS, CPPFLAGS, LDFLAGS that were # Clear all `lib` and `include` dirs from `CFLAGS`, `CPPFLAGS`, `LDFLAGS` that were
# previously added by macosxsdk # previously added by `macosxsdk`.
remove_from_cflags(/ ?-mmacosx-version-min=\d+\.\d+/) remove_from_cflags(/ ?-mmacosx-version-min=\d+\.\d+/)
delete("CPATH") delete("CPATH")
remove "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib" remove "LDFLAGS", "-L#{HOMEBREW_PREFIX}/lib"
@ -58,14 +57,14 @@ module Stdenv
if HOMEBREW_PREFIX.to_s == "/usr/local" if HOMEBREW_PREFIX.to_s == "/usr/local"
delete("CMAKE_PREFIX_PATH") delete("CMAKE_PREFIX_PATH")
else else
# It was set in setup_build_environment, so we have to restore it here. # It was set in `setup_build_environment`, so we have to restore it here.
self["CMAKE_PREFIX_PATH"] = HOMEBREW_PREFIX.to_s self["CMAKE_PREFIX_PATH"] = HOMEBREW_PREFIX.to_s
end end
remove "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks" remove "CMAKE_FRAMEWORK_PATH", "#{sdk}/System/Library/Frameworks"
end end
def macosxsdk(version = nil, formula: nil, testing_formula: false) def macosxsdk(version = nil, formula: nil, testing_formula: false)
# Sets all needed lib and include dirs to CFLAGS, CPPFLAGS, LDFLAGS. # Sets all needed `lib` and `include` dirs to `CFLAGS`, `CPPFLAGS`, `LDFLAGS`.
remove_macosxsdk remove_macosxsdk
min_version = version || MacOS.version min_version = version || MacOS.version
append_to_cflags("-mmacosx-version-min=#{min_version}") append_to_cflags("-mmacosx-version-min=#{min_version}")

View File

@ -83,8 +83,8 @@ module Hardware
sysctl_bool("hw.optional.sse4_2") sysctl_bool("hw.optional.sse4_2")
end end
# NOTE: this is more reliable than checking uname. # NOTE: This is more reliable than checking `uname`. `sysctl` returns
# `sysctl` returns the right answer even when running in Rosetta 2. # the right answer even when running in Rosetta 2.
def physical_cpu_arm64? def physical_cpu_arm64?
sysctl_bool("hw.optional.arm64") sysctl_bool("hw.optional.arm64")
end end

View File

@ -126,10 +126,11 @@ class Pathname
mkpath mkpath
# Use FileUtils.mv over File.rename to handle filesystem boundaries. If src # Use `FileUtils.mv` over `File.rename` to handle filesystem boundaries. If `src`
# is a symlink, and its target is moved first, FileUtils.mv will fail: # is a symlink and its target is moved first, `FileUtils.mv` will fail
# https://bugs.ruby-lang.org/issues/7707 # (https://bugs.ruby-lang.org/issues/7707).
# In that case, use the system "mv" command. #
# In that case, use the system `mv` command.
if src.symlink? if src.symlink?
raise unless Kernel.system "mv", src.to_s, dst raise unless Kernel.system "mv", src.to_s, dst
else else
@ -178,7 +179,9 @@ class Pathname
T.unsafe(self).open("a", **open_args) { |f| f.puts(content) } T.unsafe(self).open("a", **open_args) { |f| f.puts(content) }
end end
# @note This always overwrites. # Write to a file atomically.
#
# NOTE: This always overwrites.
# #
# @api public # @api public
sig { params(content: String).void } sig { params(content: String).void }
@ -201,6 +204,7 @@ class Pathname
# Changing file ownership failed, moving on. # Changing file ownership failed, moving on.
nil nil
end end
begin begin
# This operation will affect filesystem ACL's # This operation will affect filesystem ACL's
chmod(old_stat.mode) chmod(old_stat.mode)

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,6 @@ class KegOnlyReason
!by_macos? !by_macos?
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
return @explanation unless @explanation.empty? return @explanation unless @explanation.empty?

View File

@ -25,7 +25,6 @@ module Homebrew
@text.include? string @text.include? string
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
@text @text

View File

@ -106,7 +106,6 @@ class GitRepository
popen_git("log", "-1", "--pretty=%B", commit, "--", safe:, err: :out)&.strip popen_git("log", "-1", "--pretty=%B", commit, "--", safe:, err: :out)&.strip
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = pathname.to_s def to_s = pathname.to_s

View File

@ -48,7 +48,6 @@ class Keg
EOS EOS
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = [] s = []
@ -67,7 +66,6 @@ class Keg
# Error for when a directory is not writable. # Error for when a directory is not writable.
class DirectoryNotWritableError < LinkError class DirectoryNotWritableError < LinkError
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
<<~EOS <<~EOS
@ -176,11 +174,9 @@ class Keg
path.parent path.parent
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = path.to_s def to_s = path.to_s
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}:#{path}>" "#<#{self.class.name}:#{path}>"

View File

@ -14,7 +14,7 @@ module Language
# e.g. `resource "github.com/foo/bar"`. # e.g. `resource "github.com/foo/bar"`.
sig { params(resources: T::Array[Resource], target: T.any(String, Pathname)).void } sig { params(resources: T::Array[Resource], target: T.any(String, Pathname)).void }
def self.stage_deps(resources, target) def self.stage_deps(resources, target)
# odeprecated "Language::Go::stage_deps", "Go modules" # odeprecated "`Language::Go.stage_deps`", "Go modules"
if resources.empty? if resources.empty?
if Homebrew::EnvConfig.developer? if Homebrew::EnvConfig.developer?
odie "Tried to stage empty Language::Go resources array" odie "Tried to stage empty Language::Go resources array"

View File

@ -150,8 +150,8 @@ module Language
# #
# @param venv_root [Pathname, String] the path to the root of the virtualenv # @param venv_root [Pathname, String] the path to the root of the virtualenv
# (often `libexec/"venv"`) # (often `libexec/"venv"`)
# @param python [String, Pathname] which interpreter to use (e.g. "python3" # @param python [String, Pathname] which interpreter to use (e.g. `"python3"`
# or "python3.x") # or `"python3.x"`)
# @param formula [Formula] the active {Formula} # @param formula [Formula] the active {Formula}
# @return [Virtualenv] a {Virtualenv} instance # @return [Virtualenv] a {Virtualenv} instance
sig { sig {
@ -166,7 +166,7 @@ module Language
def virtualenv_create(venv_root, python = "python", formula = T.cast(self, Formula), def virtualenv_create(venv_root, python = "python", formula = T.cast(self, Formula),
system_site_packages: true, without_pip: true) system_site_packages: true, without_pip: true)
# Limit deprecation to 3.12+ for now (or if we can't determine the version). # Limit deprecation to 3.12+ for now (or if we can't determine the version).
# Some used this argument for setuptools, which we no longer bundle since 3.12. # Some used this argument for `setuptools`, which we no longer bundle since 3.12.
unless without_pip unless without_pip
python_version = Language::Python.major_minor_version(python) python_version = Language::Python.major_minor_version(python)
if python_version.nil? || python_version.null? || python_version >= "3.12" if python_version.nil? || python_version.null? || python_version >= "3.12"
@ -198,8 +198,8 @@ module Language
# Returns true if a formula option for the specified python is currently # Returns true if a formula option for the specified python is currently
# active or if the specified python is required by the formula. Valid # active or if the specified python is required by the formula. Valid
# inputs are "python", "python2", and :python3. Note that # inputs are `"python"`, `"python2"` and `:python3`. Note that
# "with-python", "without-python", "with-python@2", and "without-python@2" # `"with-python"`, `"without-python"`, `"with-python@2"` and `"without-python@2"`
# formula options are handled correctly even if not associated with any # formula options are handled correctly even if not associated with any
# corresponding depends_on statement. # corresponding depends_on statement.
sig { params(python: String).returns(T::Boolean) } sig { params(python: String).returns(T::Boolean) }

View File

@ -16,15 +16,20 @@ class LazyObject < Delegator
@__delegate__ = @__callable__.call @__delegate__ = @__callable__.call
# rubocop:enable Naming/MemoizedInstanceVariableName # rubocop:enable Naming/MemoizedInstanceVariableName
end end
private :__getobj__
def __setobj__(callable) def __setobj__(callable)
@__callable__ = callable @__callable__ = callable
end end
private :__setobj__
# Forward to the inner object to make lazy objects type-checkable. # Forward to the inner object to make lazy objects type-checkable.
#
# @!visibility private
def is_a?(klass) def is_a?(klass)
# see https://sorbet.org/docs/faq#how-can-i-fix-type-errors-that-arise-from-super # see https://sorbet.org/docs/faq#how-can-i-fix-type-errors-that-arise-from-super
T.bind(self, T.untyped) T.bind(self, T.untyped)
__getobj__.is_a?(klass) || super __getobj__.is_a?(klass) || super
end end
end end

View File

@ -39,9 +39,11 @@ module Homebrew
) do ) do
extend Forwardable extend Forwardable
# @!attribute [r] version
# @api public # @api public
delegate version: :bundle_version delegate version: :bundle_version
# @!attribute [r] short_version
# @api public # @api public
delegate short_version: :bundle_version delegate short_version: :bundle_version
end end

View File

@ -54,12 +54,15 @@ module Homebrew
) do ) do
extend Forwardable extend Forwardable
# @!attribute [r] version
# @api public # @api public
delegate version: :bundle_version delegate version: :bundle_version
# @!attribute [r] short_version
# @api public # @api public
delegate short_version: :bundle_version delegate short_version: :bundle_version
# @!attribute [r] nice_version
# @api public # @api public
delegate nice_version: :bundle_version delegate nice_version: :bundle_version
end end

View File

@ -106,7 +106,6 @@ class Locale
locale_groups.find { |locales| locales.any? { |locale| include?(locale) } } locale_groups.find { |locales| locales.any? { |locale| include?(locale) } }
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
[@language, @script, @region].compact.join("-") [@language, @script, @region].compact.join("-")

View File

@ -132,6 +132,7 @@ class MacOSVersion < Version
alias requires_popcnt? requires_nehalem_cpu? alias requires_popcnt? requires_nehalem_cpu?
# Represents the absence of a version. # Represents the absence of a version.
#
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`. # NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
NULL = MacOSVersion.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze NULL = MacOSVersion.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze
end end
@ -141,7 +142,7 @@ require "lazy_object"
module MacOSVersionErrorCompat module MacOSVersionErrorCompat
def const_missing(name) def const_missing(name)
if name == :MacOSVersionError if name == :MacOSVersionError
odisabled "MacOSVersionError", "MacOSVersion::Error" odisabled "`MacOSVersionError`", "`MacOSVersion::Error`"
return MacOSVersion::Error return MacOSVersion::Error
end end
@ -158,7 +159,7 @@ end
module MacOSVersions module MacOSVersions
SYMBOLS = LazyObject.new do # rubocop:disable Style/MutableConstant SYMBOLS = LazyObject.new do # rubocop:disable Style/MutableConstant
odisabled "MacOSVersions::SYMBOLS", "MacOSVersion::SYMBOLS" odisabled "`MacOSVersions::SYMBOLS`", "`MacOSVersion::SYMBOLS`"
MacOSVersion::SYMBOLS MacOSVersion::SYMBOLS
end end
end end
@ -167,7 +168,7 @@ module OS
module Mac module Mac
# TODO: Replace `::Version` with `Version` when this is removed. # TODO: Replace `::Version` with `Version` when this is removed.
Version = LazyObject.new do # rubocop:disable Style/MutableConstant Version = LazyObject.new do # rubocop:disable Style/MutableConstant
odisabled "OS::Mac::Version", "MacOSVersion" odisabled "`OS::Mac::Version`", "`MacOSVersion`"
MacOSVersion MacOSVersion
end end
end end

View File

@ -287,17 +287,18 @@ class Migrator
def repin def repin
return unless pinned? return unless pinned?
# old_pin_record is a relative symlink and when we try to to read it # `old_pin_record` is a relative symlink and when we try to to read it
# from <dir> we actually try to find file # from <dir> we actually try to find file
# <dir>/../<...>/../Cellar/name/version. # <dir>/../<...>/../Cellar/name/version.
# To repin formula we need to update the link thus that it points to # To repin formula we need to update the link thus that it points to
# the right directory. # the right directory.
# NOTE: old_pin_record.realpath.sub(oldname, newname) is unacceptable #
# here, because it resolves every symlink for old_pin_record and then # NOTE: `old_pin_record.realpath.sub(oldname, newname)` is unacceptable
# here, because it resolves every symlink for `old_pin_record` and then
# substitutes oldname with newname. It breaks things like # substitutes oldname with newname. It breaks things like
# Pathname#make_relative_symlink, where Pathname#relative_path_from # `Pathname#make_relative_symlink`, where `Pathname#relative_path_from`
# is used to find relative path from source to destination parent and # is used to find the relative path from source to destination parent
# it assumes no symlinks. # and it assumes no symlinks.
src_oldname = (old_pin_record.dirname/old_pin_link_record).expand_path src_oldname = (old_pin_record.dirname/old_pin_link_record).expand_path
new_pin_record.make_relative_symlink(src_oldname.sub(oldname, newname)) new_pin_record.make_relative_symlink(src_oldname.sub(oldname, newname))
old_pin_record.delete old_pin_record.delete

View File

@ -38,7 +38,6 @@ class Mktemp
@quiet = true @quiet = true
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"[Mktemp: #{tmpdir} retain=#{@retain} quiet=#{@quiet}]" "[Mktemp: #{tmpdir} retain=#{@retain} quiet=#{@quiet}]"

View File

@ -11,7 +11,6 @@ class Option
@description = description @description = description
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = flag def to_s = flag
@ -30,7 +29,6 @@ class Option
name.hash name.hash
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{flag.inspect}>" "#<#{self.class.name}: #{flag.inspect}>"
@ -134,13 +132,11 @@ class Options
alias to_ary to_a alias to_ary to_a
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
@options.map(&:to_s).join(" ") @options.map(&:to_s).join(" ")
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{to_a.inspect}>" "#<#{self.class.name}: #{to_a.inspect}>"

View File

@ -51,7 +51,6 @@ class EmbeddedPatch
Utils.safe_popen_write("patch", *args) { |p| p.write(data) } Utils.safe_popen_write("patch", *args) { |p| p.write(data) }
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{strip.inspect}>" "#<#{self.class.name}: #{strip.inspect}>"
@ -151,7 +150,6 @@ class ExternalPatch
raise BuildError.new(f, cmd, args, ENV.to_hash) raise BuildError.new(f, cmd, args, ENV.to_hash)
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{strip.inspect} #{url.inspect}>" "#<#{self.class.name}: #{strip.inspect} #{url.inspect}>"

View File

@ -39,7 +39,6 @@ class PkgVersion
end end
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = to_str def to_s = to_str

View File

@ -140,7 +140,6 @@ class Requirement
[self.class, name, tags].hash [self.class, name, tags].hash
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: #{tags.inspect}>" "#<#{self.class.name}: #{tags.inspect}>"

View File

@ -27,7 +27,6 @@ class ArchRequirement < Requirement
"The #{@arch} architecture is required for this software." "The #{@arch} architecture is required for this software."
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: arch=#{@arch.to_s.inspect} #{tags.inspect}>" "#<#{self.class.name}: arch=#{@arch.to_s.inspect} #{tags.inspect}>"

View File

@ -96,7 +96,6 @@ class MacOSRequirement < Requirement
[super, comparator, version].hash [super, comparator, version].hash
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: version#{@comparator}#{@version.to_s.inspect} #{tags.inspect}>" "#<#{self.class.name}: version#{@comparator}#{@version.to_s.inspect} #{tags.inspect}>"

View File

@ -48,7 +48,6 @@ class XcodeRequirement < Requirement
end end
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def inspect def inspect
"#<#{self.class.name}: version>=#{@version.inspect} #{tags.inspect}>" "#<#{self.class.name}: version>=#{@version.inspect} #{tags.inspect}>"

View File

@ -146,17 +146,22 @@ class Resource < Downloadable
super(verify_download_integrity:) super(verify_download_integrity:)
end end
# @!attribute [w] livecheck
# {Livecheck} can be used to check for newer versions of the software. # {Livecheck} can be used to check for newer versions of the software.
# This method evaluates the DSL specified in the livecheck block of the # This method evaluates the DSL specified in the livecheck block of the
# {Resource} (if it exists) and sets the instance variables of a {Livecheck} # {Resource} (if it exists) and sets the instance variables of a {Livecheck}
# object accordingly. This is used by `brew livecheck` to check for newer # object accordingly. This is used by `brew livecheck` to check for newer
# versions of the software. # versions of the software.
# #
# <pre>livecheck do # ### Example
#
# ```ruby
# livecheck do
# url "https://example.com/foo/releases" # url "https://example.com/foo/releases"
# regex /foo-(\d+(?:\.\d+)+)\.tar/ # regex /foo-(\d+(?:\.\d+)+)\.tar/
# end</pre> # end
# ```
#
# @!attribute [w] livecheck
def livecheck(&block) def livecheck(&block)
return @livecheck unless block return @livecheck unless block
@ -302,7 +307,6 @@ class ResourceStageContext
@staging = staging @staging = staging
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"<#{self.class}: resource=#{resource} staging=#{staging}>" "<#{self.class}: resource=#{resource} staging=#{staging}>"

View File

@ -4,27 +4,26 @@
module RuboCop module RuboCop
module Cop module Cop
module Homebrew module Homebrew
# Checks for code that can be written with simpler conditionals # Checks for code that can be simplified using `Object#blank?`.
# using `Object#blank?`.
# #
# @note # NOTE: Auto-correction for this cop is unsafe because `' '.empty?` returns `false`,
# This cop is unsafe autocorrection, because `' '.empty?` returns false, # but `' '.blank?` returns `true`. Therefore, auto-correction is not compatible
# but `' '.blank?` returns true. Therefore, autocorrection is not compatible # if the receiver is a non-empty blank string.
# if the receiver is a non-empty blank string, tab, or newline meta characters.
# #
# @example # ### Example
# # Converts usages of `nil? || empty?` to `blank?`
# #
# # bad # ```ruby
# foo.nil? || foo.empty? # # bad
# foo == nil || foo.empty? # foo.nil? || foo.empty?
# foo == nil || foo.empty?
# #
# # good # # good
# foo.blank? # foo.blank?
# ```
class Blank < Base class Blank < Base
extend AutoCorrector extend AutoCorrector
MSG_NIL_OR_EMPTY = "Use `%<prefer>s` instead of `%<current>s`." MSG = "Use `%<prefer>s` instead of `%<current>s`."
# `(send nil $_)` is not actually a valid match for an offense. Nodes # `(send nil $_)` is not actually a valid match for an offense. Nodes
# that have a single method call on the left hand side # that have a single method call on the left hand side
@ -49,7 +48,7 @@ module RuboCop
nil_or_empty?(node) do |var1, var2| nil_or_empty?(node) do |var1, var2|
return if var1 != var2 return if var1 != var2
message = format(MSG_NIL_OR_EMPTY, prefer: replacement(var1), current: node.source) message = format(MSG, prefer: replacement(var1), current: node.source)
add_offense(node, message:) do |corrector| add_offense(node, message:) do |corrector|
autocorrect(corrector, node) autocorrect(corrector, node)
end end

View File

@ -9,20 +9,23 @@ module RuboCop
module Cask module Cask
# This cop makes sure that OS conditionals are consistent. # This cop makes sure that OS conditionals are consistent.
# #
# @example # ### Example
# # bad
# cask 'foo' do
# if MacOS.version == :high_sierra
# sha256 "..."
# end
# end
# #
# # good # ```ruby
# cask 'foo' do # # bad
# on_high_sierra do # cask 'foo' do
# sha256 "..." # if MacOS.version == :high_sierra
# end # sha256 "..."
# end # end
# end
#
# # good
# cask 'foo' do
# on_high_sierra do
# sha256 "..."
# end
# end
# ```
class OnSystemConditionals < Base class OnSystemConditionals < Base
extend Forwardable extend Forwardable
extend AutoCorrector extend AutoCorrector

View File

@ -6,16 +6,17 @@ module RuboCop
module Cask module Cask
# This cop checks that a cask's `url` stanza is formatted correctly. # This cop checks that a cask's `url` stanza is formatted correctly.
# #
# @example # ### Example
# # bad
# url "https://example.com/download/foo.dmg",
# verified: "https://example.com/download"
# #
# ```ruby
# # bad
# url "https://example.com/download/foo.dmg",
# verified: "https://example.com/download"
# #
# # good # # good
# url "https://example.com/download/foo.dmg", # url "https://example.com/download/foo.dmg",
# verified: "example.com/download/" # verified: "example.com/download/"
# # ```
class Url < Base class Url < Base
extend AutoCorrector extend AutoCorrector
extend Forwardable extend Forwardable

View File

@ -8,16 +8,19 @@ module RuboCop
module Cask module Cask
# This cop audits variables in casks. # This cop audits variables in casks.
# #
# @example # ### Example
# # bad
# cask do
# arch = Hardware::CPU.intel? ? "darwin" : "darwin-arm64"
# end
# #
# # good # ```ruby
# cask 'foo' do # # bad
# arch arm: "darwin-arm64", intel: "darwin" # cask do
# end # arch = Hardware::CPU.intel? ? "darwin" : "darwin-arm64"
# end
#
# # good
# cask 'foo' do
# arch arm: "darwin-arm64", intel: "darwin"
# end
# ```
class Variables < Base class Variables < Base
extend Forwardable extend Forwardable
extend AutoCorrector extend AutoCorrector

View File

@ -6,27 +6,30 @@ require "rubocops/extend/formula_cop"
module RuboCop module RuboCop
module Cop module Cop
module FormulaAudit module FormulaAudit
# This cop makes sure that caveats don't recommend unsupported or unsafe operations. # This cop ensures that caveats don't recommend unsupported or unsafe operations.
# #
# @example # ### Example
# # bad
# def caveats
# <<~EOS
# Use `setuid` to allow running the exeutable by non-root users.
# EOS
# end
# #
# # good # ```ruby
# def caveats # # bad
# <<~EOS # def caveats
# Use `sudo` to run the executable. # <<~EOS
# EOS # Use `setuid` to allow running the exeutable by non-root users.
# end # EOS
# end
#
# # good
# def caveats
# <<~EOS
# Use `sudo` to run the executable.
# EOS
# end
# ```
class Caveats < FormulaCop class Caveats < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, _body_node) def audit_formula(_node, _class_node, _parent_class_node, _body_node)
caveats_strings.each do |n| caveats_strings.each do |n|
if regex_match_group(n, /\bsetuid\b/i) if regex_match_group(n, /\bsetuid\b/i)
problem "Don't recommend setuid in the caveats, suggest sudo instead." problem "Don't recommend `setuid` in the caveats, suggest `sudo` instead."
end end
problem "Don't use ANSI escape codes in the caveats." if regex_match_group(n, /\e/) problem "Don't use ANSI escape codes in the caveats." if regex_match_group(n, /\e/)

View File

@ -6,43 +6,46 @@ module RuboCop
module Homebrew module Homebrew
# Checks if collection can be blank-compacted with `compact_blank`. # Checks if collection can be blank-compacted with `compact_blank`.
# #
# @note # NOTE: It is unsafe by default because false positives may occur in the
# It is unsafe by default because false positives may occur in the # blank check of block arguments to the receiver object.
# blank check of block arguments to the receiver object.
# #
# For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and # For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and
# `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `blank?`. # `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `blank?`.
# This will work fine when the receiver is a hash object. # This will work fine when the receiver is a hash object.
# #
# And `compact_blank!` has different implementations for `Array`, `Hash`, and # And `compact_blank!` has different implementations for `Array`, `Hash`, and
# `ActionController::Parameters`. # `ActionController::Parameters`.
# `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`. # `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
# `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`. # `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
# If the cop makes a mistake, autocorrected code may get unexpected behavior. # If the cop makes a mistake, autocorrected code may get unexpected behavior.
# #
# @example # ### Examples
# #
# # bad # ```ruby
# collection.reject(&:blank?) # # bad
# collection.reject { |_k, v| v.blank? } # collection.reject(&:blank?)
# collection.reject { |_k, v| v.blank? }
# #
# # good # # good
# collection.compact_blank # collection.compact_blank
# ```
# #
# # bad # ```ruby
# collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!` # # bad
# collection.delete_if { |_, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!` # collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!` # collection.delete_if { |_, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
# collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!` # collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
# # collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
# # good
# collection.compact_blank!
# #
# # good
# collection.compact_blank!
# ```
class CompactBlank < Base class CompactBlank < Base
include RangeHelp include RangeHelp
extend AutoCorrector extend AutoCorrector
MSG = "Use `%<preferred_method>s` instead." MSG = "Use `%<preferred_method>s` instead."
RESTRICT_ON_SEND = [:reject, :delete_if, :reject!].freeze RESTRICT_ON_SEND = [:reject, :delete_if, :reject!].freeze
def_node_matcher :reject_with_block?, <<~PATTERN def_node_matcher :reject_with_block?, <<~PATTERN

View File

@ -70,7 +70,7 @@ module RuboCop
# Compact the above into this list as we're able to remove detailed notations, etc over time. # Compact the above into this list as we're able to remove detailed notations, etc over time.
when when
# Check for http:// GitHub homepage URLs, https:// is preferred. # Check for http:// GitHub homepage URLs, https:// is preferred.
# Note: only check homepages that are repo pages, not *.github.com hosts # NOTE: Only check homepages that are repo pages, not *.github.com hosts.
%r{^http://github\.com/}, %r{^http://github\.com/},
%r{^http://[^/]*\.github\.io/}, %r{^http://[^/]*\.github\.io/},

View File

@ -4,9 +4,10 @@
module RuboCop module RuboCop
module Cop module Cop
module Homebrew module Homebrew
# This cop restricts usage of IO.read functions for security reasons. # This cop restricts usage of `IO.read` functions for security reasons.
class IORead < Base class IORead < Base
MSG = "The use of `IO.%<method>s` is a security risk." MSG = "The use of `IO.%<method>s` is a security risk."
RESTRICT_ON_SEND = [:read, :readlines].freeze RESTRICT_ON_SEND = [:read, :readlines].freeze
def on_send(node) def on_send(node)

View File

@ -7,23 +7,25 @@ module RuboCop
# Enforces the use of `collection.exclude?(obj)` # Enforces the use of `collection.exclude?(obj)`
# over `!collection.include?(obj)`. # over `!collection.include?(obj)`.
# #
# @note # NOTE: This cop is unsafe because false positives will occur for
# This cop is unsafe because false positive will occur for # receiver objects that do not have an `#exclude?` method (e.g. `IPAddr`).
# receiver objects that do not have an `exclude?` method. (e.g. `IPAddr`)
# #
# @example # ### Example
# # bad
# !array.include?(2)
# !hash.include?(:key)
# #
# # good # ```ruby
# array.exclude?(2) # # bad
# hash.exclude?(:key) # !array.include?(2)
# !hash.include?(:key)
# #
# # good
# array.exclude?(2)
# hash.exclude?(:key)
# ```
class NegateInclude < Base class NegateInclude < Base
extend AutoCorrector extend AutoCorrector
MSG = "Use `.exclude?` and remove the negation part." MSG = "Use `.exclude?` and remove the negation part."
RESTRICT_ON_SEND = [:!].freeze RESTRICT_ON_SEND = [:!].freeze
def_node_matcher :negate_include_call?, <<~PATTERN def_node_matcher :negate_include_call?, <<~PATTERN

View File

@ -7,37 +7,41 @@ module RuboCop
# Checks code that can be written more easily using # Checks code that can be written more easily using
# `Object#presence` defined by Active Support. # `Object#presence` defined by Active Support.
# #
# @example # ### Examples
# # bad
# a.present? ? a : nil
# #
# # bad # ```ruby
# !a.present? ? nil : a # # bad
# a.present? ? a : nil
# #
# # bad # # bad
# a.blank? ? nil : a # !a.present? ? nil : a
# #
# # bad # # bad
# !a.blank? ? a : nil # a.blank? ? nil : a
# #
# # good # # bad
# a.presence # !a.blank? ? a : nil
# #
# @example # # good
# # bad # a.presence
# a.present? ? a : b # ```
# #
# # bad # ```ruby
# !a.present? ? b : a # # bad
# a.present? ? a : b
# #
# # bad # # bad
# a.blank? ? b : a # !a.present? ? b : a
# #
# # bad # # bad
# !a.blank? ? a : b # a.blank? ? b : a
# #
# # good # # bad
# a.presence || b # !a.blank? ? a : b
#
# # good
# a.presence || b
# ```
class Presence < Base class Presence < Base
include RangeHelp include RangeHelp
extend AutoCorrector extend AutoCorrector

View File

@ -4,24 +4,24 @@
module RuboCop module RuboCop
module Cop module Cop
module Homebrew module Homebrew
# Checks for code that can be written with simpler conditionals # Checks for code that can be simplified using `Object#present?`.
# using `Object#present?`.
# #
# @example # ### Example
# # Converts usages of `!nil? && !empty?` to `present?`
# #
# # bad # ```ruby
# !foo.nil? && !foo.empty? # # bad
# !foo.nil? && !foo.empty?
# #
# # bad # # bad
# foo != nil && !foo.empty? # foo != nil && !foo.empty?
# #
# # good # # good
# foo.present? # foo.present?
# ```
class Present < Base class Present < Base
extend AutoCorrector extend AutoCorrector
MSG_EXISTS_AND_NOT_EMPTY = "Use `%<prefer>s` instead of `%<current>s`." MSG = "Use `%<prefer>s` instead of `%<current>s`."
def_node_matcher :exists_and_not_empty?, <<~PATTERN def_node_matcher :exists_and_not_empty?, <<~PATTERN
(and (and
@ -41,7 +41,7 @@ module RuboCop
exists_and_not_empty?(node) do |var1, var2| exists_and_not_empty?(node) do |var1, var2|
return if var1 != var2 return if var1 != var2
message = format(MSG_EXISTS_AND_NOT_EMPTY, prefer: replacement(var1), current: node.source) message = format(MSG, prefer: replacement(var1), current: node.source)
add_offense(node, message:) do |corrector| add_offense(node, message:) do |corrector|
autocorrect(corrector, node) autocorrect(corrector, node)
@ -53,7 +53,7 @@ module RuboCop
exists_and_not_empty?(node) do |var1, var2| exists_and_not_empty?(node) do |var1, var2|
return if var1 != var2 return if var1 != var2
add_offense(node, message: MSG_EXISTS_AND_NOT_EMPTY) do |corrector| add_offense(node, message: MSG) do |corrector|
autocorrect(corrector, node) autocorrect(corrector, node)
end end
end end

View File

@ -7,28 +7,26 @@ module RuboCop
# Checks to make sure safe navigation isn't used with `blank?` in # Checks to make sure safe navigation isn't used with `blank?` in
# a conditional. # a conditional.
# #
# @note # NOTE: While the safe navigation operator is generally a good idea, when
# While the safe navigation operator is generally a good idea, when # checking `foo&.blank?` in a conditional, `foo` being `nil` will actually
# checking `foo&.blank?` in a conditional, `foo` being `nil` will actually # do the opposite of what the author intends:
# do the opposite of what the author intends.
# #
# For example: # ```ruby
# foo&.blank? #=> nil
# foo.blank? #=> true
# ```
# #
# [source,ruby] # ### Example
# ----
# foo&.blank? #=> nil
# foo.blank? #=> true
# ----
# #
# @example # ```ruby
# # bad # # bad
# do_something if foo&.blank? # do_something if foo&.blank?
# do_something unless foo&.blank? # do_something unless foo&.blank?
#
# # good
# do_something if foo.blank?
# do_something unless foo.blank?
# #
# # good
# do_something if foo.blank?
# do_something unless foo.blank?
# ```
class SafeNavigationWithBlank < Base class SafeNavigationWithBlank < Base
extend AutoCorrector extend AutoCorrector

View File

@ -30,7 +30,7 @@ module RuboCop
method_calls.delete(:service) method_calls.delete(:service)
# NOTE: Solving the first problem here might solve the second one too # NOTE: Solving the first problem here might solve the second one too
# so we don't show both of them at the same time. # so we don't show both of them at the same time.
if !method_calls.keys.intersect?(REQUIRED_METHOD_CALLS) if !method_calls.keys.intersect?(REQUIRED_METHOD_CALLS)
offending_node(service_node) offending_node(service_node)
problem "Service blocks require `run` or `name` to be defined." problem "Service blocks require `run` or `name` to be defined."

View File

@ -174,10 +174,19 @@ module RuboCop
# Matches a method with a receiver. Yields to a block with matching method node. # Matches a method with a receiver. Yields to a block with matching method node.
# #
# @example to match `Formula.factory(name)` # ### Examples
# find_instance_method_call(node, "Formula", :factory) #
# @example to match `build.head?` # Match `Formula.factory(name)`.
# find_instance_method_call(node, :build, :head?) #
# ```ruby
# find_instance_method_call(node, "Formula", :factory)
# ```
#
# Match `build.head?`.
#
# ```ruby
# find_instance_method_call(node, :build, :head?)
# ```
def find_instance_method_call(node, instance, method_name) def find_instance_method_call(node, instance, method_name)
methods = find_every_method_call_by_name(node, method_name) methods = find_every_method_call_by_name(node, method_name)
methods.each do |method| methods.each do |method|
@ -194,8 +203,13 @@ module RuboCop
# Matches receiver part of method. Yields to a block with parent node of receiver. # Matches receiver part of method. Yields to a block with parent node of receiver.
# #
# @example to match `ARGV.<whatever>()` # ### Example
# find_instance_call(node, "ARGV") #
# Match `ARGV.<whatever>()`.
#
# ```ruby
# find_instance_call(node, "ARGV")
# ```
def find_instance_call(node, name) def find_instance_call(node, name)
node.each_descendant(:send) do |method_node| node.each_descendant(:send) do |method_node|
next if method_node.receiver.nil? next if method_node.receiver.nil?

View File

@ -40,9 +40,11 @@ module RuboCop
until until
while while
].freeze ].freeze
private_constant :SHELL_BUILTINS
# https://github.com/ruby/ruby/blob/v2_6_3/process.c#L2495 # https://github.com/ruby/ruby/blob/v2_6_3/process.c#L2495
SHELL_METACHARACTERS = %W[* ? { } [ ] < > ( ) ~ & | \\ $ ; ' ` " \n #].freeze SHELL_METACHARACTERS = %W[* ? { } [ ] < > ( ) ~ & | \\ $ ; ' ` " \n #].freeze
private_constant :SHELL_METACHARACTERS
# This cop makes sure that shell command arguments are separated. # This cop makes sure that shell command arguments are separated.
class ShellCommands < Base class ShellCommands < Base
@ -60,6 +62,8 @@ module RuboCop
[:Utils, :popen_write], [:Utils, :popen_write],
[:Utils, :safe_popen_write], [:Utils, :safe_popen_write],
].freeze ].freeze
private_constant :TARGET_METHODS
RESTRICT_ON_SEND = TARGET_METHODS.map(&:second).uniq.freeze RESTRICT_ON_SEND = TARGET_METHODS.map(&:second).uniq.freeze
def on_send(node) def on_send(node)

View File

@ -207,7 +207,7 @@ module RuboCop
problem "Use versioned rather than branch tarballs for stable checksums." problem "Use versioned rather than branch tarballs for stable checksums."
end end
# Use new-style archive downloads # Use new-style archive downloads.
archive_gh_pattern = %r{https://.*github.*/(?:tar|zip)ball/} archive_gh_pattern = %r{https://.*github.*/(?:tar|zip)ball/}
audit_urls(urls, archive_gh_pattern) do |_, url| audit_urls(urls, archive_gh_pattern) do |_, url|
next if url.end_with?(".git") next if url.end_with?(".git")

View File

@ -138,7 +138,7 @@ class SoftwareSpec
end end
def go_resource(name, &block) def go_resource(name, &block)
# odeprecated "SoftwareSpec#go_resource", "Go modules" # odeprecated "`SoftwareSpec#go_resource`", "Go modules"
resource name, Resource::Go, &block resource name, Resource::Go, &block
end end
@ -207,18 +207,6 @@ class SoftwareSpec
depends_on UsesFromMacOSDependency.new(dep, tags, bounds:) depends_on UsesFromMacOSDependency.new(dep, tags, bounds:)
end end
# @deprecated
def uses_from_macos_elements
# TODO: Remember to remove the delegate from `Formula`.
odisabled "#uses_from_macos_elements", "#declared_deps"
end
# @deprecated
def uses_from_macos_names
# TODO: Remember to remove the delegate from `Formula`.
odisabled "#uses_from_macos_names", "#declared_deps"
end
def deps def deps
dependency_collector.deps.dup_without_system_deps dependency_collector.deps.dup_without_system_deps
end end
@ -318,7 +306,6 @@ class Bottle
"#{name}--#{version}#{extname}" "#{name}--#{version}#{extname}"
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = to_str def to_s = to_str

View File

@ -80,21 +80,12 @@ class Formula
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T::Boolean) } sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T::Boolean) }
def loaded_from_api?(*args, **options, &block); end def loaded_from_api?(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def resource(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def deps(*args, **options, &block); end def deps(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def declared_deps(*args, **options, &block); end def declared_deps(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def uses_from_macos_elements(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def uses_from_macos_names(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def requirements(*args, **options, &block); end def requirements(*args, **options, &block); end
@ -161,9 +152,6 @@ class Formula
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def env(*args, **options, &block); end def env(*args, **options, &block); end
sig { params(args: T.untyped, options: T.untyped, block: T.untyped).returns(T.untyped) }
def conflicts(*args, **options, &block); end
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def self.loaded_from_api?; end def self.loaded_from_api?; end

View File

@ -16,7 +16,6 @@ module Homebrew
@column = column @column = column
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
"#{line}#{column&.to_s&.prepend(":")}" "#{line}#{column&.to_s&.prepend(":")}"

View File

@ -179,7 +179,7 @@ module Homebrew
# -f (--force) : we know what we are doing, force apply patches # -f (--force) : we know what we are doing, force apply patches
# -d / (--directory=/) : change to root directory, since we use absolute file paths # -d / (--directory=/) : change to root directory, since we use absolute file paths
# -p0 (--strip=0) : do not strip path prefixes, since we are at root directory # -p0 (--strip=0) : do not strip path prefixes, since we are at root directory
# NOTE: we use short flags where for compatibility # NOTE: We use short flags for compatibility.
patch_command = %w[patch -g 0 -f -d / -p0] patch_command = %w[patch -g 0 -f -d / -p0]
patches = system_command(shellcheck, args: ["--format=diff", *args]).stdout patches = system_command(shellcheck, args: ["--format=diff", *args]).stdout
Utils.safe_popen_write(*patch_command) { |p| p.write(patches) } if patches.present? Utils.safe_popen_write(*patch_command) { |p| p.write(patches) } if patches.present?

View File

@ -13,12 +13,18 @@ class Tab
FILENAME = "INSTALL_RECEIPT.json" FILENAME = "INSTALL_RECEIPT.json"
# Check whether the formula was installed as a dependency.
#
# @api internal # @api internal
attr_accessor :installed_as_dependency attr_accessor :installed_as_dependency
# Check whether the formula was installed on request.
#
# @api internal # @api internal
attr_accessor :installed_on_request attr_accessor :installed_on_request
# Check whether the formula was poured from a bottle.
#
# @api internal # @api internal
attr_accessor :poured_from_bottle attr_accessor :poured_from_bottle
@ -27,6 +33,8 @@ class Tab
:built_on :built_on
attr_writer :used_options, :unused_options, :compiler, :source_modified_time attr_writer :used_options, :unused_options, :compiler, :source_modified_time
# Returns the formula's runtime dependencies.
#
# @api internal # @api internal
attr_writer :runtime_dependencies attr_writer :runtime_dependencies
@ -72,7 +80,8 @@ class Tab
end end
# Returns the {Tab} for an install receipt at `path`. # Returns the {Tab} for an install receipt at `path`.
# Results are cached. #
# NOTE: Results are cached.
def self.from_file(path) def self.from_file(path)
cache.fetch(path) do |p| cache.fetch(path) do |p|
content = File.read(p) content = File.read(p)
@ -282,7 +291,10 @@ class Tab
spec == :stable spec == :stable
end end
# The options used to install the formula.
#
# @api internal # @api internal
sig { returns(Options) }
def used_options def used_options
Options.create(@used_options) Options.create(@used_options)
end end
@ -321,6 +333,7 @@ class Tab
built_as_bottle built_as_bottle
end end
sig { returns(T.nilable(Tap)) }
def tap def tap
tap_name = source["tap"] tap_name = source["tap"]
Tap.fetch(tap_name) if tap_name Tap.fetch(tap_name) if tap_name
@ -407,7 +420,6 @@ class Tab
tabfile.atomic_write(to_json) tabfile.atomic_write(to_json)
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
s = [] s = []

View File

@ -154,7 +154,6 @@ class Tap
attr_reader :name attr_reader :name
# @api public # @api public
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s = name def to_s = name

View File

@ -73,8 +73,8 @@ RSpec.describe "ENV" do
expect(subject["foo"]).to eq("1") expect(subject["foo"]).to eq("1")
end end
# NOTE: this may be a wrong behavior; we should probably reject objects that # NOTE: This may be a wrong behavior; we should probably reject objects that
# do not respond to #to_str. For now this documents existing behavior. # do not respond to `#to_str`. For now this documents existing behavior.
it "coerces a value to a string" do it "coerces a value to a string" do
subject.append "foo", 42 subject.append "foo", 42
expect(subject["foo"]).to eq("42") expect(subject["foo"]).to eq("42")

View File

@ -332,8 +332,8 @@ RSpec.describe Cask::Cask, :cask do
expect(JSON.pretty_generate(h["variations"])).to eq expected_sha256_variations.strip expect(JSON.pretty_generate(h["variations"])).to eq expected_sha256_variations.strip
end end
# @note The calls to `Cask.generating_hash!` and `Cask.generated_hash!` # NOTE: The calls to `Cask.generating_hash!` and `Cask.generated_hash!`
# are not idempotent so they can only be used in one test. # are not idempotent so they can only be used in one test.
it "returns the correct hash placeholders" do it "returns the correct hash placeholders" do
described_class.generating_hash! described_class.generating_hash!
expect(described_class).to be_generating_hash expect(described_class).to be_generating_hash

View File

@ -87,7 +87,7 @@ RSpec.describe Caveats do
expect(caveats).to include("tmux") expect(caveats).to include("tmux")
end end
# @todo This should get deprecated and the service block `plist_name` method should get used instead. # TODO: This should get deprecated and the service block `plist_name` method should get used instead.
it "prints info when there are custom service files" do it "prints info when there are custom service files" do
f = formula do f = formula do
url "foo-1.0" url "foo-1.0"

View File

@ -79,9 +79,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::ElectronBuilder do
end).to eq(find_versions_cached_return_hash.merge({ regex: })) end).to eq(find_versions_cached_return_hash.merge({ regex: }))
# NOTE: A regex should be provided using the `#regex` method in a # NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy` # `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a # block here simply to ensure this method works as expected when a
# regex isn't provided. # regex isn't provided.
expect(electron_builder.find_versions(url: http_url, provided_content: content) do |yaml| expect(electron_builder.find_versions(url: http_url, provided_content: content) do |yaml|
regex = /^v?(\d+(?:\.\d+)+)$/i regex = /^v?(\d+(?:\.\d+)+)$/i
yaml["version"][regex, 1] yaml["version"][regex, 1]

View File

@ -75,7 +75,7 @@ RSpec.describe Homebrew::Livecheck::Strategy::HeaderMatch do
end end
it "returns an array of version strings when given headers and a block" do it "returns an array of version strings when given headers and a block" do
# Returning a string from block, no regex # Returning a string from block, no regex.
expect( expect(
header_match.versions_from_headers(headers[:location]) do |headers| header_match.versions_from_headers(headers[:location]) do |headers|
v = Version.parse(headers["location"], detected_from_url: true) v = Version.parse(headers["location"], detected_from_url: true)
@ -83,16 +83,17 @@ RSpec.describe Homebrew::Livecheck::Strategy::HeaderMatch do
end, end,
).to eq(versions[:location]) ).to eq(versions[:location])
# Returning a string from block, explicit regex # Returning a string from block, explicit regex.
expect( expect(
header_match.versions_from_headers(headers[:location], regexes[:latest]) do |headers, regex| header_match.versions_from_headers(headers[:location], regexes[:latest]) do |headers, regex|
headers["location"] ? headers["location"][regex, 1] : nil headers["location"] ? headers["location"][regex, 1] : nil
end, end,
).to eq(versions[:location]) ).to eq(versions[:location])
# Returning an array of strings from block # Returning an array of strings from block.
# NOTE: Strategies runs `#compact` on an array from a block, so nil #
# values are filtered out without needing to use `#compact` in the block. # NOTE: Strategies runs `#compact` on an array from a block, so nil values
# are filtered out without needing to use `#compact` in the block.
expect( expect(
header_match.versions_from_headers( header_match.versions_from_headers(
headers[:content_disposition_and_location], headers[:content_disposition_and_location],

View File

@ -126,9 +126,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Json do
end).to eq(find_versions_cached_return_hash) end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a # NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy` # `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a # block here simply to ensure this method works as expected when a
# regex isn't provided. # regex isn't provided.
expect(json.find_versions(url: http_url, provided_content: content) do |json| expect(json.find_versions(url: http_url, provided_content: content) do |json|
regex = /^v?(\d+(?:\.\d+)+)$/i regex = /^v?(\d+(?:\.\d+)+)$/i
json["versions"].select { |item| item["version"]&.match?(regex) } json["versions"].select { |item| item["version"]&.match?(regex) }

View File

@ -109,11 +109,11 @@ RSpec.describe Homebrew::Livecheck::Strategy::PageMatch do
.to eq(find_versions_cached_return_hash) .to eq(find_versions_cached_return_hash)
# NOTE: Ideally, a regex should always be provided to `#find_versions` # NOTE: Ideally, a regex should always be provided to `#find_versions`
# for `PageMatch` but there are currently some `livecheck` blocks in # for `PageMatch` but there are currently some `livecheck` blocks in
# casks where `#regex` isn't used and the regex only exists within a # casks where `#regex` isn't used and the regex only exists within a
# `strategy` block. This isn't ideal but, for the moment, we allow a # `strategy` block. This isn't ideal but, for the moment, we allow a
# `strategy` block to act as a substitution for a regex and we need to # `strategy` block to act as a substitution for a regex and we need to
# test this scenario to ensure it works. # test this scenario to ensure it works.
# #
# Under normal circumstances, a regex should be established in a # Under normal circumstances, a regex should be established in a
# `livecheck` block using `#regex` and passed into the `strategy` block # `livecheck` block using `#regex` and passed into the `strategy` block

View File

@ -218,9 +218,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Xml do
end).to eq(find_versions_cached_return_hash) end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a # NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy` # `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a # block here simply to ensure this method works as expected when a
# regex isn't provided. # regex isn't provided.
expect(xml.find_versions(url: http_url, provided_content: content_version_text) do |xml| expect(xml.find_versions(url: http_url, provided_content: content_version_text) do |xml|
regex = /^v?(\d+(?:\.\d+)+)$/i regex = /^v?(\d+(?:\.\d+)+)$/i
xml.get_elements("/versions/version").map { |item| item.text[regex, 1] } xml.get_elements("/versions/version").map { |item| item.text[regex, 1] }

View File

@ -127,9 +127,9 @@ RSpec.describe Homebrew::Livecheck::Strategy::Yaml do
end).to eq(find_versions_cached_return_hash) end).to eq(find_versions_cached_return_hash)
# NOTE: A regex should be provided using the `#regex` method in a # NOTE: A regex should be provided using the `#regex` method in a
# `livecheck` block but we're using a regex literal in the `strategy` # `livecheck` block but we're using a regex literal in the `strategy`
# block here simply to ensure this method works as expected when a # block here simply to ensure this method works as expected when a
# regex isn't provided. # regex isn't provided.
expect(yaml.find_versions(url: http_url, provided_content: content) do |yaml| expect(yaml.find_versions(url: http_url, provided_content: content) do |yaml|
regex = /^v?(\d+(?:\.\d+)+)$/i regex = /^v?(\d+(?:\.\d+)+)$/i
yaml["versions"].select { |item| item["version"]&.match?(regex) } yaml["versions"].select { |item| item["version"]&.match?(regex) }

View File

@ -1058,8 +1058,8 @@ RSpec.describe Homebrew::Service do
} }
end end
# @note The calls to `Formula.generating_hash!` and `Formula.generated_hash!` # NOTE: The calls to `Formula.generating_hash!` and `Formula.generated_hash!`
# are not idempotent so they can only be used in one test. # are not idempotent so they can only be used in one test.
it "replaces local paths with placeholders" do it "replaces local paths with placeholders" do
f = stub_formula do f = stub_formula do
service do service do

View File

@ -10,7 +10,7 @@ RSpec.describe Tapioca::Compilers::Args do
Homebrew::Cmd::List.parser Homebrew::Cmd::List.parser
end end
# good testing candidate, bc it has multiple for each of switch, flag, and comma_array args: # Good testing candidate because it has multiple for each of `switch`, `flag` and `comma_array` args:
let(:update_python_resources_parser) do let(:update_python_resources_parser) do
require "dev-cmd/update-python-resources" require "dev-cmd/update-python-resources"
Homebrew::DevCmd::UpdatePythonResources.parser Homebrew::DevCmd::UpdatePythonResources.parser

View File

@ -35,8 +35,8 @@ module Cachable
# A list of all classes that have been loaded into memory that mixin or # A list of all classes that have been loaded into memory that mixin or
# inherit `Cachable` at the class or module level. # inherit `Cachable` at the class or module level.
# #
# Note: Classes that inherit from `Formula` are excluded since it's not # NOTE: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches. # necessary to track and clear individual formula caches.
def self.class_list def self.class_list
@class_list ||= [] @class_list ||= []
end end
@ -44,8 +44,8 @@ module Cachable
# Clear the cache of every class or module that mixes in or inherits # Clear the cache of every class or module that mixes in or inherits
# `Cachable` at the class or module level. # `Cachable` at the class or module level.
# #
# Note: Classes that inherit from `Formula` are excluded since it's not # NOTE: Classes that inherit from `Formula` are excluded since it's not
# necessary to track and clear individual formula caches. # necessary to track and clear individual formula caches.
def self.clear_all_caches def self.clear_all_caches
class_list.each(&:clear_cache) class_list.each(&:clear_cache)
end end

View File

@ -6,11 +6,11 @@ RSpec.describe UnpackStrategy::Zstd do
let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.zst" } let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.zst" }
it "is correctly detected" do it "is correctly detected" do
# UnpackStrategy.detect(path) for a .tar.XXX file returns either UnpackStrategy::Tar if # `UnpackStrategy.detect(path)` for a `.tar.XXX` file returns either `UnpackStrategy::Tar` if
# the host's tar is able to extract that compressed file or UnpackStrategy::XXX otherwise, # the host's `tar` is able to extract that compressed file or `UnpackStrategy::XXX` otherwise,
# such as UnpackStrategy::Zstd. On macOS UnpackStrategy.detect("container.tar.zst") # such as `UnpackStrategy::Zstd`. On macOS `UnpackStrategy.detect("container.tar.zst")`
# returns UnpackStrategy::Zstd, and on ubuntu-22.04 it returns UnpackStrategy::Tar, # returns `UnpackStrategy::Zstd` and on Ubuntu 22.04 it returns `UnpackStrategy::Tar`,
# because the host's version of tar is recent enough and zstd is installed. # because the host's version of `tar` is recent enough and `zstd` is installed.
expect(UnpackStrategy.detect(path)).to(be_a(described_class).or(be_a(UnpackStrategy::Tar))) expect(UnpackStrategy.detect(path)).to(be_a(described_class).or(be_a(UnpackStrategy::Tar)))
end end
end end

View File

@ -72,7 +72,7 @@ RSpec.describe Utils::Shell do
specify "::csh_quote" do specify "::csh_quote" do
expect(described_class.send(:csh_quote, "")).to eq("''") expect(described_class.send(:csh_quote, "")).to eq("''")
expect(described_class.send(:csh_quote, "\\")).to eq("\\\\") expect(described_class.send(:csh_quote, "\\")).to eq("\\\\")
# NOTE: this test is different than for sh # NOTE: This test is different than for `sh`.
expect(described_class.send(:csh_quote, "\n")).to eq("'\\\n'") expect(described_class.send(:csh_quote, "\n")).to eq("'\\\n'")
expect(described_class.send(:csh_quote, "$")).to eq("\\$") expect(described_class.send(:csh_quote, "$")).to eq("\\$")
expect(described_class.send(:csh_quote, "word")).to eq("word") expect(described_class.send(:csh_quote, "word")).to eq("word")

View File

@ -15,7 +15,6 @@ class URL
@specs.freeze @specs.freeze
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
@url @url

View File

@ -202,7 +202,6 @@ module Utils
end end
end end
# @!visibility private
sig { returns(String) } sig { returns(String) }
def to_s def to_s
to_sym.to_s to_sym.to_s

Some files were not shown because too many files have changed in this diff Show More