172 lines
3.7 KiB
Ruby
Raw Normal View History

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
BLANK_RE = /\A[[:space:]]*\z/
# 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])
# 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