Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ActiveSupport from runtime #16463

Merged
merged 24 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 1 addition & 19 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Bo98 marked this conversation as resolved.
Show resolved Hide resolved

# 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-*/.*
Expand All @@ -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-*/
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/artifact/abstract_artifact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/artifact/abstract_uninstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "utils/user"
require "cask/artifact/abstract_artifact"
require "cask/pkg"
require "extend/hash/keys"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I scoped these to the files that used them, rather than in global


module Cask
module Artifact
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/artifact/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# frozen_string_literal: true

require "cask/artifact/abstract_artifact"
require "extend/hash/keys"

module Cask
module Artifact
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/artifact/pkg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

require "utils/user"
require "cask/artifact/abstract_artifact"
require "extend/hash/keys"

module Cask
module Artifact
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/artifact/relocated.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# frozen_string_literal: true

require "cask/artifact/abstract_artifact"
require "extend/hash/keys"

module Cask
module Artifact
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/cask_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "cask/cask"
require "uri"
require "utils/curl"
require "extend/hash/keys"

module Cask
# Loads a cask from various sources.
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

require "lazy_object"
require "locale"
require "extend/hash/keys"

module Cask
# Configuration for installing casks.
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/dsl/conflicts_with.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# frozen_string_literal: true

require "delegate"
require "extend/hash/keys"

module Cask
class DSL
Expand Down
6 changes: 3 additions & 3 deletions Library/Homebrew/cli/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ def initialize(&block)
# 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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor changes that are required when adding sigs to vendored methods


@constraints = []
@conflicts = []
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/dev-cmd/bottle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require "erb"
require "utils/gzip"
require "api"
require "extend/hash/deep_merge"

BOTTLE_ERB = <<-EOS.freeze
bottle do
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/dev-cmd/pr-upload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "formula"
require "github_packages"
require "github_releases"
require "extend/hash/deep_merge"

module Homebrew
module_function
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/download_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ def parse_basename(url, search_query: true)
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 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen this pattern in the wild. Very cool!

end
else
components[:path] = [url]
Expand Down
23 changes: 22 additions & 1 deletion Library/Homebrew/extend/array.rb
Original file line number Diff line number Diff line change
@@ -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>.
dduugg marked this conversation as resolved.
Show resolved Hide resolved
#
# %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.
#
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions Library/Homebrew/extend/array.rbi
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# typed: strict

class Array
sig { returns(T.nilable(Elem)) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elem is an upstream RBI creation that isn't available at runtime, so these need to be in RBI files. (And similarly for Enumerable::Enum, Hash::K, Hash::V, etc. below…)

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
30 changes: 30 additions & 0 deletions Library/Homebrew/extend/enumerable.rb
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions Library/Homebrew/extend/enumerable.rbi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# typed: strict

class Hash
sig { returns(T::Hash[Hash::K, Hash::V]) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to use a runtime sig with returns(T.self_type), but that's a bug: sorbet/sorbet#7586

def compact_blank; end
end
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true

require "fileutils"
Expand All @@ -18,7 +19,14 @@
# 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|
Expand All @@ -38,32 +46,35 @@
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:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visibility modifier added due to code comment above

basename = [
".permissions_check",
Thread.current.object_id,
Process.pid,
rand(1000000)
rand(1_000_000),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rubocop formatting change

].join(".")

file_name = join(dir, basename)
FileUtils.touch(file_name)
stat(file_name)
rescue Errno::ENOENT
file_name = nil

Check warning on line 77 in Library/Homebrew/extend/file/atomic.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/extend/file/atomic.rb#L77

Added line #L77 was not covered by tests
ensure
FileUtils.rm_f(file_name) if file_name
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true

class Hash
Expand All @@ -22,10 +23,10 @@ def deep_merge(other_hash, &block)
# 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)
Copy link
Member Author

@dduugg dduugg Jan 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rubocop-performance wanted these changes ¯\(ツ)

else
other_val
end
Expand Down
20 changes: 20 additions & 0 deletions Library/Homebrew/extend/hash/deep_merge.rbi
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions Library/Homebrew/extend/hash/deep_transform_values.rb
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 25 in Library/Homebrew/extend/hash/deep_transform_values.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/extend/hash/deep_transform_values.rb#L25

Added line #L25 was not covered by tests
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
17 changes: 17 additions & 0 deletions Library/Homebrew/extend/hash/deep_transform_values.rbi
Original file line number Diff line number Diff line change
@@ -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
Loading