Expressive guard clause DSL for method precondition validation
gem install philiprehberger-guard_clauseExpressive guard clause DSL for method precondition validation
Add to your Gemfile:
gem "philiprehberger-guard_clause"
Or install directly:
gem install philiprehberger-guard_clause
require "philiprehberger/guard_clause"
Philiprehberger::GuardClause.guard(name).not_nil('name is required').not_empty
Philiprehberger::GuardClause.guard(age).not_nil.positive.gte(18)
Philiprehberger::GuardClause.guard(price)
.not_nil('price is required')
.positive('price must be positive')
.lte(10_000, 'price exceeds maximum')
Philiprehberger::GuardClause.guard(retries).eq(3, 'retries must be exactly 3')
Philiprehberger::GuardClause.guard(score).gt(0, 'score must be positive')
Philiprehberger::GuardClause.guard(percentage).lt(100, 'percentage must be under 100')
Philiprehberger::GuardClause.guard(email).matches(/@/, 'invalid email format')
Philiprehberger::GuardClause.guard(role).one_of(%i[admin user guest], 'invalid role')
Philiprehberger::GuardClause.guard(user).is_a(User, message: "expected a User instance")
Philiprehberger::GuardClause.guard(count).is_a(Integer)
Philiprehberger::GuardClause.guard(age).between(18, 120, message: "age out of range")
Philiprehberger::GuardClause.guard(password).min_length(8, message: "password too short")
Philiprehberger::GuardClause.guard(username).max_length(20, message: "username too long")
Philiprehberger::GuardClause.guard(number).satisfies(message: "must be even") { |v| v.even? }
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")
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).
Philiprehberger::GuardClause.guard(url).starts_with("https://", message: "must be HTTPS")
Philiprehberger::GuardClause.guard(filename).ends_with(".rb", message: "must be a Ruby file")
Iterate over a collection and validate each element with a nested guard:
Philiprehberger::GuardClause.guard([1, 2, 3])
.each { |g| g.positive('must be positive') }
In soft mode, errors are collected with the element index prepended:
guard = Philiprehberger::GuardClause.guard([-1, 2, -3], soft: true)
guard.each { |g| g.positive('must be positive') }
guard.errors # => ['[0] must be positive', '[2] must be positive']
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']
| 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 |
#gt(n, msg) | Assert value > n |
#lt(n, msg) | Assert value < n |
#eq(value, msg) | Assert value == value |
#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 |
#each(&block) | Iterate over collection elements, yielding a Guard for each |
#value | Return the guarded value |
#valid? | Return true if no errors (soft mode) |
#errors | Return collected errors (soft mode) |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: