{"id":18369133,"url":"https://github.com/angusgmorrison/doppel","last_synced_at":"2025-10-19T04:32:33.874Z","repository":{"id":43127771,"uuid":"298864843","full_name":"AngusGMorrison/doppel","owner":"AngusGMorrison","description":"A concurrent, non-blocking, compositing cache for Go templates.","archived":false,"fork":false,"pushed_at":"2022-04-28T07:15:50.000Z","size":75,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-15T20:54:19.292Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AngusGMorrison.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-26T17:24:54.000Z","updated_at":"2023-10-20T15:35:08.000Z","dependencies_parsed_at":"2022-09-05T12:31:00.421Z","dependency_job_id":null,"html_url":"https://github.com/AngusGMorrison/doppel","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AngusGMorrison%2Fdoppel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AngusGMorrison%2Fdoppel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AngusGMorrison%2Fdoppel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AngusGMorrison%2Fdoppel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AngusGMorrison","download_url":"https://codeload.github.com/AngusGMorrison/doppel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248281424,"owners_count":21077423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-05T23:28:33.422Z","updated_at":"2025-10-19T04:32:33.776Z","avatar_url":"https://github.com/AngusGMorrison.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# doppel\n\n_doppel is undergoing a facelift: to see the refactor in progress, check out branch `simplify`._\n\nA concurrent, non-blocking, composing cache for Go templates.\n\ndoppel provides a simple, thread-safe way to compose and cache nested templates as they're required. Rather than parsing sub-templates from scratch each time they're needed, a Doppel instance compiles a named combination of templates the first time it's requested and stores it in memory. Once parsed, retrieval is on the order of nanoseconds.\n\nCommon sub-templates (e.g. a recurrent nav bar) are parsed once and shared between compositions, reducing both the Doppel's memory footprint and the number of parsing operations required.\n\nEach Get request to the Doppel is non-blocking and preemptible via a `context.Context`. Even where a template must be parsed for the first time, concurrent requests for other templates proceed freely.\n\n**Package documentation**: https://godoc.org/github.com/AngusGMorrison/doppel\n\n## Why?\n\nThis is an over-engineered solution to a simple problem. You can almost certainly afford to parse all of your templates into memory at application start. Rather, doppel is a study in advanced concurrency patterns and concepts using goroutines, and an exploration of the trade-offs between using CSPs over locks.\n\n## Schematics\nAt the core of doppel are `CacheSchematics` and `TemplateSchematics`. A `CacheSchematic` is an acyclic graph of named `TemplateSchematic`s that collectively describe how to build a complete template from component parts.\n\nFor example, template `homepage` may contain unique `content` and `sidebar` subtemplates, but also depend on a common `nav` which itself depends on a universal `base` template. Here's what the `CacheSchematic` looks like:\n\n```Go\nschematic := CacheSchematic{\n  \"base\": {\"\", []string{\"path/to/base\"},\n  \"nav\": {\"base\", []string{\"path/to/nav\"},\n  \"homepage\": {\"nav\", []string{\"path/to/homepage\", \"path/to/content\", \"path/to/sidebar\"},\n}\n\nd, err := doppel.New(schematic)\n// ...\ntmpl, err := d.Get(context.Background(), \"homepage\")\n// ...\n```\n\nWhen `homepage` is requested for the first time, it requests the `nav` template from the cache. The first time `nav` is requested, it will request `base` from the cache. However, if `nav` has previously been requested, its cached value is a composition of `nav` and `base`, so `base` does not require a lookup.\n\nWith the `nav` template retrieved, `homepage` is parsed from a combination of `nav` and the subtemplates unique to `homepage`, given as a slice of strings. The completed `homepage` template is then cached eliminating the parsing phase the next time it is requested.\n\nEach `CacheSchematic` is checked for cycles before use.\n\n## Package-level and local Doppels\nFor convenience, doppel provides a package-level cache, instantiated with `Initialize(cs CacheSchematic, ...opts CacheOption)`, along with the functions `Get(ctx context.Context, name string)`, `Shutdown(gracePeriod time.Duration)` and `Close()` to perform operations on it.\n\nNew Doppels can be instantiated with `New(cs CacheSchematic, ...opts CacheOption)`, which returns a `*Doppel` with a live cache or an error. The same operations are available to these local Doppels.\n\n## CacheOptions\nVarious functional options are available for customizing the cache:\n* `WithGlobalTimeout`: enforce a time limit for all requests to the cache.\n* `WithLogger`: provide a logger for insight into each request's status.\n* `WithRetryTimeouts`: specify that parsing should be reattempted for cache entries with errors resulting from request `context` cancellations or timeouts.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fangusgmorrison%2Fdoppel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fangusgmorrison%2Fdoppel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fangusgmorrison%2Fdoppel/lists"}