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/*/lib
|
||||||
!**/vendor/bundle/ruby/*/gems/addressable-*/data
|
!**/vendor/bundle/ruby/*/gems/addressable-*/data
|
||||||
!**/vendor/bundle/ruby/*/gems/public_suffix-*/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
|
# Ignore partially included gems where we don't need all files
|
||||||
**/vendor/gems/mechanize-*/.*
|
**/vendor/gems/mechanize-*/.*
|
||||||
@ -89,6 +70,7 @@
|
|||||||
**/vendor/gems/mechanize-*/test/
|
**/vendor/gems/mechanize-*/test/
|
||||||
|
|
||||||
# Ignore dependencies we don't wish to vendor
|
# Ignore dependencies we don't wish to vendor
|
||||||
|
**/vendor/bundle/ruby/*/gems/activesupport-*/
|
||||||
**/vendor/bundle/ruby/*/gems/ast-*/
|
**/vendor/bundle/ruby/*/gems/ast-*/
|
||||||
**/vendor/bundle/ruby/*/gems/bootsnap-*/
|
**/vendor/bundle/ruby/*/gems/bootsnap-*/
|
||||||
**/vendor/bundle/ruby/*/gems/bundler-*/
|
**/vendor/bundle/ruby/*/gems/bundler-*/
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "attrable"
|
require "attrable"
|
||||||
require "active_support/core_ext/object/deep_dup"
|
require "extend/object/deep_dup"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
module Artifact
|
module Artifact
|
||||||
|
|||||||
@ -6,6 +6,7 @@ require "timeout"
|
|||||||
require "utils/user"
|
require "utils/user"
|
||||||
require "cask/artifact/abstract_artifact"
|
require "cask/artifact/abstract_artifact"
|
||||||
require "cask/pkg"
|
require "cask/pkg"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
module Artifact
|
module Artifact
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cask/artifact/abstract_artifact"
|
require "cask/artifact/abstract_artifact"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
module Artifact
|
module Artifact
|
||||||
|
|||||||
@ -5,6 +5,7 @@ require "plist"
|
|||||||
|
|
||||||
require "utils/user"
|
require "utils/user"
|
||||||
require "cask/artifact/abstract_artifact"
|
require "cask/artifact/abstract_artifact"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
module Artifact
|
module Artifact
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "cask/artifact/abstract_artifact"
|
require "cask/artifact/abstract_artifact"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
module Artifact
|
module Artifact
|
||||||
|
|||||||
@ -5,6 +5,7 @@ require "cask/cache"
|
|||||||
require "cask/cask"
|
require "cask/cask"
|
||||||
require "uri"
|
require "uri"
|
||||||
require "utils/curl"
|
require "utils/curl"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
# Loads a cask from various sources.
|
# Loads a cask from various sources.
|
||||||
|
|||||||
@ -5,6 +5,7 @@ require "json"
|
|||||||
|
|
||||||
require "lazy_object"
|
require "lazy_object"
|
||||||
require "locale"
|
require "locale"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
# Configuration for installing casks.
|
# Configuration for installing casks.
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "delegate"
|
require "delegate"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
module Cask
|
module Cask
|
||||||
class DSL
|
class DSL
|
||||||
|
|||||||
@ -126,9 +126,9 @@ module Homebrew
|
|||||||
# Filter out Sorbet runtime type checking method calls.
|
# Filter out Sorbet runtime type checking method calls.
|
||||||
cmd_location = T.must(caller_locations).select do |location|
|
cmd_location = T.must(caller_locations).select do |location|
|
||||||
T.must(location.path).exclude?("/gems/sorbet-runtime-")
|
T.must(location.path).exclude?("/gems/sorbet-runtime-")
|
||||||
end.second
|
end.fetch(1)
|
||||||
@command_name = cmd_location.label.chomp("_args").tr("_", "-")
|
@command_name = T.must(cmd_location.label).chomp("_args").tr("_", "-")
|
||||||
@is_dev_cmd = cmd_location.absolute_path.start_with?(Commands::HOMEBREW_DEV_CMD_PATH)
|
@is_dev_cmd = T.must(cmd_location.absolute_path).start_with?(Commands::HOMEBREW_DEV_CMD_PATH)
|
||||||
|
|
||||||
@constraints = []
|
@constraints = []
|
||||||
@conflicts = []
|
@conflicts = []
|
||||||
|
|||||||
@ -11,6 +11,7 @@ require "utils/inreplace"
|
|||||||
require "erb"
|
require "erb"
|
||||||
require "utils/gzip"
|
require "utils/gzip"
|
||||||
require "api"
|
require "api"
|
||||||
|
require "extend/hash/deep_merge"
|
||||||
|
|
||||||
BOTTLE_ERB = <<-EOS.freeze
|
BOTTLE_ERB = <<-EOS.freeze
|
||||||
bottle do
|
bottle do
|
||||||
|
|||||||
@ -5,6 +5,7 @@ require "cli/parser"
|
|||||||
require "formula"
|
require "formula"
|
||||||
require "github_packages"
|
require "github_packages"
|
||||||
require "github_releases"
|
require "github_releases"
|
||||||
|
require "extend/hash/deep_merge"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module_function
|
module_function
|
||||||
|
|||||||
@ -360,7 +360,7 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
|
|||||||
end
|
end
|
||||||
|
|
||||||
if search_query && (uri_query = uri.query.presence)
|
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
|
end
|
||||||
else
|
else
|
||||||
components[:path] = [url]
|
components[:path] = [url]
|
||||||
|
|||||||
@ -1,7 +1,27 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Array
|
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
|
# Converts the array to a comma-separated sentence where the last element is
|
||||||
# joined by the connector word.
|
# joined by the connector word.
|
||||||
#
|
#
|
||||||
@ -48,6 +68,7 @@ class Array
|
|||||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
sig { params(words_connector: String, two_words_connector: String, last_word_connector: String).returns(String) }
|
||||||
def to_sentence(words_connector: ", ", two_words_connector: " and ", last_word_connector: " and ")
|
def to_sentence(words_connector: ", ", two_words_connector: " and ", last_word_connector: " and ")
|
||||||
case length
|
case length
|
||||||
when 0
|
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
@ -18,7 +19,14 @@ class File
|
|||||||
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
||||||
# file.write('hello')
|
# file.write('hello')
|
||||||
# end
|
# 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)
|
require "tempfile" unless defined?(Tempfile)
|
||||||
|
|
||||||
Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
|
Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
|
||||||
@ -38,32 +46,35 @@ class File
|
|||||||
if old_stat
|
if old_stat
|
||||||
# Set correct permissions on new file
|
# Set correct permissions on new file
|
||||||
begin
|
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
|
# 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
|
rescue Errno::EPERM, Errno::EACCES
|
||||||
# Changing file ownership failed, moving on.
|
# Changing file ownership failed, moving on.
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overwrite original file with temp file
|
# Overwrite original file with temp file
|
||||||
rename(temp_file.path, file_name)
|
rename(T.must(temp_file.path), file_name)
|
||||||
return_val
|
return_val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Private utility method.
|
# 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 = [
|
basename = [
|
||||||
".permissions_check",
|
".permissions_check",
|
||||||
Thread.current.object_id,
|
Thread.current.object_id,
|
||||||
Process.pid,
|
Process.pid,
|
||||||
rand(1000000)
|
rand(1_000_000),
|
||||||
].join(".")
|
].join(".")
|
||||||
|
|
||||||
file_name = join(dir, basename)
|
file_name = join(dir, basename)
|
||||||
FileUtils.touch(file_name)
|
FileUtils.touch(file_name)
|
||||||
stat(file_name)
|
stat(file_name)
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
file_name = nil
|
||||||
ensure
|
ensure
|
||||||
FileUtils.rm_f(file_name) if file_name
|
FileUtils.rm_f(file_name) if file_name
|
||||||
end
|
end
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Hash
|
class Hash
|
||||||
@ -22,10 +23,10 @@ class Hash
|
|||||||
# Same as +deep_merge+, but modifies +self+.
|
# Same as +deep_merge+, but modifies +self+.
|
||||||
def deep_merge!(other_hash, &block)
|
def deep_merge!(other_hash, &block)
|
||||||
merge!(other_hash) do |key, this_val, other_val|
|
merge!(other_hash) do |key, this_val, other_val|
|
||||||
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
if T.unsafe(this_val).is_a?(Hash) && other_val.is_a?(Hash)
|
||||||
this_val.deep_merge(other_val, &block)
|
T.unsafe(this_val).deep_merge(other_val, &block)
|
||||||
elsif block_given?
|
elsif block
|
||||||
block.call(key, this_val, other_val)
|
yield(key, this_val, other_val)
|
||||||
else
|
else
|
||||||
other_val
|
other_val
|
||||||
end
|
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "active_support/core_ext/object/duplicable"
|
require "extend/object/duplicable"
|
||||||
|
|
||||||
class Object
|
class Object
|
||||||
# Returns a deep copy of object if it's duplicable. If it's
|
# 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
|
# object.instance_variable_defined?(:@a) # => false
|
||||||
# dup.instance_variable_defined?(:@a) # => true
|
# dup.instance_variable_defined?(:@a) # => true
|
||||||
|
sig { returns(T.self_type) }
|
||||||
def deep_dup
|
def deep_dup
|
||||||
duplicable? ? dup : self
|
duplicable? ? dup : self
|
||||||
end
|
end
|
||||||
@ -26,8 +28,9 @@ class Array
|
|||||||
#
|
#
|
||||||
# array[1][2] # => nil
|
# array[1][2] # => nil
|
||||||
# dup[1][2] # => 4
|
# dup[1][2] # => 4
|
||||||
|
sig { returns(T.self_type) }
|
||||||
def deep_dup
|
def deep_dup
|
||||||
map(&:deep_dup)
|
T.unsafe(self).map(&:deep_dup)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -40,16 +43,35 @@ class Hash
|
|||||||
#
|
#
|
||||||
# hash[:a][:c] # => nil
|
# hash[:a][:c] # => nil
|
||||||
# dup[:a][:c] # => "c"
|
# dup[:a][:c] # => "c"
|
||||||
|
sig { returns(T.self_type) }
|
||||||
def deep_dup
|
def deep_dup
|
||||||
hash = dup
|
hash = dup
|
||||||
each_pair do |key, value|
|
each_pair do |key, value|
|
||||||
if (::String === key && key.frozen?) || ::Symbol === key
|
case key
|
||||||
hash[key] = value.deep_dup
|
when ::String, ::Symbol
|
||||||
|
hash[key] = T.unsafe(value).deep_dup
|
||||||
else
|
else
|
||||||
hash.delete(key)
|
hash.delete(key)
|
||||||
hash[key.deep_dup] = value.deep_dup
|
hash[T.unsafe(key).deep_dup] = T.unsafe(value).deep_dup
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
#--
|
#--
|
||||||
@ -23,27 +24,34 @@ class Object
|
|||||||
#
|
#
|
||||||
# False for method objects;
|
# False for method objects;
|
||||||
# true otherwise.
|
# true otherwise.
|
||||||
def duplicable?
|
sig { returns(T::Boolean) }
|
||||||
true
|
def duplicable? = true
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class Method
|
class Method
|
||||||
# Methods are not duplicable:
|
# Methods are not duplicable:
|
||||||
#
|
#
|
||||||
# method(:puts).duplicable? # => false
|
# method(:puts).duplicable? # => false
|
||||||
# method(:puts).dup # => TypeError: allocator undefined for Method
|
# method(:puts).dup # => TypeError: allocator undefined for Method
|
||||||
def duplicable?
|
sig { returns(FalseClass) }
|
||||||
false
|
def duplicable? = false
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class UnboundMethod
|
class UnboundMethod
|
||||||
# Unbound methods are not duplicable:
|
# Unbound methods are not duplicable:
|
||||||
#
|
#
|
||||||
# method(:puts).unbind.duplicable? # => false
|
# method(:puts).unbind.duplicable? # => false
|
||||||
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
|
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
|
||||||
def duplicable?
|
sig { returns(FalseClass) }
|
||||||
false
|
def duplicable? = false
|
||||||
end
|
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
|
end
|
||||||
@ -4,6 +4,7 @@
|
|||||||
require "context"
|
require "context"
|
||||||
require "resource"
|
require "resource"
|
||||||
require "metafiles"
|
require "metafiles"
|
||||||
|
require "extend/file/atomic"
|
||||||
|
|
||||||
module DiskUsageExtension
|
module DiskUsageExtension
|
||||||
sig { returns(Integer) }
|
sig { returns(Integer) }
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class String
|
class String
|
||||||
@ -7,7 +8,6 @@ class String
|
|||||||
# "hello".exclude? "lo" # => false
|
# "hello".exclude? "lo" # => false
|
||||||
# "hello".exclude? "ol" # => true
|
# "hello".exclude? "ol" # => true
|
||||||
# "hello".exclude? ?h # => false
|
# "hello".exclude? ?h # => false
|
||||||
def exclude?(string)
|
sig { params(string: String).returns(T::Boolean) }
|
||||||
!include?(string)
|
def exclude?(string) = !include?(string)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
@ -566,7 +566,7 @@ on_request: installed_on_request?, options: options)
|
|||||||
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
|
unsatisfied_reqs = Hash.new { |h, k| h[k] = [] }
|
||||||
formulae = [formula]
|
formulae = [formula]
|
||||||
formula_deps_map = formula.recursive_dependencies
|
formula_deps_map = formula.recursive_dependencies
|
||||||
.index_by(&:name)
|
.each_with_object({}) { |dep, h| h[dep.name] = dep }
|
||||||
|
|
||||||
while (f = formulae.pop)
|
while (f = formulae.pop)
|
||||||
runtime_requirements = runtime_requirements(f)
|
runtime_requirements = runtime_requirements(f)
|
||||||
@ -1208,7 +1208,7 @@ on_request: installed_on_request?, options: options)
|
|||||||
formula.fetch_bottle_tab
|
formula.fetch_bottle_tab
|
||||||
@bottle_tab_runtime_dependencies = formula.bottle_tab_attributes
|
@bottle_tab_runtime_dependencies = formula.bottle_tab_attributes
|
||||||
.fetch("runtime_dependencies", [])
|
.fetch("runtime_dependencies", [])
|
||||||
.index_by { |dep| dep["full_name"] }
|
.each_with_object({}) { |dep, h| h[dep["full_name"]] = dep }
|
||||||
.freeze
|
.freeze
|
||||||
true
|
true
|
||||||
rescue DownloadError, ArgumentError
|
rescue DownloadError, ArgumentError
|
||||||
|
|||||||
@ -8,8 +8,8 @@ require "utils/bottles"
|
|||||||
require "service"
|
require "service"
|
||||||
require "utils/curl"
|
require "utils/curl"
|
||||||
require "deprecate_disable"
|
require "deprecate_disable"
|
||||||
|
require "extend/hash/deep_transform_values"
|
||||||
require "active_support/core_ext/hash/deep_transform_values"
|
require "extend/hash/keys"
|
||||||
|
|
||||||
# The {Formulary} is responsible for creating instances of {Formula}.
|
# The {Formulary} is responsible for creating instances of {Formula}.
|
||||||
# It is not meant to be used directly from formulae.
|
# It is not meant to be used directly from formulae.
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
require "utils/curl"
|
require "utils/curl"
|
||||||
require "json"
|
require "json"
|
||||||
require "zlib"
|
require "zlib"
|
||||||
|
require "extend/hash/keys"
|
||||||
|
|
||||||
# GitHub Packages client.
|
# GitHub Packages client.
|
||||||
#
|
#
|
||||||
|
|||||||
@ -10,14 +10,10 @@ require "json/add/exception"
|
|||||||
require "forwardable"
|
require "forwardable"
|
||||||
require "set"
|
require "set"
|
||||||
|
|
||||||
# Only require "core_ext" here to ensure we're only requiring the minimum of
|
require "extend/array"
|
||||||
# what we need.
|
require "extend/blank"
|
||||||
require "active_support/core_ext/array/access"
|
require "extend/enumerable"
|
||||||
require "active_support/core_ext/enumerable"
|
require "extend/string"
|
||||||
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"
|
|
||||||
|
|
||||||
HOMEBREW_API_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_API_DEFAULT_DOMAIN").freeze
|
HOMEBREW_API_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_API_DEFAULT_DOMAIN").freeze
|
||||||
HOMEBREW_BOTTLE_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_BOTTLE_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})]
|
%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$/
|
HOMEBREW_BOTTLES_EXTNAME_REGEX = /\.([a-z0-9_]+)\.bottle\.(?:(\d+)\.)?tar\.gz$/
|
||||||
|
|
||||||
require "extend/module"
|
|
||||||
require "extend/blank"
|
|
||||||
require "env_config"
|
require "env_config"
|
||||||
require "macos_version"
|
require "macos_version"
|
||||||
require "os"
|
require "os"
|
||||||
@ -130,7 +124,6 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "context"
|
require "context"
|
||||||
require "extend/array"
|
|
||||||
require "git_repository"
|
require "git_repository"
|
||||||
require "extend/pathname"
|
require "extend/pathname"
|
||||||
require "cli/args"
|
require "cli/args"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# typed: true
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
@ -32,11 +32,11 @@ module Homebrew
|
|||||||
NICE_NAME = "GNU"
|
NICE_NAME = "GNU"
|
||||||
|
|
||||||
# The `Regexp` used to determine if the strategy applies to the URL.
|
# The `Regexp` used to determine if the strategy applies to the URL.
|
||||||
URL_MATCH_REGEX = %r{
|
URL_MATCH_REGEX = T.let(%r{
|
||||||
^https?://
|
^https?://
|
||||||
(?:(?:[^/]+?\.)*gnu\.org/(?:gnu|software)/(?<project_name>[^/]+)/
|
(?:(?:[^/]+?\.)*gnu\.org/(?:gnu|software)/(?<project_name>[^/]+)/
|
||||||
|(?<project_name>[^/]+)\.gnu\.org/?$)
|
|(?<project_name>[^/]+)\.gnu\.org/?$)
|
||||||
}ix
|
}ix, Regexp)
|
||||||
|
|
||||||
# Whether the strategy can be applied to the provided URL.
|
# Whether the strategy can be applied to the provided URL.
|
||||||
#
|
#
|
||||||
|
|||||||
@ -2,6 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "standalone"
|
require_relative "standalone"
|
||||||
require_relative "extend/module"
|
|
||||||
|
|
||||||
require "rubocops/all"
|
require "rubocops/all"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# typed: true
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "active_support/core_ext/array/access"
|
require "extend/array"
|
||||||
require "rubocops/shared/helper_functions"
|
require "rubocops/shared/helper_functions"
|
||||||
require "shellwords"
|
require "shellwords"
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "sorbet-runtime"
|
require "sorbet-runtime"
|
||||||
|
require "extend/module"
|
||||||
|
|
||||||
# Disable runtime checking unless enabled.
|
# Disable runtime checking unless enabled.
|
||||||
# In the future we should consider not doing this monkey patch,
|
# In the future we should consider not doing this monkey patch,
|
||||||
|
|||||||
@ -92,7 +92,7 @@ class Tab
|
|||||||
end
|
end
|
||||||
|
|
||||||
if attributes["source"]["spec"].nil?
|
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?
|
attributes["source"]["spec"] = if version.head?
|
||||||
"head"
|
"head"
|
||||||
else
|
else
|
||||||
|
|||||||
@ -12,9 +12,9 @@ module Homebrew
|
|||||||
# After updating this, run `brew vendor-gems --update=--bundler`.
|
# After updating this, run `brew vendor-gems --update=--bundler`.
|
||||||
HOMEBREW_BUNDLER_VERSION = "2.4.18"
|
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.
|
# This will trigger it to reinstall properly if `brew install-bundler-gems` needs it.
|
||||||
VENDOR_VERSION = 4
|
VENDOR_VERSION = 5
|
||||||
private_constant :VENDOR_VERSION
|
private_constant :VENDOR_VERSION
|
||||||
|
|
||||||
RUBY_BUNDLE_VENDOR_DIRECTORY = (HOMEBREW_LIBRARY_PATH/"vendor/bundle/ruby").freeze
|
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