Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial Template Support #77

Merged
merged 6 commits into from
Feb 14, 2024
Merged

Partial Template Support #77

merged 6 commits into from
Feb 14, 2024

Conversation

lukasjarosch
Copy link
Owner

Partial Templates allow the user to deduplicate template code by defining a template at a single place and reuse it within different templates.

  • Whenever a template is defined using the {{ define "unique_tpl_name" }} block, it should be available to use by all other templates by using the {{ template "unique_tpl_name" . }} syntax
  • There should be no general rule which dictates how partial templates are defined (e.g. a specific file suffix)

@lukasjarosch lukasjarosch linked an issue Feb 8, 2024 that may be closed by this pull request
@lukasjarosch
Copy link
Owner Author

  • Partial templates are discovered by creating each template and counting how many templates are defined by it. If a template file defines more than 1 template, it is considered a partial template.

template.go Outdated Show resolved Hide resolved
@andaryjo
Copy link
Collaborator

andaryjo commented Feb 9, 2024

Great feature which will solve a lot of problems for us! 🚀

What was a bit tricky for me to understand at first is that there is a slight difference between supplying . and .Inventory as context to a template definition. . is the whole context which includes the inventory (and .TargetName) and .Inventory is only the inventory. Depending on what you choose, in your partial template definition you either need to add a .Inventory in before your references or you don't.

But that comes with a catch. In case I would invoke a template definition like this:

{{ template "dummy" .Inventory.commander }}

... I could no longer use the .TargetName reference within that partial template. If you need the target name, you could only provide the whole context to the partial template, which might limit their use cases. For example, in some scenarios a user might want to invoke the same partial template with different contexts so that it ultimately renders different data. Is there maybe a way to keep the .TargetName accessible for partial templates even though it is not in the inventory context?

@lukasjarosch
Copy link
Owner Author

lukasjarosch commented Feb 9, 2024

Glad that this feature will solve lots of problems for you 😃. And thank you for testing this out ❤️

This is indeed an issue. But unfortunately Go templates only accept one context parameter.
I've come up with a solution which might be sufficient.

I have introduced a new template function context which essentially allows you to build a map[string]interface{}
on the fly.
This allows you to pass in as many arguments as you like, as long as there is an even amount of arguments
and the keys of the map are strings.

This helps you in solving the TargetName issue.

{{ template "my_template" context "TargetName" .TargetName "Something" .Inventory.something }}

This will create a map with keys TargetName and Something with the respective values.
So in your partial you can then use {{ .TargetName }} again.

Unfortunately I'm not able to automatically inject the targetname as the value is not known when the template functions are defined.

Is this a solution which will work for you?

@andaryjo
Copy link
Collaborator

Oh that is a nice solution. That would even allow us to provide multiple parts of the Inventory to a template instead of the whole Inventory (even though I'm not sure yet why one would that haha).

Tested and it works. Thanks for the quick fix.

@andaryjo
Copy link
Collaborator

andaryjo commented Feb 11, 2024

I also tested supplying variables into the template, both "primitives" (like strings you define on the fly) as well as declaring parts of the Inventory as variable and supplying them.

{{ $commander := "cody" }}
{{ $firstElement := (index .Inventory.elements 0 ) }
{{ template "dummy" context "Element $firstElement "Commander" $commander }}

With a template like this:

{{ define "dumy" }}
{{ . }}
{{ end }}

... you are then able to see all of those contexts getting supplied correctly (even though they get rearranged alphabetically which threw me off at first).

However, you are not actually able to access .Element or .Commander. When supplying contexts to a template like this, you seemingly can only access conecxts where their name is the name of a field that is also available to the template that invokes the partial template:

executing "dummy" at <.Commander>: can't evaluate field Commander in type struct { Inventory interface {}; TargetName string }

@lukasjarosch
Copy link
Owner Author

Thanks for repeatedly challenging the implementation 👍🏼
Unfortunately I'm not able to reproduce your error.

Given this target

---
target:
  skipper:
    use:
      - network # not used
  elements:
    - this:
        is:
          a:
            nested: map
    - welcome: home

This template:

# main template

{{- $commander := "cody" }}
{{- $firstElement := (index .Inventory.elements 0 ) }}
{{ template "with_data" context "TargetName" .TargetName "Network" .Inventory.network "Commander" $commander "Elements" $firstElement }}

And this partial

{{ define "with_data" }}

# {{ .TargetName }} partial

{{ . }}

{{ .Elements.this.is.a.nested }}

{{ .Commander }}

{{ end }}

Yields the following rendered main.md file:

# main template


# example partial

map[Commander:cody Elements:this:
    is:
        a:
            nested: map
 Network:foo: bar
 TargetName:example]

map

cody

Did I miss anything?

@andaryjo
Copy link
Collaborator

I can't reproduce it either. It's possible I just messed up the local skipper vendoring. Everything works now. 🚀

@andaryjo andaryjo self-requested a review February 14, 2024 03:08
@lukasjarosch lukasjarosch merged commit 69d912a into main Feb 14, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for partial templates
3 participants