diff --git a/docs/content/extras/scratch.md b/docs/content/extras/scratch.md index e3c1d9f40..c9e6970a0 100644 --- a/docs/content/extras/scratch.md +++ b/docs/content/extras/scratch.md @@ -14,9 +14,13 @@ weight: 80 `Scratch` -- a "scratchpad" for your node- or page-scoped variables. In most cases you can do well without `Scratch`, but there are some use cases that aren't solvable with Go's templates without `Scratch`'s help, due to scoping issues. -`Scratch` is added to both `Node` and `Page` -- with the three methods `Set`, `Get` and `Add`. `Set` and `Add` takes a `key` and the `value` to add. Get returns the `value` for the `key` given. +`Scratch` is added to both `Node` and `Page` -- with following methods: +* `Set` and `Add` takes a `key` and the `value` to add. +* `Get` returns the `value` for the `key` given. +* `SetInMap` takes a `key`, `mapKey` and `value` +* `GetSortedMapValues` returns array of values from `key` sorted by `mapKey` -`Set` can store values of any type. `Add` accepts values that support Go's `+` operator. +`Set` and `SetInMap` can store values of any type. `Add` accepts values that support Go's `+` operator. The scope of the backing data is global for the given `Node` or `Page`, and spans partial and shortcode includes. @@ -37,6 +41,12 @@ The usage is best illustrated with some samples: {{ $.Scratch.Set "v1" 123 }} {{ $.Scratch.Get "v1" }} {{/* => 123 */}} + +{{ $.Scratch.SetInMap "a3" "b" "XX" }} +{{ $.Scratch.SetInMap "a3" "a" "AA" }} +{{ $.Scratch.SetInMap "a3" "c" "CC" }} +{{ $.Scratch.SetInMap "a3" "b" "BB" }} +{{ $.Scratch.GetSortedMapValues "a3" }} {{/* => []interface {}{"AA", "BB", "CC"} */}} ``` **Note:** The examples above uses the special `$` variable, which refers to the top-level node. This is the behavior you most likely want, and will help remove some confusion when using `Scratch` inside page range loops -- and you start inadvertently calling the wrong `Scratch`. But there may be use cases for `{{ .Scratch.Add "key" "some value" }}`. diff --git a/hugolib/scratch.go b/hugolib/scratch.go index 0f5c4b484..16d1c1440 100644 --- a/hugolib/scratch.go +++ b/hugolib/scratch.go @@ -15,6 +15,7 @@ package hugolib import ( "github.com/spf13/hugo/helpers" + "sort" ) // Scratch is a writable context used for stateful operations in Page/Node rendering. @@ -52,6 +53,41 @@ func (c *Scratch) Get(key string) interface{} { return c.values[key] } +// SetInMap stores a value to a map with the given key in the Node context. +// This map can later be retrieved with GetSortedMapValues. +func (c *Scratch) SetInMap(key string, mapKey string, value interface{}) string { + _, found := c.values[key] + if !found { + c.values[key] = make(map[string]interface{}) + } + + c.values[key].(map[string]interface{})[mapKey] = value + return "" +} + +// GetSortedMapValues returns a sorted map previously filled with SetInMap +func (c *Scratch) GetSortedMapValues(key string) interface{} { + if c.values[key] == nil { + return nil + } + + unsortedMap := c.values[key].(map[string]interface{}) + + var keys []string + for mapKey, _ := range unsortedMap { + keys = append(keys, mapKey) + } + + sort.Strings(keys) + + sortedArray := make([]interface{}, len(unsortedMap)) + for i, mapKey := range keys { + sortedArray[i] = unsortedMap[mapKey] + } + + return sortedArray +} + func newScratch() *Scratch { return &Scratch{values: make(map[string]interface{})} } diff --git a/hugolib/scratch_test.go b/hugolib/scratch_test.go index c736f5a99..b49d8982b 100644 --- a/hugolib/scratch_test.go +++ b/hugolib/scratch_test.go @@ -47,3 +47,21 @@ func TestScratchGet(t *testing.T) { t.Errorf("Should not return anything, but got %v", nothing) } } + +func TestScratchSetInMap(t *testing.T) { + scratch := newScratch() + scratch.SetInMap("key", "lux", "Lux") + scratch.SetInMap("key", "abc", "Abc") + scratch.SetInMap("key", "zyx", "Zyx") + scratch.SetInMap("key", "abc", "Abc (updated)") + scratch.SetInMap("key", "def", "Def") + assert.Equal(t, []interface{}{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"}, scratch.GetSortedMapValues("key")) +} + +func TestScratchGetSortedMapValues(t *testing.T) { + scratch := newScratch() + nothing := scratch.GetSortedMapValues("nothing") + if nothing != nil { + t.Errorf("Should not return anything, but got %v", nothing) + } +}