Merge pull request #16463 from dduugg/no-active-support

Remove ActiveSupport from runtime
This commit is contained in:
Mike McQuaid 2024-01-22 19:22:12 +00:00 committed by GitHub
commit 86e1c8aacf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 409 additions and 652 deletions

20
.gitignore vendored
View File

@ -53,25 +53,6 @@
!**/vendor/bundle/ruby/*/gems/*/lib
!**/vendor/bundle/ruby/*/gems/addressable-*/data
!**/vendor/bundle/ruby/*/gems/public_suffix-*/data
!**/vendor/bundle/ruby/*/gems/rubocop-performance-*/config
!**/vendor/bundle/ruby/*/gems/rubocop-rails-*/config
!**/vendor/bundle/ruby/*/gems/rubocop-rspec-*/config
!**/vendor/bundle/ruby/*/gems/rubocop-sorbet-*/config
# Ignore activesupport, except the ones we need.
**/vendor/bundle/ruby/*/gems/activesupport-*/lib/**/*
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/*/
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/array/access.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/enumerable.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/file/atomic.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/hash/deep_merge.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/hash/deep_transform_values.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/hash/keys.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/object/deep_dup.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/object/duplicable.rb
!**/vendor/bundle/ruby/*/gems/activesupport-*/lib/active_support/core_ext/string/exclude.rb
# Ignore partially included gems where we don't need all files
**/vendor/gems/mechanize-*/.*
@ -89,6 +70,7 @@
**/vendor/gems/mechanize-*/test/
# Ignore dependencies we don't wish to vendor
**/vendor/bundle/ruby/*/gems/activesupport-*/
**/vendor/bundle/ruby/*/gems/ast-*/
**/vendor/bundle/ruby/*/gems/bootsnap-*/
**/vendor/bundle/ruby/*/gems/bundler-*/

View File

@ -2,7 +2,7 @@
# frozen_string_literal: true
require "attrable"
require "active_support/core_ext/object/deep_dup"
require "extend/object/deep_dup"
module Cask
module Artifact

View File

@ -6,6 +6,7 @@ require "timeout"
require "utils/user"
require "cask/artifact/abstract_artifact"
require "cask/pkg"
require "extend/hash/keys"
module Cask
module Artifact

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true
require "cask/artifact/abstract_artifact"
require "extend/hash/keys"
module Cask
module Artifact

View File

@ -5,6 +5,7 @@ require "plist"
require "utils/user"
require "cask/artifact/abstract_artifact"
require "extend/hash/keys"
module Cask
module Artifact

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true
require "cask/artifact/abstract_artifact"
require "extend/hash/keys"
module Cask
module Artifact

View File

@ -5,6 +5,7 @@ require "cask/cache"
require "cask/cask"
require "uri"
require "utils/curl"
require "extend/hash/keys"
module Cask
# Loads a cask from various sources.

View File

@ -5,6 +5,7 @@ require "json"
require "lazy_object"
require "locale"
require "extend/hash/keys"
module Cask
# Configuration for installing casks.

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true
require "delegate"
require "extend/hash/keys"
module Cask
class DSL

View File

@ -126,9 +126,9 @@ module Homebrew
# Filter out Sorbet runtime type checking method calls.
cmd_location = T.must(caller_locations).select do |location|
T.must(location.path).exclude?("/gems/sorbet-runtime-")
end.second
@command_name = cmd_location.label.chomp("_args").tr("_", "-")
@is_dev_cmd = cmd_location.absolute_path.start_with?(Commands::HOMEBREW_DEV_CMD_PATH)
end.fetch(1)
@command_name = T.must(cmd_location.label).chomp("_args").tr("_", "-")
@is_dev_cmd = T.must(cmd_location.absolute_path).start_with?(Commands::HOMEBREW_DEV_CMD_PATH)
@constraints = []
@conflicts = []

View File

@ -11,6 +11,7 @@ require "utils/inreplace"
require "erb"
require "utils/gzip"
require "api"
require "extend/hash/deep_merge"
BOTTLE_ERB = <<-EOS.freeze
bottle do

View File

@ -5,6 +5,7 @@ require "cli/parser"
require "formula"
require "github_packages"
require "github_releases"
require "extend/hash/deep_merge"
module Homebrew
module_function

View File

@ -360,7 +360,7 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
end
if search_query && (uri_query = uri.query.presence)
components[:query] = URI.decode_www_form(uri_query).map(&:second)
components[:query] = URI.decode_www_form(uri_query).map { _2 }
end
else
components[:path] = [url]

View File

@ -1,7 +1,27 @@
# typed: true
# typed: strict
# frozen_string_literal: true
class Array
# Equal to <tt>self[1]</tt>.
#
# %w( a b c d e ).second # => "b"
def second = self[1]
# Equal to <tt>self[2]</tt>.
#
# %w( a b c d e ).third # => "c"
def third = self[2]
# Equal to <tt>self[3]</tt>.
#
# %w( a b c d e ).fourth # => "d"
def fourth = self[3]
# Equal to <tt>self[4]</tt>.
#
# %w( a b c d e ).fifth # => "e"
def fifth = self[4]
# Converts the array to a comma-separated sentence where the last element is
# joined by the connector word.
#
@ -48,6 +68,7 @@ class Array
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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 ")
case length
when 0

View File

@ -0,0 +1,15 @@
# typed: strict
class Array
sig { returns(T.nilable(Elem)) }
def second; end
sig { returns(T.nilable(Elem)) }
def third; end
sig { returns(T.nilable(Elem)) }
def fourth; end
sig { returns(T.nilable(Elem)) }
def fifth; end
end

View File

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

View File

@ -0,0 +1,6 @@
# typed: strict
class Hash
sig { returns(T::Hash[Hash::K, Hash::V]) }
def compact_blank; end
end

View File

@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true
require "fileutils"
@ -18,7 +19,14 @@ class File
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
# file.write('hello')
# end
def self.atomic_write(file_name, temp_dir = dirname(file_name))
sig {
type_parameters(:out).params(
file_name: T.any(Pathname, String),
temp_dir: String,
_block: T.proc.params(arg0: Tempfile).returns(T.type_parameter(:out)),
).returns(T.type_parameter(:out))
}
def self.atomic_write(file_name, temp_dir = dirname(file_name), &_block)
require "tempfile" unless defined?(Tempfile)
Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
@ -38,32 +46,35 @@ class File
if old_stat
# Set correct permissions on new file
begin
chown(old_stat.uid, old_stat.gid, temp_file.path)
chown(old_stat.uid, old_stat.gid, T.must(temp_file.path))
# This operation will affect filesystem ACL's
chmod(old_stat.mode, temp_file.path)
chmod(old_stat.mode, T.must(temp_file.path))
rescue Errno::EPERM, Errno::EACCES
# Changing file ownership failed, moving on.
end
end
# Overwrite original file with temp file
rename(temp_file.path, file_name)
rename(T.must(temp_file.path), file_name)
return_val
end
end
# Private utility method.
def self.probe_stat_in(dir) #:nodoc:
sig { params(dir: String).returns(T.nilable(File::Stat)) }
private_class_method def self.probe_stat_in(dir) # :nodoc:
basename = [
".permissions_check",
Thread.current.object_id,
Process.pid,
rand(1000000)
rand(1_000_000),
].join(".")
file_name = join(dir, basename)
FileUtils.touch(file_name)
stat(file_name)
rescue Errno::ENOENT
file_name = nil
ensure
FileUtils.rm_f(file_name) if file_name
end

View File

@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true
class Hash
@ -22,10 +23,10 @@ class Hash
# Same as +deep_merge+, but modifies +self+.
def deep_merge!(other_hash, &block)
merge!(other_hash) do |key, this_val, other_val|
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
this_val.deep_merge(other_val, &block)
elsif block_given?
block.call(key, this_val, other_val)
if T.unsafe(this_val).is_a?(Hash) && other_val.is_a?(Hash)
T.unsafe(this_val).deep_merge(other_val, &block)
elsif block
yield(key, this_val, other_val)
else
other_val
end

View File

@ -0,0 +1,20 @@
# typed: strict
# frozen_string_literal: true
class Hash
sig do
type_parameters(:k2).params(
other_hash: T::Hash[T.type_parameter(:k2), T.untyped],
block: T.nilable(T.proc.params(k: T.untyped, v1: T.untyped, v2: T.untyped).returns(T.untyped))
).returns(T::Hash[T.any(Hash::K, T.type_parameter(:k2)), T.untyped])
end
def deep_merge(other_hash, &block); end
sig do
type_parameters(:k2).params(
other_hash: T::Hash[T.type_parameter(:k2), T.untyped],
block: T.nilable(T.proc.params(k: T.untyped, v1: T.untyped, v2: T.untyped).returns(T.untyped))
).returns(T::Hash[T.any(Hash::K, T.type_parameter(:k2)), T.untyped])
end
def deep_merge!(other_hash, &block); end
end

View File

@ -0,0 +1,46 @@
# typed: strict
# frozen_string_literal: true
class Hash
# Returns a new hash with all values converted by the block operation.
# This includes the values from the root hash and from all
# nested hashes and arrays.
#
# 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)
# Destructively converts all values by using the block operation.
# This includes the values from the root hash and from all
# nested hashes and arrays.
def deep_transform_values!(&block) = _deep_transform_values_in_object!(self, &block)
private
# Support methods for deep transforming nested hashes and arrays.
sig { params(object: T.anything, block: T.proc.params(v: T.untyped).returns(T.untyped)).returns(T.untyped) }
def _deep_transform_values_in_object(object, &block)
case object
when Hash
object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
when Array
object.map { |e| _deep_transform_values_in_object(e, &block) }
else
yield(object)
end
end
sig { params(object: T.anything, block: T.proc.params(v: T.untyped).returns(T.untyped)).returns(T.untyped) }
def _deep_transform_values_in_object!(object, &block)
case object
when Hash
object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
when Array
object.map! { |e| _deep_transform_values_in_object!(e, &block) }
else
yield(object)
end
end
end

View File

@ -0,0 +1,17 @@
# typed: strict
class Hash
sig {
type_parameters(:out).params(
block: T.proc.params(o: Hash::V).returns(T.type_parameter(:out))
).returns(T::Hash[Hash::K, T.type_parameter(:out)])
}
def deep_transform_values(&block); end
sig {
type_parameters(:out).params(
block: T.proc.params(o: Hash::V).returns(T.type_parameter(:out))
).returns(T::Hash[Hash::K, T.type_parameter(:out)])
}
def deep_transform_values!(&block); end
end

View File

@ -0,0 +1,117 @@
# typed: strict
# frozen_string_literal: true
class Hash
# Validates all keys in a hash match <tt>*valid_keys</tt>, raising
# +ArgumentError+ on a mismatch.
#
# Note that keys are treated differently than HashWithIndifferentAccess,
# meaning that string and symbol keys will not match.
#
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age)
# # => 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 }
def assert_valid_keys(*valid_keys)
valid_keys.flatten!
each_key do |k|
next if valid_keys.include?(k)
raise ArgumentError,
"Unknown key: #{T.unsafe(k).inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(", ")}"
end
end
# Returns a new hash with all keys converted by the block operation.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# 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)
# Destructively converts all keys by using the block operation.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_transform_keys!(&block) = _deep_transform_keys_in_object!(self, &block)
# Returns a new hash with all keys converted to strings.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# 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)
# Destructively converts all keys to strings.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
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
# they respond to +to_sym+. This includes the keys from the root hash
# and from all nested hashes and arrays.
#
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
#
# hash.deep_symbolize_keys
# # => {:person=>{:name=>"Rob", :age=>"28"}}
def deep_symbolize_keys
deep_transform_keys do |key|
T.unsafe(key).to_sym
rescue
key
end
end
# 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
# nested hashes and arrays.
def deep_symbolize_keys!
deep_transform_keys! do |key|
T.unsafe(key).to_sym
rescue
key
end
end
private
# Support methods for deep transforming nested hashes and arrays.
sig { params(object: T.anything, block: T.proc.params(k: T.untyped).returns(T.untyped)).returns(T.untyped) }
def _deep_transform_keys_in_object(object, &block)
case object
when Hash
object.each_with_object({}) do |(key, value), result|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
end
when Array
object.map { |e| _deep_transform_keys_in_object(e, &block) }
else
object
end
end
sig { params(object: T.anything, block: T.proc.params(k: T.untyped).returns(T.untyped)).returns(T.untyped) }
def _deep_transform_keys_in_object!(object, &block)
case object
when Hash
# We can't use `each_key` here because we're updating the hash in-place
object.keys.each do |key| # rubocop:disable Style/HashEachMethods
value = object.delete(key)
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
end
object
when Array
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
else
object
end
end
end

View File

@ -0,0 +1,30 @@
# typed: strict
# frozen_string_literal: true
class Hash
sig {
type_parameters(:out).params(
block: T.proc.params(o: K).returns(T.type_parameter(:out))
).returns(T::Hash[T.type_parameter(:out), V])
}
def deep_transform_keys(&block); end
sig {
type_parameters(:out).params(
block: T.proc.params(o: K).returns(T.type_parameter(:out))
).returns(T::Hash[T.type_parameter(:out), V])
}
def deep_transform_keys!(&block); end
sig { returns(T::Hash[String, V]) }
def deep_stringify_keys; end
sig { returns(T::Hash[String, V]) }
def deep_stringify_keys!; end
sig { returns(T::Hash[Symbol, V]) }
def deep_symbolize_keys; end
sig { returns(T::Hash[Symbol, V]) }
def deep_symbolize_keys!; end
end

View File

@ -1,6 +1,7 @@
# typed: strict
# frozen_string_literal: true
require "active_support/core_ext/object/duplicable"
require "extend/object/duplicable"
class Object
# Returns a deep copy of object if it's duplicable. If it's
@ -12,6 +13,7 @@ class Object
#
# object.instance_variable_defined?(:@a) # => false
# dup.instance_variable_defined?(:@a) # => true
sig { returns(T.self_type) }
def deep_dup
duplicable? ? dup : self
end
@ -26,8 +28,9 @@ class Array
#
# array[1][2] # => nil
# dup[1][2] # => 4
sig { returns(T.self_type) }
def deep_dup
map(&:deep_dup)
T.unsafe(self).map(&:deep_dup)
end
end
@ -40,16 +43,35 @@ class Hash
#
# hash[:a][:c] # => nil
# dup[:a][:c] # => "c"
sig { returns(T.self_type) }
def deep_dup
hash = dup
each_pair do |key, value|
if (::String === key && key.frozen?) || ::Symbol === key
hash[key] = value.deep_dup
case key
when ::String, ::Symbol
hash[key] = T.unsafe(value).deep_dup
else
hash.delete(key)
hash[key.deep_dup] = value.deep_dup
hash[T.unsafe(key).deep_dup] = T.unsafe(value).deep_dup
end
end
hash
end
end
class Module
# Returns a copy of module or class if it's anonymous. If it's
# named, returns +self+.
#
# Object.deep_dup == Object # => true
# klass = Class.new
# klass.deep_dup == klass # => false
sig { returns(T.self_type) }
def deep_dup
if name.nil?
super
else
self
end
end
end

View File

@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true
#--
@ -23,27 +24,34 @@ class Object
#
# False for method objects;
# true otherwise.
def duplicable?
true
end
sig { returns(T::Boolean) }
def duplicable? = true
end
class Method
# Methods are not duplicable:
#
# method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
def duplicable?
false
end
# method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
sig { returns(FalseClass) }
def duplicable? = false
end
class UnboundMethod
# Unbound methods are not duplicable:
#
# method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
def duplicable?
false
end
# method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
sig { returns(FalseClass) }
def duplicable? = false
end
require "singleton"
module Singleton
# Singleton instances are not duplicable:
#
# Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
sig { returns(FalseClass) }
def duplicable? = false
end

View File

@ -4,6 +4,7 @@
require "context"
require "resource"
require "metafiles"
require "extend/file/atomic"
module DiskUsageExtension
sig { returns(Integer) }

View File

@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true
class String
@ -7,7 +8,6 @@ class String
# "hello".exclude? "lo" # => false
# "hello".exclude? "ol" # => true
# "hello".exclude? ?h # => false
def exclude?(string)
!include?(string)
end
sig { params(string: String).returns(T::Boolean) }
def exclude?(string) = !include?(string)
end

View File

@ -566,7 +566,7 @@ on_request: installed_on_request?, options: options)
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
formulae = [formula]
formula_deps_map = formula.recursive_dependencies
.index_by(&:name)
.each_with_object({}) { |dep, h| h[dep.name] = dep }
while (f = formulae.pop)
runtime_requirements = runtime_requirements(f)
@ -1208,7 +1208,7 @@ on_request: installed_on_request?, options: options)
formula.fetch_bottle_tab
@bottle_tab_runtime_dependencies = formula.bottle_tab_attributes
.fetch("runtime_dependencies", [])
.index_by { |dep| dep["full_name"] }
.each_with_object({}) { |dep, h| h[dep["full_name"]] = dep }
.freeze
true
rescue DownloadError, ArgumentError

View File

@ -8,8 +8,8 @@ require "utils/bottles"
require "service"
require "utils/curl"
require "deprecate_disable"
require "active_support/core_ext/hash/deep_transform_values"
require "extend/hash/deep_transform_values"
require "extend/hash/keys"
# The {Formulary} is responsible for creating instances of {Formula}.
# It is not meant to be used directly from formulae.

View File

@ -4,6 +4,7 @@
require "utils/curl"
require "json"
require "zlib"
require "extend/hash/keys"
# GitHub Packages client.
#

View File

@ -10,14 +10,10 @@ require "json/add/exception"
require "forwardable"
require "set"
# Only require "core_ext" here to ensure we're only requiring the minimum of
# what we need.
require "active_support/core_ext/array/access"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/file/atomic"
require "active_support/core_ext/hash/deep_merge"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/string/exclude"
require "extend/array"
require "extend/blank"
require "extend/enumerable"
require "extend/string"
HOMEBREW_API_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_API_DEFAULT_DOMAIN").freeze
HOMEBREW_BOTTLE_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_BOTTLE_DEFAULT_DOMAIN").freeze
@ -70,8 +66,6 @@ HOMEBREW_PULL_OR_COMMIT_URL_REGEX =
%r[https://github\.com/([\w-]+)/([\w-]+)?/(?:pull/(\d+)|commit/[0-9a-fA-F]{4,40})]
HOMEBREW_BOTTLES_EXTNAME_REGEX = /\.([a-z0-9_]+)\.bottle\.(?:(\d+)\.)?tar\.gz$/
require "extend/module"
require "extend/blank"
require "env_config"
require "macos_version"
require "os"
@ -130,7 +124,6 @@ module Homebrew
end
require "context"
require "extend/array"
require "git_repository"
require "extend/pathname"
require "cli/args"

View File

@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true
module Homebrew
@ -32,11 +32,11 @@ module Homebrew
NICE_NAME = "GNU"
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{
URL_MATCH_REGEX = T.let(%r{
^https?://
(?:(?:[^/]+?\.)*gnu\.org/(?:gnu|software)/(?<project_name>[^/]+)/
|(?<project_name>[^/]+)\.gnu\.org/?$)
}ix
}ix, Regexp)
# Whether the strategy can be applied to the provided URL.
#

View File

@ -2,6 +2,5 @@
# frozen_string_literal: true
require_relative "standalone"
require_relative "extend/module"
require "rubocops/all"

View File

@ -1,7 +1,7 @@
# typed: true
# frozen_string_literal: true
require "active_support/core_ext/array/access"
require "extend/array"
require "rubocops/shared/helper_functions"
require "shellwords"

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true
require "sorbet-runtime"
require "extend/module"
# Disable runtime checking unless enabled.
# In the future we should consider not doing this monkey patch,

View File

@ -92,7 +92,7 @@ class Tab
end
if attributes["source"]["spec"].nil?
version = PkgVersion.parse path.to_s.split("/").second_to_last
version = PkgVersion.parse(File.basename(File.dirname(path)))
attributes["source"]["spec"] = if version.head?
"head"
else

View File

@ -12,9 +12,9 @@ module Homebrew
# After updating this, run `brew vendor-gems --update=--bundler`.
HOMEBREW_BUNDLER_VERSION = "2.4.18"
# Bump this whenever a committed vendored gem is later added to gitignore.
# Bump this whenever a committed vendored gem is later added to or exclusion removed from gitignore.
# This will trigger it to reinstall properly if `brew install-bundler-gems` needs it.
VENDOR_VERSION = 4
VENDOR_VERSION = 5
private_constant :VENDOR_VERSION
RUBY_BUNDLE_VENDOR_DIRECTORY = (HOMEBREW_LIBRARY_PATH/"vendor/bundle/ruby").freeze

View File

@ -1,20 +0,0 @@
Copyright (c) 2005-2022 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,104 +0,0 @@
# frozen_string_literal: true
class Array
# Returns the tail of the array from +position+.
#
# %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
# %w( a b c d ).from(2) # => ["c", "d"]
# %w( a b c d ).from(10) # => []
# %w().from(0) # => []
# %w( a b c d ).from(-2) # => ["c", "d"]
# %w( a b c ).from(-10) # => []
def from(position)
self[position, length] || []
end
# Returns the beginning of the array up to +position+.
#
# %w( a b c d ).to(0) # => ["a"]
# %w( a b c d ).to(2) # => ["a", "b", "c"]
# %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
# %w().to(0) # => []
# %w( a b c d ).to(-2) # => ["a", "b", "c"]
# %w( a b c ).to(-10) # => []
def to(position)
if position >= 0
take position + 1
else
self[0..position]
end
end
# Returns a new array that includes the passed elements.
#
# [ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
# [ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
def including(*elements)
self + elements.flatten(1)
end
# Returns a copy of the Array excluding the specified elements.
#
# ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
# [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
#
# Note: This is an optimization of <tt>Enumerable#excluding</tt> that uses <tt>Array#-</tt>
# instead of <tt>Array#reject</tt> for performance reasons.
def excluding(*elements)
self - elements.flatten(1)
end
# Alias for #excluding.
def without(*elements)
excluding(*elements)
end
# Equal to <tt>self[1]</tt>.
#
# %w( a b c d e ).second # => "b"
def second
self[1]
end
# Equal to <tt>self[2]</tt>.
#
# %w( a b c d e ).third # => "c"
def third
self[2]
end
# Equal to <tt>self[3]</tt>.
#
# %w( a b c d e ).fourth # => "d"
def fourth
self[3]
end
# Equal to <tt>self[4]</tt>.
#
# %w( a b c d e ).fifth # => "e"
def fifth
self[4]
end
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
#
# (1..42).to_a.forty_two # => 42
def forty_two
self[41]
end
# Equal to <tt>self[-3]</tt>.
#
# %w( a b c d e ).third_to_last # => "c"
def third_to_last
self[-3]
end
# Equal to <tt>self[-2]</tt>.
#
# %w( a b c d e ).second_to_last # => "d"
def second_to_last
self[-2]
end
end

View File

@ -1,260 +0,0 @@
# frozen_string_literal: true
module Enumerable
INDEX_WITH_DEFAULT = Object.new
private_constant :INDEX_WITH_DEFAULT
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
# when we omit an identity.
# :stopdoc:
# We can't use Refinements here because Refinements with Module which will be prepended
# doesn't work well https://bugs.ruby-lang.org/issues/13446
alias :_original_sum_with_required_identity :sum
private :_original_sum_with_required_identity
# :startdoc:
# Calculates a sum from the elements.
#
# payments.sum { |p| p.price * p.tax_rate }
# payments.sum(&:price)
#
# The latter is a shortcut for:
#
# payments.inject(0) { |sum, p| sum + p.price }
#
# It can also calculate the sum without the use of a block.
#
# [5, 15, 10].sum # => 30
# ['foo', 'bar'].sum # => "foobar"
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
#
# The default sum of an empty list is zero. You can override this default:
#
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
def sum(identity = nil, &block)
if identity
_original_sum_with_required_identity(identity, &block)
elsif block_given?
map(&block).sum(identity)
else
inject(:+) || 0
end
end
# Convert an enumerable to a hash, using the block result as the key and the
# element as the value.
#
# people.index_by(&:login)
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
#
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
def index_by
if block_given?
result = {}
each { |elem| result[yield(elem)] = elem }
result
else
to_enum(:index_by) { size if respond_to?(:size) }
end
end
# Convert an enumerable to a hash, using the element as the key and the block
# result as the value.
#
# post = Post.new(title: "hey there", body: "what's up?")
#
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# # => { title: "hey there", body: "what's up?" }
#
# If an argument is passed instead of a block, it will be used as the value
# for all elements:
#
# %i( created_at updated_at ).index_with(Time.now)
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
def index_with(default = INDEX_WITH_DEFAULT)
if block_given?
result = {}
each { |elem| result[elem] = yield(elem) }
result
elsif default != INDEX_WITH_DEFAULT
result = {}
each { |elem| result[elem] = default }
result
else
to_enum(:index_with) { size if respond_to?(:size) }
end
end
# Returns +true+ if the enumerable has more than 1 element. Functionally
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
# if more than one person is over 26.
def many?
cnt = 0
if block_given?
any? do |element|
cnt += 1 if yield element
cnt > 1
end
else
any? { (cnt += 1) > 1 }
end
end
# Returns a new array that includes the passed elements.
#
# [ 1, 2, 3 ].including(4, 5)
# # => [ 1, 2, 3, 4, 5 ]
#
# ["David", "Rafael"].including %w[ Aaron Todd ]
# # => ["David", "Rafael", "Aaron", "Todd"]
def including(*elements)
to_a.including(*elements)
end
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
# collection does not include the object.
def exclude?(object)
!include?(object)
end
# Returns a copy of the enumerable excluding the specified elements.
#
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
# # => ["David", "Rafael"]
#
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
# # => ["David", "Rafael"]
#
# {foo: 1, bar: 2, baz: 3}.excluding :bar
# # => {foo: 1, baz: 3}
def excluding(*elements)
elements.flatten!(1)
reject { |element| elements.include?(element) }
end
# Alias for #excluding.
def without(*elements)
excluding(*elements)
end
# Extract the given key from each element in the enumerable.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
# # => ["David", "Rafael", "Aaron"]
#
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
# # => [[1, "David"], [2, "Rafael"]]
def pluck(*keys)
if keys.many?
map { |element| keys.map { |key| element[key] } }
else
key = keys.first
map { |element| element[key] }
end
end
# Extract the given key from the first element in the enumerable.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
# # => "David"
#
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
# # => [1, "David"]
def pick(*keys)
return if none?
if keys.many?
keys.map { |key| first[key] }
else
first[keys.first]
end
end
# Returns a new +Array+ without the blank items.
# Uses Object#blank? for determining if an item is blank.
#
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
# # => [1, 2, true]
#
# Set.new([nil, "", 1, 2])
# # => [2, 1] (or [1, 2])
#
# When called on a +Hash+, returns a new +Hash+ without the blank values.
#
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# #=> { b: 1, f: true }
def compact_blank
reject(&:blank?)
end
end
class Hash
# Hash#reject has its own definition, so this needs one too.
def compact_blank #:nodoc:
reject { |_k, v| v.blank? }
end
# Removes all blank values from the +Hash+ in place and returns self.
# Uses Object#blank? for determining if a value is blank.
#
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
# h.compact_blank!
# # => { b: 1, f: true }
def compact_blank!
# use delete_if rather than reject! because it always returns self even if nothing changed
delete_if { |_k, v| v.blank? }
end
end
class Range #:nodoc:
# Optimize range sum to use arithmetic progression if a block is not given and
# we have a range of numeric values.
def sum(identity = nil)
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
super
else
actual_last = exclude_end? ? (last - 1) : last
if actual_last >= first
sum = identity || 0
sum + (actual_last - first + 1) * (actual_last + first) / 2
else
identity || 0
end
end
end
end
# Using Refinements here in order not to expose our internal method
using Module.new {
refine Array do
alias :orig_sum :sum
end
}
class Array #:nodoc:
# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
def sum(init = nil, &block)
if init.is_a?(Numeric) || first.is_a?(Numeric)
init ||= 0
orig_sum(init, &block)
else
super
end
end
# Removes all blank elements from the +Array+ in place and returns self.
# Uses Object#blank? for determining if an item is blank.
#
# a = [1, "", nil, 2, " ", [], {}, false, true]
# a.compact_blank!
# # => [1, 2, true]
def compact_blank!
# use delete_if rather than reject! because it always returns self even if nothing changed
delete_if(&:blank?)
end
end

View File

@ -1,46 +0,0 @@
# frozen_string_literal: true
class Hash
# Returns a new hash with all values converted by the block operation.
# This includes the values from the root hash and from all
# nested hashes and arrays.
#
# 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)
end
# Destructively converts all values by using the block operation.
# This includes the values from the root hash and from all
# nested hashes and arrays.
def deep_transform_values!(&block)
_deep_transform_values_in_object!(self, &block)
end
private
# Support methods for deep transforming nested hashes and arrays.
def _deep_transform_values_in_object(object, &block)
case object
when Hash
object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
when Array
object.map { |e| _deep_transform_values_in_object(e, &block) }
else
yield(object)
end
end
def _deep_transform_values_in_object!(object, &block)
case object
when Hash
object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
when Array
object.map! { |e| _deep_transform_values_in_object!(e, &block) }
else
yield(object)
end
end
end

View File

@ -1,143 +0,0 @@
# frozen_string_literal: true
class Hash
# Returns a new hash with all keys converted to strings.
#
# hash = { name: 'Rob', age: '28' }
#
# hash.stringify_keys
# # => {"name"=>"Rob", "age"=>"28"}
def stringify_keys
transform_keys(&:to_s)
end
# Destructively converts all keys to strings. Same as
# +stringify_keys+, but modifies +self+.
def stringify_keys!
transform_keys!(&:to_s)
end
# Returns a new hash with all keys converted to symbols, as long as
# they respond to +to_sym+.
#
# hash = { 'name' => 'Rob', 'age' => '28' }
#
# hash.symbolize_keys
# # => {:name=>"Rob", :age=>"28"}
def symbolize_keys
transform_keys { |key| key.to_sym rescue key }
end
alias_method :to_options, :symbolize_keys
# Destructively converts all keys to symbols, as long as they respond
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
def symbolize_keys!
transform_keys! { |key| key.to_sym rescue key }
end
alias_method :to_options!, :symbolize_keys!
# Validates all keys in a hash match <tt>*valid_keys</tt>, raising
# +ArgumentError+ on a mismatch.
#
# Note that keys are treated differently than HashWithIndifferentAccess,
# meaning that string and symbol keys will not match.
#
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => 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
def assert_valid_keys(*valid_keys)
valid_keys.flatten!
each_key do |k|
unless valid_keys.include?(k)
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
end
end
end
# Returns a new hash with all keys converted by the block operation.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# 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)
end
# Destructively converts all keys by using the block operation.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_transform_keys!(&block)
_deep_transform_keys_in_object!(self, &block)
end
# Returns a new hash with all keys converted to strings.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
#
# hash = { person: { name: 'Rob', age: '28' } }
#
# hash.deep_stringify_keys
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
def deep_stringify_keys
deep_transform_keys(&:to_s)
end
# Destructively converts all keys to strings.
# This includes the keys from the root hash and from all
# nested hashes and arrays.
def deep_stringify_keys!
deep_transform_keys!(&:to_s)
end
# 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
# and from all nested hashes and arrays.
#
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
#
# hash.deep_symbolize_keys
# # => {:person=>{:name=>"Rob", :age=>"28"}}
def deep_symbolize_keys
deep_transform_keys { |key| key.to_sym rescue key }
end
# 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
# nested hashes and arrays.
def deep_symbolize_keys!
deep_transform_keys! { |key| key.to_sym rescue key }
end
private
# Support methods for deep transforming nested hashes and arrays.
def _deep_transform_keys_in_object(object, &block)
case object
when Hash
object.each_with_object({}) do |(key, value), result|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
end
when Array
object.map { |e| _deep_transform_keys_in_object(e, &block) }
else
object
end
end
def _deep_transform_keys_in_object!(object, &block)
case object
when Hash
object.keys.each do |key|
value = object.delete(key)
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
end
object
when Array
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
else
object
end
end
end