Skip to content

Commit

Permalink
Addition of Locals Function with Go Generics as an Alternative to c.L…
Browse files Browse the repository at this point in the history
…ocals (#2813)

* Add type-specific local value handling with generics in Ctx

Introduced a new function, Locals, that utilizes Go's generics to handle and retrieve type-specific local values within a request context. This enhancement provides more accurate data type control within the context. Included are tests for generic and custom struct use-cases to ensure the function performs as expected.

* Update documentation for Go generics in Locals method

Added documentation to explain the new version of the Locals method that uses Go's generics feature. This version allows for better control of data types when manipulating and retrieving local values within a request's context. Examples are provided, along with a caution on using correct data types to prevent a runtime panic.

* update ctx.md

* Correct indentation in API documentation

* Refactor Locals function and add new test case

* Refactor Locals function and add new test case

---------

Co-authored-by: Deza Farras Tsany <[email protected]>
  • Loading branch information
ryanbekhen and defartsa23 authored Jan 29, 2024
1 parent 31246ff commit 738e062
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
16 changes: 16 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,22 @@ func (c *DefaultCtx) Locals(key any, value ...any) any {
return value[0]
}

// Locals function utilizing Go's generics feature.
// This function allows for manipulating and retrieving local values within a request context with a more specific data type.
func Locals[V any](c Ctx, key any, value ...V) V {
var v V
var ok bool
if len(value) == 0 {
v, ok = c.Locals(key).(V)
} else {
v, ok = c.Locals(key, value[0]).(V)
}
if !ok {
return v // return zero of type V
}
return v
}

// Location sets the response Location HTTP header to the specified path parameter.
func (c *DefaultCtx) Location(path string) {
c.setCanonical(HeaderLocation, path)
Expand Down
45 changes: 45 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,51 @@ func Test_Ctx_Locals(t *testing.T) {
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}

// go test -run Test_Ctx_Locals_Generic
func Test_Ctx_Locals_Generic(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
Locals[string](c, "john", "doe")
Locals[int](c, "age", 18)
Locals[bool](c, "isHuman", true)
return c.Next()
})
app.Get("/test", func(c Ctx) error {
require.Equal(t, "doe", Locals[string](c, "john"))
require.Equal(t, 18, Locals[int](c, "age"))
require.Equal(t, true, Locals[bool](c, "isHuman"))
require.Equal(t, 0, Locals[int](c, "isHuman"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}

// go test -run Test_Ctx_Locals_GenericCustomStruct
func Test_Ctx_Locals_GenericCustomStruct(t *testing.T) {
t.Parallel()

type User struct {
name string
age int
}

app := New()
app.Use(func(c Ctx) error {
Locals[User](c, "user", User{"john", 18})
return c.Next()
})
app.Use("/test", func(c Ctx) error {
require.Equal(t, User{"john", 18}, Locals[User](c, "user"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}

// go test -run Test_Ctx_Method
func Test_Ctx_Method(t *testing.T) {
t.Parallel()
Expand Down
25 changes: 25 additions & 0 deletions docs/api/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,31 @@ app.Get("/admin", func(c fiber.Ctx) error {
})
```
An alternative version of the Locals method that takes advantage of Go's generics feature is also available. This version
allows for the manipulation and retrieval of local values within a request's context with a more specific data type.
```go title="Signature"
func Locals[V any](c Ctx, key any, value ...any) V
```
```go title="Example"
app.Use(func(c Ctx) error {
fiber.Locals[string](c, "john", "doe")
fiber.Locals[int](c, "age", 18)
fiber.Locals[bool](c, "isHuman", true)
return c.Next()
})
app.Get("/test", func(c Ctx) error {
fiber.Locals[string](c, "john") // "doe"
fiber.Locals[int](c, "age") // 18
fiber.Locals[bool](c, "isHuman") // true
return nil
})
````

Make sure to understand and correctly implement the Locals method in both its standard and generic form for better control
over route-specific data within your application.

## Location

Sets the response [Location](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Location) HTTP header to the specified path parameter.
Expand Down

0 comments on commit 738e062

Please sign in to comment.