# Policy Wrap API **Unit**: `Murphy.Policy.Wrap` Policy Wrap combines multiple `IExecutablePolicy` instances into a single executable policy, applied outermost-first. ## Overview Policy Wrap is useful for building a sophisticated resilience strategy out of simpler patterns, such as: - Retry wrapping Timeout, so a timed-out attempt is retried - Circuit Breaker wrapping Bulkhead, so persistent rejection opens the circuit - Any combination of `IExecutablePolicy` implementations, nested to any depth **Note**: Unlike other builders, `TPolicyWrapBuilder` does **not** derive from `TPolicyBuilder` and has no `Handle()` method - a wrap has no exception filter of its own, since each wrapped policy applies its own filter. ## Table of Contents - [IPolicyWrap Interface](#ipolicywrap-interface) - [TPolicyWrap Class](#tpolicywrap-class) - [TPolicyWrapBuilder Class](#tpolicywrapbuilder-class) - [Usage Examples](#usage-examples) --- ## IPolicyWrap Interface The interface for the Policy Wrap pattern. Being itself an `IExecutablePolicy`, a wrap can be nested inside another wrap. ### Declaration ```pascal type IPolicyWrap = interface(IExecutablePolicy) ['{DA04B25E-C53F-4C39-BB0D-EFC7E6A5537D}'] end; ``` ### Methods #### Execute ```pascal procedure Execute(AProc: TProc); ``` **Description**: Executes `AProc` through every wrapped policy, applied outermost-first. **Behavior**: `Wrap([A, B])` executes `A(B(action))` - `A` is the outermost policy and sees the composed behavior of everything nested inside it. Internally this is implemented via recursion rather than a pre-built closure chain, so a wrap instance holds no per-call mutable state and can be reused, including concurrently. --- ## TPolicyWrap Class Concrete implementation of the Policy Wrap pattern. ### Declaration ```pascal type TPolicyWrap = class(TPolicy, IPolicyWrap) private FPolicies: TArray; procedure ExecuteFrom(AIndex: Integer; AProc: TProc); public constructor CreateWrap(const APolicies: TArray); procedure Execute(AProc: TProc); end; ``` ### Constructor ```pascal constructor CreateWrap(const APolicies: TArray); ``` **Parameters**: - `APolicies: TArray` - Policies to combine, outermost-first **Raises**: `EArgumentException` if `APolicies` is empty **Note**: Typically called by `TPolicyWrapBuilder.Wrap`, not directly by users. ### Implementation Details `ExecuteFrom` recurses through `FPolicies` by index: at each step it calls the current policy's `Execute`, passing a closure that recurses into the next index. When the index passes the end of the array, the original action finally runs. Because each recursive call captures only its own index and the immutable `FPolicies` array, the same `TPolicyWrap` instance is safe to execute repeatedly and concurrently. --- ## TPolicyWrapBuilder Class Builder class for creating Policy Wrap instances. ### Declaration ```pascal type TPolicyWrapBuilder = class sealed public class function Wrap(const APolicies: TArray): IPolicyWrap; end; ``` #### Wrap ```pascal class function Wrap(const APolicies: TArray): IPolicyWrap; ``` **Description**: Combines the given policies outermost-first: `Wrap([A, B])` executes `A(B(action))`. **Parameters**: - `APolicies: TArray` - Policies to combine, outermost-first **Returns**: `IPolicyWrap` - A single executable policy representing the whole chain **Raises**: `EArgumentException` if `APolicies` is empty **Note**: Only types implementing `IExecutablePolicy` can be wrapped - this includes Retry, Circuit Breaker, Rate Limit, Timeout, Bulkhead, Hedging, and other `IPolicyWrap` instances. `ICachePolicy` does **not** implement `IExecutablePolicy` (it returns a value via `TFunc`) and cannot be wrapped. --- ## Usage Examples ### Example 1: Retry Wrapping Timeout ```pascal uses Murphy.Policy.Retry, Murphy.Policy.Timeout, Murphy.Policy.Wrap; var Policy: IPolicyWrap; begin Policy := TPolicyWrapBuilder.Wrap([ TRetryBuilder .Handle(ETimeoutRejectedException) .Retry(3), TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(1)) ]); Policy.Execute(procedure begin CallExternalService; end); end; ``` ### Example 2: Three-Policy Composition ```pascal uses Murphy.Policy.Retry, Murphy.Policy.CircuitBreaker, Murphy.Policy.Bulkhead, Murphy.Policy.Wrap; var Policy: IPolicyWrap; begin Policy := TPolicyWrapBuilder.Wrap([ TRetryBuilder.Handle(Exception).Retry(3).Wait(TTimeSpan.FromSeconds(1)), TCircuitBreakerBuilder.Handle(Exception).Fail(5).Within(TTimeSpan.FromSeconds(30)), TBulkheadBuilder.Handle([]).Limit(10) ]); // Retry(CircuitBreaker(Bulkhead(action))) Policy.Execute(procedure begin CallExternalService; end); end; ``` ### Example 3: Nesting Wraps ```pascal var InnerPolicy: IPolicyWrap; OuterPolicy: IPolicyWrap; begin InnerPolicy := TPolicyWrapBuilder.Wrap([ TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)), TBulkheadBuilder.Handle([]).Limit(5) ]); OuterPolicy := TPolicyWrapBuilder.Wrap([ TRetryBuilder.Handle(Exception).Retry(3), InnerPolicy ]); OuterPolicy.Execute(procedure begin CallExternalService; end); end; ``` --- ## See Also - [Policy Wrap Pattern Guide](../Patterns/PolicyWrap.md) - Comprehensive usage guide with more examples - [Base Policy API](Base-Policy.md) - `IExecutablePolicy`, the contract required to participate in a wrap - [Combining Patterns](../Patterns/Combining-Patterns.md) - Using Policy Wrap alongside manual composition --- [← Hedging Policy](Hedging-Policy.md) | [API Reference Index](README.md) | [Pattern Guides](../Home.md#pattern-guides)