tpl: Allow 'Querify' to take lone slice/interface argument
authorUjjwal Goyal <importujjwal@gmail.com>
Sat, 6 Mar 2021 18:38:10 +0000 (00:08 +0530)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 9 May 2021 11:14:14 +0000 (13:14 +0200)
Querify can now take a lone string/interface slice (with string
keys) as a parameter, or multiple string parameters, to build
URL queries.

Querify earlier used 'Dictionary' to add key/value pairs to a
map to build URL queries. Changed to dynamically generate ordered
key/value pairs. Cannot take string slice as key (earlier
possible due to Dictionary).

Added tests and benchmarks for querify.

Closes #6735

tpl/collections/collections.go
tpl/collections/collections_test.go
tpl/collections/init.go

index 3521f9cd57f7a34be2228f1c781b6491431d6382..669e386f89461a35730c35e1abb918c3067907f2 100644 (file)
@@ -424,13 +424,39 @@ func (ns *Namespace) Last(limit interface{}, seq interface{}) (interface{}, erro
 // Querify encodes the given parameters in URL-encoded form ("bar=baz&foo=quux") sorted by key.
 func (ns *Namespace) Querify(params ...interface{}) (string, error) {
        qs := url.Values{}
-       vals, err := ns.Dictionary(params...)
-       if err != nil {
-               return "", errors.New("querify keys must be strings")
+
+       if len(params) == 1 {
+               switch v := params[0].(type) {
+               case []string:
+                       if len(v)%2 != 0 {
+                               return "", errors.New("invalid query")
+                       }
+
+                       for i := 0; i < len(v); i += 2 {
+                               qs.Add(v[i], v[i+1])
+                       }
+
+                       return qs.Encode(), nil
+
+               case []interface{}:
+                       params = v
+
+               default:
+                       return "", errors.New("query keys must be strings")
+               }
+       }
+
+       if len(params)%2 != 0 {
+               return "", errors.New("invalid query")
        }
 
-       for name, value := range vals {
-               qs.Add(name, fmt.Sprintf("%v", value))
+       for i := 0; i < len(params); i += 2 {
+               switch v := params[i].(type) {
+               case string:
+                       qs.Add(v, fmt.Sprintf("%v", params[i+1]))
+               default:
+                       return "", errors.New("query keys must be strings")
+               }
        }
 
        return qs.Encode(), nil
index a9bf9a09b35901e11a7d94240bfc7cb2e132b239..1e0569751025c905d5b878b609f3347d6a6b5aa8 100644 (file)
@@ -564,9 +564,16 @@ func TestQuerify(t *testing.T) {
        }{
                {[]interface{}{"a", "b"}, "a=b"},
                {[]interface{}{"a", "b", "c", "d", "f", " &"}, `a=b&c=d&f=+%26`},
+               {[]interface{}{[]string{"a", "b"}}, "a=b"},
+               {[]interface{}{[]string{"a", "b", "c", "d", "f", " &"}}, `a=b&c=d&f=+%26`},
+               {[]interface{}{[]interface{}{"x", "y"}}, `x=y`},
+               {[]interface{}{[]interface{}{"x", 5}}, `x=5`},
                // errors
                {[]interface{}{5, "b"}, false},
                {[]interface{}{"a", "b", "c"}, false},
+               {[]interface{}{[]string{"a", "b", "c"}}, false},
+               {[]interface{}{[]string{"a", "b"}, "c"}, false},
+               {[]interface{}{[]interface{}{"c", "d", "e"}}, false},
        } {
                errMsg := qt.Commentf("[%d] %v", i, test.params)
 
@@ -582,6 +589,32 @@ func TestQuerify(t *testing.T) {
        }
 }
 
+func BenchmarkQuerify(b *testing.B) {
+       ns := New(&deps.Deps{})
+       params := []interface{}{"a", "b", "c", "d", "f", " &"}
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := ns.Querify(params...)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkQuerifySlice(b *testing.B) {
+       ns := New(&deps.Deps{})
+       params := []string{"a", "b", "c", "d", "f", " &"}
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := ns.Querify(params)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
 func TestSeq(t *testing.T) {
        t.Parallel()
        c := qt.New(t)
index 1e64126100218744570a3896d5f9309dca6dd69d..4126b4410e463702351d44d52aab03b35702bbb0 100644 (file)
@@ -122,6 +122,10 @@ func init() {
                                        `<a href="https://www.google.com?{{ (querify "q" "test" "page" 3) | safeURL }}">Search</a>`,
                                        `<a href="https://www.google.com?page=3&amp;q=test">Search</a>`,
                                },
+                               {
+                                       `{{ slice "foo" 1 "bar" 2 | querify | safeHTML }}`,
+                                       `bar=2&foo=1`,
+                               },
                        },
                )