Truncated; .Site.Params; First function

* Add `.Truncated` bool to each page; will be set true if the
  `.Summary` is truncated and it's worth showing a "more" link of some
  kind.
* Add `Params` to the site config, defining `.Site.Params` accessible
  to each page; this lets the site maintainer associate arbitrary data
  with names, on a site-wide basis.
* Provide a `First` function to templates:
  * Use-case: `{{range First 5 .Site.Recent}}` or anything else which
    is a simple iterable provided by hugolib
* Tests by me for `.Truncated` and `First`

Also @noahcampbell contributed towards this:

* Add UnitTest for `.Site.Params`:
> Digging into this test case a bit more, I'm realizing that we need
> to create a param test case to ensure that for each type we render
> (page, index, homepage, rss, etc.) that the proper fields are
> represented.  This will help us refactor without fear in the
> future.

Sample config.yaml:

```yaml
title: "Test site"
params:
  Subtitle: "More tests always good"
  AuthorName: "John Doe"
  SidebarRecentLimit: 5
```

Signed-off-by: Noah Campbell <noahcampbell@gmail.com>
This commit is contained in:
Phil Pennock
2013-11-10 12:04:51 -08:00
committed by Noah Campbell
parent 6017599a3c
commit 40d05f12a7
7 changed files with 157 additions and 9 deletions

View File

@@ -1,6 +1,7 @@
package bundle
import (
"errors"
"github.com/eknkc/amber"
helpers "github.com/spf13/hugo/template"
"html/template"
@@ -40,6 +41,36 @@ func Gt(a interface{}, b interface{}) bool {
return left > right
}
// First is exposed to templates, to iterate over the first N items in a
// rangeable list.
func First(limit int, seq interface{}) (interface{}, error) {
if limit < 1 {
return nil, errors.New("can't return negative/empty count of items from sequence")
}
seqv := reflect.ValueOf(seq)
// this is better than my first pass; ripped from text/template/exec.go indirect():
for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
if seqv.IsNil() {
return nil, errors.New("can't iterate over a nil value")
}
if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
break
}
}
switch seqv.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
// okay
default:
return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
}
if limit > seqv.Len() {
limit = seqv.Len()
}
return seqv.Slice(0, limit).Interface(), nil
}
func IsSet(a interface{}, key interface{}) bool {
av := reflect.ValueOf(a)
kv := reflect.ValueOf(key)
@@ -113,6 +144,7 @@ func NewTemplate() Template {
"isset": IsSet,
"echoParam": ReturnWhenSet,
"safeHtml": SafeHtml,
"First": First,
}
templates.Funcs(funcMap)

View File

@@ -0,0 +1,55 @@
package bundle
import (
"reflect"
"testing"
)
func TestGt(t *testing.T) {
for i, this := range []struct{
left interface{}
right interface{}
leftShouldWin bool
}{
{ 5, 8, false },
{ 8, 5, true },
{ 5, 5, false },
{ -2, 1, false },
{ 2, -5, true },
{ "8", "5", true },
{ "5", "0001", true },
{ []int{100,99}, []int{1,2,3,4}, false },
} {
leftIsBigger := Gt(this.left, this.right)
if leftIsBigger != this.leftShouldWin {
var which string
if leftIsBigger {
which = "expected right to be bigger, but left was"
} else {
which = "expected left to be bigger, but right was"
}
t.Errorf("[%d] %v compared to %v: %s", i, this.left, this.right, which)
}
}
}
func TestFirst(t *testing.T) {
for i, this := range []struct{
count int
sequence interface{}
expect interface{}
} {
{ 2, []string{"a", "b", "c"}, []string{"a", "b"} },
{ 3, []string{"a", "b"}, []string{"a", "b"} },
{ 2, []int{100, 200, 300}, []int{100, 200} },
} {
results, err := First(this.count, this.sequence)
if err != nil {
t.Errorf("[%d] failed: %s", i, err)
continue
}
if !reflect.DeepEqual(results, this.expect) {
t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)
}
}
}