diff --git a/filters.go b/filters.go index 7fa7a75..be77eb4 100644 --- a/filters.go +++ b/filters.go @@ -7,15 +7,9 @@ import ( // FilterFunction is the type filter functions must fulfil type FilterFunction func(in *Value, param *Value) (out *Value, err *Error) -var filters map[string]FilterFunction - -func init() { - filters = make(map[string]FilterFunction) -} - // FilterExists returns true if the given filter is already registered -func FilterExists(name string) bool { - _, existing := filters[name] +func (set *TemplateSet) FilterExists(name string) bool { + _, existing := set.filters[name] return existing } @@ -23,27 +17,27 @@ func FilterExists(name string) bool { // want to call this function in the filter's init() function: // // http://golang.org/doc/effective_go.html#init -func RegisterFilter(name string, fn FilterFunction) error { - if FilterExists(name) { +func (set *TemplateSet) RegisterFilter(name string, fn FilterFunction) error { + if set.FilterExists(name) { return fmt.Errorf("filter with name '%s' is already registered", name) } - filters[name] = fn + set.filters[name] = fn return nil } // ReplaceFilter replaces an already registered filter with a new implementation. Use this // function with caution since it allows you to change existing filter behaviour. -func ReplaceFilter(name string, fn FilterFunction) error { - if !FilterExists(name) { +func (set *TemplateSet) ReplaceFilter(name string, fn FilterFunction) error { + if !set.FilterExists(name) { return fmt.Errorf("filter with name '%s' does not exist (therefore cannot be overridden)", name) } - filters[name] = fn + set.filters[name] = fn return nil } // MustApplyFilter behaves like ApplyFilter, but panics on an error. -func MustApplyFilter(name string, value *Value, param *Value) *Value { - val, err := ApplyFilter(name, value, param) +func (set *TemplateSet) MustApplyFilter(name string, value *Value, param *Value) *Value { + val, err := set.ApplyFilter(name, value, param) if err != nil { panic(err) } @@ -52,8 +46,8 @@ func MustApplyFilter(name string, value *Value, param *Value) *Value { // ApplyFilter applies a filter to a given value using the given parameters. // Returns a *pongo2.Value or an error. -func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) { - fn, existing := filters[name] +func (set *TemplateSet) ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) { + fn, existing := set.filters[name] if !existing { return nil, &Error{ Sender: "applyfilter", @@ -113,7 +107,7 @@ func (p *Parser) parseFilter() (*filterCall, *Error) { } // Get the appropriate filter function and bind it - filterFn, exists := filters[identToken.Val] + filterFn, exists := p.template.set.filters[identToken.Val] if !exists { return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", identToken.Val), identToken) } diff --git a/tags.go b/tags.go index d25da94..3f0defd 100644 --- a/tags.go +++ b/tags.go @@ -47,21 +47,15 @@ type tag struct { parser TagParser } -var tags map[string]*tag - -func init() { - tags = make(map[string]*tag) -} - // Registers a new tag. You usually want to call this // function in the tag's init() function: // http://golang.org/doc/effective_go.html#init -func RegisterTag(name string, parserFn TagParser) error { - _, existing := tags[name] +func (set *TemplateSet) RegisterTag(name string, parserFn TagParser) error { + _, existing := set.tags[name] if existing { return fmt.Errorf("tag with name '%s' is already registered", name) } - tags[name] = &tag{ + set.tags[name] = &tag{ name: name, parser: parserFn, } @@ -70,12 +64,12 @@ func RegisterTag(name string, parserFn TagParser) error { // Replaces an already registered tag with a new implementation. Use this // function with caution since it allows you to change existing tag behaviour. -func ReplaceTag(name string, parserFn TagParser) error { - _, existing := tags[name] +func (set *TemplateSet) ReplaceTag(name string, parserFn TagParser) error { + _, existing := set.tags[name] if !existing { return fmt.Errorf("tag with name '%s' does not exist (therefore cannot be overridden)", name) } - tags[name] = &tag{ + set.tags[name] = &tag{ name: name, parser: parserFn, } @@ -93,7 +87,7 @@ func (p *Parser) parseTagElement() (INodeTag, *Error) { } // Check for the existing tag - tag, exists := tags[tokenName.Val] + tag, exists := p.template.set.tags[tokenName.Val] if !exists { // Does not exists return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", tokenName.Val), tokenName) diff --git a/template_sets.go b/template_sets.go index 9a73eac..39333c1 100644 --- a/template_sets.go +++ b/template_sets.go @@ -48,6 +48,8 @@ type TemplateSet struct { // added your first template to the set (restrictions are statically checked). // After you added one, it's not possible anymore (for your personal security). firstTemplateCreated bool + tags map[string]*tag + filters map[string]FilterFunction bannedTags map[string]bool bannedFilters map[string]bool @@ -56,10 +58,8 @@ type TemplateSet struct { templateCacheMutex sync.Mutex } -// NewSet can be used to create sets with different kind of templates -// (e. g. web from mail templates), with different globals or -// other configurations. -func NewSet(name string, loaders ...TemplateLoader) *TemplateSet { +// newSet only be used to create sets without default tags and filters +func newSet(name string, loaders ...TemplateLoader) *TemplateSet { if len(loaders) == 0 { panic(fmt.Errorf("at least one template loader must be specified")) } @@ -68,6 +68,8 @@ func NewSet(name string, loaders ...TemplateLoader) *TemplateSet { name: name, loaders: loaders, Globals: make(Context), + tags: make(map[string]*tag), + filters: make(map[string]FilterFunction), bannedTags: make(map[string]bool), bannedFilters: make(map[string]bool), templateCache: make(map[string]*Template), @@ -75,6 +77,21 @@ func NewSet(name string, loaders ...TemplateLoader) *TemplateSet { } } +// NewSet can be used to create sets with different kind of templates +// (e. g. web from mail templates), with different globals or +// other configurations. +func NewSet(name string, loaders ...TemplateLoader) *TemplateSet { + set := newSet(name, loaders...) + + for name, tag := range DefaultSet.tags { + set.tags[name] = tag + } + for name, filter := range DefaultSet.filters { + set.filters[name] = filter + } + return set +} + func (set *TemplateSet) AddLoader(loaders ...TemplateLoader) { set.loaders = append(set.loaders, loaders...) } @@ -97,7 +114,7 @@ func (set *TemplateSet) resolveFilenameForLoader(loader TemplateLoader, tpl *Tem // BanTag bans a specific tag for this template set. See more in the documentation for TemplateSet. func (set *TemplateSet) BanTag(name string) error { - _, has := tags[name] + _, has := set.tags[name] if !has { return fmt.Errorf("tag '%s' not found", name) } @@ -115,7 +132,7 @@ func (set *TemplateSet) BanTag(name string) error { // BanFilter bans a specific filter for this template set. See more in the documentation for TemplateSet. func (set *TemplateSet) BanFilter(name string) error { - _, has := filters[name] + _, has := set.filters[name] if !has { return fmt.Errorf("filter '%s' not found", name) } @@ -288,7 +305,7 @@ var ( DefaultLoader = MustNewLocalFileSystemLoader("") // DefaultSet is a set created for you for convinience reasons. - DefaultSet = NewSet("default", DefaultLoader) + DefaultSet = newSet("default", DefaultLoader) // Methods on the default set FromString = DefaultSet.FromString @@ -297,6 +314,12 @@ var ( FromCache = DefaultSet.FromCache RenderTemplateString = DefaultSet.RenderTemplateString RenderTemplateFile = DefaultSet.RenderTemplateFile + ReplaceTag = DefaultSet.ReplaceTag + RegisterTag = DefaultSet.RegisterTag + ReplaceFilter = DefaultSet.ReplaceFilter + RegisterFilter = DefaultSet.RegisterFilter + ApplyFilter = DefaultSet.ApplyFilter + MustApplyFilter = DefaultSet.MustApplyFilter // Globals for the default set Globals = DefaultSet.Globals diff --git a/variable.go b/variable.go index 96e047e..d5b6d08 100644 --- a/variable.go +++ b/variable.go @@ -214,7 +214,7 @@ func (nv *nodeVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *E if !nv.expr.FilterApplied("safe") && !value.safe && value.IsString() && ctx.Autoescape { // apply escape filter - value, err = filters["escape"](value, nil) + value, err = ctx.template.set.filters["escape"](value, nil) if err != nil { return err }