Class: Servus::Guard
- Inherits:
-
Object
- Object
- Servus::Guard
- Defined in:
- lib/servus/guard.rb
Overview
Base class for guards that encapsulate validation logic with rich error responses.
Guard classes define reusable validation rules with declarative metadata and localized error messages. They provide a clean, performant alternative to scattering validation logic throughout services.
Direct Known Subclasses
Servus::Guards::FalseyGuard, Servus::Guards::PresenceGuard, Servus::Guards::StateGuard, Servus::Guards::TruthyGuard
Class Attribute Summary collapse
-
.error_code_value ⇒ String?
readonly
Returns the error code.
-
.http_status_code ⇒ Integer?
readonly
Returns the HTTP status code.
-
.message_block ⇒ Proc?
readonly
Returns the message data block.
-
.message_template ⇒ String, ...
readonly
Returns the message template.
Instance Attribute Summary collapse
-
#kwargs ⇒ Object
readonly
Returns the value of attribute kwargs.
Class Method Summary collapse
-
.derive_method_name(guard_class) ⇒ String
private
Converts a guard class name to a method name.
-
.error_code(code) ⇒ void
Declares the error code for API responses.
-
.execute!(guard_class) ⇒ void
Executes a guard and throws :guard_failure with the guard's error if validation fails.
-
.execute?(guard_class) ⇒ Boolean
Executes a guard and returns boolean result without throwing.
-
.http_status(status) ⇒ void
Declares the HTTP status code for API responses.
-
.inherited(subclass) ⇒ void
private
Hook called when a class inherits from Guard.
-
.message(template) { ... } ⇒ void
Declares the message template and data block.
-
.register_guard_methods(guard_class) ⇒ void
private
Defines bang and predicate methods on Servus::Guards for the guard class.
Instance Method Summary collapse
-
#error ⇒ Servus::Support::Errors::GuardError
Returns a GuardError instance configured with this guard's metadata.
-
#initialize(**kwargs) ⇒ Guard
constructor
Initializes a new guard instance with the provided arguments.
-
#message ⇒ String
Returns the formatted error message.
-
#method_missing(method_name, *args) ⇒ Object
private
Provides convenience access to kwargs as methods.
-
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
private
Checks if the guard responds to a method.
-
#test ⇒ Boolean
Tests whether the guard passes.
Constructor Details
#initialize(**kwargs) ⇒ Guard
Initializes a new guard instance with the provided arguments.
215 216 217 |
# File 'lib/servus/guard.rb', line 215 def initialize(**kwargs) @kwargs = kwargs end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Provides convenience access to kwargs as methods.
This allows the message data block to access parameters directly
(e.g., amount instead of kwargs[:amount]).
275 276 277 |
# File 'lib/servus/guard.rb', line 275 def method_missing(method_name, *args, &) kwargs[method_name] || super end |
Class Attribute Details
.error_code_value ⇒ String? (readonly)
Returns the error code.
109 110 111 |
# File 'lib/servus/guard.rb', line 109 def error_code_value @error_code_value end |
.http_status_code ⇒ Integer? (readonly)
Returns the HTTP status code.
91 92 93 |
# File 'lib/servus/guard.rb', line 91 def http_status_code @http_status_code end |
.message_block ⇒ Proc? (readonly)
Returns the message data block.
145 146 147 |
# File 'lib/servus/guard.rb', line 145 def end |
.message_template ⇒ String, ... (readonly)
Returns the message template.
140 141 142 |
# File 'lib/servus/guard.rb', line 140 def end |
Instance Attribute Details
#kwargs ⇒ Object (readonly)
Returns the value of attribute kwargs.
210 211 212 |
# File 'lib/servus/guard.rb', line 210 def kwargs @kwargs end |
Class Method Details
.derive_method_name(guard_class) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Converts a guard class name to a method name.
Strips the 'Guard' suffix and converts to snake_case. The resulting name is used with 'enforce_' and 'check_' prefixes.
201 202 203 204 205 206 207 |
# File 'lib/servus/guard.rb', line 201 def derive_method_name(guard_class) class_name = guard_class.name.split('::').last class_name.gsub(/Guard$/, '') .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end |
.error_code(code) ⇒ void
This method returns an undefined value.
Declares the error code for API responses.
102 103 104 |
# File 'lib/servus/guard.rb', line 102 def error_code(code) @error_code_value = code end |
.execute!(guard_class) ⇒ void
This method returns an undefined value.
Executes a guard and throws :guard_failure with the guard's error if validation fails.
This is the bang (!) execution method that halts execution on failure. The caller is responsible for catching the thrown error and handling it.
53 54 55 56 57 58 |
# File 'lib/servus/guard.rb', line 53 def execute!(guard_class, **) guard = guard_class.new(**) return if guard.test(**) throw(:guard_failure, guard.error) end |
.execute?(guard_class) ⇒ Boolean
Executes a guard and returns boolean result without throwing.
This is the predicate (?) execution method for conditional checks.
71 72 73 |
# File 'lib/servus/guard.rb', line 71 def execute?(guard_class, **) guard_class.new(**).test(**) end |
.http_status(status) ⇒ void
This method returns an undefined value.
Declares the HTTP status code for API responses.
84 85 86 |
# File 'lib/servus/guard.rb', line 84 def http_status(status) @http_status_code = status end |
.inherited(subclass) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Hook called when a class inherits from Guard.
Automatically defines guard methods on the Servus::Guards module.
154 155 156 157 |
# File 'lib/servus/guard.rb', line 154 def inherited(subclass) super register_guard_methods(subclass) end |
.message(template) { ... } ⇒ void
This method returns an undefined value.
Declares the message template and data block.
The template can be a String (static or with %{} interpolation), a Symbol (I18n key), a Proc (dynamic), or a Hash (inline translations).
The block provides data for message interpolation and is evaluated in the guard instance's context.
132 133 134 135 |
# File 'lib/servus/guard.rb', line 132 def (template, &block) = template = block if block_given? end |
.register_guard_methods(guard_class) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Defines bang and predicate methods on Servus::Guards for the guard class.
Creates two methods:
- enforce_
! (throws :guard_failure on validation failure) - check_
? (returns boolean)
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/servus/guard.rb', line 173 def register_guard_methods(guard_class) return unless guard_class.name base_name = derive_method_name(guard_class) # Define bang method (throws on failure) Servus::Guards.define_method("enforce_#{base_name}!") do |**kwargs| Servus::Guard.execute!(guard_class, **kwargs) end # Define predicate method (returns boolean) Servus::Guards.define_method("check_#{base_name}?") do |**kwargs| Servus::Guard.execute?(guard_class, **kwargs) end end |
Instance Method Details
#error ⇒ Servus::Support::Errors::GuardError
Returns a GuardError instance configured with this guard's metadata.
Called when a guard fails to create the error that gets thrown. The caller decides how to handle the error (e.g., wrap in a failure response).
256 257 258 259 260 261 262 |
# File 'lib/servus/guard.rb', line 256 def error Servus::Support::Errors::GuardError.new( , code: self.class.error_code_value || 'validation_failed', http_status: self.class.http_status_code || 422 ) end |
#message ⇒ String
Returns the formatted error message.
Uses Support::MessageResolver to resolve the template and interpolate data from the message block.
242 243 244 245 246 247 248 |
# File 'lib/servus/guard.rb', line 242 def Servus::Support::MessageResolver.new( template: self.class., data: self.class. ? instance_exec(&self.class.) : {}, i18n_scope: 'guards' ).resolve(context: self) end |
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks if the guard responds to a method.
285 286 287 |
# File 'lib/servus/guard.rb', line 285 def respond_to_missing?(method_name, include_private = false) kwargs.key?(method_name) || super end |
#test ⇒ Boolean
Tests whether the guard passes.
Subclasses must implement this method with explicit keyword arguments that define the guard's contract.
231 232 233 |
# File 'lib/servus/guard.rb', line 231 def test raise NotImplementedError, "#{self.class} must implement #test" end |