Skip to content

Select asset templates by the final extension only - #114

Open
youdie006 wants to merge 1 commit into
unrolled:v1from
youdie006:asset-extension-final-only
Open

Select asset templates by the final extension only#114
youdie006 wants to merge 1 commit into
unrolled:v1from
youdie006:asset-extension-final-only

Conversation

@youdie006

Copy link
Copy Markdown

compileTemplatesFromDir and compileTemplatesFromAsset implement the same rule -- load every file whose extension is in Options.Extensions -- but they compute ext differently.

render.go:291, with the comment stating the case it exists for:

// Fix same-extension-dirs bug: some dir might be named to: "users.tmpl", "local.html".
// These dirs should be excluded as they are not valid golang templates, but files under
// them should be treat as normal.
...
ext = filepath.Ext(rel)

render.go:363, the asset twin, never got that fix:

ext = "." + strings.Join(strings.Split(rel, ".")[1:], ".")

For rel = "users.tmpl/index.tmpl" that produces ".tmpl/index.tmpl". No extension anyone can configure contains a path separator, so the for _, extension := range r.opt.Extensions below it is vacuously false and the template is skipped. The same happens for any multi-dot name -- a.b.tmpl gives ".b.tmpl".

Nothing fails at startup. New returns fine and the template is simply missing, so it surfaces as a render error at request time.

files := map[string]string{
	"testdata/dotdir/users.tmpl/index.tmpl": "Hello asset",
	"testdata/dotdir/a.b.tmpl":              "Hello ab",
}
dir   := New(Options{Directory: "testdata/dotdir"})
asset := New(Options{Directory: "testdata/dotdir", Asset: ..., AssetNames: ...})

dir.TemplateLookup("users.tmpl/index")    // non-nil
asset.TemplateLookup("users.tmpl/index")  // nil

The fix

Use filepath.Ext in the asset loader too, so both paths select the same files and produce the same template names. One line.

Behaviour change

Asset/go-bindata mode only, and only in the direction of loading files that were previously never loaded: anything under a dot-named directory, and any multi-dot filename. The resulting template names match what directory mode already produces (users.tmpl/index, a.b). No existing test row changes -- the suite is green unmodified. Every current fixture is single-dot, which is why this never showed up.

Verification

make ci (go test -cover -race -count=1 ./...): ok github.com/unrolled/render 1.071s, coverage: 88.4%. go vet ./... clean. golangci-lint run ./...: 0 issues, the same count as on v1, so I have not added any.

One test added to render_html_test.go driving the same two files through both loaders, plus two small fixtures. On v1 it reports:

render_html_test.go:553: asset mode did not load "users.tmpl/index"
render_html_test.go:553: asset mode did not load "a.b"

I checked the boundary from three directions rather than only confirming it goes green:

mutation result
revert to the Split(rel, ".")[1:] form fails
take everything after the first dot of the basename instead fails
keep the Split form but take the last element passes

The third is deliberate: it is semantically filepath.Ext, and its passing shows the test pins "only the final extension" rather than the literal call I happened to write.

What I did not change

  • compileTemplatesFromDir -- already correct, and touching it would move directory-mode output.
  • The if strings.Contains(rel, ".") guard on both sites. It is redundant now, since filepath.Ext returns "" when there is no dot, but removing it is cosmetic and widens the diff.
  • Both break statements at render.go:311 and render.go:383 -- they fire on a match, which is correct.
  • name := rel[0:len(rel)-len(ext)] -- correct once ext is, and it now matches the directory path exactly.

Disclosure: AI-assisted. I found and prepared this with an AI assistant, and I ran and verified everything above myself.

compileTemplatesFromDir takes filepath.Ext(rel), with a comment naming
the case it fixes: a directory called users.tmpl whose files are still
normal templates. compileTemplatesFromAsset never got that fix and
still joins everything after the first dot, so for
users.tmpl/index.tmpl it computes .tmpl/index.tmpl.

No configured extension contains a path separator, so the match below
is vacuously false and the template is silently skipped. The same
happens for any multi-dot name such as a.b.tmpl.

Use filepath.Ext here too, so both loaders select the same files and
produce the same template names.
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.

1 participant