From b78cb9c0c6e3c1afe9b404573bbcfdc2477f90f5 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:00:39 +0200 Subject: [PATCH 01/15] feat: add registry opts --- registry.go | 26 ++++++++++++++++++++++++++ registry_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/registry.go b/registry.go index c86ed58..d63f792 100644 --- a/registry.go +++ b/registry.go @@ -68,3 +68,29 @@ func AddAlias(aliasMap FunctionAliasMap, originalFunction string, aliases ...str func AddNotice(notices *[]FunctionNotice, notice *FunctionNotice) { *notices = append(*notices, *notice) } + +// WithRegistry returns a HandlerOption that adds the provided registry to the handler. +// This option allows you to integrate additional functions into the template processing +// environment by adding a new registry to the handler. +// +// Example: +// +// handler := New(WithRegistry(myRegistry)) +func WithRegistry(reg Registry) HandlerOption[*DefaultHandler] { + return func(dh *DefaultHandler) error { + return dh.AddRegistry(reg) + } +} + +// WithRegistries returns a HandlerOption that adds the provided registries to the handler. +// This option simplifies the process of adding multiple sets of functionalities into the +// template engine at once. +// +// Example: +// +// handler := New(WithRegistries(myRegistry1, myRegistry2, myRegistry3)) +func WithRegistries(registries ...Registry) HandlerOption[*DefaultHandler] { + return func(dh *DefaultHandler) error { + return dh.AddRegistries(registries...) + } +} diff --git a/registry_test.go b/registry_test.go index d4a5258..6ad7a3d 100644 --- a/registry_test.go +++ b/registry_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestAddFunction(t *testing.T) { @@ -53,3 +54,40 @@ func TestAddAlias(t *testing.T) { AddAlias(aliasMap, "nonExistentFunc", "aliasX") assert.Contains(t, aliasMap, "nonExistentFunc", "Aliases should be added under 'nonExistentFunc' even if the function doesn't exist") } + +func TestWithRegistry(t *testing.T) { + // Define a registry with a function and an alias + mockRegistry := new(MockRegistry) + mockRegistry.linkHandlerMustCrash = true + mockRegistry.On("Uid").Return("mockRegistry") + mockRegistry.On("LinkHandler", mock.Anything).Return(errMock) + + // Create a handler with the registry + handler := New(WithRegistry(mockRegistry)) + handler.Build() + + // Check that the function and alias are present in the handler + assert.Contains(t, handler.registries, mockRegistry, "Registry should be added to the handler") +} + +func TestWithRegistries(t *testing.T) { + // Define two registries with functions and aliases + mockRegistry1 := new(MockRegistry) + mockRegistry1.On("Uid").Return("mockRegistry1") + mockRegistry1.On("LinkHandler", mock.Anything).Return(nil) + mockRegistry1.On("RegisterFunctions", mock.Anything).Return(nil) + + mockRegistry2 := new(MockRegistry) + mockRegistry2.linkHandlerMustCrash = true + mockRegistry2.On("Uid").Return("mockRegistry2") + mockRegistry2.On("LinkHandler", mock.Anything).Return(nil) + mockRegistry1.On("RegisterFunctions", mock.Anything).Return(nil) + + // Create a handler with the registries + handler := New(WithRegistries(mockRegistry1, mockRegistry2)) + handler.Build() + + // Check that the functions and aliases are present in the handler + assert.Contains(t, handler.registries, mockRegistry1, "Registry 1 should be added to the handler") + assert.Contains(t, handler.registries, mockRegistry2, "Registry 2 should be added to the handler") +} From d356b04d27dd834f9e337a39fc65a4e3ed739e5d Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:02:50 +0200 Subject: [PATCH 02/15] chore: be sure to understand the functions by changing their names --- alias.go | 4 ++-- handler.go | 25 ++++++++++---------- handler_test.go | 8 +++---- sprigin/sprig_backward_compatibility.go | 4 ++-- sprigin/sprig_backward_compatibility_test.go | 4 ++-- sprout_test.go | 4 ++-- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/alias.go b/alias.go index 37ec083..3ea6cec 100644 --- a/alias.go +++ b/alias.go @@ -12,8 +12,8 @@ type FunctionAliasMap = map[string][]string func AssignAliases(h Handler) { for originalFunction, aliases := range h.Aliases() { for _, alias := range aliases { - if fn, ok := h.Functions()[originalFunction]; ok { - h.Functions()[alias] = fn + if fn, ok := h.RawFunctions()[originalName]; ok { + h.RawFunctions()[alias] = fn } } } diff --git a/handler.go b/handler.go index 97e7d50..1413bf3 100644 --- a/handler.go +++ b/handler.go @@ -25,16 +25,15 @@ type Handler interface { // processing environment. AddRegistry(registry Registry) error - // AddRegistries registers multiple registries into the Handler. This method - // simplifies the process of adding multiple sets of functionalities into the - // template engine at once. - AddRegistries(registries ...Registry) error - - // Functions returns the map of registered functions managed by the Handler. - Functions() FunctionMap + // RawFunctions returns the map of registered functions without any alias, + // notices or other additional information. This function is useful for + // special cases where you need to access raw data from registries. + // + // ⚠ To access the function map for the template engine use `Build()` instead. + RawFunctions() FunctionMap - // Aliases returns the map of function aliases managed by the Handler. - Aliases() FunctionAliasMap + // RawAliases returns the map of function aliases managed by the Handler. + RawAliases() FunctionAliasMap // Notices returns the list of function notices managed by the Handler. Notices() []FunctionNotice @@ -137,7 +136,7 @@ func (dh *DefaultHandler) Logger() *slog.Logger { return dh.logger } -// Functions returns the map of registered functions managed by the DefaultHandler. +// RawFunctions returns the map of registered functions managed by the DefaultHandler. // // ⚠ This function is for special cases where you need to access the function // map for the template engine use `Build()` instead. @@ -145,17 +144,17 @@ func (dh *DefaultHandler) Logger() *slog.Logger { // This function map contains all the functions that have been added to the handler, // typically for use in templating engines. Each entry in the map associates a function // name with its corresponding implementation. -func (dh *DefaultHandler) Functions() FunctionMap { +func (dh *DefaultHandler) RawFunctions() FunctionMap { return dh.cachedFuncsMap } -// Aliases returns the map of function aliases managed by the DefaultHandler. +// RawAliases returns the map of function aliases managed by the DefaultHandler. // // The alias map allows certain functions to be referenced by multiple names. This // can be useful in templating environments where different names might be preferred // for the same underlying function. The alias map associates each original function // name with a list of aliases that can be used interchangeably. -func (dh *DefaultHandler) Aliases() FunctionAliasMap { +func (dh *DefaultHandler) RawAliases() FunctionAliasMap { return dh.cachedFuncsAlias } diff --git a/handler_test.go b/handler_test.go index 03416d7..f5e4f8a 100644 --- a/handler_test.go +++ b/handler_test.go @@ -269,12 +269,12 @@ func TestDefaultHandler_Registries(t *testing.T) { assert.Len(t, dh.registries, 2, "Registries should return the correct number of registries") } -// TestDefaultHandler_Functions tests the Functions method of DefaultHandler. -func TestDefaultHandler_Functions(t *testing.T) { +// TestDefaultHandler_RawFunctions tests the Functions method of DefaultHandler. +func TestDefaultHandler_RawFunctions(t *testing.T) { funcsMap := make(FunctionMap) dh := &DefaultHandler{cachedFuncsMap: funcsMap} - assert.Equal(t, funcsMap, dh.Functions(), "Functions should return the correct FunctionMap") + assert.Equal(t, funcsMap, dh.RawFunctions(), "Functions should return the correct FunctionMap") } // TestDefaultHandler_Aliases tests the Aliases method of DefaultHandler. @@ -282,7 +282,7 @@ func TestDefaultHandler_Aliases(t *testing.T) { aliasesMap := make(FunctionAliasMap) dh := &DefaultHandler{cachedFuncsAlias: aliasesMap} - assert.Equal(t, aliasesMap, dh.Aliases(), "Aliases should return the correct FunctionAliasMap") + assert.Equal(t, aliasesMap, dh.RawAliases(), "Aliases should return the correct FunctionAliasMap") } // TestDefaultHandler_Build tests the Build method of DefaultHandler. diff --git a/sprigin/sprig_backward_compatibility.go b/sprigin/sprig_backward_compatibility.go index 2224437..d01ec9c 100644 --- a/sprigin/sprig_backward_compatibility.go +++ b/sprigin/sprig_backward_compatibility.go @@ -144,11 +144,11 @@ func (sh *SprigHandler) Logger() *slog.Logger { return slog.New(slog.Default().Handler()) } -func (sh *SprigHandler) Functions() sprout.FunctionMap { +func (sh *SprigHandler) RawFunctions() sprout.FunctionMap { return sh.funcsMap } -func (sh *SprigHandler) Aliases() sprout.FunctionAliasMap { +func (sh *SprigHandler) RawAliases() sprout.FunctionAliasMap { return sh.funcsAlias } diff --git a/sprigin/sprig_backward_compatibility_test.go b/sprigin/sprig_backward_compatibility_test.go index a210805..49dfc4b 100644 --- a/sprigin/sprig_backward_compatibility_test.go +++ b/sprigin/sprig_backward_compatibility_test.go @@ -53,8 +53,8 @@ func TestSprigHandler(t *testing.T) { handler.Build() - assert.GreaterOrEqual(t, len(handler.Functions()), sprigFunctionCount) - assert.Len(t, handler.Aliases(), 37) // Hardcoded for backward compatibility + assert.GreaterOrEqual(t, len(handler.RawFunctions()), sprigFunctionCount) + assert.Len(t, handler.RawAliases(), 37) // Hardcoded for backward compatibility assert.Len(t, handler.registries, 18) // Hardcoded for backward compatibility diff --git a/sprout_test.go b/sprout_test.go index 589e3c4..d4a5101 100644 --- a/sprout_test.go +++ b/sprout_test.go @@ -64,13 +64,13 @@ func TestWithSafeFuncs(t *testing.T) { assert.True(t, handler.wantSafeFuncs) handler.cachedFuncsMap["test"] = func() {} - funcCount := len(handler.Functions()) + funcCount := len(handler.RawFunctions()) handler.Build() assert.Len(t, handler.cachedFuncsMap, funcCount*2) var keys []string - for k := range handler.Functions() { + for k := range handler.RawFunctions() { keys = append(keys, k) } From 6e65561d3a649caaad229dde7f127edc636ff6f1 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:03:47 +0200 Subject: [PATCH 03/15] chore: remove deprecated NewFunctionhandler --- alias_test.go | 10 +++++----- notice.go | 2 +- notice_test.go | 4 ++-- sprout.go | 7 ------- sprout_test.go | 9 ++++----- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/alias_test.go b/alias_test.go index bc65068..40a871a 100644 --- a/alias_test.go +++ b/alias_test.go @@ -12,7 +12,7 @@ import ( // TestWithAlias checks that aliases are correctly added to a function. func TestWithAlias(t *testing.T) { - handler := NewFunctionHandler() + handler := New() originalFunc := "originalFunc" alias1 := "alias1" alias2 := "alias2" @@ -28,7 +28,7 @@ func TestWithAlias(t *testing.T) { } func TestWithAlias_Empty(t *testing.T) { - handler := NewFunctionHandler() + handler := New() originalFunc := "originalFunc" // Apply the WithAlias option with no aliases. @@ -39,7 +39,7 @@ func TestWithAlias_Empty(t *testing.T) { } func TestWithAliases(t *testing.T) { - handler := NewFunctionHandler() + handler := New() originalFunc1 := "originalFunc1" alias1 := "alias1" alias2 := "alias2" @@ -65,7 +65,7 @@ func TestWithAliases(t *testing.T) { // TestRegisterAliases checks that aliases are correctly registered in the function map. func TestRegisterAliases(t *testing.T) { - handler := NewFunctionHandler() + handler := New() originalFunc := "originalFunc" alias1 := "alias1" alias2 := "alias2" @@ -84,7 +84,7 @@ func TestRegisterAliases(t *testing.T) { } func TestAliasesInTemplate(t *testing.T) { - handler := NewFunctionHandler() + handler := New() originalFuncName := "originalFunc" alias1 := "alias1" alias2 := "alias2" diff --git a/notice.go b/notice.go index 1a1f93c..a185491 100644 --- a/notice.go +++ b/notice.go @@ -91,7 +91,7 @@ func NewDebugNotice(functionName, message string) *FunctionNotice { // It should be called after all functions and notices have been added and // inside the Build function in case of using a custom handler. func AssignNotices(h Handler) { - funcs := h.Functions() + funcs := h.RawFunctions() for _, notice := range h.Notices() { for _, functionName := range notice.FunctionNames { if fn, ok := funcs[functionName]; ok { diff --git a/notice_test.go b/notice_test.go index 36c216a..fbe004b 100644 --- a/notice_test.go +++ b/notice_test.go @@ -91,8 +91,8 @@ func TestAssignNotices(t *testing.T) { assert.Contains(t, handler.Notices(), *notice) assert.Len(t, handler.notices, 1, "there should be exactly 1 notice") - require.Contains(t, handler.Functions(), originalFunc) - assert.NotEqual(t, reflect.ValueOf(mockFunc).Pointer(), reflect.ValueOf(handler.Functions()[originalFunc]).Pointer(), "the function should have been wrapped") + require.Contains(t, handler.RawFunctions(), originalFunc) + assert.NotEqual(t, reflect.ValueOf(mockFunc).Pointer(), reflect.ValueOf(handler.RawFunctions()[originalFunc]).Pointer(), "the function should have been wrapped") } func TestCreateWrappedFunction(t *testing.T) { diff --git a/sprout.go b/sprout.go index f2ab31b..9b2335b 100644 --- a/sprout.go +++ b/sprout.go @@ -45,10 +45,3 @@ func New(opts ...HandlerOption[*DefaultHandler]) *DefaultHandler { return dh } - -// Deprecated: NewFunctionHandler creates a new function handler with the -// default values. It is deprecated and should not be used. Use `New` instead. -func NewFunctionHandler(opts ...HandlerOption[*DefaultHandler]) *DefaultHandler { - slog.Warn("NewFunctionHandler are deprecated. Use `New` instead") - return New(opts...) -} diff --git a/sprout_test.go b/sprout_test.go index d4a5101..f004108 100644 --- a/sprout_test.go +++ b/sprout_test.go @@ -8,16 +8,15 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNewFunctionHandler_DefaultValues(t *testing.T) { - handler := NewFunctionHandler() +func TestNew_DefaultValues(t *testing.T) { + handler := New() assert.NotNil(t, handler) assert.NotNil(t, handler.Logger) } -func TestNewFunctionHandler_CustomValues(t *testing.T) { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - handler := NewFunctionHandler( + handler := New( WithLogger(logger), ) @@ -29,7 +28,7 @@ func TestWithLogger(t *testing.T) { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) option := WithLogger(logger) - handler := NewFunctionHandler() + handler := New() option(handler) // Apply the option assert.Equal(t, logger, handler.Logger()) From 57b6716490859db42fe49dd62981f39351871a9a Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:04:05 +0200 Subject: [PATCH 04/15] fix: ensure the original function exist on alias generation --- alias.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/alias.go b/alias.go index 3ea6cec..2a2cb69 100644 --- a/alias.go +++ b/alias.go @@ -10,7 +10,12 @@ type FunctionAliasMap = map[string][]string // It should be called after all functions and aliases have been added and // inside the Build function in case of using a custom handler. func AssignAliases(h Handler) { - for originalFunction, aliases := range h.Aliases() { + for originalName, aliases := range h.RawAliases() { + _, exists := h.RawFunctions()[originalName] + if !exists { + continue + } + for _, alias := range aliases { if fn, ok := h.RawFunctions()[originalName]; ok { h.RawFunctions()[alias] = fn From 86a4d68b652d9af1532a4abc0d716fc3251a0268 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:04:58 +0200 Subject: [PATCH 05/15] chore: rename wrap function for clarity --- notice.go | 12 +++--------- notice_test.go | 6 +++--- sprout.go | 8 +++++++- sprout_test.go | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/notice.go b/notice.go index a185491..ab00629 100644 --- a/notice.go +++ b/notice.go @@ -7,12 +7,6 @@ import ( "github.com/go-sprout/sprout/internal/runtime" ) -// wrappedFunc is a type alias for a function that accepts a variadic number of -// arguments of any type and returns a single result of any type along with an -// error. This is typically used for functions that need to be wrapped with -// additional logic, such as logging or notice handling. -type wrappedFunc = func(args ...any) (any, error) - // NoticeKind represents the type of notice that can be applied to a function. // It is an enumeration with different possible values that dictate how the // notice should behave. @@ -95,19 +89,19 @@ func AssignNotices(h Handler) { for _, notice := range h.Notices() { for _, functionName := range notice.FunctionNames { if fn, ok := funcs[functionName]; ok { - wrappedFn := createWrappedFunction(h, notice, functionName, fn) + wrappedFn := noticeWrapper(h, notice, functionName, fn) funcs[functionName] = wrappedFn } } } } -// createWrappedFunction creates a wrapped function that logs a notice after +// noticeWrapper creates a wrapped function that logs a notice after // calling the original function. The notice is logged using the handler's // logger instance. The wrapped function is returned as a wrappedFunc, which // is a type alias for a function that takes a variadic list of arguments // and returns an `any` result and an `error`. -func createWrappedFunction(h Handler, notice FunctionNotice, functionName string, fn any) wrappedFunc { +func noticeWrapper(h Handler, notice FunctionNotice, functionName string, fn any) wrappedFunction { return func(args ...any) (any, error) { out, err := runtime.SafeCall(fn, args...) switch notice.Kind { diff --git a/notice_test.go b/notice_test.go index fbe004b..ef56527 100644 --- a/notice_test.go +++ b/notice_test.go @@ -103,9 +103,9 @@ func TestCreateWrappedFunction(t *testing.T) { mockFunc := func() string { return "cheese" } // Create a wrapped function. - wrappedFunc := createWrappedFunction(handler, *NewInfoNotice(originalFunc, "amazing"), originalFunc, mockFunc) - wrappedFunc2 := createWrappedFunction(handler, *NewDeprecatedNotice(originalFunc, "oh no"), originalFunc, mockFunc) - wrappedFunc3 := createWrappedFunction(handler, *NewNotice(NoticeKindDebug, []string{originalFunc}, "Nice this function returns $out"), originalFunc, mockFunc) + wrappedFunc := noticeWrapper(handler, *NewInfoNotice(originalFunc, "amazing"), originalFunc, mockFunc) + wrappedFunc2 := noticeWrapper(handler, *NewDeprecatedNotice(originalFunc, "oh no"), originalFunc, mockFunc) + wrappedFunc3 := noticeWrapper(handler, *NewNotice(NoticeKindDebug, []string{originalFunc}, "Nice this function returns $out"), originalFunc, mockFunc) // Call the wrapped function. out, err := wrappedFunc() diff --git a/sprout.go b/sprout.go index 9b2335b..04b37fe 100644 --- a/sprout.go +++ b/sprout.go @@ -7,7 +7,13 @@ import ( // HandlerOption[Handler] defines a type for functional options that configure // a typed Handler. -type HandlerOption[T Handler] func(T) +type HandlerOption[T Handler] func(T) error + +// wrappedFunction is a type alias for a function that accepts a variadic number of +// arguments of any type and returns a single result of any type along with an +// error. This is typically used for functions that need to be wrapped with +// additional logic, such as logging or notice handling. +type wrappedFunction = func(args ...any) (any, error) // New creates and returns a new instance of DefaultHandler with optional // configurations. diff --git a/sprout_test.go b/sprout_test.go index f004108..93b242a 100644 --- a/sprout_test.go +++ b/sprout_test.go @@ -15,6 +15,7 @@ func TestNew_DefaultValues(t *testing.T) { assert.NotNil(t, handler.Logger) } +func TestNew_CustomValues(t *testing.T) { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) handler := New( WithLogger(logger), From 0ac45cf3f768da17e1c904510aee943eb9c5304c Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:05:36 +0200 Subject: [PATCH 06/15] chore: support error in opts --- alias.go | 8 +++++--- handler.go | 12 +++++++----- notice.go | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/alias.go b/alias.go index 2a2cb69..2ba481e 100644 --- a/alias.go +++ b/alias.go @@ -44,9 +44,9 @@ func AssignAliases(h Handler) { // // handler := New(WithAlias("originalFunc", "alias1", "alias2")) func WithAlias(originalFunction string, aliases ...string) HandlerOption[*DefaultHandler] { - return func(p *DefaultHandler) { + return func(p *DefaultHandler) error { if len(aliases) == 0 { - return + return nil } if _, ok := p.cachedFuncsAlias[originalFunction]; !ok { @@ -54,6 +54,7 @@ func WithAlias(originalFunction string, aliases ...string) HandlerOption[*Defaul } p.cachedFuncsAlias[originalFunction] = append(p.cachedFuncsAlias[originalFunction], aliases...) + return nil } } @@ -74,7 +75,7 @@ func WithAlias(originalFunction string, aliases ...string) HandlerOption[*Defaul // "originalFunc2": {"alias2_1", "alias2_2"}, // })) func WithAliases(aliases FunctionAliasMap) HandlerOption[*DefaultHandler] { - return func(p *DefaultHandler) { + return func(p *DefaultHandler) error { for originalFunction, aliasList := range aliases { if _, ok := p.cachedFuncsAlias[originalFunction]; !ok { p.cachedFuncsAlias[originalFunction] = make([]string, 0) @@ -82,5 +83,6 @@ func WithAliases(aliases FunctionAliasMap) HandlerOption[*DefaultHandler] { p.cachedFuncsAlias[originalFunction] = append(p.cachedFuncsAlias[originalFunction], aliasList...) } + return nil } } diff --git a/handler.go b/handler.go index 1413bf3..62c9cf7 100644 --- a/handler.go +++ b/handler.go @@ -170,17 +170,18 @@ func (dh *DefaultHandler) Notices() []FunctionNotice { // WithLogger sets the logger used by a DefaultHandler. func WithLogger(l *slog.Logger) HandlerOption[*DefaultHandler] { - return func(p *DefaultHandler) { + return func(p *DefaultHandler) error { p.logger = l + return nil } } // WithHandler updates a DefaultHandler with settings from another DefaultHandler. // This is useful for copying configurations between handlers. func WithHandler(new Handler) HandlerOption[*DefaultHandler] { - return func(fnh *DefaultHandler) { + return func(fnh *DefaultHandler) error { if new == nil { - return + return nil } if fhCast, ok := new.(*DefaultHandler); ok { @@ -197,15 +198,16 @@ func WithHandler(new Handler) HandlerOption[*DefaultHandler] { // To use a safe function, prepend `safe` to the original function name, // example: `safeOriginalFuncName` instead of `originalFuncName`. func WithSafeFuncs(enabled bool) HandlerOption[*DefaultHandler] { - return func(dh *DefaultHandler) { + return func(dh *DefaultHandler) error { dh.wantSafeFuncs = enabled + return nil } } // safeWrapper create a safe wrapper function that calls the original function // and logs any errors that occur during the function call without interrupting // the execution of the template. -func (dh *DefaultHandler) safeWrapper(functionName string, fn any) wrappedFunc { +func (dh *DefaultHandler) safeWrapper(functionName string, fn any) wrappedFunction { return func(args ...any) (any, error) { out, err := runtime.SafeCall(fn, args...) if err != nil { diff --git a/notice.go b/notice.go index ab00629..6fe2978 100644 --- a/notice.go +++ b/notice.go @@ -124,7 +124,7 @@ func noticeWrapper(h Handler, notice FunctionNotice, functionName string, fn any // You can use the ApplyOnAliases method on the FunctionNotice to control // whether the notice should be applied to aliases. func WithNotices(notices ...*FunctionNotice) HandlerOption[*DefaultHandler] { - return func(p *DefaultHandler) { + return func(p *DefaultHandler) error { // Preallocate the slice if we expect to append multiple notices if cap(p.notices) < len(p.notices)+len(notices) { newNotices := make([]FunctionNotice, len(p.notices), len(p.notices)+len(notices)) @@ -141,5 +141,7 @@ func WithNotices(notices ...*FunctionNotice) HandlerOption[*DefaultHandler] { // Append the notice directly without dereferencing p.notices = append(p.notices, *notice) } + + return nil } } From 33b361f6c12db2f9b2f8fdd302060e815ec1f182 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:06:00 +0200 Subject: [PATCH 07/15] fix: prevent rebuild an already builded function map --- handler.go | 46 ++++++++++++++++++++++++++++++++-------------- handler_test.go | 3 +++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/handler.go b/handler.go index 62c9cf7..e46541f 100644 --- a/handler.go +++ b/handler.go @@ -55,6 +55,7 @@ type DefaultHandler struct { notices []FunctionNotice wantSafeFuncs bool + built bool cachedFuncsMap FunctionMap cachedFuncsAlias FunctionAliasMap @@ -103,26 +104,22 @@ func (dh *DefaultHandler) AddRegistries(registries ...Registry) error { // Build retrieves the complete suite of functiosn and alias that has been configured // within this Handler. This handler is ready to be used with template engines -// that accept FuncMap, such as html/template or text/template. +// that accept FuncMap, such as html/template or text/template. It will also +// cache the function map for future use to avoid rebuilding the function map +// multiple times, so it is safe to call this method multiple times to retrieve +// the same builded function map. // // NOTE: This will replace the `FuncsMap()`, `TxtFuncMap()` and `HtmlFuncMap()` from sprig func (dh *DefaultHandler) Build() FunctionMap { + if dh.built { + return dh.cachedFuncsMap + } + AssignAliases(dh) // Ensure all aliases are processed before returning the registry AssignNotices(dh) // Ensure all notices are processed before returning the registry + dh.buildSafeFuncs() - // If safe functions are enabled, wrap all functions with a safe wrapper - // that logs any errors that occur during function execution. - if dh.wantSafeFuncs { - safeFuncs := make(FunctionMap) - for funcName, fn := range dh.cachedFuncsMap { - safeFuncs[safeFuncName(funcName)] = dh.safeWrapper(funcName, fn) - } - - for funcName, fn := range safeFuncs { - dh.cachedFuncsMap[funcName] = fn - } - } - + dh.built = true return dh.cachedFuncsMap } @@ -187,6 +184,8 @@ func WithHandler(new Handler) HandlerOption[*DefaultHandler] { if fhCast, ok := new.(*DefaultHandler); ok { *fnh = *fhCast } + + return nil } } @@ -236,3 +235,22 @@ func safeFuncName(name string) string { return b.String() } + +// buildSafeFuncs wraps all functions with a safe wrapper that logs any errors +// that occur during function execution. If safe functions are enabled in the +// DefaultHandler, this method will prepend "safe" to the function name and +// create a safe wrapper for each function. +func (dh *DefaultHandler) buildSafeFuncs() { + if !dh.wantSafeFuncs { + return + } + + safeFuncs := make(FunctionMap) + for funcName, fn := range dh.cachedFuncsMap { + safeFuncs[safeFuncName(funcName)] = dh.safeWrapper(funcName, fn) + } + + for funcName, fn := range safeFuncs { + dh.cachedFuncsMap[funcName] = fn + } +} diff --git a/handler_test.go b/handler_test.go index f5e4f8a..b1bce3e 100644 --- a/handler_test.go +++ b/handler_test.go @@ -301,6 +301,9 @@ func TestDefaultHandler_Build(t *testing.T) { builtFuncsMap := dh.Build() assert.Equal(t, funcsMap, builtFuncsMap, "Build should return the correct FunctionMap") + + builtFuncsMapSecond := dh.Build() + assert.Equal(t, builtFuncsMap, builtFuncsMapSecond, "Build should return the same FunctionMap on subsequent calls") } func TestDefaultHandler_safeWrapper(t *testing.T) { From e8b19ee4f5c3f7101c3cdf9279355bcc82369f2b Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:16:59 +0200 Subject: [PATCH 08/15] fix: resolving missing errorcheck --- alias_test.go | 12 ++++++------ notice_test.go | 8 ++++---- sprout.go | 4 +++- sprout_test.go | 7 ++++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/alias_test.go b/alias_test.go index 40a871a..321ffee 100644 --- a/alias_test.go +++ b/alias_test.go @@ -18,7 +18,7 @@ func TestWithAlias(t *testing.T) { alias2 := "alias2" // Apply the WithAlias option with two aliases. - WithAlias(originalFunc, alias1, alias2)(handler) + require.NoError(t, WithAlias(originalFunc, alias1, alias2)(handler)) // Check that the aliases were added. assert.Contains(t, handler.cachedFuncsAlias, originalFunc) @@ -32,7 +32,7 @@ func TestWithAlias_Empty(t *testing.T) { originalFunc := "originalFunc" // Apply the WithAlias option with no aliases. - WithAlias(originalFunc)(handler) + require.NoError(t, WithAlias(originalFunc)(handler)) // Check that no aliases were added. assert.NotContains(t, handler.cachedFuncsAlias, originalFunc) @@ -47,10 +47,10 @@ func TestWithAliases(t *testing.T) { alias3 := "alias3" // Apply the WithAliases option with two sets of aliases. - WithAliases(FunctionAliasMap{ + require.NoError(t, WithAliases(FunctionAliasMap{ originalFunc1: {alias1, alias2}, originalFunc2: {alias3}, - })(handler) + })(handler)) // Check that the aliases were added. assert.Contains(t, handler.cachedFuncsAlias, originalFunc1) @@ -75,7 +75,7 @@ func TestRegisterAliases(t *testing.T) { handler.cachedFuncsMap[originalFunc] = mockFunc // Apply the WithAlias option and then register the aliases. - WithAlias(originalFunc, alias1, alias2)(handler) + require.NoError(t, WithAlias(originalFunc, alias1, alias2)(handler)) AssignAliases(handler) // Check that the aliases are mapped to the same function as the original function in funcsRegistry. @@ -94,7 +94,7 @@ func TestAliasesInTemplate(t *testing.T) { handler.cachedFuncsMap[originalFuncName] = mockFunc // Apply the WithAlias option and then register the aliases. - WithAlias(originalFuncName, alias1, alias2)(handler) + require.NoError(t, WithAlias(originalFuncName, alias1, alias2)(handler)) // Create a template with the aliases. tmpl, err := template.New("test").Funcs(handler.Build()).Parse(`{{originalFunc}} {{alias1}} {{alias2}}`) diff --git a/notice_test.go b/notice_test.go index ef56527..bc183e4 100644 --- a/notice_test.go +++ b/notice_test.go @@ -41,7 +41,7 @@ func TestWithNotice(t *testing.T) { notice := NewInfoNotice(originalFunc, "amazing") // Apply the WithNotices option with one notice. - WithNotices(notice)(handler) + require.NoError(t, WithNotices(notice)(handler)) // Check that the aliases were added. assert.Contains(t, handler.Notices(), *notice) @@ -49,7 +49,7 @@ func TestWithNotice(t *testing.T) { // Apply the WithNotices option with multiple notices. notice2 := NewDeprecatedNotice(originalFunc, "oh no") - WithNotices(notice, notice2)(handler) + require.NoError(t, WithNotices(notice, notice2)(handler)) // Check that the aliases were added. assert.Contains(t, handler.Notices(), *notice) @@ -58,7 +58,7 @@ func TestWithNotice(t *testing.T) { // Apply the WithNotices option with an empty message notice3 := NewDebugNotice(originalFunc, "") - WithNotices(notice3)(handler) + require.NoError(t, WithNotices(notice3)(handler)) assert.Contains(t, handler.Notices(), *notice) assert.Contains(t, handler.Notices(), *notice2) @@ -67,7 +67,7 @@ func TestWithNotice(t *testing.T) { // Try to apply a notice with an empty function name. notice4 := &FunctionNotice{} - WithNotices(notice4)(handler) + require.NoError(t, WithNotices(notice4)(handler)) // Check that the aliases were not added. assert.NotContains(t, handler.Notices(), *notice4) diff --git a/sprout.go b/sprout.go index 04b37fe..0832965 100644 --- a/sprout.go +++ b/sprout.go @@ -46,7 +46,9 @@ func New(opts ...HandlerOption[*DefaultHandler]) *DefaultHandler { } for _, opt := range opts { - opt(dh) + if err := opt(dh); err != nil { + dh.logger.With("error", err).Error("Failed to apply handler option") + } } return dh diff --git a/sprout_test.go b/sprout_test.go index 93b242a..1f8aa65 100644 --- a/sprout_test.go +++ b/sprout_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNew_DefaultValues(t *testing.T) { @@ -30,7 +31,7 @@ func TestWithLogger(t *testing.T) { option := WithLogger(logger) handler := New() - option(handler) // Apply the option + require.NoError(t, option(handler)) // Apply the option assert.Equal(t, logger, handler.Logger()) } @@ -42,7 +43,7 @@ func TestWithParser(t *testing.T) { option := WithHandler(fnHandler) handler := New() - option(handler) // Apply the option + require.NoError(t, option(handler)) // Apply the option assert.Equal(t, fnHandler, handler) } @@ -54,7 +55,7 @@ func TestWithNilHandler(t *testing.T) { option := WithHandler(nil) beforeApply := fnHandler - option(beforeApply) + require.NoError(t, option(beforeApply)) // Apply the option assert.Equal(t, beforeApply, fnHandler) } From 3d2a727ce530f01290ca8139e60736cd0ec65a65 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 16:30:52 +0200 Subject: [PATCH 09/15] docs: update documentation --- docs/advanced/how-to-create-a-handler.md | 4 ++-- docs/features/loader-system-registry.md | 11 ++++++++++ docs/introduction/getting-started.md | 26 +++++++++++++++++++----- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/docs/advanced/how-to-create-a-handler.md b/docs/advanced/how-to-create-a-handler.md index 3e9bf02..8a52e34 100644 --- a/docs/advanced/how-to-create-a-handler.md +++ b/docs/advanced/how-to-create-a-handler.md @@ -11,8 +11,8 @@ The `Handler` interface in Sprout defines the basic methods required to manage r * `Logger() *slog.Logger`: Returns the logger instance used for logging. * `AddRegistry(registry Registry) error`: Adds a single registry to the handler. * `AddRegistries(registries ...Registry) error`: Adds multiple registries to the handler. -* `Functions() FunctionMap`: Returns the map of registered functions. -* `Aliases() FunctionAliasMap`: Returns the map of function aliases. +* `RawFunctions() FunctionMap`: Returns the map of registered functions. +* `RawAliases() FunctionAliasMap`: Returns the map of function aliases. * `Build() FunctionMap`: Builds and returns the complete function map, ready to be used in templates. ### Step 2: Create Your Custom Handler Struct diff --git a/docs/features/loader-system-registry.md b/docs/features/loader-system-registry.md index b67d71c..807b1ea 100644 --- a/docs/features/loader-system-registry.md +++ b/docs/features/loader-system-registry.md @@ -43,6 +43,17 @@ tpl := template.Must( ) ``` +You can also use the option to add registries when initializing the handler: + +```go +handler := sprout.New( + // Add one registry + sprout.WithRegistry(ownregistry.NewRegistry()), + // Add more than one at the same time + sprout.WithRegistries(reg1.NewRegistry(), reg2.NewRegistry()), +) +``` + This code sets up your project to utilize the functions from your custom registry, making it easy to integrate and extend functionality. ## How to create a registry diff --git a/docs/introduction/getting-started.md b/docs/introduction/getting-started.md index 6b31d1d..7679462 100644 --- a/docs/introduction/getting-started.md +++ b/docs/introduction/getting-started.md @@ -38,19 +38,27 @@ handler := sprout.New() Sprout supports various customization options using handler options: -* **Logger Configuration:** - +* **Logger Configuration:**\ You can customize the logging behavior by providing a custom logger: ```go logger := slog.New(slog.NewTextHandler(os.Stdout)) handler := sprout.New(sprout.WithLogger(logger)) ``` +* **Load Registry:**\ + You can load a registry directly on your handler using the `WithRegistry` option: + + ```go + handler := sprout.New(sprout.WithRegistry(ownregistry.NewRegistry())) + ``` + + See more below or in dedicated page [loader-system-registry.md](../features/loader-system-registry.md "mention"). * **Aliases Management:**\ You can specify your custom aliases directly on your handler: -
handler := sprout.New(sprout.WithAlias("originalFunc", "alias"))
-    
+ ```go + handler := sprout.New(sprout.WithAlias("originalFunc", "alias")) + ``` See more below or in dedicated page [function-aliases.md](../features/function-aliases.md "mention"). * **Notices:**\ @@ -103,6 +111,14 @@ You can also add multiple registries at once: handler.AddRegistries(conversion.NewRegistry(), std.NewRegistry()) ``` +Or add registries directly when initializing the handler: + +```go +handler := sprout.New( + sprout.WithRegistries(conversion.NewRegistry(), std.NewRegistry()), +) +``` + ### Function Aliases Sprout supports function aliases, allowing you to call the same function by different names. @@ -130,7 +146,7 @@ funcs := handler.Build() tpl := template.New("example").Funcs(funcs).Parse(`{{ hello }}`) ``` -This prepares all registered functions and aliases for use in templates. +This prepares all registered functions and aliases for use in templates. This also caches the function map for better performance. ### Working with Templates From 0491fc376823c8ebc9f06cf0cf74e9d3c955f076 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Sun, 15 Sep 2024 22:33:26 +0200 Subject: [PATCH 10/15] perf: prevent duplicate check --- alias.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/alias.go b/alias.go index 2ba481e..bcbdf2b 100644 --- a/alias.go +++ b/alias.go @@ -11,15 +11,13 @@ type FunctionAliasMap = map[string][]string // inside the Build function in case of using a custom handler. func AssignAliases(h Handler) { for originalName, aliases := range h.RawAliases() { - _, exists := h.RawFunctions()[originalName] + fn, exists := h.RawFunctions()[originalName] if !exists { continue } for _, alias := range aliases { - if fn, ok := h.RawFunctions()[originalName]; ok { - h.RawFunctions()[alias] = fn - } + h.RawFunctions()[alias] = fn } } } From 9cb746ce611ab6939843839bba6dbd5ccac99c9c Mon Sep 17 00:00:00 2001 From: Atomys Date: Mon, 16 Sep 2024 01:41:39 +0200 Subject: [PATCH 11/15] chore: apply suggestions from code review Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Signed-off-by: Atomys --- handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index e46541f..1c1d13d 100644 --- a/handler.go +++ b/handler.go @@ -250,7 +250,7 @@ func (dh *DefaultHandler) buildSafeFuncs() { safeFuncs[safeFuncName(funcName)] = dh.safeWrapper(funcName, fn) } - for funcName, fn := range safeFuncs { - dh.cachedFuncsMap[funcName] = fn + for safeFuncName, fn := range safeFuncs { + dh.cachedFuncsMap[safeFuncName] = fn } } From 01315b68f8dbd1fc9d7a301533a02c608f826274 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Mon, 16 Sep 2024 01:43:47 +0200 Subject: [PATCH 12/15] chore: use godoc links --- handler.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 1c1d13d..2ba103f 100644 --- a/handler.go +++ b/handler.go @@ -109,7 +109,9 @@ func (dh *DefaultHandler) AddRegistries(registries ...Registry) error { // multiple times, so it is safe to call this method multiple times to retrieve // the same builded function map. // -// NOTE: This will replace the `FuncsMap()`, `TxtFuncMap()` and `HtmlFuncMap()` from sprig +// NOTE: This replaces the [github.com/Masterminds/sprig.FuncMap], +// [github.com/Masterminds/sprig.TxtFuncMap] and [github.com/Masterminds/sprig.HtmlFuncMap] +// from sprig func (dh *DefaultHandler) Build() FunctionMap { if dh.built { return dh.cachedFuncsMap From 2eb89418c8a98d08d4a09202c4d38b66240161bf Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Mon, 16 Sep 2024 01:53:37 +0200 Subject: [PATCH 13/15] chore: change buildSafeFuncs to AssignSafeFuncs to follow the same convetion --- handler.go | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/handler.go b/handler.go index 2ba103f..a9e0efa 100644 --- a/handler.go +++ b/handler.go @@ -119,7 +119,9 @@ func (dh *DefaultHandler) Build() FunctionMap { AssignAliases(dh) // Ensure all aliases are processed before returning the registry AssignNotices(dh) // Ensure all notices are processed before returning the registry - dh.buildSafeFuncs() + if dh.wantSafeFuncs { + AssignSafeFuncs(dh) // Ensure all functions are wrapped with safe functions + } dh.built = true return dh.cachedFuncsMap @@ -205,14 +207,33 @@ func WithSafeFuncs(enabled bool) HandlerOption[*DefaultHandler] { } } +// AssignSafeFuncs wraps all functions with a safe wrapper that logs any errors +// that occur during function execution. If safe functions are enabled in the +// DefaultHandler, this method will prepend "safe" to the function name and +// create a safe wrapper for each function. +// +// E.G. all functions will have both the original function name and a safe function name: +// +// originalFuncName -> SafeOriginalFuncName +func AssignSafeFuncs(handler Handler) { + safeFuncs := make(FunctionMap) + for funcName, fn := range handler.RawFunctions() { + safeFuncs[safeFuncName(funcName)] = safeWrapper(handler, funcName, fn) + } + + for funcName, fn := range safeFuncs { + handler.RawFunctions()[funcName] = fn + } +} + // safeWrapper create a safe wrapper function that calls the original function // and logs any errors that occur during the function call without interrupting // the execution of the template. -func (dh *DefaultHandler) safeWrapper(functionName string, fn any) wrappedFunction { +func safeWrapper(handler Handler, functionName string, fn any) wrappedFunction { return func(args ...any) (any, error) { out, err := runtime.SafeCall(fn, args...) if err != nil { - dh.Logger().With("function", functionName, "error", err).Error("function call failed") + handler.Logger().With("function", functionName, "error", err).Error("function call failed") } return out, nil } @@ -237,22 +258,3 @@ func safeFuncName(name string) string { return b.String() } - -// buildSafeFuncs wraps all functions with a safe wrapper that logs any errors -// that occur during function execution. If safe functions are enabled in the -// DefaultHandler, this method will prepend "safe" to the function name and -// create a safe wrapper for each function. -func (dh *DefaultHandler) buildSafeFuncs() { - if !dh.wantSafeFuncs { - return - } - - safeFuncs := make(FunctionMap) - for funcName, fn := range dh.cachedFuncsMap { - safeFuncs[safeFuncName(funcName)] = dh.safeWrapper(funcName, fn) - } - - for safeFuncName, fn := range safeFuncs { - dh.cachedFuncsMap[safeFuncName] = fn - } -} From e9eaa8c39968299a2c1d6e5a7f94f72c9b2df254 Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Mon, 16 Sep 2024 02:01:21 +0200 Subject: [PATCH 14/15] fix: safe wrapper test --- handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler_test.go b/handler_test.go index b1bce3e..a3b36a0 100644 --- a/handler_test.go +++ b/handler_test.go @@ -314,7 +314,7 @@ func TestDefaultHandler_safeWrapper(t *testing.T) { _, err := fn() require.Error(t, err, "fn should return an error") - safeFn := handler.safeWrapper("fn", fn) + safeFn := safeWrapper(handler, "fn", fn) _, safeErr := safeFn() require.NoError(t, safeErr, "safeFn should not return an error") assert.Equal(t, "[ERROR] function call failed\n", loggerHandler.messages.String()) From 648a8c57ccd528a3f4c32a36c773ebe10e52966b Mon Sep 17 00:00:00 2001 From: 42Atomys Date: Mon, 16 Sep 2024 16:56:47 +0200 Subject: [PATCH 15/15] chore: use only variadic for registry --- docs/features/loader-system-registry.md | 3 --- docs/introduction/getting-started.md | 4 ++-- registry.go | 13 ------------- registry_test.go | 15 --------------- sprout.go | 2 +- 5 files changed, 3 insertions(+), 34 deletions(-) diff --git a/docs/features/loader-system-registry.md b/docs/features/loader-system-registry.md index 807b1ea..8d220d5 100644 --- a/docs/features/loader-system-registry.md +++ b/docs/features/loader-system-registry.md @@ -47,9 +47,6 @@ You can also use the option to add registries when initializing the handler: ```go handler := sprout.New( - // Add one registry - sprout.WithRegistry(ownregistry.NewRegistry()), - // Add more than one at the same time sprout.WithRegistries(reg1.NewRegistry(), reg2.NewRegistry()), ) ``` diff --git a/docs/introduction/getting-started.md b/docs/introduction/getting-started.md index 7679462..a408921 100644 --- a/docs/introduction/getting-started.md +++ b/docs/introduction/getting-started.md @@ -46,10 +46,10 @@ Sprout supports various customization options using handler options: handler := sprout.New(sprout.WithLogger(logger)) ``` * **Load Registry:**\ - You can load a registry directly on your handler using the `WithRegistry` option: + You can load a registry directly on your handler using the `WithRegistries` option: ```go - handler := sprout.New(sprout.WithRegistry(ownregistry.NewRegistry())) + handler := sprout.New(sprout.WithRegistries(ownregistry.NewRegistry())) ``` See more below or in dedicated page [loader-system-registry.md](../features/loader-system-registry.md "mention"). diff --git a/registry.go b/registry.go index d63f792..f0bcd57 100644 --- a/registry.go +++ b/registry.go @@ -69,19 +69,6 @@ func AddNotice(notices *[]FunctionNotice, notice *FunctionNotice) { *notices = append(*notices, *notice) } -// WithRegistry returns a HandlerOption that adds the provided registry to the handler. -// This option allows you to integrate additional functions into the template processing -// environment by adding a new registry to the handler. -// -// Example: -// -// handler := New(WithRegistry(myRegistry)) -func WithRegistry(reg Registry) HandlerOption[*DefaultHandler] { - return func(dh *DefaultHandler) error { - return dh.AddRegistry(reg) - } -} - // WithRegistries returns a HandlerOption that adds the provided registries to the handler. // This option simplifies the process of adding multiple sets of functionalities into the // template engine at once. diff --git a/registry_test.go b/registry_test.go index 6ad7a3d..1fa1a19 100644 --- a/registry_test.go +++ b/registry_test.go @@ -55,21 +55,6 @@ func TestAddAlias(t *testing.T) { assert.Contains(t, aliasMap, "nonExistentFunc", "Aliases should be added under 'nonExistentFunc' even if the function doesn't exist") } -func TestWithRegistry(t *testing.T) { - // Define a registry with a function and an alias - mockRegistry := new(MockRegistry) - mockRegistry.linkHandlerMustCrash = true - mockRegistry.On("Uid").Return("mockRegistry") - mockRegistry.On("LinkHandler", mock.Anything).Return(errMock) - - // Create a handler with the registry - handler := New(WithRegistry(mockRegistry)) - handler.Build() - - // Check that the function and alias are present in the handler - assert.Contains(t, handler.registries, mockRegistry, "Registry should be added to the handler") -} - func TestWithRegistries(t *testing.T) { // Define two registries with functions and aliases mockRegistry1 := new(MockRegistry) diff --git a/sprout.go b/sprout.go index 0832965..88398ec 100644 --- a/sprout.go +++ b/sprout.go @@ -28,7 +28,7 @@ type wrappedFunction = func(args ...any) (any, error) // logger := slog.New(slog.NewTextHandler(os.Stdout)) // handler := New( // WithLogger(logger), -// WithRegistry(myRegistry), +// WithRegistries(myRegistry), // ) // // In the above example, the DefaultHandler is created with a custom logger and