Skip to main content
โšก Calmops

Implementing a blank? Helper in Ruby

Introduction

Ruby on Rails provides a blank? method on all objects, but in plain Ruby you often need to check whether a value is “empty” in a broad sense โ€” nil, an empty string, a whitespace-only string, an empty array, or false. This article shows how to implement and use a blank? helper effectively.

The Problem

Checking for “emptiness” in Ruby requires handling multiple cases:

value = nil
value = ""
value = "   "   # whitespace only
value = []
value = {}
value = false

A naive check like if value misses empty strings and whitespace. value.empty? raises NoMethodError on nil. You need something more robust.

A Simple blank? Implementation

class Client
  # Returns true if all credential values are present (non-blank)
  # @return [Boolean]
  def credentials?
    credentials.values.none? { |v| blank?(v) }
  end

  private

  # Returns true if the value is nil, false, empty, or whitespace-only
  # @param s [Object] any value
  # @return [Boolean]
  def blank?(s)
    s.respond_to?(:empty?) ? s.empty? : !s
  end
end

This works because:

  • nil.respond_to?(:empty?) โ†’ false, so !nil โ†’ true (nil is blank)
  • false.respond_to?(:empty?) โ†’ false, so !false โ†’ true (false is blank)
  • "".respond_to?(:empty?) โ†’ true, so "".empty? โ†’ true (empty string is blank)
  • "hello".respond_to?(:empty?) โ†’ true, so "hello".empty? โ†’ false (not blank)
  • [].respond_to?(:empty?) โ†’ true, so [].empty? โ†’ true (empty array is blank)

Handling Whitespace-Only Strings

The simple version above treats " " as non-blank (it’s not empty). To also treat whitespace-only strings as blank:

def blank?(s)
  if s.respond_to?(:strip)
    s.strip.empty?
  elsif s.respond_to?(:empty?)
    s.empty?
  else
    !s
  end
end

blank?(nil)     # => true
blank?(false)   # => true
blank?("")      # => true
blank?("   ")   # => true  (whitespace only)
blank?("hello") # => false
blank?([])      # => true
blank?([1, 2])  # => false
blank?({})      # => true
blank?(0)       # => false  (0 is not blank โ€” unlike Rails)

Module Version for Reuse

Wrap it in a module so any class can use it:

module Blankable
  # Returns true if value is nil, false, empty, or whitespace-only
  def blank?(value)
    case value
    when NilClass, FalseClass
      true
    when String
      value.strip.empty?
    when Array, Hash
      value.empty?
    else
      false
    end
  end

  # Opposite of blank?
  def present?(value)
    !blank?(value)
  end
end

class UserValidator
  include Blankable

  def validate(user)
    errors = []
    errors << "Name is required"  if blank?(user[:name])
    errors << "Email is required" if blank?(user[:email])
    errors
  end
end

validator = UserValidator.new
puts validator.validate({ name: "", email: "[email protected]" }).inspect
# => ["Name is required"]

puts validator.validate({ name: "Alice", email: "[email protected]" }).inspect
# => []

Rails’ blank? Method

If you’re using Rails (ActiveSupport), blank? is already defined on all objects:

nil.blank?      # => true
false.blank?    # => true
"".blank?       # => true
"   ".blank?    # => true
[].blank?       # => true
{}.blank?       # => true
0.blank?        # => false
"hello".blank?  # => false
[1].blank?      # => false

# present? is the opposite
"hello".present?  # => true
nil.present?      # => false

You can use ActiveSupport standalone without the full Rails stack:

require 'active_support/core_ext/object/blank'

Practical Usage Patterns

Validating Required Fields

def create_user(params)
  required = [:name, :email, :password]
  missing  = required.select { |field| blank?(params[field]) }

  unless missing.empty?
    raise ArgumentError, "Missing required fields: #{missing.join(', ')}"
  end

  User.new(params)
end

Default Values with present?

def display_name(user)
  # Use nickname if present, fall back to full name
  if present?(user[:nickname])
    user[:nickname]
  else
    "#{user[:first_name]} #{user[:last_name]}".strip
  end
end

Filtering Blank Values from Collections

def clean_tags(tags)
  tags.reject { |tag| blank?(tag) }.map(&:strip).uniq
end

clean_tags(["ruby", "", "  ", "rails", nil, "ruby"])
# => ["ruby", "rails"]

Safe String Coercion

def to_s_or_nil(value)
  str = value.to_s.strip
  blank?(str) ? nil : str
end

to_s_or_nil(nil)     # => nil
to_s_or_nil("")      # => nil
to_s_or_nil("  ")    # => nil
to_s_or_nil("hello") # => "hello"

Comparison: nil?, empty?, blank?

Value .nil? .empty? blank?
nil true NoMethodError true
false false NoMethodError true
"" false true true
" " false false true
"hello" false false false
[] false true true
[1] false false false
0 false NoMethodError false

Resources

Comments