2023-11-26 10:05:14 -08:00
|
|
|
# typed: strict
|
2023-11-26 09:33:42 -08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class Object
|
|
|
|
# An object is blank if it's false, empty, or a whitespace string.
|
|
|
|
# For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
|
|
|
|
#
|
|
|
|
# This simplifies
|
|
|
|
#
|
|
|
|
# !address || address.empty?
|
|
|
|
#
|
|
|
|
# to
|
|
|
|
#
|
|
|
|
# address.blank?
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2023-11-26 09:33:42 -08:00
|
|
|
def blank?
|
|
|
|
respond_to?(:empty?) ? !!T.unsafe(self).empty? : false
|
|
|
|
end
|
|
|
|
|
|
|
|
# An object is present if it's not blank.
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = !blank?
|
2023-11-26 09:33:42 -08:00
|
|
|
|
|
|
|
# Returns the receiver if it's present otherwise returns +nil+.
|
|
|
|
# <tt>object.presence</tt> is equivalent to
|
|
|
|
#
|
|
|
|
# object.present? ? object : nil
|
|
|
|
#
|
|
|
|
# For example, something like
|
|
|
|
#
|
|
|
|
# state = params[:state] if params[:state].present?
|
|
|
|
# country = params[:country] if params[:country].present?
|
|
|
|
# region = state || country || 'US'
|
|
|
|
#
|
|
|
|
# becomes
|
|
|
|
#
|
|
|
|
# region = params[:state].presence || params[:country].presence || 'US'
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T.nilable(T.self_type)) }
|
2023-11-26 09:33:42 -08:00
|
|
|
def presence
|
|
|
|
self if present?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class NilClass
|
|
|
|
# +nil+ is blank:
|
|
|
|
#
|
|
|
|
# nil.blank? # => true
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(TrueClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def blank? = true
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(FalseClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = false # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class FalseClass
|
|
|
|
# +false+ is blank:
|
|
|
|
#
|
|
|
|
# false.blank? # => true
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(TrueClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def blank? = true
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(FalseClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = false # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class TrueClass
|
|
|
|
# +true+ is not blank:
|
|
|
|
#
|
|
|
|
# true.blank? # => false
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(FalseClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def blank? = false
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(TrueClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = true # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class Array
|
|
|
|
# An array is blank if it's empty:
|
|
|
|
#
|
|
|
|
# [].blank? # => true
|
|
|
|
# [1,2,3].blank? # => false
|
|
|
|
#
|
|
|
|
# @return [true, false]
|
2023-11-26 10:10:43 -08:00
|
|
|
alias blank? empty?
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = !empty? # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class Hash
|
|
|
|
# A hash is blank if it's empty:
|
|
|
|
#
|
|
|
|
# {}.blank? # => true
|
|
|
|
# { key: 'value' }.blank? # => false
|
|
|
|
#
|
|
|
|
# @return [true, false]
|
2023-11-26 10:10:43 -08:00
|
|
|
alias blank? empty?
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = !empty? # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class Symbol
|
|
|
|
# A Symbol is blank if it's empty:
|
|
|
|
#
|
|
|
|
# :''.blank? # => true
|
|
|
|
# :symbol.blank? # => false
|
2023-11-26 10:10:43 -08:00
|
|
|
alias blank? empty?
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = !empty? # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class String
|
2024-01-18 22:18:42 +00:00
|
|
|
BLANK_RE = /\A[[:space:]]*\z/
|
2023-12-16 11:47:46 +00:00
|
|
|
# This is a cache that is intentionally mutable
|
|
|
|
# rubocop:disable Style/MutableConstant
|
2023-11-26 15:36:02 -08:00
|
|
|
ENCODED_BLANKS_ = T.let(Hash.new do |h, enc|
|
|
|
|
h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
|
|
|
|
end, T::Hash[Encoding, Regexp])
|
2023-12-16 11:47:46 +00:00
|
|
|
# rubocop:enable Style/MutableConstant
|
2023-11-26 09:33:42 -08:00
|
|
|
|
|
|
|
# A string is blank if it's empty or contains whitespaces only:
|
|
|
|
#
|
|
|
|
# ''.blank? # => true
|
|
|
|
# ' '.blank? # => true
|
|
|
|
# "\t\n\r".blank? # => true
|
|
|
|
# ' blah '.blank? # => false
|
|
|
|
#
|
|
|
|
# Unicode whitespace is supported:
|
|
|
|
#
|
|
|
|
# "\u00a0".blank? # => true
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2023-11-26 09:33:42 -08:00
|
|
|
def blank?
|
|
|
|
# The regexp that matches blank strings is expensive. For the case of empty
|
|
|
|
# strings we can speed up this method (~3.5x) with an empty? call. The
|
|
|
|
# penalty for the rest of strings is marginal.
|
|
|
|
empty? ||
|
|
|
|
begin
|
|
|
|
BLANK_RE.match?(self)
|
|
|
|
rescue Encoding::CompatibilityError
|
2023-11-26 15:36:02 -08:00
|
|
|
T.must(ENCODED_BLANKS_[encoding]).match?(self)
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(T::Boolean) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = !blank? # :nodoc:
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class Numeric # :nodoc:
|
|
|
|
# No number is blank:
|
|
|
|
#
|
|
|
|
# 1.blank? # => false
|
|
|
|
# 0.blank? # => false
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(FalseClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def blank? = false
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(TrueClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = true
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
class Time # :nodoc:
|
|
|
|
# No Time is blank:
|
|
|
|
#
|
|
|
|
# Time.now.blank? # => false
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(FalseClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def blank? = false
|
2023-11-26 09:33:42 -08:00
|
|
|
|
2023-11-26 10:05:14 -08:00
|
|
|
sig { returns(TrueClass) }
|
2024-01-20 10:13:18 -08:00
|
|
|
def present? = true
|
2023-11-26 09:33:42 -08:00
|
|
|
end
|