diff --git a/README.md b/README.md index cb6bf51..c9b2e9d 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,12 @@ This project is missing more functionality than it implements. It may accidental - [ ] Sass caching - [ ] Customization - [x] Templates - - [x] link tag - - [x] include tag + - [x] `link` tag - [ ] Remaining Jekyll Liquid tags - [ ] Jekyll Liquid filters - [x] Includes + - [ ] parameters + - [ ] `include_relative` - [x] Permalinks - [ ] Pagination - [ ] Themes @@ -55,16 +56,6 @@ Intentional differences from Jekyll: go get -u osteele/gojekyll/cmd/gojekyll ``` -You get slightly better Liquid template parsing from some unmerged pull requests to the **acstech/liquid** library. If you want to use [my fork](https://github.com/osteele/liquid) instead: - -```bash -cd $(go env GOPATH)/src/github.com/acstech/liquid -git remote add osteele https://github.com/osteele/liquid.git -git pull -f osteele -``` - -(See articles by [Shlomi Noach](http://code.openark.org/blog/development/forking-golang-repositories-on-github-and-managing-the-import-path) and [Francesc Campoy](http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html) for how this works and why it is necessary.) - ## Usage ```bash @@ -74,23 +65,6 @@ gojekyll help gojekyll help build ``` -### Liquid Template Server - -The embedded Liquid server isn't very compliant with Shopfiy Liquid syntax. - -You can run a "Liquid Template Server" on the same machine, and tell `gojekyll` to use this instead. -This is currently about 10x slower than using the embedded engine, but still 5x faster than Ruby `jekyll`. - -1. Download and run (liquid-template-server)[https://github.com/osteele/liquid-template-server]. -2. Invoke `gojekyll` with the `--use-liquid-server` option; e.g.: - - ```bash - gojekyll --use-liquid-server build - gojekyll --use-liquid-server serve - ``` - -Neither the embedded Liquid server nor the Liquid Template Server implements very many Jekyll Liquid filters or tags. I'm adding to these as necessary to support my own sites. - ## Contributing Install package dependencies and development tools: @@ -121,8 +95,6 @@ go tool pprof gojekyll gojekyll.prof ## Credits -For rendering Liquid templates: ACS Technologies's fork [acstech/liquid](https://github.com/acstech/liquid) of Karl Seguin's [karlseguin/liquid](https://github.com/karlseguin/liquid) Go implementation; or, Jun Yang's JavaScript implementation [harttle/shopify-liquid](https://github.com/harttle/shopify-liquid/) via JSON-RPC. - Jascha Ephraim's [jaschaephraim/lrserver](https://github.com/jaschaephraim/lrserver) Live Reload server. to open the URL in a browser. diff --git a/liquid/liquid_filters.go b/liquid/liquid_filters.go index 097482c..461161d 100644 --- a/liquid/liquid_filters.go +++ b/liquid/liquid_filters.go @@ -19,6 +19,17 @@ func init() { core.RegisterFilter("where_exp", whereExpFactory) } +func AddStandardFilters(e *LocalWrapperEngine) { + e.engine.DefineFilter("date_to_rfc822", func(date time.Time) interface{} { + return date.Format(time.RFC822) + // Out: Mon, 07 Nov 2008 13:07:54 -0800 + }) + e.engine.DefineFilter("date_to_string", func(date time.Time) interface{} { + return date.Format("02 Jan 2005") + // Out: 07 Nov 2008 + }) +} + func arrayToSentenceStringFactory(parameters []core.Value) core.Filter { conj := "and " if len(parameters) > 0 { diff --git a/liquid/liquid_filters_test.go b/liquid/liquid_filters_test.go index dce7509..6cf5ad9 100644 --- a/liquid/liquid_filters_test.go +++ b/liquid/liquid_filters_test.go @@ -25,7 +25,7 @@ var filterTestScope = map[string]interface{}{ } func timeMustParse(s string) time.Time { - t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + t, err := time.Parse(time.RFC3339, s) if err != nil { panic(err) } diff --git a/liquid/local.go b/liquid/local.go index 31bba7f..dec5d7a 100644 --- a/liquid/local.go +++ b/liquid/local.go @@ -1,74 +1,66 @@ package liquid import ( - "bytes" + "fmt" "io" - "strings" - "github.com/acstech/liquid" - "github.com/acstech/liquid/core" + "github.com/osteele/liquid" + "github.com/osteele/liquid/chunks" ) -// LocalWrapperEngine is a wrapper around acstech/liquid. +// LocalWrapperEngine is a wrapper around osteele/liquid. type LocalWrapperEngine struct { - config *core.Configuration - linkHandler LinkTagHandler -} - -type localTemplate struct { - engine *LocalWrapperEngine - lt *liquid.Template + engine liquid.Engine + linkHandler LinkTagHandler + includeTagHandler IncludeTagHandler } // NewLocalWrapperEngine creates a LocalEngine. func NewLocalWrapperEngine() LocalEngine { - return &LocalWrapperEngine{} + e := &LocalWrapperEngine{engine: liquid.NewEngine()} + AddStandardFilters(e) + e.engine.DefineTag("link", func(filename string) (func(io.Writer, chunks.Context) error, error) { + return func(w io.Writer, _ chunks.Context) error { + url, found := e.linkHandler(filename) + if !found { + return fmt.Errorf("missing link filename: %s", filename) + } + _, err := w.Write([]byte(url)) + return err + }, nil + }) + e.engine.DefineTag("include", func(filename string) (func(io.Writer, chunks.Context) error, error) { + return func(w io.Writer, ctx chunks.Context) error { + return e.includeTagHandler(filename, w, ctx.GetVariableMap()) + }, nil + }) + return e } // LinkTagHandler sets the link tag handler. -func (engine *LocalWrapperEngine) LinkTagHandler(h LinkTagHandler) { - engine.linkHandler = h +func (e *LocalWrapperEngine) LinkTagHandler(h LinkTagHandler) { + e.linkHandler = h } // IncludeHandler sets the include tag handler. -func (engine *LocalWrapperEngine) IncludeHandler(h IncludeTagHandler) { - engine.config = liquid.Configure().IncludeHandler(func(name string, w io.Writer, context map[string]interface{}) { - name = strings.TrimLeft(strings.TrimRight(name, "}}"), "{{") - err := h(name, w, context) - if err != nil { - panic(err) - } - }) +func (e *LocalWrapperEngine) IncludeHandler(h IncludeTagHandler) { + e.includeTagHandler = h } // Parse is a wrapper for liquid.Parse. -func (engine *LocalWrapperEngine) Parse(text []byte) (Template, error) { - template, err := liquid.Parse(text, engine.config) - return &localTemplate{engine, template}, err +func (e *LocalWrapperEngine) Parse(source []byte) (Template, error) { + // fmt.Println("parse", string(source)) + t, err := e.engine.ParseTemplate(source) + // return &localTemplate{t}, err + return t, err } // ParseAndRender parses and then renders the template. -func (engine *LocalWrapperEngine) ParseAndRender(text []byte, scope map[string]interface{}) ([]byte, error) { - template, err := engine.Parse(text) +func (e *LocalWrapperEngine) ParseAndRender(source []byte, scope map[string]interface{}) ([]byte, error) { + t, err := e.Parse(source) if err != nil { return nil, err } - return template.Render(scope) -} - -// Render is a wrapper around liquid's template.Render that turns panics into errors. -func (template *localTemplate) Render(scope map[string]interface{}) (out []byte, err error) { - defer func() { - if r := recover(); r != nil { - if e, ok := r.(error); ok { - err = e - } else { - panic(r) - } - } - }() - SetLinkHandler(template.engine.linkHandler) - writer := new(bytes.Buffer) - template.lt.Render(writer, scope) - return writer.Bytes(), nil + // fmt.Println("render", t) + return t.Render(scope) }