Skip to content

Commit 4a5e940

Browse files
committed
Fix union, complement, symdiff, and intersect for transient resources
Fixes #13181
1 parent 48a7aee commit 4a5e940

File tree

6 files changed

+75
-15
lines changed

6 files changed

+75
-15
lines changed

‎resources/resource.go

+21-11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var (
4747
_ resource.Cloner = (*genericResource)(nil)
4848
_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
4949
_ resource.Identifier = (*genericResource)(nil)
50+
_ resource.TransientIdentifier = (*genericResource)(nil)
5051
_ targetPathProvider = (*genericResource)(nil)
5152
_ sourcePathProvider = (*genericResource)(nil)
5253
_ identity.IdentityGroupProvider = (*genericResource)(nil)
@@ -359,6 +360,9 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo {
359360
type genericResource struct {
360361
publishInit *sync.Once
361362

363+
key string
364+
keyInit *sync.Once
365+
362366
sd ResourceSourceDescriptor
363367
paths internal.ResourcePaths
364368

@@ -444,19 +448,24 @@ func (l *genericResource) Data() any {
444448
}
445449

446450
func (l *genericResource) Key() string {
447-
basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash
448-
var key string
449-
if basePath == "" {
450-
key = l.RelPermalink()
451-
} else {
452-
key = strings.TrimPrefix(l.RelPermalink(), basePath)
453-
}
451+
l.keyInit.Do(func() {
452+
basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash
453+
if basePath == "" {
454+
l.key = l.RelPermalink()
455+
} else {
456+
l.key = strings.TrimPrefix(l.RelPermalink(), basePath)
457+
}
454458

455-
if l.spec.Cfg.IsMultihost() {
456-
key = l.spec.Lang() + key
457-
}
459+
if l.spec.Cfg.IsMultihost() {
460+
l.key = l.spec.Lang() + l.key
461+
}
462+
})
463+
464+
return l.key
465+
}
458466

459-
return key
467+
func (l *genericResource) TransientKey() string {
468+
return l.Key()
460469
}
461470

462471
func (l *genericResource) targetPath() string {
@@ -623,6 +632,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour
623632

624633
func (l genericResource) clone() *genericResource {
625634
l.publishInit = &sync.Once{}
635+
l.keyInit = &sync.Once{}
626636
return &l
627637
}
628638

‎resources/resource/resourcetypes.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,19 @@ type ResourcesLanguageMerger interface {
170170

171171
// Identifier identifies a resource.
172172
type Identifier interface {
173-
// Key is is mostly for internal use and should be considered opaque.
173+
// Key is mostly for internal use and should be considered opaque.
174174
// This value may change between Hugo versions.
175175
Key() string
176176
}
177177

178+
// TransientIdentifier identifies a transient resource.
179+
type TransientIdentifier interface {
180+
// TransientKey is mostly for internal use and should be considered opaque.
181+
// This value is implemented by transient resources where pointers may be short lived and
182+
// not suitable for use as a map keys.
183+
TransientKey() string
184+
}
185+
178186
// WeightProvider provides a weight.
179187
type WeightProvider interface {
180188
Weight() int

‎resources/resource_spec.go

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro
187187
Staler: &AtomicStaler{},
188188
h: &resourceHash{},
189189
publishInit: &sync.Once{},
190+
keyInit: &sync.Once{},
190191
paths: rp,
191192
spec: r,
192193
sd: rd,

‎resources/transform.go

+6
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ var (
5252
_ identity.IdentityGroupProvider = (*resourceAdapterInner)(nil)
5353
_ resource.Source = (*resourceAdapter)(nil)
5454
_ resource.Identifier = (*resourceAdapter)(nil)
55+
_ resource.TransientIdentifier = (*resourceAdapter)(nil)
5556
_ targetPathProvider = (*resourceAdapter)(nil)
5657
_ sourcePathProvider = (*resourceAdapter)(nil)
58+
_ resource.Identifier = (*resourceAdapter)(nil)
5759
_ resource.ResourceNameTitleProvider = (*resourceAdapter)(nil)
5860
_ resource.WithResourceMetaProvider = (*resourceAdapter)(nil)
5961
_ identity.DependencyManagerProvider = (*resourceAdapter)(nil)
@@ -279,6 +281,10 @@ func (r *resourceAdapter) Key() string {
279281
return r.target.(resource.Identifier).Key()
280282
}
281283

284+
func (r *resourceAdapter) TransientKey() string {
285+
return r.Key()
286+
}
287+
282288
func (r *resourceAdapter) targetPath() string {
283289
r.init(false, false)
284290
return r.target.(targetPathProvider).targetPath()

‎tpl/collections/collections_integration_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,32 @@ tags: ['tag-b']
249249
"2: Intersect: 1|\n2: Union: 3|\n2: SymDiff: 2|\n2: Uniq: 3|",
250250
)
251251
}
252+
253+
// Issue #13181
254+
func TestUnionResourcesMatch(t *testing.T) {
255+
t.Parallel()
256+
257+
files := `
258+
-- config.toml --
259+
disableKinds = ['rss','sitemap', 'taxonomy', 'term', 'page']
260+
-- layouts/index.html --
261+
{{ $a := resources.Match "*a*" }}
262+
{{ $b := resources.Match "*b*" }}
263+
{{ $union := $a | union $b }}
264+
{{ range $i, $e := $union }}
265+
{{ $i }}: {{ .Name }}
266+
{{ end }}$
267+
-- assets/a1.html --
268+
<div>file1</div>
269+
-- assets/a2.html --
270+
<div>file2</div>
271+
-- assets/a3_b1.html --
272+
<div>file3</div>
273+
-- assets/b2.html --
274+
<div>file4</div>
275+
`
276+
277+
b := hugolib.Test(t, files)
278+
279+
b.AssertFileContentExact("public/index.html", "0: /a3_b1.html\n\n1: /b2.html\n\n2: /a1.html\n\n3: /a2.html\n$")
280+
}

‎tpl/collections/reflect_helpers.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ import (
2020

2121
"github.com/gohugoio/hugo/common/hashing"
2222
"github.com/gohugoio/hugo/common/types"
23+
"github.com/gohugoio/hugo/resources/resource"
2324
)
2425

2526
var (
2627
zero reflect.Value
27-
errorType = reflect.TypeOf((*error)(nil)).Elem()
28+
errorType = reflect.TypeFor[error]()
2829
)
2930

3031
func numberToFloat(v reflect.Value) (float64, error) {
@@ -56,7 +57,13 @@ func normalize(v reflect.Value) any {
5657
return f
5758
}
5859
}
59-
return types.Unwrapv(v.Interface())
60+
61+
vv := types.Unwrapv(v.Interface())
62+
if ip, ok := vv.(resource.TransientIdentifier); ok {
63+
return ip.TransientKey()
64+
}
65+
66+
return vv
6067
}
6168

6269
// collects identities from the slices in seqs into a set. Numeric values are normalized,
@@ -151,7 +158,6 @@ func convertNumber(v reflect.Value, to reflect.Kind) (reflect.Value, error) {
151158
case reflect.Uint64:
152159
n = reflect.ValueOf(uint64(i))
153160
}
154-
155161
}
156162

157163
if !n.IsValid() {

0 commit comments

Comments
 (0)