New targets & new renderers and write methods [WIP]
authorspf13 <steve.francia@gmail.com>
Tue, 4 Nov 2014 05:39:37 +0000 (00:39 -0500)
committerspf13 <steve.francia@gmail.com>
Tue, 4 Nov 2014 05:39:37 +0000 (00:39 -0500)
hugolib/planner.go
hugolib/site.go
target/file.go
target/file_test.go [deleted file]
target/page.go [new file with mode: 0644]
target/page_test.go [new file with mode: 0644]

index ae32f9a38a2557a527a383dbb6933799acc53933..7f03bbb0af07bfe02f42b9f2cae52960c3d10981 100644 (file)
@@ -24,23 +24,23 @@ func (s *Site) ShowPlan(out io.Writer) (err error) {
                }
                fmt.Fprintf(out, "\n")
                fmt.Fprintf(out, " canonical => ")
-               if s.Target == nil {
+               if s.Targets.Page == nil {
                        fmt.Fprintf(out, "%s\n\n", "!no target specified!")
                        continue
                }
 
-               trns, err := s.Target.Translate(p.TargetPath())
+               trns, err := s.PageTarget().Translate(p.TargetPath())
                if err != nil {
                        return err
                }
                fmt.Fprintf(out, "%s\n", trns)
 
-               if s.Alias == nil {
+               if s.Targets.Alias == nil {
                        continue
                }
 
                for _, alias := range p.Aliases {
-                       aliasTrans, err := s.Alias.Translate(alias)
+                       aliasTrans, err := s.AliasTarget().Translate(alias)
                        if err != nil {
                                return err
                        }
index d71eda35977f9c3e86ad6a5f13f6275418edff5a..2e26beb842aace4fd062a10f5054ed4db8088e74 100644 (file)
@@ -69,8 +69,7 @@ type Site struct {
        Shortcodes  map[string]ShortcodeFunc
        Menus       Menus
        timer       *nitro.B
-       Target      target.Output
-       Alias       target.AliasPublisher
+       Targets     targetList
        Completed   chan bool
        RunMode     runmode
        params      map[string]interface{}
@@ -78,6 +77,12 @@ type Site struct {
        futureCount int
 }
 
+type targetList struct {
+       Page  target.Output
+       File  target.Output
+       Alias target.AliasPublisher
+}
+
 type SiteInfo struct {
        BaseUrl         template.URL
        Taxonomies      TaxonomyList
@@ -157,10 +162,6 @@ func (s *Site) Build() (err error) {
 
 func (s *Site) Analyze() {
        s.Process()
-       s.initTarget()
-       s.Alias = &target.HTMLRedirectAlias{
-               PublishDir: s.absPublishDir(),
-       }
        s.ShowPlan(os.Stdout)
 }
 
@@ -671,7 +672,7 @@ func (s *Site) RenderAliases() error {
                        if err != nil {
                                return err
                        }
-                       if err := s.WriteAlias(a, template.HTML(plink)); err != nil {
+                       if err := s.WriteDestAlias(a, template.HTML(plink)); err != nil {
                                return err
                        }
                }
@@ -733,7 +734,12 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
                        layouts = append(layouts, "_default/single.html")
                }
 
-               results <- s.render("page "+p.FullFilePath(), p, p.TargetPath(), s.appendThemeTemplates(layouts)...)
+               b, err := s.renderPage("page "+p.FullFilePath(), p, s.appendThemeTemplates(layouts)...)
+               if err != nil {
+                       results <- err
+               } else {
+                       results <- s.WriteDestPage(p.TargetPath(), b)
+               }
        }
 }
 
@@ -843,23 +849,32 @@ func taxonomyRenderer(s *Site, taxes <-chan taxRenderInfo, results chan<- error,
        for t := range taxes {
                n, base := s.newTaxonomyNode(t)
                layouts := []string{"taxonomy/" + t.singular + ".html", "indexes/" + t.singular + ".html", "_default/taxonomy.html", "_default/list.html"}
-               err := s.render("taxononomy "+t.singular, n, base+".html", s.appendThemeTemplates(layouts)...)
+               b, err := s.renderPage("taxononomy "+t.singular, n, s.appendThemeTemplates(layouts)...)
                if err != nil {
                        results <- err
                        continue
+               } else {
+                       err := s.WriteDestPage(base+".html", b)
+                       if err != nil {
+                               results <- err
+                       }
                }
 
                if !viper.GetBool("DisableRSS") {
                        // XML Feed
                        s.setUrls(n, base+".xml")
                        rssLayouts := []string{"taxonomy/" + t.singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
-                       err := s.render("taxonomy "+t.singular+" rss", n, base+".xml", s.appendThemeTemplates(rssLayouts)...)
+                       b, err := s.renderXML("taxonomy "+t.singular+" rss", n, s.appendThemeTemplates(rssLayouts)...)
                        if err != nil {
                                results <- err
                                continue
+                       } else {
+                               err := s.WriteDestFile(base+".xml", b)
+                               if err != nil {
+                                       results <- err
+                               }
                        }
                }
-               results <- nil
        }
 }
 
@@ -879,10 +894,13 @@ func (s *Site) RenderListsOfTaxonomyTerms() (err error) {
                layouts := []string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"}
                layouts = s.appendThemeTemplates(layouts)
                if s.layoutExists(layouts...) {
-                       err := s.render("taxonomy terms for "+singular, n, plural+"/index.html", layouts...)
+                       b, err := s.renderPage("taxonomy terms for "+singular, n, layouts...)
                        if err != nil {
                                return err
                        }
+                       if err := s.WriteDestPage(plural+"/index.html", b); err != nil {
+                               return err
+                       }
                }
        }
 
@@ -903,19 +921,25 @@ func (s *Site) RenderSectionLists() error {
                n.Data["Pages"] = data.Pages()
                layouts := []string{"section/" + section + ".html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"}
 
-               err := s.render("section "+section, n, section, s.appendThemeTemplates(layouts)...)
+               b, err := s.renderPage("section "+section, n, s.appendThemeTemplates(layouts)...)
                if err != nil {
                        return err
                }
+               if err := s.WriteDestPage(section, b); err != nil {
+                       return err
+               }
 
                if !viper.GetBool("DisableRSS") {
                        // XML Feed
                        rssLayouts := []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
                        s.setUrls(n, section+".xml")
-                       err = s.render("section "+section+" rss", n, section+".xml", s.appendThemeTemplates(rssLayouts)...)
+                       b, err = s.renderXML("section "+section+" rss", n, s.appendThemeTemplates(rssLayouts)...)
                        if err != nil {
                                return err
                        }
+                       if err := s.WriteDestFile(section+".xml", b); err != nil {
+                               return err
+                       }
                }
        }
        return nil
@@ -932,10 +956,13 @@ func (s *Site) newHomeNode() *Node {
 func (s *Site) RenderHomePage() error {
        n := s.newHomeNode()
        layouts := []string{"index.html", "_default/list.html", "_default/single.html"}
-       err := s.render("homepage", n, "/", s.appendThemeTemplates(layouts)...)
+       b, err := s.renderPage("homepage", n, s.appendThemeTemplates(layouts)...)
        if err != nil {
                return err
        }
+       if err := s.WriteDestPage("/", b); err != nil {
+               return err
+       }
 
        if !viper.GetBool("DisableRSS") {
                // XML Feed
@@ -953,19 +980,13 @@ func (s *Site) RenderHomePage() error {
 
                if !viper.GetBool("DisableRSS") {
                        rssLayouts := []string{"rss.xml", "_default/rss.xml", "_internal/_default/rss.xml"}
-                       err := s.render("homepage rss", n, ".xml", s.appendThemeTemplates(rssLayouts)...)
+                       b, err := s.renderXML("homepage rss", n, s.appendThemeTemplates(rssLayouts)...)
                        if err != nil {
                                return err
                        }
-               }
-       }
-
-       // Force `UglyUrls` option to force `404.html` file name
-       switch s.Target.(type) {
-       case *target.Filesystem:
-               if !s.Target.(*target.Filesystem).UglyUrls {
-                       s.Target.(*target.Filesystem).UglyUrls = true
-                       defer func() { s.Target.(*target.Filesystem).UglyUrls = false }()
+                       if err := s.WriteDestFile("rss.xml", b); err != nil {
+                               return err
+                       }
                }
        }
 
@@ -974,10 +995,13 @@ func (s *Site) RenderHomePage() error {
        n.Permalink = s.permalink("404.html")
 
        nfLayouts := []string{"404.html"}
-       nfErr := s.render("404 page", n, "404.html", s.appendThemeTemplates(nfLayouts)...)
+       b, nfErr := s.renderPage("404 page", n, s.appendThemeTemplates(nfLayouts)...)
        if nfErr != nil {
                return nfErr
        }
+       if err := s.WriteDestFile("404.html", b); err != nil {
+               return err
+       }
 
        return nil
 }
@@ -1017,20 +1041,23 @@ func (s *Site) RenderSitemap() error {
        }
 
        // Force `UglyUrls` option to force `sitemap.xml` file name
-       switch s.Target.(type) {
+       switch s.PageTarget().(type) {
        case *target.Filesystem:
-               s.Target.(*target.Filesystem).UglyUrls = true
+               s.PageTarget().(*target.PagePub).UglyUrls = true
                optChanged = true
        }
 
        smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
-       err := s.render("sitemap", n, "sitemap.xml", s.appendThemeTemplates(smLayouts)...)
+       b, err := s.renderXML("sitemap", n, s.appendThemeTemplates(smLayouts)...)
        if err != nil {
                return err
        }
+       if err := s.WriteDestFile("sitemap.xml", b); err != nil {
+               return err
+       }
 
        if optChanged {
-               s.Target.(*target.Filesystem).UglyUrls = viper.GetBool("UglyUrls")
+               s.PageTarget().(*target.PagePub).UglyUrls = viper.GetBool("UglyUrls")
        }
 
        return nil
@@ -1087,20 +1114,24 @@ func (s *Site) layoutExists(layouts ...string) bool {
        return found
 }
 
-func (s *Site) render(name string, d interface{}, out string, layouts ...string) (err error) {
+func (s *Site) renderXML(name string, d interface{}, layouts ...string) (io.Reader, error) {
+       renderBuffer := s.NewXMLBuffer()
+       err := s.render(name, d, renderBuffer, layouts...)
+       return renderBuffer, err
+}
 
-       layout, found := s.findFirstLayout(layouts...)
-       if found == false {
-               jww.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
-               return
-       }
+func (s *Site) renderPage(name string, d interface{}, layouts ...string) (io.Reader, error) {
+       renderBuffer := new(bytes.Buffer)
+       err := s.render(name, d, renderBuffer, layouts...)
+
+       var outBuffer = new(bytes.Buffer)
 
        transformLinks := transform.NewEmptyTransforms()
 
        if viper.GetBool("CanonifyUrls") {
                absURL, err := transform.AbsURL(viper.GetString("BaseUrl"))
                if err != nil {
-                       return err
+                       return nil, err
                }
                transformLinks = append(transformLinks, absURL...)
        }
@@ -1110,17 +1141,18 @@ func (s *Site) render(name string, d interface{}, out string, layouts ...string)
        }
 
        transformer := transform.NewChain(transformLinks...)
+       transformer.Apply(outBuffer, renderBuffer)
+       return outBuffer, err
+}
 
-       var renderBuffer *bytes.Buffer
-
-       if strings.HasSuffix(out, ".xml") {
-               renderBuffer = s.NewXMLBuffer()
-       } else {
-               renderBuffer = new(bytes.Buffer)
+func (s *Site) render(name string, d interface{}, renderBuffer *bytes.Buffer, layouts ...string) error {
+       layout, found := s.findFirstLayout(layouts...)
+       if found == false {
+               jww.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
+               return nil
        }
 
-       err = s.renderThing(d, layout, renderBuffer)
-       if err != nil {
+       if err := s.renderThing(d, layout, renderBuffer); err != nil {
                // Behavior here should be dependent on if running in server or watch mode.
                jww.ERROR.Println(fmt.Errorf("Error while rendering %s: %v", name, err))
                if !s.Running() {
@@ -1128,14 +1160,7 @@ func (s *Site) render(name string, d interface{}, out string, layouts ...string)
                }
        }
 
-       var outBuffer = new(bytes.Buffer)
-       if strings.HasSuffix(out, ".xml") {
-               outBuffer = renderBuffer
-       } else {
-               transformer.Apply(outBuffer, renderBuffer)
-       }
-
-       return s.WritePublic(out, outBuffer)
+       return nil
 }
 
 func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
@@ -1160,33 +1185,48 @@ func (s *Site) NewXMLBuffer() *bytes.Buffer {
        return bytes.NewBufferString(header)
 }
 
-func (s *Site) initTarget() {
-       if s.Target == nil {
-               s.Target = &target.Filesystem{
+func (s *Site) PageTarget() target.Output {
+       if s.Targets.Page == nil {
+               s.Targets.Page = &target.PagePub{
                        PublishDir: s.absPublishDir(),
                        UglyUrls:   viper.GetBool("UglyUrls"),
                }
        }
+       return s.Targets.Page
 }
 
-func (s *Site) WritePublic(path string, reader io.Reader) (err error) {
-       s.initTarget()
-
-       jww.DEBUG.Println("writing to", path)
-       return s.Target.Publish(path, reader)
+func (s *Site) FileTarget() target.Output {
+       if s.Targets.File == nil {
+               s.Targets.File = &target.Filesystem{
+                       PublishDir: s.absPublishDir(),
+               }
+       }
+       return s.Targets.File
 }
 
-func (s *Site) WriteAlias(path string, permalink template.HTML) (err error) {
-       if s.Alias == nil {
-               s.initTarget()
-               s.Alias = &target.HTMLRedirectAlias{
+func (s *Site) AliasTarget() target.AliasPublisher {
+       if s.Targets.Alias == nil {
+               s.Targets.Alias = &target.HTMLRedirectAlias{
                        PublishDir: s.absPublishDir(),
                }
+
        }
+       return s.Targets.Alias
+}
 
-       jww.DEBUG.Println("alias created at", path)
+func (s *Site) WriteDestFile(path string, reader io.Reader) (err error) {
+       jww.DEBUG.Println("creating file:", path)
+       return s.FileTarget().Publish(path, reader)
+}
+
+func (s *Site) WriteDestPage(path string, reader io.Reader) (err error) {
+       jww.DEBUG.Println("creating page:", path)
+       return s.PageTarget().Publish(path, reader)
+}
 
-       return s.Alias.Publish(path, permalink)
+func (s *Site) WriteDestAlias(path string, permalink template.HTML) (err error) {
+       jww.DEBUG.Println("alias created at:", path)
+       return s.AliasTarget().Publish(path, permalink)
 }
 
 func (s *Site) draftStats() string {
index fa4e796815fdd57a171d3f6233c511d9b550923b..37851cae291b1f7d8cf15015ad4625bea19730a8 100644 (file)
@@ -1,7 +1,6 @@
 package target
 
 import (
-       "fmt"
        "io"
        "path"
 
@@ -23,13 +22,10 @@ type Output interface {
 }
 
 type Filesystem struct {
-       UglyUrls         bool
-       DefaultExtension string
-       PublishDir       string
+       PublishDir string
 }
 
 func (fs *Filesystem) Publish(path string, r io.Reader) (err error) {
-
        translated, err := fs.Translate(path)
        if err != nil {
                return
@@ -39,42 +35,11 @@ func (fs *Filesystem) Publish(path string, r io.Reader) (err error) {
 }
 
 func (fs *Filesystem) Translate(src string) (dest string, err error) {
-       if src == "/" {
-               if fs.PublishDir != "" {
-                       return path.Join(fs.PublishDir, "index.html"), nil
-               }
-               return "index.html", nil
-       }
-
-       dir, file := path.Split(src)
-       ext := fs.extension(path.Ext(file))
-       name := filename(file)
-       if fs.PublishDir != "" {
-               dir = path.Join(fs.PublishDir, dir)
-       }
-
-       if fs.UglyUrls || file == "index.html" {
-               return path.Join(dir, fmt.Sprintf("%s%s", name, ext)), nil
-       }
-
-       return path.Join(dir, name, fmt.Sprintf("index%s", ext)), nil
+       return path.Join(fs.PublishDir, src), nil
 }
 
 func (fs *Filesystem) extension(ext string) string {
-       switch ext {
-       case ".md", ".rst": // TODO make this list configurable.  page.go has the list of markup types.
-               return ".html"
-       }
-
-       if ext != "" {
-               return ext
-       }
-
-       if fs.DefaultExtension != "" {
-               return fs.DefaultExtension
-       }
-
-       return ".html"
+       return ext
 }
 
 func filename(f string) string {
diff --git a/target/file_test.go b/target/file_test.go
deleted file mode 100644 (file)
index 258eae4..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-package target
-
-import (
-       "testing"
-)
-
-func TestFileTranslator(t *testing.T) {
-       tests := []struct {
-               content  string
-               expected string
-       }{
-               {"/", "index.html"},
-               {"index.html", "index.html"},
-               {"bar/index.html", "bar/index.html"},
-               {"foo", "foo/index.html"},
-               {"foo.html", "foo/index.html"},
-               {"foo.xhtml", "foo/index.xhtml"},
-               {"section", "section/index.html"},
-               {"section/", "section/index.html"},
-               {"section/foo", "section/foo/index.html"},
-               {"section/foo.html", "section/foo/index.html"},
-               {"section/foo.rss", "section/foo/index.rss"},
-       }
-
-       for _, test := range tests {
-               f := new(Filesystem)
-               dest, err := f.Translate(test.content)
-               if err != nil {
-                       t.Fatalf("Translate returned and unexpected err: %s", err)
-               }
-
-               if dest != test.expected {
-                       t.Errorf("Tranlate expected return: %s, got: %s", test.expected, dest)
-               }
-       }
-}
-
-func TestFileTranslatorBase(t *testing.T) {
-       tests := []struct {
-               content  string
-               expected string
-       }{
-               {"/", "a/base/index.html"},
-       }
-
-       for _, test := range tests {
-               f := &Filesystem{PublishDir: "a/base"}
-               fts := &Filesystem{PublishDir: "a/base/"}
-
-               for _, fs := range []*Filesystem{f, fts} {
-                       dest, err := fs.Translate(test.content)
-                       if err != nil {
-                               t.Fatalf("Translated returned and err: %s", err)
-                       }
-
-                       if dest != test.expected {
-                               t.Errorf("Translate expected: %s, got: %s", test.expected, dest)
-                       }
-               }
-       }
-}
-
-func TestTranslateUglyUrls(t *testing.T) {
-       tests := []struct {
-               content  string
-               expected string
-       }{
-               {"foo.html", "foo.html"},
-               {"/", "index.html"},
-               {"section", "section.html"},
-               {"index.html", "index.html"},
-       }
-
-       for _, test := range tests {
-               f := &Filesystem{UglyUrls: true}
-               dest, err := f.Translate(test.content)
-               if err != nil {
-                       t.Fatalf("Translate returned an unexpected err: %s", err)
-               }
-
-               if dest != test.expected {
-                       t.Errorf("Translate expected return: %s, got: %s", test.expected, dest)
-               }
-       }
-}
-
-func TestTranslateDefaultExtension(t *testing.T) {
-       f := &Filesystem{DefaultExtension: ".foobar"}
-       dest, _ := f.Translate("baz")
-       if dest != "baz/index.foobar" {
-               t.Errorf("Translate expected return: %s, got %s", "baz/index.foobar", dest)
-       }
-}
diff --git a/target/page.go b/target/page.go
new file mode 100644 (file)
index 0000000..32bb56d
--- /dev/null
@@ -0,0 +1,71 @@
+package target
+
+import (
+       "fmt"
+       "html/template"
+       "io"
+       "path"
+
+       "github.com/spf13/hugo/helpers"
+       "github.com/spf13/hugo/hugofs"
+)
+
+type PagePublisher interface {
+       Translator
+       Publish(string, template.HTML) error
+}
+
+type PagePub struct {
+       UglyUrls         bool
+       DefaultExtension string
+       PublishDir       string
+}
+
+func (pp *PagePub) Publish(path string, r io.Reader) (err error) {
+
+       translated, err := pp.Translate(path)
+       if err != nil {
+               return
+       }
+
+       return helpers.WriteToDisk(translated, r, hugofs.DestinationFS)
+}
+
+func (pp *PagePub) Translate(src string) (dest string, err error) {
+       if src == "/" {
+               if pp.PublishDir != "" {
+                       return path.Join(pp.PublishDir, "index.html"), nil
+               }
+               return "index.html", nil
+       }
+
+       dir, file := path.Split(src)
+       ext := pp.extension(path.Ext(file))
+       name := filename(file)
+       if pp.PublishDir != "" {
+               dir = path.Join(pp.PublishDir, dir)
+       }
+
+       if pp.UglyUrls || file == "index.html" {
+               return path.Join(dir, fmt.Sprintf("%s%s", name, ext)), nil
+       }
+
+       return path.Join(dir, name, fmt.Sprintf("index%s", ext)), nil
+}
+
+func (pp *PagePub) extension(ext string) string {
+       switch ext {
+       case ".md", ".rst": // TODO make this list configurable.  page.go has the list of markup types.
+               return ".html"
+       }
+
+       if ext != "" {
+               return ext
+       }
+
+       if pp.DefaultExtension != "" {
+               return pp.DefaultExtension
+       }
+
+       return ".html"
+}
diff --git a/target/page_test.go b/target/page_test.go
new file mode 100644 (file)
index 0000000..69ad1cf
--- /dev/null
@@ -0,0 +1,93 @@
+package target
+
+import (
+       "testing"
+)
+
+func TestPageTranslator(t *testing.T) {
+       tests := []struct {
+               content  string
+               expected string
+       }{
+               {"/", "index.html"},
+               {"index.html", "index.html"},
+               {"bar/index.html", "bar/index.html"},
+               {"foo", "foo/index.html"},
+               {"foo.html", "foo/index.html"},
+               {"foo.xhtml", "foo/index.xhtml"},
+               {"section", "section/index.html"},
+               {"section/", "section/index.html"},
+               {"section/foo", "section/foo/index.html"},
+               {"section/foo.html", "section/foo/index.html"},
+               {"section/foo.rss", "section/foo/index.rss"},
+       }
+
+       for _, test := range tests {
+               f := new(PagePub)
+               dest, err := f.Translate(test.content)
+               if err != nil {
+                       t.Fatalf("Translate returned and unexpected err: %s", err)
+               }
+
+               if dest != test.expected {
+                       t.Errorf("Tranlate expected return: %s, got: %s", test.expected, dest)
+               }
+       }
+}
+
+func TestPageTranslatorBase(t *testing.T) {
+       tests := []struct {
+               content  string
+               expected string
+       }{
+               {"/", "a/base/index.html"},
+       }
+
+       for _, test := range tests {
+               f := &PagePub{PublishDir: "a/base"}
+               fts := &PagePub{PublishDir: "a/base/"}
+
+               for _, fs := range []*PagePub{f, fts} {
+                       dest, err := fs.Translate(test.content)
+                       if err != nil {
+                               t.Fatalf("Translated returned and err: %s", err)
+                       }
+
+                       if dest != test.expected {
+                               t.Errorf("Translate expected: %s, got: %s", test.expected, dest)
+                       }
+               }
+       }
+}
+
+func TestTranslateUglyUrls(t *testing.T) {
+       tests := []struct {
+               content  string
+               expected string
+       }{
+               {"foo.html", "foo.html"},
+               {"/", "index.html"},
+               {"section", "section.html"},
+               {"index.html", "index.html"},
+       }
+
+       for _, test := range tests {
+               f := &PagePub{UglyUrls: true}
+               dest, err := f.Translate(test.content)
+               if err != nil {
+                       t.Fatalf("Translate returned an unexpected err: %s", err)
+               }
+
+               if dest != test.expected {
+                       t.Errorf("Translate expected return: %s, got: %s", test.expected, dest)
+               }
+       }
+}
+
+func TestTranslateDefaultExtension(t *testing.T) {
+       f := &PagePub{DefaultExtension: ".foobar"}
+       dest, _ := f.Translate("baz")
+       if dest != "baz/index.foobar" {
+               t.Errorf("Translate expected return: %s, got %s", "baz/index.foobar", dest)
+       }
+}