shithub: hugo

Download patch

ref: 0e7716a42450401c7998aa81ad2ed98c8ab109e8
parent: 417c5e2b67b97fa80a0b6f77d259966f03b95344
author: Bjørn Erik Pedersen <[email protected]>
date: Sun Apr 15 13:07:49 EDT 2018

resource: Implement Resource.Content

Fixes #4622

--- a/resource/image.go
+++ b/resource/image.go
@@ -568,6 +568,7 @@
 
 func (i *Image) clone() *Image {
 	g := *i.genericResource
+	g.resourceContent = &resourceContent{}
 
 	return &Image{
 		imaging:         i.imaging,
--- a/resource/image_test.go
+++ b/resource/image_test.go
@@ -322,6 +322,18 @@
 	assert.NotNil(svg)
 }
 
+func TestSVGImageContent(t *testing.T) {
+	assert := require.New(t)
+	spec := newTestResourceSpec(assert)
+	svg := fetchResourceForSpec(spec, assert, "circle.svg")
+	assert.NotNil(svg)
+
+	content, err := svg.Content()
+	assert.NoError(err)
+	assert.IsType("", content)
+	assert.Contains(content.(string), `<svg height="100" width="100">`)
+}
+
 func BenchmarkResizeParallel(b *testing.B) {
 	assert := require.New(b)
 	img := fetchSunset(assert)
--- a/resource/resource.go
+++ b/resource/resource.go
@@ -87,6 +87,14 @@
 
 	// Params set in front matter for this resource.
 	Params() map[string]interface{}
+
+	// Content returns this resource's content. It will be equivalent to reading the content
+	// that RelPermalink points to in the published folder.
+	// The return type will be contextual, and should be what you would expect:
+	// * Page: template.HTML
+	// * JSON: String
+	// * Etc.
+	Content() (interface{}, error)
 }
 
 // Resources represents a slice of resources, which can be a mix of different types.
@@ -360,6 +368,11 @@
 	return path.Join(d.dir, d.file)
 }
 
+type resourceContent struct {
+	content     string
+	contentInit sync.Once
+}
+
 // genericResource represents a generic linkable resource.
 type genericResource struct {
 	// The relative path to this resource.
@@ -390,8 +403,28 @@
 	osFileInfo   os.FileInfo
 
 	targetPathBuilder func(rel string) string
+
+	// We create copies of this struct, so this needs to be a pointer.
+	*resourceContent
 }
 
+func (l *genericResource) Content() (interface{}, error) {
+	var err error
+	l.contentInit.Do(func() {
+		var b []byte
+
+		b, err := afero.ReadFile(l.sourceFs(), l.AbsSourceFilename())
+		if err != nil {
+			return
+		}
+
+		l.content = string(b)
+
+	})
+
+	return l.content, err
+}
+
 func (l *genericResource) sourceFs() afero.Fs {
 	if l.overriddenSourceFs != nil {
 		return l.overriddenSourceFs
@@ -444,6 +477,7 @@
 // Implement the Cloner interface.
 func (l genericResource) WithNewBase(base string) Resource {
 	l.base = base
+	l.resourceContent = &resourceContent{}
 	return &l
 }
 
@@ -611,5 +645,6 @@
 		params:            make(map[string]interface{}),
 		name:              baseFilename,
 		title:             baseFilename,
+		resourceContent:   &resourceContent{},
 	}
 }