How to specify a function call instead of field reference in a partial?


#1

Hello,

This is again in continuation of the debugprint.html partial that I’ve been working on lately.

The issue is of a different kind this time.

I’d like to call {{ partial "debugprint.html" .File }} in my template, and have it print all variables listed in Hugo | File Variables.

The documentation starts with:

The .File object contains the following fields:
.File.Path
(and so on…)

So I thought that within the .File scope, printing .Path would work. But that’s not the case. Correct me if I am wrong… but looks like .File.Path calls the getter function Path… there is no public field called Path.

So below does not work in debugprint.html:

        {{ else if $typeIsFile }}
            {{ $fileVarNames   := (slice "Path" "LogicalName" "TranslationBaseName" "Ext" "Lang" "Dir") }}
            <!-- Below does not work.. needs to be fixed.
                 The error is seen when you use access this if condition as:
                 {{/* partial "debugprint.html" .File */}}
                 The issue is that I don't know how set the $fileVarSymbols values so that the elements are
                 called as functions instead of fields. i.e. call Path function in .File instead of trying
                 to get the value of the field Path.
            -->
            {{ $fileVarSymbols := (slice .Path  .LogicalName  .TranslationBaseName  .Ext  .Lang  .Dir ) }}
            <table>
                <tr><th class="key">File Variable</th><th class="value">Value</th></tr>
                {{ range $idx, $elem := $fileVarNames }}
                    {{ printf "<tr><td class=\"key\">%s</td><td class=\"value\">" $elem | safeHTML }}
                    {{ partial "debugprint.html" (index $fileVarSymbols $idx) }}
                    {{ printf "</td></tr>" | safeHTML }}
                {{ end }}
            </table>

Full code: https://gist.github.com/kaushalmodi/afb6c3e5098eccc028bddc91cf1eb53a#file-debugprint-html-L81-L98

With that partial in place, if I call {{ partial "debugprint.html" .File }} (in a page template like single.html, I get this error:

ERROR 2017/11/14 11:22:24 Error while rendering “page”: template: /home/kmodi/stow/pub_dotfiles/emacs/dot-emacs.d/elisp/ox-hugo/test/site/themes/bare_min/layouts/_default/single.html:76:7: executing “main” at <partial "debugprint…>: error calling partial: template: theme/partials/debugprint.html:88:41: executing “theme/partials/debugprint.html” at <.Path>: can’t evaluate field Path in type source.File

Is there a way to write that $fileVarSymbols slice so that the .File.Path gets called instead of an attempt to access .File.Path field?


PS: Note that the same tactic worked for printing all the relevant .Site and .GitInfo object fields… {{ partial "debugprint.html" .Site }} and {{ partial "debugprint.html" .GitInfo }} works beautifully.


#2

Can you use the built-in Go text/template call function to execute the method? Something like:

{{ call (printf ".%s" $idx) }}

#3

No, I get:

ERROR 2017/11/14 12:33:51 Error while rendering “page”: template: /home/kmodi/stow/pub_dotfiles/emacs/dot-emacs.d/elisp/ox-hugo/test/site/themes/bare_min/layouts/_default/single.html:78:7: executing “main” at <partial "debugprint…>: error calling partial: template: theme/partials/debugprint.html:102:71: executing “theme/partials/debugprint.html” at : error calling call: non-function of type string

Full gist – You can try it out by put that partial in your site and adding {{ partial "debugprint.html" .File }} to any page template.


#4

@moorereason This issue is related to How to convert a string to a "symbol" or vice-versa.

call expects a function identifier, not a string containing that…

call
	Returns the result of calling the first argument, which
	must be a function, with the remaining arguments as parameters.
	Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
	Y is a func-valued field, map entry, or the like.
	The first argument must be the result of an evaluation
	that yields a value of function type (as distinct from
	a predefined function such as print). The function must
	return either one or two result values, the second of which
	is of type error. If the arguments don't match the function
	or the returned error value is non-nil, execution stops.

So need to figure of if converting a string to an identifier is possible.


#5

I had even tried this:

{{ $fileVarNames   := (slice "UniqueID" "BaseFileName" "TranslationBaseName" "Lang" "Section" "LogicalName" "Dir" "Ext" "Path") }}
<!-- Below does not work.. needs to be fixed.
-->
{{ $uniqueID            := (call .UniqueID) }}
{{ $baseFileName        := (call .BaseFileName) }}
{{ $translationBaseName := (call .TranslationBaseName) }}
{{ $lang                := (call .Lang) }}
{{ $section             := (call .Section) }}
{{ $logicalName         := (call .LogicalName) }}
{{ $dir                 := (call .Dir) }}
{{ $ext                 := (call .Ext) }}
{{ $path                := (call .Path) }}
{{ $fileVarSymbols      := (slice $uniqueID $baseFileName $translationBaseName $lang $section $logicalName $dir $ext $path) }}
<table>
    <tr><th class="key">File Variable</th><th class="value">Value</th></tr>
    {{ range $idx, $elem := $fileVarNames }}
    {{ printf "<tr><td class=\"key\">%s</td><td class=\"value\">" $elem | safeHTML }}
        {{ partial "debugprint.html" (index $fileVarSymbols $idx) }}
        {{ printf "</td></tr>" | safeHTML }}
    {{ end }}
</table>

But I got this error:

ERROR 2017/11/14 12:42:04 Error while rendering “page”: template: /home/kmodi/stow/pub_dotfiles/emacs/dot-emacs.d/elisp/ox-hugo/test/site/themes/bare_min/layouts/_default/single.html:78:7: executing “main” at <partial "debugprint…>: error calling partial: template: theme/partials/debugprint.html:88:45: executing “theme/partials/debugprint.html” at <.UniqueID>: can’t evaluate field UniqueID in type source.File

… Again, it’s trying to reference the UniqueID field instead of calling a function named that… even though I have that call… call. So it errors out at that first call invocation.


#6

I came up with this hack: https://github.com/kaushalmodi/ox-hugo/blob/bd7171269413cc2abe00b8672f575758ffba3b16/test/site/themes/bare_min/layouts/partials/debugprint.html#L30-L49 to get around this limitation.

With that, this {{ partial "debugprint.html" (slice "File" .) }} will print all File object vars.


#7

@moorereason OK, I think this can probably be fixed in hugo. Earlier I thought that this was a Go templates limitation. May be it is, but can you confirm if this is fixable or not in hugo?

Scope of .File in partial

So, in any page template file like single.html, this works!

{{ with .File }}
    {{ .Path }}
{{ end }}

So my understanding would be that passing .File to a partial would be analogous to creating a scoped env like with does… but it’s not…

So, to experiment with that, create a partial called filedebug.html with just this:

<!-- filedebug.html -->
{{ .Path }}

… and now call that partial instead of the with block:

{{ partial "filedebug.html" .File }}

You will get this error:

ERROR 2017/11/14 15:09:47 Error while rendering “page”: template: /home/kmodi/stow/pub_dotfiles/emacs/dot-emacs.d/elisp/ox-hugo/test/site/themes/bare_min/layouts/_default/single.html:85:7: executing “main” at <partial "filedebug.h…>: error calling partial: template: theme/partials/filedebug.html:1:3: executing “theme/partials/filedebug.html” at <.Path>: can’t evaluate field Path in type source.File

Even if you change that partial file definition to:

{{ with . }}
    {{ .Path }}
{{ end }}

… you will get that same error.

So it’s clear that the partial is modifying the scope in some manner… but I don’t have Go knowledge to figure that one out. But shouldn’t the scope inside {{ partial "filedebug.html" .File }} be the exact same as the one inside {{ with .File }}?

Scope of .Site in partial

The .File is special because there we are calling getting functions instead of directly fetching exportable/global variables (as in the case of .Site or .GitInfo. So using .Site for this example.

Again, repeating the above exercise, in any page template file like single.html, this works as expected:

{{ with .Site }}
    {{ .Title }}
{{ end }}

Now create a partial called sitedebug.html with just this:

<!-- sitedebug.html -->
{{ .Title }}

… and now call that partial instead of the with block:

{{ partial "sitedebug.html" .Site }}

This works too (in the case of .Site)!


So… is this a bug? Or can the difference in behavior be explained?

It’s as if the scope within a partial is almost like that of within a with block… except that it works only for fetching variables, not for calling functions.

@bep Should I file an issue?


#8

Maybe related to this:

https://github.com/golang/go/issues/20503


#9

I tried putting {{ call .Path }} in the filedebug.html partial, but it gives the same error.


#10

In any case: This isn’t a Hugo issue, so create the issue in the Go repo. But if you want their attention, you should create a standalone (non Hugo) main func reproduceable.


#11

Isn’t partial a Hugo template function?

The discrepancy is in the scope/context inside a partial vs with.

But if you want their attention, you should create a standalone (non Hugo) main func reproduceable.

That’s completely out of my knowledge domain unfortunately… to recreate this… I’ll need to reverse engineer how Hugo partial works and basically learn the whole Go language.


Is this really completely unrelated to Hugo?


#12

It is obviously related to Hugo (we use Go templates), but I’m pretty sure that if it is an issue, the fix lives in Go – so the issue should live there, too.


#13

ok… I was suggesting this is Hugo issue because the “partial” jargon is Hugo invention, right? I cannot find the partial template function on https://golang.org/pkg/text/template/ or https://golang.org/pkg/html/template/, but I do find that reference in https://github.com/gohugoio/hugo/blob/master/tpl/partials/.


#14

Let me rephrase: I’m 99% sure that this is a Go issue, and the person investigating that should use that information as a piece in the puzzle.


#15

OK. I’ll take your word for it.

I can only request that you or someone else knowing and working in Go create a Hugo-devoid example to show this issue in Go template.

As you can see, I did everything I could and spent hours to figure out why .File was behaving differently in a partial… and I even boiled down the issue to that little filedebug.html partial example. But that’s only far I can get at this moment.

Please help propagate this further.


#16

@bep,
I tried replicating this issue with just Go, and it looks like this could be an issue with Hugo. See this playbook for a demo of the issue: https://play.golang.org/p/LHIUEHySKD. Embed a *File to fix this issue.


#17

Well, there is at least a discrepancy somewhere in the Go template handling of embedded types, because this works:

{{ with .File }}
    {{ .Path }}
{{ end }}

Steve may chime in with why he implemented it like this. I may revisit this as part of the “bundle” PR, but … not before then.


#18

Should I open an Issue for this? I believe that you are planning to land the bundle feature in the next release… can you please look at this?


#19

The File is now (in my branch) an interface which should be OK in this case – but I’m not looking into this specific case; I have plenty on my plate as it is.


#20

Thank you!