Skip to content

Add Servus::Guard System#10

Merged
sebscholl merged 9 commits intomainfrom
feature/guards
Dec 16, 2025
Merged

Add Servus::Guard System#10
sebscholl merged 9 commits intomainfrom
feature/guards

Conversation

@sebscholl
Copy link
Contributor

@sebscholl sebscholl commented Dec 12, 2025

Summary

Introduces a comprehensive Guards system for declarative precondition validation in services. Guards halt execution when conditions aren't met, returning
structured error responses.

New Built-in Guards

Guard Purpose Example
PresenceGuard Validates values are present (not nil/empty) enforce_presence!(user: user)
TruthyGuard Validates object attributes are truthy enforce_truthy!(on: user, check: :active)
FalseyGuard Validates object attributes are falsey enforce_falsey!(on: user, check: :banned)
StateGuard Validates attribute matches expected value(s) enforce_state!(on: order, check: :status, is: :pending)

Features

  • Two method styles per guard:

    • enforce_*! - halts execution on failure, returns failure response
    • check_*? - returns boolean for conditional logic
  • Rich error responses:

    result.error.message     # "User.active must be truthy (got false)"
    result.error.code        # "must_be_truthy"
    result.error.http_status # 422
  • Multiple attribute support:
    enforce_truthy!(on: user, check: [:active, :verified, :confirmed])
    enforce_state!(on: account, check: :status, is: [:active, :trial])

  • Rails generator:
    rails g servus:guard sufficient_balance

Breaking Changes

  • Removed PositiveGuard (too simple - use schema validation with minimum: 1 instead)

Files Changed

New Files

  • lib/servus/guards/truthy_guard.rb
  • lib/servus/guards/falsey_guard.rb
  • lib/servus/guards/state_guard.rb
  • lib/generators/servus/guard/guard_generator.rb
  • lib/generators/servus/guard/templates/guard.rb.erb
  • lib/generators/servus/guard/templates/guard_spec.rb.erb
  • spec/servus/guards/truthy_spec.rb
  • spec/servus/guards/falsey_spec.rb
  • spec/servus/guards/state_spec.rb

Modified Files

  • lib/servus/guards.rb - Updated loader for new guards
  • lib/servus/guards/presence_guard.rb - Improved error messages
  • spec/servus/guards/presence_spec.rb - Updated tests
  • spec/servus/base_guards_spec.rb - Updated integration tests
  • docs/features/6_guards.md - Complete documentation rewrite
  • docs/integration/1_configuration.md - Updated guard list
  • CHANGELOG.md - Added release notes
  • READme.md - Added Guards section

Deleted Files

  • lib/servus/guards/positive.rb
  • spec/servus/guards/positive_spec.rb

Test Plan

  • All 323 tests passing
  • TruthyGuard: 14 tests (single/multiple attributes, error messages)
  • FalseyGuard: 14 tests (single/multiple attributes, error messages)
  • StateGuard: 17 tests (single/multiple expected values, error messages)
  • PresenceGuard: 23 tests (updated for new error format)
  • Integration tests: 15 tests (guards in services, nested methods, mixed usage)

sebscholl and others added 9 commits December 12, 2025 17:02
This commit introduces a new guard system for Servus that provides
self-contained validation objects with rich error responses.

Key features:
- Servus::Guard base class with declarative DSL
- Automatic registration via Guards::Registry
- Integration with Servus::Base via throw/catch for performance
- Support for localized messages (I18n, inline translations)
- Method signature-based parameter contracts
- Backward compatible with existing raise-based error handling

Core guards included:
- EnsurePresent: validates values are not nil/empty
- EnsurePositive: validates numeric values are > 0

The guard system uses throw/catch instead of exceptions for control
flow, providing ~4x better performance than raise-based approaches
while maintaining clean syntax at any nesting level.

Usage example:
  class TransferService < Servus::Base
    def call
      # ... perform transfer ...
      success(result)
    end
  end

All changes include comprehensive test coverage following existing
Servus testing patterns.
- Removed the Guards registry and integrated guard method definitions directly into the Servus::Guards module.
- Updated the Guard class to provide bang (!) and predicate (?) methods for guard execution.
- Introduced configuration options for guards directory and inclusion of default guards.
- Modified ensure_positive and ensure_present guards to use new message formatting.
- Updated tests to reflect changes in guard method definitions and removed references to the old registry.
- Added new tests for predicate guard methods to ensure correct behavior.
- Removed the existing event bus specification document.
- Updated configuration documentation to include guards directory and settings.
- Refactored the Servus::Guard class to remove severity level declaration.
- Implemented auto-loading for custom guards in Rails.
- Created built-in guards: EnsurePresent and EnsurePositive.
- Added comprehensive documentation for guards, including usage, creation, and testing.
- Updated tests to reflect changes in guard implementation and error handling.
- Change class names from Ensure* to *Guard (e.g., EnsurePresent -> PresenceGuard)
- Update method generation to use enforce_*! and check_*? prefixes
- Rename guard files: ensure_present.rb -> presence.rb, ensure_positive.rb -> positive.rb
- Update all documentation and examples to reflect new naming
- Add comprehensive naming convention documentation

The new pattern:
- Guard classes describe WHAT is checked, not HOW (e.g., SufficientBalanceGuard)
- Framework adds action verbs: enforce_*! (throws) and check_*? (boolean)
- Example: SufficientBalanceGuard generates enforce_sufficient_balance! and check_sufficient_balance?

This makes guard names cleaner and more semantic, avoiding redundant action verbs.
- Updated guard class naming to strip both "Ensure" prefix and "Guard" suffix for cleaner method names.
- Modified `derive_method_name` to reflect new naming conventions.
- Enhanced error handling in `ControllerHelpers` to use `error.http_status` and `error.api_error` for rendering.
- Refactored error classes to provide consistent HTTP status and API error responses.
- Created a dummy Rails application for testing and development purposes.
- Implemented PresenceGuard to ensure all provided values are present (not nil or empty).
- Created StateGuard to validate that an attribute matches an expected value or one of several allowed values.
- Developed TruthyGuard to check that specified attributes on an object are truthy.
- Introduced MessageResolver for resolving message templates with interpolation support, handling strings, symbols, hashes, and procs.
- Added comprehensive specs for each guard and the message resolver to ensure functionality and error handling.
@sebscholl sebscholl changed the title DRAFT: Add Servus::Guard System Add Servus::Guard System Dec 16, 2025
@sebscholl sebscholl marked this pull request as ready for review December 16, 2025 15:44
@sebscholl sebscholl merged commit e0a1ef2 into main Dec 16, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant