1
0
mirror of https://github.com/danog/gojekyll.git synced 2025-01-22 12:51:24 +01:00

Update LTS protocol; more README

This commit is contained in:
Oliver Steele 2017-06-19 10:27:57 -04:00
parent 1d65456040
commit 0bb1227769
7 changed files with 118 additions and 71 deletions

View File

@ -58,18 +58,35 @@ 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.)
## Run
## Usage
```bash
gojekyll build # builds into ./_site
gojekyll serve # serves from memory, w/ live reload
gojekyll render index.md # render a file to stdout
gojekyll render / # render a URL to stdout
gojekyll data / # print a file or URL's variables
gojekyll --remote-liquid # use a local Liquid engine server
gojekyll build # builds into ./_site
gojekyll serve # serves from memory, w/ live reload
gojekyll render index.md # render a file to stdout
gojekyll render / # render a URL to stdout
gojekyll data / # print a file or URL's variables
gojekyll # use a local Liquid engine server
gojekyll help
```
### 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.
## Development
`./scripts/gojekyll` is an alternative to the `gojekyll` executable, that uses `go run` each time it's invoked.

View File

@ -50,7 +50,7 @@ func main() {
Destination: &destination,
},
cli.BoolFlag{
Name: "remote-liquid",
Name: "use-liquid-server",
Usage: "Use Liquid JSON-RPC server",
Destination: &useRemoteLiquidEngine,
},

View File

@ -16,13 +16,17 @@ type Template interface {
// LocalEngine runs in the same process, and can resolve tag arguments via local handlers.
type LocalEngine interface {
Engine
IncludeHandler(func(string, io.Writer, map[string]interface{}))
LinkHandler(LinkHandler)
IncludeHandler(IncludeTagHandler)
LinkTagHandler(LinkTagHandler)
}
// RemoteEngine runs out-of-process. It needs static tables for tag argument resolution.
type RemoteEngine interface {
Engine
FileURLMap(map[string]string)
IncludeDirs([]string)
FileURLMap(map[string]string) error
IncludeDirs([]string) error
}
// IncludeTagHandler resolves the filename in a Liquid include tag into the expanded content
// of the included file.
type IncludeTagHandler func(string, io.Writer, map[string]interface{}) error

View File

@ -13,13 +13,13 @@ func init() {
liquid.Tags["link"] = LinkFactory
}
// A LinkHandler given an include tag file name returns a URL.
type LinkHandler func(string) (string, bool)
// A LinkTagHandler given an include tag file name returns a URL.
type LinkTagHandler func(string) (string, bool)
var currentLinkHandler LinkHandler
var currentLinkHandler LinkTagHandler
// SetLinkHandler sets the function that resolves an include tag file name to a URL.
func SetLinkHandler(h LinkHandler) {
func SetLinkHandler(h LinkTagHandler) {
currentLinkHandler = h
}

View File

@ -3,6 +3,7 @@ package liquid
import (
"bytes"
"io"
"strings"
"github.com/acstech/liquid"
"github.com/acstech/liquid/core"
@ -11,7 +12,7 @@ import (
// LocalWrapperEngine is a wrapper around acstech/liquid.
type LocalWrapperEngine struct {
config *core.Configuration
linkHandler LinkHandler
linkHandler LinkTagHandler
}
type localTemplate struct {
@ -24,14 +25,20 @@ func NewLocalWrapperEngine() LocalEngine {
return &LocalWrapperEngine{}
}
// LinkHandler sets the link tag handler.
func (engine *LocalWrapperEngine) LinkHandler(h LinkHandler) {
// LinkTagHandler sets the link tag handler.
func (engine *LocalWrapperEngine) LinkTagHandler(h LinkTagHandler) {
engine.linkHandler = h
}
// IncludeHandler sets the include tag handler.
func (engine *LocalWrapperEngine) IncludeHandler(h func(string, io.Writer, map[string]interface{})) {
engine.config = liquid.Configure().IncludeHandler(h)
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)
}
})
}
// Parse is a wrapper for liquid.Parse.

View File

@ -1,6 +1,8 @@
package liquid
import (
"fmt"
"path/filepath"
"reflect"
"github.com/ybbus/jsonrpc"
@ -8,23 +10,36 @@ import (
// RPCClientEngine connects via JSON-RPC to a Liquid template server.
type RPCClientEngine struct {
rpcClient *jsonrpc.RPCClient
rpcSessionID string
rpcClient *jsonrpc.RPCClient
}
// DefaultServer is the default HTTP address for a Liquid template server.
// This is an unclaimed port number from https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Registered_ports
const DefaultServer = "localhost:4545"
// RPCVersion is the Liquid Template Server RPC version.
const RPCVersion = "0.0.1"
type remoteTemplate struct {
engine RemoteEngine
text []byte
}
// RPCError wraps jsonrpc.RPCError into an Error.
type RPCError struct{ jsonrpc.RPCError }
func (engine *RPCError) Error() string {
return engine.Message
}
// NewRPCClientEngine creates a RemoteEngine.
func NewRPCClientEngine(address string) RemoteEngine {
func NewRPCClientEngine(address string) (RemoteEngine, error) {
rpcClient := jsonrpc.NewRPCClient("http://" + address)
return &RPCClientEngine{rpcClient: rpcClient}
engine := RPCClientEngine{rpcClient: rpcClient}
if err := engine.createSession(); err != nil {
return nil, err
}
return &engine, nil
}
// Parse parses the template.
@ -32,37 +47,30 @@ func (engine *RPCClientEngine) Parse(text []byte) (Template, error) {
return &remoteTemplate{engine, text}, nil
}
// RPCError wraps jsonrpc.RPCError into an Error
type RPCError struct{ jsonrpc.RPCError }
func (engine *RPCError) Error() string {
return engine.Message
}
func (engine *RPCClientEngine) getSessionID() string {
if engine.rpcSessionID != "" {
return engine.rpcSessionID
}
func (engine *RPCClientEngine) createSession() error {
res, err := engine.rpcClient.Call("session")
if err != nil {
panic(err)
return err
}
if res.Error != nil {
panic(&RPCError{*res.Error})
return &RPCError{*res.Error}
}
var result struct {
SessionID string
SessionID string
RPCVersion string
}
if err := res.GetObject(&result); err != nil {
panic(err)
return err
}
engine.rpcSessionID = result.SessionID
return engine.rpcSessionID
if result.RPCVersion != RPCVersion {
return fmt.Errorf("Liquid server RPC mismatch: expected %s; actual %s", RPCVersion, result.RPCVersion)
}
engine.rpcClient.SetCustomHeader("Session-ID", result.SessionID)
return err
}
func (engine *RPCClientEngine) rpcCall(method string, params ...interface{}) (*jsonrpc.RPCResponse, error) {
args := append([]interface{}{engine.getSessionID()}, params...)
res, err := engine.rpcClient.Call(method, args...)
res, err := engine.rpcClient.Call(method, params...)
if err != nil {
return nil, err
}
@ -73,19 +81,22 @@ func (engine *RPCClientEngine) rpcCall(method string, params ...interface{}) (*j
}
// FileURLMap sets the filename -> permalink map that is used during link tag expansion.
func (engine *RPCClientEngine) FileURLMap(m map[string]string) {
_, err := engine.rpcCall("fileUrls", m)
if err != nil {
panic(err)
}
func (engine *RPCClientEngine) FileURLMap(m map[string]string) (err error) {
_, err = engine.rpcCall("fileUrls", m)
return
}
// IncludeDirs specifies the directories that the include tag looks in.
func (engine *RPCClientEngine) IncludeDirs(dirs []string) {
_, err := engine.rpcCall("includeDirs", dirs)
if err != nil {
panic(err)
// IncludeDirs specifies the search directories for the include tag.
func (engine *RPCClientEngine) IncludeDirs(dirs []string) (err error) {
abs := make([]string, len(dirs))
for i, dir := range dirs {
abs[i], err = filepath.Abs(dir)
if err != nil {
return
}
}
_, err = engine.rpcCall("includeDirs", abs)
return
}
// ParseAndRender parses and then renders the template.

42
site.go
View File

@ -183,47 +183,55 @@ func (s *Site) initTemplateAttributes() {
}
}
func (s *Site) createLocalEngine() liquid.Engine {
func (s *Site) makeLocalLiquidEngine() liquid.Engine {
engine := liquid.NewLocalWrapperEngine()
engine.LinkHandler(s.GetFileURL)
includeHandler := func(name string, w io.Writer, scope map[string]interface{}) {
name = strings.TrimLeft(strings.TrimRight(name, "}}"), "{{")
engine.LinkTagHandler(s.GetFileURL)
includeHandler := func(name string, w io.Writer, scope map[string]interface{}) error {
filename := filepath.Join(s.Source, s.config.IncludesDir, name)
template, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
return err
}
text, err := engine.ParseAndRender(template, scope)
if err != nil {
panic(err)
return err
}
_, err = w.Write(text)
if err != nil {
panic(err)
}
return err
}
engine.IncludeHandler(includeHandler)
return engine
}
func (s *Site) createRemoteEngine() liquid.Engine {
engine := liquid.NewRPCClientEngine(liquid.DefaultServer)
func (s *Site) makeLiquidClient() (engine liquid.RemoteEngine, err error) {
engine, err = liquid.NewRPCClientEngine(liquid.DefaultServer)
if err != nil {
return
}
urls := map[string]string{}
for _, p := range s.Paths {
urls[p.Path()] = p.Permalink()
}
engine.FileURLMap(urls)
engine.IncludeDirs([]string{filepath.Join(s.Source, s.config.IncludesDir)})
return engine
err = engine.FileURLMap(urls)
if err != nil {
return
}
err = engine.IncludeDirs([]string{filepath.Join(s.Source, s.config.IncludesDir)})
return
}
// LiquidEngine create a liquid engine with site-specific behavior.
// LiquidEngine create a liquid engine configured to with include paths and link tag resolution
// for this site.
func (s *Site) LiquidEngine() liquid.Engine {
if s.liquidEngine == nil {
if s.UseRemoteLiquidEngine {
s.liquidEngine = s.createRemoteEngine()
var err error
s.liquidEngine, err = s.makeLiquidClient()
if err != nil {
panic(err)
}
} else {
s.liquidEngine = s.createLocalEngine()
s.liquidEngine = s.makeLocalLiquidEngine()
}
}
return s.liquidEngine