Giter VIP home page Giter VIP logo

jet's People

Contributors

annismckenzie avatar baxiry avatar c2h5oh avatar diogogmt avatar gitter-badger avatar jan-herout avatar jhsx avatar kataras avatar matheusd avatar oliverkofoed avatar pipe01 avatar razonyang avatar ryanlath avatar sarge avatar sauerbraten avatar shinji19 avatar svyatoslavg avatar tooolbox avatar vetcher avatar vojtechvitek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jet's Issues

Add debug mode that disables caching (while developing)

It would be great to have a debug mode that disables template caching (and could also print out debug statements about template blocks found etc.) so as to not require constantly restarting/recompiling the app while developing.

Is that something you could add?

Reflection?

Firstly, congratulations, nice work!
I had a quick skim though the code and saw reflection being used in a couple of places. Do you think performance could be optimised further without reflection?

Convert default funcs into jet.Funcs

This is specifically targeted towards the strings.Replace default func that takes an integer as the last argument to indicate the maximum number of replacements that should happen. For one, this looks weird in a template and we can also default this to -1 to indicate an unlimited number of replacements. This is easily done using jet.Funcs.

Add a special func type that can be called directly without the overhead of reflect.Call

Adding this will give ability for users to create fast functions, and with this we can remove the len builtin, and implement trans as a default func.

ex:

set.AddGlobal("len",func(a JetArguments) reflect.Value {
       a.RequireNumOfArguments(1,1)
       slice := a.GetKind(0,reflect.Slice,reflect.Array,reflect.Map,reflect.String)
       return reflect.Value(slice.Len())
})

Handling non existent variables in template

Some template frameworks (at least in Java) replaces non existent variables with blank values. Is it possible to do something like this in jet?

My use case is that there is a common html snippet which renders content based on some variables.

If variables are not set it should assume those values as blank and continue executing. Right now Jet is breaking and expecting me to set those variables albeit empty everywhere.

Variable in block parameter

I have this

Go code:

...
func theaterPage(client *gin.Context) {
	channel := client.Param("channel") // is just a string like "Left4Bot"
	view, err := views.GetTemplate("theater.tpl.html")
	if err != nil {
		return
	}
	view.Execute(client.Writer, jet.VarMap{
		"ChannelName": reflect.ValueOf(channel),
	}, nil)
}

My block:

{{block embedPlayer(channel="toby3d", autoplay=false)}}
	<iframe src="//www.hitbox.tv/#!/embed/{{ channel }}?autoplay={{ autoplay }}" width="360" height="640" frameborder="0" allowfullscreen></iframe>
{{end}}

My template:

{{ block body() }}
	<main>
		{{ yield embedPlayer(channel=.ChannelName, autoplay=true) }}
	</main>
{{ end }}

And this does not work. How can I implement a variable in a block parameter?

[Wiki] Syntax error

There's a snippet on wiki page:

templateName := "home.jet"
t, err := View.GetTemplate(templateName)
if err != nil {
    // template could not be loaded
}
var w bytes.Buffer // needs to conform to io.Writer interface (like gin's context.Writer for example)
vars := map[string]interface{}
if err = t.Execute(&w, vars, nil); err != nil {
    // error when executing template
}

which causes syntax error:

cannot use map[string]interface {} literal (type map[string]interface {}) as type jet.VarMap in argument to tpl.Execute

Fix:

-vars := map[string]interface{}
+vars := map[string]reflect.Value{}

or

-vars := map[string]interface{}
+vars := jet.VarMap{}

I wonder -- was it a design change? I kinda like map[string]interface{} better than map[string]reflect.Value, since I can easily pass my structs as interface{} without having to convert them via reflect.ValueOf().

Strip html space

{{if ...}}...{{end}} or {{...}} will leave space in the output.
go1.6+ template like {{- ...}} or {{... -}} is OK.
Thanks!

Add instrumentation timings in development mode

It would be nice to know which parts of the rendered template took the longest. Would it be hard to add instrumentation on the cached representation so one could fetch that after executing the template? Would be nice for web frameworks or optimizing a template for speed. Twig (template engine used in Symfony, PHP) has something like this:
17118315556_c1d5e499ce_b

[actually part of their web developer debug toolbar available while developing]

Variable declaration then use in layout

I want to do something like this:

<!-- file: "layout.jet" -->
<!DOCTYPE html>
<html>
  <head>
    <title>{{yield title()}}</title>
  </head>
  <body>
    <div class="menu">
      <menu {{if mnu=="m1"}} class="active"{{end}}>Menu #1</menu>
      <menu {{if mnu=="m2"}} class="active"{{end}}>Menu #2</menu>
      <menu {{if mnu=="m3"}} class="active"{{end}}>Menu #3</menu>
    </div>
    {{yield body()}}
  </body>
</html>

<!-- file: "home.jet" -->
{{extends "layout.jet"}}
{{ mnu := "m1" }}
{{block title()}}My title{{end}}
{{block body()}}
  <main>
    This content will be yielded in the layout above.
  </main>
{{end}}

But I got runtime error message :
Jet Runtime Error("layout.jet":8): identifier "mnu" is not available in the current scope map[]

if I declare mnu variable in layout.jet file, the page will be rendered as I expected.
but how can I set the value of mnu variable in view file?

I tried {{ mnu := yield menuValueBlock() }} in layout file but failed.

Wiki: Document Func

// Func function implementing this type are called directly, which is faster than calling through reflect.
// if a function is being called many times in the execution of a template, you may consider implement
// a wrapper to that func implementing a Func
type Func func(Arguments) reflect.Value

how to call template function?

I use a template variable to send a function, how to call it in the template?

func Full() string {
	return "ok"
}

data := make(map[string]interface{})
data["Full"]= Full

Wiki: Document jet.Ranger

// Ranger a value implementing a ranger interface is able to iterate on his value
// and can be used directly in a range statement 
type Ranger interface {
    Range() (reflect.Value, reflect.Value, bool)
}

Introspecting templates for variables used

Hey there

Is it possible to introspect Jet templates once their parsed, or before their parsed? Ideally I'd like to be able to pull out which variables are used in a template before execution, so I can decrease lookup time by not loading unnecessary data. The use case here is per-user template execution for emails. Users have KV pairs stored in a database, and when rendering a template it'd be great if I don't have to pull them all in.

Thanks!

Error 500 if block name is "content"

I always get error 500 if I try execute template which contains block with name content() like that:

{{yield content()}}
...
{{block content()}} {{end}}

But after change name all is okay. Tested in enabled development mode.

ARMv7 / x32 bit support

When I try to build my project with Jet template engine I get an error:

$ GOOS=linux GOARCH=arm  go build -i -o rixvox
# github.com/CloudyKit/jet
..\..\CloudyKit\jet\default.go:56: constant 99999999999 overflows int

Inconsistency with add operator

Taking in example

    RunJetTest(t, data, nil, "actionNode_AddIntString", `{{ 2+"1" }}`, "3")
    RunJetTest(t, data, nil, "actionNode_AddStringInt", `{{ "1"+2 }}`, "12")

The first test the left operand is a number and the right is a string then the right operand is converted to a number and the expression is evaluated as add expression.

The second test the left operand is a string and the right is a number the expression is evaluated as a string concatenation.

I propose a breaking change in the template language, adding a operator for string concatenation and let numeric operators to work only with numbers.
Like php $val.$val2.

@annismckenzie

Add internationalization support

Support for internationalization can be done by adding a new token type in the lexer, and an evaluator that would call the translator.Translate passed to the template.ExecuteI18n.

Also add a builtin function trans, a type Translator interface{ Translate(v string,i ...interface{}) error }

Pass new scope to yield and include

I'm trying to imitate some of the approaches other frameworks take to provide a common library of reusable parts for forms. The {{import "form_theme_bootstrap.jet" }} and the {{yield}} directive are good building blocks to achieve that. The problem arrises when trying to pass local variables to use when yielding, for example, an input-field block in the sense that it's not possible (at least I don't see a way).

Let me explain with some code:

<!-- file: "form_theme_bootstrap.jet" -->
{{block input-field}}
  <div class="{{container_class}} {{if has_error}}invalid{{else}}valid{{end}}">
    <input id="{{name}}" name="{{name}}" value="{{value}}" />
    {{if has_error}}
      <div class="form_error">{{error}}</div>
    {{end}}
  </div>
{{end}}
<!-- file: "user_form_edit.jet" -->
{{import "form_theme_bootstrap.jet"}}

<form>
  <!-- not sure how passing a hash/map of local variables would work  -->
  {{yield input-field { name: "firstname", value: .Firstname, has_error: errs.HasErrorFor("Firstname"), error: errs.ErrorFor("Firstname"), container_class: "" }  }}
</form>

Do you see what I'm trying to do? Having a way to construct a map on the fly in the template would be really great, that way I could use that as the context for the yield.

Thanks again for all the hard work and this great library!

How to connect CloudyKit/jet to web framework gin?

I work whith gin and want connect in project templates engine CloudyKit/jet.
When you try to connect i have error becouse jet use dont standart type for templates (
if I am not mistaken *jet.Templates)

How to connect CloudyKit/jet to web framework gin?

Multiple Directory Or Template File

Possible add multiple template directory or add template file?
I don't see any options.

var View = jet.NewHTMLSet("./templates"))

Eg:
View.AddDir("./modules/group/views")
Or:

View.AddTemplateFile("./modules/group/views/add.html")
View.AddTemplateFile("./modules/group/views/delete.html")
View.AddTemplateFile("./modules/group/views/list.html") 
View.AddTemplateFile("./modules/group/views/update.html")

OR:

fileName:="./modules/group/views/update.html"
content,err:=ioutil.ReadFile(fileName)
if err!=nil{
    View.AddTemplate(fileName,content)
}

├── modules
│   ├── config
│   │   ├── config.go
│   │   └── init.go
│   ├── group
│   │   ├── acl.go
│   │   ├── controller.go
│   │   ├── init.go
│   │   ├── locale
│   │   │   ├── en-US.yaml
│   │   │   └── tr-TR.yaml
│   │   ├── locale.go
│   │   ├── models.go
│   │   └── views
│   │   ├── add.html
│   │   ├── delete.html
│   │   ├── list.html
│   │   └── update.html
├── templates
│   ├── a.html
│   ├── a.js
│   ├── admin
│   │   └── menu
│   │   └── index.gohtml
│   ├── default
│   │   └── layouts
│   │   └── layout.gohtml
│   ├── index.gohtml
│   └── v1
│   └── layouts
│   └── layout.tpl

{{extends}} syntax does not available

This is my first time trying Jet.

Layout and template created :

<!-- file: "views/layouts/application.jet" -->
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    {{yield body()}}
  </body>
</html>

<!-- file: "views/home.jet" -->
{{extends "layouts/application.jet"}}
{{block body()}}
  <main>
    This content will be yielded in the layout above.
  </main>
{{end}}

Echo middleware :

package middleware

import (
	"fmt"
	"io"

	"github.com/CloudyKit/jet"
	"github.com/labstack/echo"
)

// Template type.
type Template struct {
	View *jet.Set
}

// Render satisfies the Echo.Renderer interface.
func (r *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	t, err := r.View.GetTemplate(name)
	if err != nil {
		panic(fmt.Errorf("Fatal error template file: %s", err))
	}
	vars, ok := data.(jet.VarMap)
	if !ok { // data passed wasn't a var map
		vars = jet.VarMap{}
	}
	err = t.Execute(w, vars, nil)
	if err != nil {
		// render an error response?
		// log the error?
		// up to you
	}
	return nil
}

Main package :

package main

import (
	"path/filepath"

	"github.com/CloudyKit/jet"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/my/project/app/controllers"
	middlewares "github.com/my/project/app/middleware"
	"github.com/labstack/echo"
)

func main() {
	// Echo instance
	e := echo.New()

	// Template middleware
	view := jet.NewHTMLSet(filepath.Join("./app", "views"))
	view.SetDevelopmentMode(true)
	e.Renderer = &middlewares.Template{View: view}

	// Authentication
	e.GET("/login", controllers.LoginIndex)

	// Start server
	e.Server.Addr = ":1323"
	e.Logger.Fatal(gracehttp.Serve(e.Server))
}

Endpoint handler :

package controllers

import (
	"github.com/CloudyKit/jet"
	"github.com/labstack/echo"
)

// LoginIndex handle login view.
func LoginIndex(c echo.Context) error {
	vars := jet.VarMap{}
	vars.Set("test", true)
	return c.Render(200, "welcome.jet", vars)
}

Result :

template: welcome.jet:2: unexpected token <extends> on operand goroutine 57 [running]:
github.com/my/project/vendor/github.com/labstack/echo/middleware.RecoverWithConfig.func1.1.1(0x9f5278, 0x1000, 0x15470000, 0xc60fe0, 0xc420276150)
    /home/my-user/.gvm/pkgsets/go1.8.3/global/src/github.com/my/project/vendor/github.com/labstack/echo/middleware/recover.go:75 +0x134
panic(0x9382c0, 0xc42027a9d0)
    /home/my-user/.gvm/gos/go1.8.3/src/runtime/panic.go:489 +0x2cf
github.com/my/project/app/middleware.(*Template).Render(0xc42000ebf8, 0xc51fe0, 0xc420276d90, 0x9d8412, 0xb, 0x970040, 0xc42021aed0, 0xc60fe0, 0xc420276150, 0xc42027a93a, ...)
    /home/my-user/.gvm/pkgsets/go1.8.3/global/src/github.com/my/project/app/middleware/template.go:20 +0x198
github.com/my/project/vendor/github.com/labstack/echo.(*context).Render(0xc420276150, 0xc8, 0x9d8412, 0xb, 0x970040, 0xc42021aed0, 0xc87f90, 0x1c)
    /home/my-user/.gvm/pkgsets/go1.8.3/global/src/github.com/my/project/vendor/github.com/labstack/echo/context.go:369 +0xcb
github.com/my/project/app/controllers.LoginIndex(0xc60fe0, 0xc420276150, 0xc42028a748, 0xc42021aea0)
    /home/my-user/.gvm/pkgsets/go1.8.3/global/src/github.com/my/project/app/controllers/auth.go:12

Are there something missing in my implementation?

Add defaultExtensions to the Set struct

This would simplify template loading, by default, defaultExtensions would be .html.jet .jet. then when trying to load a template "dashboard/main" Jet would first check for "dashboard/main", then "dashboard/main.html.jet" and lastly "dashboard/main.jet" none found then return an error.

this also need to work with relative paths #16

@annismckenzie, what do you think about this feature ?

Redis Caching

I want to cache templates in redis for an hour, but jet itself caches templates permanently, is there any way to go about this?

Question about getting rendered Html

I am planning to use jet for generating Html email content. How do I read all the rendered Html into a String. All the tests I've found so far point to writing it to Http response writer.

Wiki: Document jet.SafeWriter

// SafeWriter escapee func, func implementing this type will write direct in the writer skipping the escape
// faze, use this type to create special types of escapee funcs
type SafeWriter func(io.Writer, []byte)

make checking slice length simple?

{{if myslice}} or {{if len(myslice)>0}}...{{end}} not working, have to use {{if isset(len(myslice)>0)}}...{{end}}, can it be simple just like {{if mystring}}?

Email templates - how to inline CSS?

Hey guys,
this might not be related to Jet directly but asking other experienced developers doesn't hurt.

How would you approach inlining the CSS within email templates?

Say, I have a set of Jet email templates and a common .css file. During the template execution, I'd like to be able to inline all the CSS into the HTML tags.

Is there anything in Jet that would help me do it?

Or would you suggest executing the Jet template first and then iterate on the resulting HTML with tools like https://inliner.cm/ or https://templates.mailchimp.com/resources/inline-css/ ?

Thanks for your replies

Import or Extend via variable?

Hi. Thanks for your great template engine! Is it possible to use a variable or function result in the extends, import, or includes statements?

Put some examples

Not quite sure how should this be used? Thanks!

P.S. I mean examples of logic connection. How can you use jet along with gin?

if x>y syntax

{{if x>y}}blahblah...{{end}}
emit error: unexpected bad character U+003C '<' in call expression.
Is this syntax not supported?

Using globs funcs within yielded blocks

I have a function in the var map (due to it's dependency on the request itself). It was done like this:

vars.SetFunc("VURL", func(a jet.Arguments) reflect.Value {})

html.html:

{{yield bodytemplate()}}
---

page.html:

{{extends "html.html"}}
{{import "utilities.html"}}
{{block bodytemplate()}}
{{yield body()}}
{{end}}
---

index.html:

{{extends "page.html"}}
{{block body()}}
{{yield video("id")}}
{{end}}
---

utilities.html:

{{block video(url)}}
{{video := VURL(url)}}
<video src="{{video}}">
{{end}}

When it is called in {{block body()}}, it works, when it is called in {{block video(url)}} it gets the following error:

Error #01: Jet Runtime Error("utilities.html":25): identifier "VURL" is not available in the current scope map[]

I can't tell from the docs how to propagate the context (or not overwrite by accident).

Issues with nested maps

I added some tests code in the TestEvalIndexExpression test:

issues:
a) It seems you can't do map["key1"]["key2"] (you get unexpected "[")
b) Couldn't nested lookups be supported when the nested interface{} value is a map? ("indexing is not supported in value type interface")

code:

	vars := make(VarMap)
	vars.Set("nested", map[string]interface{}{"nested": map[string]interface{}{"nested": map[string]interface{}{"nested": map[string]interface{}{"name": "value", "strings": []string{"hello"}, "arr": []interface{}{"hello"}}}}})
	RunJetTest(t, vars, nil, "IndexExpressionMap_6", `{{nested.nested.nested.nested.name}}`, "value")
	RunJetTest(t, vars, nil, "IndexExpressionMap_7", `{{nested.nested.nested.nested.strings[0]}}`, "hello")
	RunJetTest(t, vars, nil, "IndexExpressionMap_8", `{{nested.nested.nested.nested.arr[0]}}`, "hello")
	RunJetTest(t, vars, nil, "IndexExpressionMap_9", `{{nested["nested"].nested.nested.name}}`, "value")
	RunJetTest(t, vars, nil, "IndexExpressionMap_10", `{{nested.nested.nested["nested"].name}}`, "value")
	RunJetTest(t, vars, nil, "IndexExpressionMap_11", `{{nested.nested.nested["nested"]["strings"][0]}}`, "hello")
	RunJetTest(t, vars, nil, "IndexExpressionMap_12", `{{nested.nested.nested["nested"]["arr"][0]}}`, "hello")
	RunJetTest(t, vars, nil, "IndexExpressionMap_13", `{{nested["nested"].nested["nested"].name}}`, "value")
	RunJetTest(t, vars, nil, "IndexExpressionMap_14", `{{nested["nested"]["nested"].nested.name}}`, "value"

output

=== RUN   	JetTest(IndexExpressionMap_7)
--- FAIL: 	JetTest(IndexExpressionMap_7) (0.00s)
	eval_test.go:127: Eval error: "Jet Runtime Error(\"IndexExpressionMap_7\":0): indexing is not supported in value type interface" executing IndexExpressionMap_7
=== RUN   	JetTest(IndexExpressionMap_8)
--- FAIL: 	JetTest(IndexExpressionMap_8) (0.00s)
	eval_test.go:127: Eval error: "Jet Runtime Error(\"IndexExpressionMap_8\":0): indexing is not supported in value type interface" executing IndexExpressionMap_8
=== RUN   	JetTest(IndexExpressionMap_9)
--- PASS: 	JetTest(IndexExpressionMap_9) (0.00s)
=== RUN   	JetTest(IndexExpressionMap_10)
--- FAIL: 	JetTest(IndexExpressionMap_10) (0.00s)
	eval_test.go:127: Eval error: "Jet Runtime Error(\"IndexExpressionMap_10\":0): indexing is not supported in value type interface" executing IndexExpressionMap_10
=== RUN   	JetTest(IndexExpressionMap_13)
--- FAIL: 	JetTest(IndexExpressionMap_13) (0.00s)
	eval_test.go:127: Eval error: "Jet Runtime Error(\"IndexExpressionMap_13\":0): indexing is not supported in value type interface" executing IndexExpressionMap_13
=== RUN   	JetTest(IndexExpressionStruct_1)
--- PASS: 	JetTest(IndexExpressionStruct_1) (0.00s)
=== RUN   	JetTest(IndexExpressionStruct_2)
--- PASS: 	JetTest(IndexExpressionStruct_2) (0.00s)
--- FAIL: TestEvalIndexExpression (0.00s)
	eval_test.go:111: Parsing error: template: IndexExpressionMap_11:1: unexpected "[" in command IndexExpressionMap_11 {{nested.nested.nested["nested"]["strings"][0]}}
	eval_test.go:111: Parsing error: template: IndexExpressionMap_12:1: unexpected "[" in command IndexExpressionMap_12 {{nested.nested.nested["nested"]["arr"][0]}}
	eval_test.go:111: Parsing error: template: IndexExpressionMap_14:1: unexpected "[" in command IndexExpressionMap_14 {{nested["nested"]["nested"].nested.name}

Wiki: Document jet.Renderer

// Renderer any resulting value from an expression in an action that implements this
// interface will not be printed, instead, we will invoke his Render() method which will be
// responsible to render his self
type Renderer interface {
    Render(*Runtime)
}

Wiki - Getting Started has bad info

On https://github.com/CloudyKit/jet/wiki#getting-started
The comment on the line of code that says:

var View = jet.NewHTMLSet("./views")) // relative path to the Go file where this code is located

Should actually say // relative to the current working directory from where the code is run or something along those lines because jet.NewHTMLSet() doesn't traverse up the directory tree, only down (from what I tested), so stuff like jet.NewHTMLSet("./../views") won't work.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.