From f387a7123040d9414350bf783bdeff17ac0572c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20W=C3=BCstenberg?= Date: Thu, 22 Sep 2022 09:41:06 +0200 Subject: [PATCH] Add generic Map implementation (#108) Instead of waiting for a good time to switch the `Map` implementation, I've decided to just offer two implementations: one for Go versions before 1.18, and one for 1.18 and onwards. This is achieved using build tags in the source files. This is obviously a breaking change for consumers of this library that use Go 1.18 and onwards. See #88 --- examples/simple/simple.go | 7 ++++-- examples/tailwindcss/tailwindcss.go | 7 ++++-- go.mod | 2 +- gomponents.go | 9 -------- gomponents_generic.go | 13 +++++++++++ gomponents_generic_test.go | 34 +++++++++++++++++++++++++++++ gomponents_non_generic.go | 13 +++++++++++ gomponents_non_generic_test.go | 34 +++++++++++++++++++++++++++++ gomponents_test.go | 24 +------------------- 9 files changed, 106 insertions(+), 37 deletions(-) create mode 100644 gomponents_generic.go create mode 100644 gomponents_generic_test.go create mode 100644 gomponents_non_generic.go create mode 100644 gomponents_non_generic_test.go diff --git a/examples/simple/simple.go b/examples/simple/simple.go index 93aa28f..0a7138a 100644 --- a/examples/simple/simple.go +++ b/examples/simple/simple.go @@ -1,3 +1,6 @@ +//go:build go1.18 +// +build go1.18 + package main import ( @@ -58,8 +61,8 @@ func Navbar(currentPath string, links []PageLink) g.Node { Ul( NavbarLink("/", "Home", currentPath), - g.Group(g.Map(len(links), func(i int) g.Node { - return NavbarLink(links[i].Path, links[i].Name, currentPath) + g.Group(g.Map(links, func(pl PageLink) g.Node { + return NavbarLink(pl.Path, pl.Name, currentPath) })), ), diff --git a/examples/tailwindcss/tailwindcss.go b/examples/tailwindcss/tailwindcss.go index 2af2d33..39a012e 100644 --- a/examples/tailwindcss/tailwindcss.go +++ b/examples/tailwindcss/tailwindcss.go @@ -1,3 +1,6 @@ +//go:build go1.18 +// +build go1.18 + package main import ( @@ -81,8 +84,8 @@ func Navbar(currentPath string, links []PageLink) g.Node { NavbarLink("/", "Home", currentPath == "/"), // We can Map custom slices to Nodes - g.Group(g.Map(len(links), func(i int) g.Node { - return NavbarLink(links[i].Path, links[i].Name, currentPath == links[i].Path) + g.Group(g.Map(links, func(pl PageLink) g.Node { + return NavbarLink(pl.Path, pl.Name, currentPath == pl.Path) })), ), ), diff --git a/go.mod b/go.mod index 5a1df18..2e147e2 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module "github.com/maragudk/gomponents" -go 1.15 +go 1.18 diff --git a/gomponents.go b/gomponents.go index a97691e..ddc3cc3 100644 --- a/gomponents.go +++ b/gomponents.go @@ -244,15 +244,6 @@ func Group(children []Node) Node { return group{children: children} } -// Map something enumerable to a list of Nodes. -func Map(length int, cb func(i int) Node) []Node { - var nodes []Node - for i := 0; i < length; i++ { - nodes = append(nodes, cb(i)) - } - return nodes -} - // If condition is true, return the given Node. Otherwise, return nil. // This helper function is good for inlining elements conditionally. func If(condition bool, n Node) Node { diff --git a/gomponents_generic.go b/gomponents_generic.go new file mode 100644 index 0000000..6e6500b --- /dev/null +++ b/gomponents_generic.go @@ -0,0 +1,13 @@ +//go:build go1.18 +// +build go1.18 + +package gomponents + +// Map a slice of anything to a slice of Nodes. +func Map[T any](ts []T, cb func(T) Node) []Node { + var nodes []Node + for _, t := range ts { + nodes = append(nodes, cb(t)) + } + return nodes +} diff --git a/gomponents_generic_test.go b/gomponents_generic_test.go new file mode 100644 index 0000000..0492ae3 --- /dev/null +++ b/gomponents_generic_test.go @@ -0,0 +1,34 @@ +//go:build go1.18 +// +build go1.18 + +package gomponents_test + +import ( + "os" + "testing" + + g "github.com/maragudk/gomponents" + "github.com/maragudk/gomponents/internal/assert" +) + +func TestMap(t *testing.T) { + t.Run("maps slices to nodes", func(t *testing.T) { + items := []string{"hat", "partyhat", "turtlehat"} + lis := g.Map(items, func(i string) g.Node { + return g.El("li", g.Text(i)) + }) + + list := g.El("ul", lis...) + + assert.Equal(t, ``, list) + }) +} + +func ExampleMap() { + items := []string{"party hat", "super hat"} + e := g.El("ul", g.Group(g.Map(items, func(i string) g.Node { + return g.El("li", g.Text(i)) + }))) + _ = e.Render(os.Stdout) + // Output: +} diff --git a/gomponents_non_generic.go b/gomponents_non_generic.go new file mode 100644 index 0000000..56570d0 --- /dev/null +++ b/gomponents_non_generic.go @@ -0,0 +1,13 @@ +//go:build !go1.18 +// +build !go1.18 + +package gomponents + +// Map something enumerable to a list of Nodes. +func Map(length int, cb func(i int) Node) []Node { + var nodes []Node + for i := 0; i < length; i++ { + nodes = append(nodes, cb(i)) + } + return nodes +} diff --git a/gomponents_non_generic_test.go b/gomponents_non_generic_test.go new file mode 100644 index 0000000..4186808 --- /dev/null +++ b/gomponents_non_generic_test.go @@ -0,0 +1,34 @@ +//go:build !go1.18 +// +build !go1.18 + +package gomponents_test + +import ( + "os" + "testing" + + g "github.com/maragudk/gomponents" + "github.com/maragudk/gomponents/internal/assert" +) + +func TestMap(t *testing.T) { + t.Run("maps slices to nodes", func(t *testing.T) { + items := []string{"hat", "partyhat", "turtlehat"} + lis := g.Map(len(items), func(i int) g.Node { + return g.El("li", g.Text(items[i])) + }) + + list := g.El("ul", lis...) + + assert.Equal(t, ``, list) + }) +} + +func ExampleMap() { + items := []string{"party hat", "super hat"} + e := g.El("ul", g.Group(g.Map(len(items), func(i int) g.Node { + return g.El("li", g.Text(items[i])) + }))) + _ = e.Render(os.Stdout) + // Output: +} diff --git a/gomponents_test.go b/gomponents_test.go index d8e9e07..ffde46d 100644 --- a/gomponents_test.go +++ b/gomponents_test.go @@ -133,7 +133,7 @@ func TestEl(t *testing.T) { assert.Equal(t, `

`, e) }) - t.Run("renders outside if node does not implement placer", func(t *testing.T) { + t.Run("renders outside if node does not implement nodeTypeDescriber", func(t *testing.T) { e := g.El("div", outsider{}) assert.Equal(t, `
outsider
`, e) }) @@ -248,28 +248,6 @@ func TestGroup(t *testing.T) { }) } -func TestMap(t *testing.T) { - t.Run("maps slices to nodes", func(t *testing.T) { - items := []string{"hat", "partyhat", "turtlehat"} - lis := g.Map(len(items), func(i int) g.Node { - return g.El("li", g.Text(items[i])) - }) - - list := g.El("ul", lis...) - - assert.Equal(t, ``, list) - }) -} - -func ExampleMap() { - items := []string{"party hat", "super hat"} - e := g.El("ul", g.Group(g.Map(len(items), func(i int) g.Node { - return g.El("li", g.Text(items[i])) - }))) - _ = e.Render(os.Stdout) - // Output: -} - func TestIf(t *testing.T) { t.Run("returns node if condition is true", func(t *testing.T) { n := g.El("div", g.If(true, g.El("span")))