Skip to content

Releases: cocoar-dev/Cocoar.Capabilities

v1.2.1

Choose a tag to compare

@windischb windischb released this 20 Mar 14:01

Bug Fix: Get / GetOrThrow Semantics

Get<T>() on ScopeOwnerApi and ScopeAnchorsApi was incorrectly throwing instead of returning null — making it identical to GetOrThrow<T>(). This violated the standard .NET convention where Get returns a default value on failure and a *OrThrow / *Required variant throws.

Before (broken):

scope.Owner.Get<MyOwner>();      // threw InvalidOperationException
scope.Owner.GetOrThrow<MyOwner>(); // threw InvalidOperationException (alias)

After (fixed):

scope.Owner.Get<MyOwner>();      // returns null
scope.Owner.GetOrThrow<MyOwner>(); // throws InvalidOperationException

Affected APIs:

  • ScopeOwnerApi.Get<T>() — now returns T?
  • ScopeAnchorsApi.Get<T>() — now returns T?
  • ScopeAnchorsApi.Get(string) — now returns object?

Internal callers (ComposeFor<T>(), GetRequiredCompositionFor<T>(), GetRequiredComposerFor<T>(), and named anchor equivalents) updated to use GetOrThrow for correct fail-fast semantics.

Documentation

  • Added VitePress documentation site with full guide, API reference, and examples

Full Changelog: v1.2.0...v1.2.1

v1.2.0

Choose a tag to compare

@windischb windischb released this 03 Nov 09:37
883031d

Release v1.2.0 - Strongly-Typed Scopes

🎉 What's New

Strongly-Typed Scopes

The headline feature of this release! Create scopes with compile-time type safety:

// New: Strongly-typed scope
var configManager = new ConfigurationManager();
using var scope = new CapabilityScope<ConfigurationManager>(configManager);

// No generic parameters needed!
var owner = scope.Owner.Get();  // Returns ConfigurationManager directly
var composer = scope.Owner.Compose();
if (scope.Owner.TryGetComposition(out var composition)) { /* ... */ }

Benefits:

  • 🎯 Compile-time type safety - catch errors early
  • 🚀 Cleaner API - no generic type parameters needed
  • 🏗️ Domain-specific scopes - inherit to create custom scope types
  • ✅ Full backward compatibility - existing code works unchanged

API Consistency Improvements

Try-Pattern Methods - Safe retrieval without exceptions:

// Owner API
if (scope.Owner.TryGetComposition(out var composition)) { /* use it */ }
if (scope.Owner.TryGetComposer(out var composer)) { /* use it */ }

// Anchors API (both typed and named)
if (scope.Anchors.TryGetCompositionFor<ILogger>(out var comp)) { /* use it */ }
if (scope.Anchors.TryGetComposition("logger", out var comp)) { /* use it */ }

Required-Pattern Methods - Throw when not found:

var composition = scope.Owner.GetRequiredComposition();  // Throws if not found
var composer = scope.Anchors.GetRequiredComposerFor<ILogger>();

Clearer Naming - *For<T>() suffix for typed operations:

// New preferred methods
scope.Anchors.ComposeFor<ILogger>()
scope.Anchors.GetCompositionFor<ILogger>()
scope.Anchors.GetComposerFor<ILogger>()

// Old methods still work but marked obsolete with migration guidance
scope.Anchors.Compose<ILogger>()  // ⚠️ Use ComposeFor<T>() instead

Complete Feature Set

CapabilityScope:

  • Constructor with owner: new CapabilityScope<T>(owner)
  • Constructor with options: new CapabilityScope<T>(owner, options)
  • Inherits from CapabilityScope for full compatibility
  • Strongly-typed Owner property returns ScopeOwnerApi<TOwner>

ScopeOwnerApi:

  • Get() - Get owner (no generic needed!)
  • TryGet(out TOwner) - Try-pattern
  • Compose() - Create composer
  • GetComposition() / TryGetComposition() / GetRequiredComposition()
  • GetComposer() / TryGetComposer() / GetRequiredComposer()

ScopeOwnerApi Enhancements:

  • ComposeFor<T>() / GetCompositionFor<T>() / GetComposerFor<T>()
  • TryGetComposition() / TryGetComposer()
  • GetRequiredComposerFor<T>()

ScopeAnchorsApi Enhancements:

  • ComposeFor<T>() / GetCompositionFor<T>() / GetComposerFor<T>()
  • TryGetCompositionFor<T>() / TryGetComposer() for typed anchors
  • TryGetComposition(string) / TryGetComposer(string) for named anchors
  • GetRequiredCompositionFor<T>() / GetRequiredComposerFor<T>()
  • GetRequiredComposition(string) / GetRequiredComposer(string)

🔄 Migration Guide

Strongly-Typed Scopes

// Before
using var scope = new CapabilityScope();
scope.Owner.Set(myObject);
var owner = scope.Owner.Get<MyType>();

// After (optional, but recommended for type safety)
using var scope = new CapabilityScope<MyType>(myObject);
var owner = scope.Owner.Get();  // Type-safe!

New Method Names

Update to new *For methods when you see obsolete warnings:

// Old (still works, but obsolete)
scope.Anchors.Compose<ILogger>()
scope.Anchors.GetComposition<ILogger>()

// New (recommended)
scope.Anchors.ComposeFor<ILogger>()
scope.Anchors.GetCompositionFor<ILogger>()

All obsolete methods include helpful messages guiding you to the new names.

✅ Backward Compatibility

Zero Breaking Changes! All existing code continues to work:

  • CapabilityScope works exactly as before
  • ✅ All existing methods preserved
  • ✅ Obsolete attributes guide migration at your own pace
  • ✅ 296 tests passing (including all backward compatibility tests)

📚 Documentation

Full documentation available:

📦 Installation

dotnet add package Cocoar.Capabilities --version 1.2.0

🙏 Feedback

We'd love to hear your feedback on the new strongly-typed scopes! Please open an issue or start a discussion.


Full Changelog: v1.1.0...v1.2.0

v1.1.0

Choose a tag to compare

@windischb windischb released this 26 Oct 10:01

What's New

Owner and Anchors API

Associate scopes with context objects for better composition control:

  • Owner API (scope.Owner.*) - Manage a single distinguished owner with safety guarantees
  • Anchors API (scope.Anchors.*) - Manage typed and named anchors
  • Weak reference storage prevents memory leaks
  • Fluent chaining with .Scope property
  • Direct composition via Owner.Compose() and Anchors.Compose<T>()

Using* Extension Methods

New fluent convenience methods for inline capability usage:

  • UsingFirst<T>() / UsingFirstOrDefault<T>() - Use first capability
  • UsingLast<T>() / UsingLastOrDefault<T>() - Use last capability
  • UsingEach<T>() - Execute for each capability
  • UsingAll<T>() - Execute with full collection
  • Action-based overloads return IComposition for chaining
  • Function-based overloads return results directly

v1.0.0 - First public release

Choose a tag to compare

@windischb windischb released this 10 Oct 15:35

🚀 Features

  • 🎯 Type-Safe Composition - Attach multiple capabilities to any object with full type safety
  • ⚡ High Performance - Zero-allocation lookups and minimal overhead
  • 🔒 Immutable Compositions - Thread-safe by design with immutable capability collections
  • 🎨 Flexible Registration - Support for multiple contract types per capability
  • 📦 Primary Capabilities - Enforce single "primary" capability per subject
  • 🔄 Recomposition - Modify existing compositions safely
  • 🎭 Custom Ordering - Control capability resolution order with flexible ordering strategies
  • 🗂️ Registry Support - Optional registries for managing compositions across your application