philiprehberger-guard_clause
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().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: