From 02133ed006e5176ba1eaa321fe3ed18bff51b1da Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Wed, 4 Jun 2014 10:47:05 -0700 Subject: [PATCH] improvements to ->html --- doc/Template.html | 2 +- scribblings/template.scrbl | 23 ++++++++++++++++++++--- template.rkt | 23 +++++++++++++++-------- tests/test-template.rkt | 25 +++++++++++++++++-------- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/doc/Template.html b/doc/Template.html index 799a9f9..1de8157 100644 --- a/doc/Template.html +++ b/doc/Template.html @@ -1,2 +1,2 @@ -9.6 Template
6.0.1.11

9.6 Template

 (require pollen/template) package: pollen

Convenience functions for templates. These are automatically imported into the eval environment when rendering with a template (see render).

This module also provides everything from sugar/coerce/value.

procedure

(->html xexpr    
  [#:tag html-tag    
  #:attrs html-attrs    
  #:splice splice-html?])  string?
  xexpr : xexpr?
  html-tag : (or/c #f txexpr-tag?) = #f
  html-attrs : (or/c #f txexpr-attrs?) = #f
  splice-html? : boolean? = #f
Convert xexpr to an HTML string. Similar to xexpr->string, but consistent with the HTML spec, text that appears within script or style blocks will not be escaped.

Examples:

> (define tx '(root (script "3 > 2") "Why is 3 > 2?"))
> (xexpr->string tx)

"<root><script>3 &gt; 2</script>Why is 3 &gt; 2?</root>"

> (->html tx)

"<root><script>3 > 2</script>Why is 3 &gt; 2?</root>"

The optional keyword arguments html-tag and html-attrs let you replace the tag and attributes in the generated HTML.

Examples:

> (define tx '(root ((id "huff")) "Bunk beds"))
> (->html tx)

"<root id=\"huff\">Bunk beds</root>"

> (->html tx #:tag 'div)

"<div id=\"huff\">Bunk beds</div>"

> (->html tx #:attrs '((id "doback")))

"<root id=\"doback\">Bunk beds</root>"

> (->html tx #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Bunk beds</div>"

The splice-html? option will strip the outer tag from the generated HTML.

Examples:

> (define tx '(root (p "Orange marmalade")))
> (->html tx)

"<root><p>Orange marmalade</p></root>"

> (->html tx #:splice #t)

"<p>Orange marmalade</p>"

Be careful not to pass existing HTML strings into this function, because the angle brackets will be escaped. Fine if that’s what you want, but you probably don’t.

Examples:

> (define tx '(p "You did" (em "what?")))
> (->html tx)

"<p>You did<em>what?</em></p>"

> (->html (->html tx))

car: contract violation

  expected: pair?

  given: "<p>You did<em>what?</em></p>"

procedure

(select key value-source)  (or/c #f txexpr-element?)

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)

procedure

(select* key value-source)  (or/c #f (listof txexpr-element?))

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)
Find matches for key in value-source, first by looking in its metas (using select-from-metas) and then by looking in its doc (using select-from-doc). With select, you get the first result; with select*, you get them all. In both cases, you get #f if there are no matches.

procedure

(select-from-metas key meta-source)  (or/c #f txexpr-element?)

  key : symbolish?
  meta-source : (or/c hash? pagenodeish? pathish?)
Look up the value of key in meta-source. The meta-source argument can be either a set of metas (i.e., a hash) or a pagenode?, from which metas are pulled. If no value exists for key, you get #f.

Examples:

> (module ice-cream pollen/markup
  '(div (question "Flavor?")
    (answer "Chocolate chip") (answer "Maple walnut"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'ice-cream submodule
> (require 'ice-cream)
> (select-from-metas 'template  metas)

"sub.xml.pt"

> ('target . select-from-metas . metas)

"print"

> (select-from-metas 'nonexistent-key metas)

#f

procedure

(select-from-doc key doc-source)  (or/c #f txexpr-element?)

  key : symbolish?
  doc-source : (or/c txexpr? pagenodeish? pathish?)
Look up the value of key in doc-source. The doc-source argument can be either be a doc (i.e., a txexpr) or a pagenode?, from which doc is pulled. If no value exists for key, you get #f.

Examples:

> (module gelato pollen/markup
  '(div (question "Flavor?")
    (answer "Nocciola") (answer "Pistachio"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'gelato submodule
> (require 'gelato)
> (select-from-doc 'question  doc)

'("Flavor?")

> ('answer . select-from-doc . doc)

'("Nocciola" "Pistachio")

> (select-from-doc 'nonexistent-key doc)

#f

 
\ No newline at end of file +9.6 Template
6.0.1.11

9.6 Template

 (require pollen/template) package: pollen

Convenience functions for templates. These are automatically imported into the eval environment when rendering with a template (see render).

This module also provides everything from sugar/coerce/value.

procedure

(->html xexpr    
  [#:tag html-tag    
  #:attrs html-attrs    
  #:splice splice-html?])  string?
  xexpr : xexpr?
  html-tag : (or/c #f txexpr-tag?) = #f
  html-attrs : (or/c #f txexpr-attrs?) = #f
  splice-html? : boolean? = #f
Convert xexpr to an HTML string. Similar to xexpr->string, but consistent with the HTML spec, text that appears within script or style blocks will not be escaped.

Examples:

> (define tx '(root (script "3 > 2") "Why is 3 > 2?"))
> (xexpr->string tx)

"<root><script>3 &gt; 2</script>Why is 3 &gt; 2?</root>"

> (->html tx)

"<root><script>3 > 2</script>Why is 3 &gt; 2?</root>"

The optional keyword arguments html-tag and html-attrs let you set the outer tag and attributes for the generated HTML. If xexpr already has an outer tag or attributes, they will be replaced.

Examples:

> (define tx '(root ((id "huff")) "Bunk beds"))
> (->html tx)

"<root id=\"huff\">Bunk beds</root>"

> (->html tx #:tag 'div)

"<div id=\"huff\">Bunk beds</div>"

> (->html tx #:attrs '((id "doback")))

"<root id=\"doback\">Bunk beds</root>"

> (->html tx #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Bunk beds</div>"

Whereas if xexpr has no tag or attributes, they will be added. If you supply attributes without a tag, you’ll get an error.

Examples:

> (define x "Drum kit")
> (->html x)

"Drum kit"

> (->html x #:tag 'div)

"<div>Drum kit</div>"

> (->html x #:tag 'div #:attrs '((id "doback")))

"<div id=\"doback\">Drum kit</div>"

> (->html x #:attrs '((id "doback")))

->html: can't use attribute list '((id doback)) without a

#:tag argument

If the generated HTML has an outer tag, the splice-html? option will strip it off. Otherwise this option has no effect.

Examples:

> (define tx '(root (p "Chicken nuggets")))
> (->html tx)

"<root><p>Chicken nuggets</p></root>"

> (->html tx #:splice #t)

"<p>Chicken nuggets</p>"

> (define x "Fancy sauce")
> (->html x)

"Fancy sauce"

; This next one won't do anything
> (->html x #:splice #t)

"Fancy sauce"

; Adds the outer tag, but then #:splice removes it
> (->html x #:tag 'div #:attrs '((id "doback")) #:splice #t)

"Fancy sauce"

Be careful not to pass existing HTML strings into this function, because the angle brackets will be escaped. Fine if that’s what you want, but you probably don’t.

Examples:

> (define tx '(p "You did" (em "what?")))
> (->html tx)

"<p>You did<em>what?</em></p>"

> (->html (->html tx))

"&lt;p&gt;You did&lt;em&gt;what?&lt;/em&gt;&lt;/p&gt;"

procedure

(select key value-source)  (or/c #f txexpr-element?)

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)

procedure

(select* key value-source)  (or/c #f (listof txexpr-element?))

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)
Find matches for key in value-source, first by looking in its metas (using select-from-metas) and then by looking in its doc (using select-from-doc). With select, you get the first result; with select*, you get them all. In both cases, you get #f if there are no matches.

procedure

(select-from-metas key meta-source)  (or/c #f txexpr-element?)

  key : symbolish?
  meta-source : (or/c hash? pagenodeish? pathish?)
Look up the value of key in meta-source. The meta-source argument can be either a set of metas (i.e., a hash) or a pagenode?, from which metas are pulled. If no value exists for key, you get #f.

Examples:

> (module ice-cream pollen/markup
  '(div (question "Flavor?")
    (answer "Chocolate chip") (answer "Maple walnut"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'ice-cream submodule
> (require 'ice-cream)
> (select-from-metas 'template  metas)

"sub.xml.pt"

> ('target . select-from-metas . metas)

"print"

> (select-from-metas 'nonexistent-key metas)

#f

procedure

(select-from-doc key doc-source)  (or/c #f txexpr-element?)

  key : symbolish?
  doc-source : (or/c txexpr? pagenodeish? pathish?)
Look up the value of key in doc-source. The doc-source argument can be either be a doc (i.e., a txexpr) or a pagenode?, from which doc is pulled. If no value exists for key, you get #f.

Examples:

> (module gelato pollen/markup
  '(div (question "Flavor?")
    (answer "Nocciola") (answer "Pistachio"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'gelato submodule
> (require 'gelato)
> (select-from-doc 'question  doc)

'("Flavor?")

> ('answer . select-from-doc . doc)

'("Nocciola" "Pistachio")

> (select-from-doc 'nonexistent-key doc)

#f

 
\ No newline at end of file diff --git a/scribblings/template.scrbl b/scribblings/template.scrbl index 84788a0..825eb7d 100644 --- a/scribblings/template.scrbl +++ b/scribblings/template.scrbl @@ -28,7 +28,7 @@ Convert @racket[_xexpr] to an HTML string. Similar to @racket[xexpr->string], bu (->html tx) ] -The optional keyword arguments @racket[_html-tag] and @racket[_html-attrs] let you replace the tag and attributes in the generated HTML. +The optional keyword arguments @racket[_html-tag] and @racket[_html-attrs] let you set the outer tag and attributes for the generated HTML. If @racket[_xexpr] already has an outer tag or attributes, they will be replaced. @examples[#:eval my-eval (define tx '(root ((id "huff")) "Bunk beds")) @@ -38,12 +38,29 @@ The optional keyword arguments @racket[_html-tag] and @racket[_html-attrs] let y (->html tx #:tag 'div #:attrs '((id "doback"))) ] -The @racket[_splice-html?] option will strip the outer tag from the generated HTML. +Whereas if @racket[_xexpr] has no tag or attributes, they will be added. If you supply attributes without a tag, you'll get an error. @examples[#:eval my-eval -(define tx '(root (p "Orange marmalade"))) +(define x "Drum kit") +(->html x) +(->html x #:tag 'div) +(->html x #:tag 'div #:attrs '((id "doback"))) +(->html x #:attrs '((id "doback"))) +] + + +If the generated HTML has an outer tag, the @racket[_splice-html?] option will strip it off. Otherwise this option has no effect. + +@examples[#:eval my-eval +(define tx '(root (p "Chicken nuggets"))) (->html tx) (->html tx #:splice #t) +(define x "Fancy sauce") +(->html x) +(code:comment @#,t{This next one won't do anything}) +(->html x #:splice #t) +(code:comment @#,t{Adds the outer tag, but then #:splice removes it}) +(->html x #:tag 'div #:attrs '((id "doback")) #:splice #t) ] diff --git a/template.rkt b/template.rkt index ab0c28c..369dfc8 100644 --- a/template.rkt +++ b/template.rkt @@ -51,7 +51,7 @@ (define (get-metas pagenode-or-path) -; ((or/c pagenode? pathish?) . -> . hash?) + ; ((or/c pagenode? pathish?) . -> . hash?) (define source-path (->source-path (cond [(pagenode? pagenode-or-path) (pagenode->path pagenode-or-path)] [else pagenode-or-path]))) @@ -61,7 +61,7 @@ (define (get-doc pagenode-or-path) -; ((or/c pagenode? pathish?) . -> . (or/c txexpr? string?)) + ; ((or/c pagenode? pathish?) . -> . (or/c txexpr? string?)) (define source-path (->source-path (cond [(pagenode? pagenode-or-path) (pagenode->path pagenode-or-path)] [else pagenode-or-path]))) @@ -77,13 +77,20 @@ (define+provide/contract (->html x #:tag [tag #f] #:attrs [attrs #f] #:splice [splice? #f]) ((xexpr?) (#:tag (or/c #f txexpr-tag?) #:attrs (or/c #f txexpr-attrs?) #:splice boolean?) . ->* . string?) - (define html-tag (or tag (get-tag x))) - (define html-attrs (or attrs (get-attrs x))) - (define html (xexpr->html (make-txexpr html-tag html-attrs (get-elements x)))) - (if splice? - (trim-outer-tag html) - html)) + (when (and (not (txexpr? x)) attrs (not tag)) + (error '->html "can't use attribute list '~a without a #:tag argument" attrs)) + + (if (or tag (txexpr? x)) + (let () + (define html-tag (or tag (get-tag x))) + (define html-attrs (or attrs (and (txexpr? x) (get-attrs x)) null)) + (define html-elements (or (and (txexpr? x) (get-elements x)) (list x))) + (define html (xexpr->html (make-txexpr html-tag html-attrs html-elements))) + (if splice? + (trim-outer-tag html) + html)) + (xexpr->html x))) (provide when/block) (define-syntax (when/block stx) diff --git a/tests/test-template.rkt b/tests/test-template.rkt index 6e349ec..1d551d8 100644 --- a/tests/test-template.rkt +++ b/tests/test-template.rkt @@ -2,11 +2,20 @@ (require rackunit) (require "../template.rkt") -(define x '(root (p "hello"))) - -(check-equal? (->html x) "

hello

") -(check-equal? (->html #:tag 'brennan x) "

hello

") -(check-equal? (->html #:attrs '((id "dale")) x) "

hello

") -(check-equal? (->html #:splice #t x) "

hello

") -(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) x) "

hello

") -(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) #:splice #t x) "

hello

") \ No newline at end of file +(define tx '(root (p "hello"))) + +(check-equal? (->html tx) "

hello

") +(check-equal? (->html #:tag 'brennan tx) "

hello

") +(check-equal? (->html #:attrs '((id "dale")) tx) "

hello

") +(check-equal? (->html #:splice #t tx) "

hello

") +(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) tx) "

hello

") +(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) #:splice #t tx) "

hello

") + +(define x "hello") + +(check-equal? (->html x) "hello") +(check-equal? (->html #:tag 'brennan x) "hello") +(check-exn exn:fail? (λ() (->html #:attrs '((id "dale")) x) "hello")) ;; won't work without tag +(check-equal? (->html #:splice #t x) "hello") +(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) x) "hello") +(check-equal? (->html #:tag 'brennan #:attrs '((id "dale")) #:splice #t x) "hello")