philiprehberger-guard_clause

Tests Gem Version Last updated

Expressive guard clause DSL for method precondition validation

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-guard_clause"

Or install directly:

gem install philiprehberger-guard_clause

Usage

require "philiprehberger/guard_clause"

Philiprehberger::GuardClause.guard(name).not_nil('name is required').not_empty
Philiprehberger::GuardClause.guard(age).not_nil.positive.gte(18)

Chaining Guards

Philiprehberger::GuardClause.guard(price)
  .not_nil('price is required')
  .positive('price must be positive')
  .lte(10_000, 'price exceeds maximum')

Regex Matching

Philiprehberger::GuardClause.guard(email).matches(/@/, 'invalid email format')

Inclusion Check

Philiprehberger::GuardClause.guard(role).one_of(%i[admin user guest], 'invalid role')

Type Checking

Philiprehberger::GuardClause.guard(user).is_a(User, message: "expected a User instance")
Philiprehberger::GuardClause.guard(count).is_a(Integer)

Range Check

Philiprehberger::GuardClause.guard(age).between(18, 120, message: "age out of range")

Length Guards

Philiprehberger::GuardClause.guard(password).min_length(8, message: "password too short")
Philiprehberger::GuardClause.guard(username).max_length(20, message: "username too long")

Custom Predicate

Philiprehberger::GuardClause.guard(number).satisfies(message: "must be even") { |v| v.even? }

Present Guard

Validates value is not nil, not empty, and not blank (whitespace-only strings):

Philiprehberger::GuardClause.guard(name).present(message: "name is required")
Philiprehberger::GuardClause.guard(tags).present(message: "tags must not be empty")

Format Validation

Validates value matches a pattern (Regexp or built-in symbol):

Philiprehberger::GuardClause.guard(id).format(:uuid, message: "must be a valid UUID")
Philiprehberger::GuardClause.guard(email).format(:email, message: "invalid email")
Philiprehberger::GuardClause.guard(code).format(/\A[A-Z]{3}\z/, message: "must be 3 uppercase letters")

Built-in patterns: :uuid (UUID v4), :email (basic email format).

String Prefix and Suffix

Philiprehberger::GuardClause.guard(url).starts_with("https://", message: "must be HTTPS")
Philiprehberger::GuardClause.guard(filename).ends_with(".rb", message: "must be a Ruby file")

Soft Mode

Collect all errors without raising:

guard = Philiprehberger::GuardClause.guard(value, soft: true)
guard.not_nil.not_empty.positive

guard.valid?   # => false
guard.errors   # => ['value must not be empty', 'value must be positive']

API

Method Description
GuardClause.guard(value, soft: false) Create a guard for the given value
#not_nil(msg) Assert value is not nil
#not_empty(msg) Assert value is not empty
#positive(msg) Assert value is positive
#gte(n, msg) Assert value >= n
#lte(n, msg) Assert value <= n
#matches(regex, msg) Assert value matches pattern
#one_of(arr, msg) Assert value is in the list
#not_equal(other, msg) Assert value differs from other
#is_a(type, message:) Assert value is an instance of type
#between(min, max, message:) Assert value is within inclusive range
#min_length(n, message:) Assert value length >= n
#max_length(n, message:) Assert value length <= n
#satisfies(message:, &block) Assert custom predicate returns truthy
#starts_with(prefix, message:) Assert string starts with prefix
#ends_with(suffix, message:) Assert string ends with suffix
#present(message:) Assert value is not nil, not empty, and not blank
#format(pattern, message:) Assert value matches a Regexp or built-in pattern
#value Return the guarded value
#valid? Return true if no errors (soft mode)
#errors Return collected errors (soft mode)

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT