Skip to content

Commit 170e91b

Browse files
42atomysccoVeille
andauthored
feat: implement registry system (alias: loader) (#46)
## Description Following the discussion about function loading in #31, this huge refactor implement each method behind multiples registries. > [!WARNING] > This pull request are in progress, somes tests must be done before allowing it to be merge and release. Each feedback are welcome ! ## TODO - [x] Allow registry to register aliases with function - [x] Test new package - [x] Provide detailed documentation for each module and glossary for handler and registry (WIP but available [here](https://docs.atom.codes/sprout/~/changes/gP9HOVz2PoFWbplTHwVo)) ## Changes - Create the Handler and Registry interface - Define the rules of a registry - Add a Readme about how to create a registry - Migrate all existing functions into separated registry - Backward compatibility function `FuncsMap` register all registry by default ## Fixes #10 ## Checklist - [x] I have read the **CONTRIBUTING.md** document. - [x] My code follows the code style of this project. - [x] I have added tests to cover my changes. - [x] All new and existing tests passed. - [x] I have updated the documentation accordingly. - [x] This change requires a change to the documentation on the website. ## Additional Information This update following the discussions #31 --------- Co-authored-by: ccoVeille <[email protected]>
1 parent 1192843 commit 170e91b

File tree

112 files changed

+7806
-5321
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+7806
-5321
lines changed

.github/codecov.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ github_checks:
1515
ignore:
1616
- benchmark/**
1717
- docs/**
18-
- sprig_functions_not_included_in_sprout.go
18+
- springin/** # ignore the springin directory due to deprecated code
19+
- registry/crypto/** # ignore the registry/crypto due to deprecated code
1920
comment:
2021
behavior: new
2122
require_changes: true

README.md

+83-89
Original file line numberDiff line numberDiff line change
@@ -39,56 +39,57 @@ You can track our progress towards Sprout v1.0 by following the documentation pa
3939
- [Roadmap to Sprout v1.0](#roadmap-to-sprout-v10)
4040
- [Transitioning from Sprig](#transitioning-from-sprig)
4141
- [Usage](#usage)
42-
- [Usage: Logger](#usage-logger)
43-
- [Usage: Alias](#usage-alias)
44-
- [Usage: Error Handling](#usage-error-handling)
45-
- [Default Value](#default-value)
46-
- [Panic](#panic)
47-
- [Error Channel](#error-channel)
42+
- [Creating a Handler](#creating-a-handler)
43+
- [Customizing the Handler](#customizing-the-handler)
44+
- [Working with Registries](#working-with-registries)
45+
- [Building Function Maps](#building-function-maps)
46+
- [Working with Templates](#working-with-templates)
47+
- [Usage: Quick Example (code only)](#usage-quick-example)
4848
- [Performence Benchmarks](#performence-benchmarks)
4949
- [Sprig v3.2.3 vs Sprout v0.2](#sprig-v323-vs-sprout-v02)
5050
- [Development Philosophy (Currently in reflexion to create our)](#development-philosophy-currently-in-reflexion-to-create-our)
5151

5252

5353
## Transitioning from Sprig
5454

55-
Sprout is designed to be a drop-in replacement for Sprig in the v1.0, with the same function names and behavior. To use Sprout in your project, simply replace the Sprig import with Sprout:
55+
Sprout provide a package `sprigin` to provide a drop-in replacement for Sprig in the v1.0, with the same function names and behavior. To use Sprout in your project, simply replace the Sprig import with sprigin:
56+
57+
> [!IMPORTANT]
58+
> The `sprigin` package is a temporary solution to provide backward compatibility with Sprig. We recommend updating your code to use the Sprout package directly to take advantage of the new features and improvements.
59+
>
60+
> A complete guide are available in the [documentation](https://docs.atom.codes/sprout/migration-from-sprig).
5661
5762
```diff
5863
import (
5964
- "github.com/Masterminds/sprig/v3"
60-
+ "github.com/go-sprout/sprout"
65+
+ "github.com/go-sprout/sprout/sprigin"
6166
)
6267

6368
tpl := template.Must(
6469
template.New("base").
6570
- Funcs(sprig.FuncMap()).
66-
+ Funcs(sprout.FuncMap()).
71+
+ Funcs(sprigin.FuncMap()).
6772
ParseGlob("*.tmpl")
6873
)
6974
```
7075

7176
## Usage
7277

73-
To use Sprout in your project, import the library and use the `FuncMap` function to add the template functions to your template:
78+
### Creating a Handler
79+
A handler in Sprout is responsible for managing the function registries and functions. The DefaultHandler is the primary implementation provided by Sprout.
7480

7581
```go
76-
import (
77-
"github.com/go-sprout/sprout"
78-
"text/template"
79-
)
82+
import "github.com/go-sprout/sprout"
8083

81-
tpl := template.Must(
82-
template.New("base").
83-
Funcs(sprout.FuncMap()).
84-
ParseGlob("*.tmpl")
85-
)
86-
```
84+
handler := sprout.New()
85+
```
86+
87+
### Customizing the Handler
8788

88-
You can customize the behavior of Sprout by creating a `FunctionHandler` and passing it to the `FuncMap` function or using the configuration functions provided by Sprout:
89+
Sprout supports various customization options using handler options:
8990

9091
```go
91-
handler := sprout.NewFunctionHandler(
92+
handler := sprout.New(
9293
// Add your logger to the handler to log errors and debug information using the
9394
// standard slog package or any other logger that implements the slog.Logger interface.
9495
// By default, Sprout uses a slog.TextHandler.
@@ -102,113 +103,106 @@ handler := sprout.NewFunctionHandler(
102103
// Set the alias for a function. By default, Sprout use alias for some functions for backward compatibility with Sprig.
103104
sprout.WithAlias("hello", "hi"),
104105
)
105-
106-
// Use the handler with the FuncMap function. The handler will be used to handle all template functions.
107-
tpl := template.Must(
108-
template.New("base").
109-
Funcs(sprout.FuncMap(sprout.WithFunctionHandler(handler))).
110-
ParseGlob("*.tmpl")
111-
)
112106
```
113-
### Usage: Logger
114107

115-
Sprout uses the `slog` package for logging. You can pass your logger
116-
to the `WithLogger` configuration function to log errors and debug information:
108+
### Working with Registries
109+
Registries in Sprout are groups of functions that can be added to a handler. They help organize functions and optimize template performance.
117110

118-
```go
119-
// Create a new logger using the slog package.
120-
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
111+
You can retrieve all built-ins registries and functions under [Registries](https://docs.atom.codes/sprout/registries/list-of-all-registries).
121112

122-
// Use the handler with the FuncMap function.
123-
tpl := template.Must(
124-
template.New("base").
125-
Funcs(sprout.FuncMap(sprout.WithLogger(logger))).
126-
ParseGlob("*.tmpl")
113+
```go
114+
import (
115+
"github.com/go-sprout/sprout/registry/conversion" // toString, toInt, toBool, ...
116+
"github.com/go-sprout/sprout/registry/std" // default, empty, any, all, ...
127117
)
128-
```
129118

130-
### Usage: Alias
119+
//...
131120

132-
Sprout provides the ability to set an alias for a function. This feature is useful for backward compatibility with Sprig. You can set an alias for a function using the `WithAlias` or `WithAliases` configuration functions.
133-
134-
See more about the alias in the [documentation](https://docs.atom.codes/sprout/function-aliases).
135-
136-
```go
137-
sprout.NewFunctionHandler(
138-
sprout.WithAlias("hello", "hi"),
121+
handler.AddRegistries(
122+
conversion.NewRegistry(),
123+
std.NewRegistry(),
139124
)
140125
```
141126

142-
### Usage: Error Handling
143-
144-
Sprout provides three error handling behaviors:
145-
- `ErrHandlingReturnDefaultValue`: Sprout returns the default value of the return type without crashes or panics.
146-
- `ErrHandlingPanic`: Sprout panics when an error occurs.
147-
- `ErrHandlingErrorChannel`: Sprout sends errors to the error channel.
148-
149-
You can set the error handling behavior using the `WithErrHandling` configuration function:
127+
### Building Function Maps
150128

129+
To use Sprout with templating engines like `html/template` or `text/template`, you need to build the function map:
151130
```go
152-
sprout.NewFunctionHandler(
153-
sprout.WithErrHandling(sprout.ErrHandlingReturnDefaultValue),
154-
)
131+
funcs := handler.Build()
132+
tpl := template.New("example").Funcs(funcs).Parse(`{{ hello }}`)
155133
```
156134

157-
#### Default Value
135+
### Working with Templates
136+
Once your function map is ready, you can use it to render templates:
137+
```go
138+
tpl, err := template.New("example").Funcs(funcs).Parse(`{{ myFunc }}`)
139+
if err != nil {
140+
log.Fatal(err)
141+
}
142+
tpl.Execute(os.Stdout, nil)
143+
```
144+
This will render the template with all functions and aliases available.
158145

159-
If you set the error handling behavior to `ErrHandlingReturnDefaultValue`, Sprout will return the default value of the return type without crashes or panics to ensure a smooth user experience when an error occurs.
160146

161-
#### Panic
147+
## Usage: Quick Example
162148

163-
If you set the error handling behavior to `ErrHandlingPanic`, Sprout will panic when an error occurs to ensure that the error is not ignored and sended back to template execution.
149+
Here is a quick example of how to use Sprout with the `text/template` package:
150+
```go
151+
package main
164152

165-
#### Error Channel
153+
import (
154+
"os"
155+
"text/template"
166156

167-
If you set the error handling behavior to `ErrHandlingErrorChannel`, you can pass an error channel to the `WithErrorChannel` configuration function. Sprout will send errors to the error channel:
157+
"github.com/go-sprout/sprout"
158+
"github.com/go-sprout/sprout/registry/std"
159+
)
168160

169-
```go
170-
errChan := make(chan error)
161+
func main() {
162+
handler := sprout.New()
163+
handler.AddRegistry(std.NewRegistry())
171164

172-
sprout.NewFunctionHandler(
173-
sprout.WithErrHandling(sprout.ErrHandlingErrorChannel),
174-
sprout.WithErrorChannel(errChan),
175-
)
165+
tpl := template.Must(
166+
template.New("example").Funcs(handler.Build()).Parse(`{{ hello }}`),
167+
)
168+
tpl.Execute(os.Stdout, nil)
169+
}
176170
```
177171

178172
## Performence Benchmarks
179173

180174
To see all the benchmarks, please refer to the [benchmarks](benchmarks/README.md) directory.
181175

182-
### Sprig v3.2.3 vs Sprout v0.2
176+
## Sprig v3.2.3 vs Sprout v0.5
183177
```
184178
goos: linux
185179
goarch: amd64
186180
pkg: sprout_benchmarks
187181
cpu: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
188-
BenchmarkSprig-16 1 2152506421 ns/op 44671136 B/op 21938 allocs/op
189-
BenchmarkSprout-16 1 1020721871 ns/op 37916984 B/op 11173 allocs/op
182+
BenchmarkSprig-16 1 2991811373 ns/op 50522680 B/op 32649 allocs/op
183+
BenchmarkSprout-16 1 1638797544 ns/op 42171152 B/op 18061 allocs/op
190184
PASS
191-
ok sprout_benchmarks 3.720s
185+
ok sprout_benchmarks 4.921s
192186
```
193187

194-
**Time improvement**: ((2152506421 - 1020721871) / 2152506421) * 100 = 52.6%
195-
**Memory improvement**: ((44671136 - 37916984) / 44671136) * 100 = 15.1%
188+
**Time improvement**: ((2991811373 - 1638797544) / 2991811373) * 100 = 45.3%
189+
**Memory improvement**: ((50522680 - 42171152) / 50522680) * 100 = 16.5%
196190

197-
So, Sprout v0.3 is approximately 52.6% faster and uses 15.1% less memory than Sprig v3.2.3. 🚀
191+
So, Sprout v0.5 is approximately 45.3% faster and uses 16.5% less memory than Sprig v3.2.3. 🚀
198192

199193
You can see the full benchmark results [here](benchmarks/README.md).
200194

201195
## Development Philosophy (Currently in reflexion to create our)
202196

203197
Our approach to extending and refining Sprout was guided by several key principles:
204198

205-
- Empowering layout construction through template functions.
206-
- Designing template functions that avoid returning errors when possible, instead displaying default values for smoother user experiences.
207-
- Ensuring template functions operate solely on provided data, without external data fetching.
208-
- Maintaining the integrity of core Go template functionalities without overrides.
209-
210-
211-
212-
213-
214-
199+
- Build on the principles of simplicity, flexibility, and consistency.
200+
- Empower developers to create robust templates without sacrificing performance or usability.
201+
- Adheres strictly to Go's templating conventions, ensuring a seamless experience for those familiar with Go's native tools.
202+
- Naming conventions across functions are standardized for predictability and ease of use.
203+
- Emphasizes error handling, preferring safe defaults over panics.
204+
- Provide a clear and comprehensive documentation to help users understand the library and its features.
205+
- Maintain a high level of code quality, ensuring that the library is well-tested, performant, and reliable.
206+
- Continuously improve and optimize the library to meet the needs of the community.
207+
- Avoids any external dependencies within template functions, ensuring all operations are self-contained and reliable.
208+
- Performance is a key consideration, with a focus on optimizing the library for speed and efficiency.

0 commit comments

Comments
 (0)