Merge pull request #16463 from dduugg/no-active-support
Remove ActiveSupport from runtime
This commit is contained in:
commit
86e1c8aacf
20
.gitignore
vendored
20
.gitignore
vendored
@ -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-*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/artifact/abstract_artifact"
|
||||
require "extend/hash/keys"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
|
||||
@ -5,6 +5,7 @@ require "plist"
|
||||
|
||||
require "utils/user"
|
||||
require "cask/artifact/abstract_artifact"
|
||||
require "extend/hash/keys"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/artifact/abstract_artifact"
|
||||
require "extend/hash/keys"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -5,6 +5,7 @@ require "json"
|
||||
|
||||
require "lazy_object"
|
||||
require "locale"
|
||||
require "extend/hash/keys"
|
||||
|
||||
module Cask
|
||||
# Configuration for installing casks.
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "delegate"
|
||||
require "extend/hash/keys"
|
||||
|
||||
module Cask
|
||||
class DSL
|
||||
|
||||
@ -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 = []
|
||||
|
||||
@ -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
|
||||
|
||||
@ -5,6 +5,7 @@ require "cli/parser"
|
||||
require "formula"
|
||||
require "github_packages"
|
||||
require "github_releases"
|
||||
require "extend/hash/deep_merge"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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
|
||||
|
||||
15
Library/Homebrew/extend/array.rbi
Normal file
15
Library/Homebrew/extend/array.rbi
Normal 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
|
||||
30
Library/Homebrew/extend/enumerable.rb
Normal file
30
Library/Homebrew/extend/enumerable.rb
Normal 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
|
||||
6
Library/Homebrew/extend/enumerable.rbi
Normal file
6
Library/Homebrew/extend/enumerable.rbi
Normal file
@ -0,0 +1,6 @@
|
||||
# typed: strict
|
||||
|
||||
class Hash
|
||||
sig { returns(T::Hash[Hash::K, Hash::V]) }
|
||||
def compact_blank; end
|
||||
end
|
||||
@ -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
|
||||
@ -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
|
||||
20
Library/Homebrew/extend/hash/deep_merge.rbi
Normal file
20
Library/Homebrew/extend/hash/deep_merge.rbi
Normal 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
|
||||
46
Library/Homebrew/extend/hash/deep_transform_values.rb
Normal file
46
Library/Homebrew/extend/hash/deep_transform_values.rb
Normal 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
|
||||
17
Library/Homebrew/extend/hash/deep_transform_values.rbi
Normal file
17
Library/Homebrew/extend/hash/deep_transform_values.rbi
Normal 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
|
||||
117
Library/Homebrew/extend/hash/keys.rb
Normal file
117
Library/Homebrew/extend/hash/keys.rb
Normal 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
|
||||
30
Library/Homebrew/extend/hash/keys.rbi
Normal file
30
Library/Homebrew/extend/hash/keys.rbi
Normal 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
|
||||
@ -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
|
||||
@ -1,3 +1,4 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
#--
|
||||
@ -23,9 +24,8 @@ class Object
|
||||
#
|
||||
# False for method objects;
|
||||
# true otherwise.
|
||||
def duplicable?
|
||||
true
|
||||
end
|
||||
sig { returns(T::Boolean) }
|
||||
def duplicable? = true
|
||||
end
|
||||
|
||||
class Method
|
||||
@ -33,9 +33,8 @@ class Method
|
||||
#
|
||||
# method(:puts).duplicable? # => false
|
||||
# method(:puts).dup # => TypeError: allocator undefined for Method
|
||||
def duplicable?
|
||||
false
|
||||
end
|
||||
sig { returns(FalseClass) }
|
||||
def duplicable? = false
|
||||
end
|
||||
|
||||
class UnboundMethod
|
||||
@ -43,7 +42,16 @@ class UnboundMethod
|
||||
#
|
||||
# method(:puts).unbind.duplicable? # => false
|
||||
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
|
||||
def duplicable?
|
||||
false
|
||||
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
|
||||
@ -4,6 +4,7 @@
|
||||
require "context"
|
||||
require "resource"
|
||||
require "metafiles"
|
||||
require "extend/file/atomic"
|
||||
|
||||
module DiskUsageExtension
|
||||
sig { returns(Integer) }
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
require "utils/curl"
|
||||
require "json"
|
||||
require "zlib"
|
||||
require "extend/hash/keys"
|
||||
|
||||
# GitHub Packages client.
|
||||
#
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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.
|
||||
#
|
||||
|
||||
@ -2,6 +2,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "standalone"
|
||||
require_relative "extend/module"
|
||||
|
||||
require "rubocops/all"
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user