Pollen Component
(require pollen-component) | package: pollen-component |
Component-based development for Pollen.
Version |
| |
Documentation |
| |
License |
| |
Code of Conduct |
| |
Distribution |
| |
Source |
| |
Bug Reports |
| |
Contributions |
|
1 Overview
Pollen projects are generally structured like the following:
"pollen.rkt"
#lang racket (provide (all-defined-out)) (define (link href . elements) `(a ((href ,href)) ,@elements))
"styles.css.pp"
#lang pollen a { color: red; }
"scripts.js.pp"
#lang pollen var links = document.getElementsByTagName('a'); // …
"index.html.pm"
#lang pollen Without ◊link["http://…"]{Pollen Component}.
The separation of structure ("pollen.rkt"), appearance ("styles.css.pp"), behavior ("scripts.js.pp") and content ("index.html.pm") is a good idea that stood the test of time. So much so, that it is not particular of Pollen projects, but the standard in document-preparation systems like the web and LaTeX. Its main advantage is consistency: generated documents use the same fonts, colors, sizes and so on throughout the pages because those are specified in a single place.
The problems with this model begin when changing from questions like “what are all the styles in effect on this document?” to questions like “what constitutes a link on this document?” To answer this, it is necessary to open at least three files—"pollen.rkt", "styles.css.pp" and "scripts.js.pp"—and search for the snippets relevant to links. One sees only the pieces and has to imagine the full picture. Also, as the project grows, it becomes harder to understand the far-reached effects the parts have on one another.
Recently, modern web development tools including React and Polymer popularized one solution to these issues: components. Components bring together the definitions of structure, appearance and behavior for distinguishable parts of the document.
Components do not replace the traditional architecture. For example, the cascading behavior of CSS is still useful to guarantee consistency throughout the document. But components yield better organization for elements that make sense on their own: links, menus and tag functions in general.
While tools like React and Polymer target application development, we believe that document-preparation systems can benefit from components as well. Thus, we present Pollen Component: an extension to Pollen that allows for component-based development.
The example above becomes the following with Pollen Component:
"pollen.rkt"
#lang racket (require pollen-component racket/dict racket/string racket/format) (provide (all-defined-out) (all-from-out racket/dict racket/string racket/format)) (components-output-types #:dynamic html #:static css javascript) (define-component (link href . elements) #:html `(a ((href ,href)) ,@elements) #:css "a { color: red; }" #:javascript "var links = document.getElementsByTagName('a'); // …")
"styles.css.pp"
#lang pollen ◊(string-join (for/list ([(component css) (in-dict (components/css))]) (~a "/*" component "*/" css)))
"scripts.js.pp"
#lang pollen ◊(string-join (for/list ([(component javascript) (in-dict (components/javascript))]) (~a "/*" component "*/" javascript)))
"index.html.pm"
#lang pollen Welcome to ◊link["http://…"]{Pollen Component}.
Unleash the full power of Pollen Component by defining CSS with CSS-expressions and JavaScript with Urlang:
"pollen.rkt"
#lang racket (require pollen-component css-expr urlang) (provide (all-defined-out)) (current-urlang-echo? #t) (components-output-types #:dynamic html #:static css javascript) (define-syntax-rule (javascript expressions ...) (with-output-to-string (λ () (urlang (urmodule javascript-module expressions ...))))) (define-component (link href . elements) #:html `(a ((href ,href)) ,@elements) #:css (css-expr [a #:color red]) #:javascript (javascript (import document) (define links (document.getElementsByTagName "a"))))
2 Installation
Pollen Component is a Racket package. Install it in DrRacket or with the following command line:
$ raco pkg install pollen-component
3 Usage
syntax
(components-output-types [#:dynamic dynamic ...] [#:static static ...])
dynamic : identifier?
static : identifier?
See Pollen Component in action on my personal website (source). It also uses CSS-expressions to define CSS.
Use components-output-types in "pollen.rkt" to specify the output types supported by components. The dynamic output types are those for which one would create tag functions, for example, HTML, Atom and LaTeX. The static output types are the styles and behavior that support the document, for example, CSS, JavaScript and LaTeX styles.
The static output types receive this name because, with respect to the component being defined, the contents of the static output types are always the same and known at the time of defining the component. In contrast, the contents of dynamic output types will not be known until the component is used. For example, there may be many different HTML links on a page, so a link component would have HTML as a dynamic output type; but the styles associated with links are always the same, defined in a stylesheet, so CSS would be a static output type.
Using components-output-types introduces bindings for define-component and components/<static>s (one for each static output type) in the current environment. Thus, components-output-types must come first and appear only once.
The body corresponding to dynamic output types turn into a function tag that detects the output type of the current document and executes the appropriate code. Undefined dynamic output types fall back to default-tag-function.
The body corresponding to static output types are accumulated in Racket parameters of association lists named components/<static>. There is one components/<static> parameter for each static output type. The keys are the components names (as symbols) and the values are the components contents for that output type as defined by body.
parameter
(current-pollen-component-dynamic-type type) → void? type : string?
"component.rkt"
#lang racket (require pollen-component) (provide (all-defined-out)) (components-output-types #:dynamic html txt #:static css javascript) (define-component (test a) #:html `(p ,a) #:txt (format "Hey you wrote : ~a" a)) ; get the html version (parameterize ([current-pollen-component-dynamic-type "html"]) (test "hello")) ; get the txt version (parameterize ([current-pollen-component-dynamic-type "txt"]) (test "hello"))
4 Acknowledgments
Thank you Matthew Butterick for Pollen and for the feedback given in private email conversations. Thank you Greg Trzeciak for the feedback given in private conversations. Thank you Luke Whittlesey for contributing current-pollen-component-dynamic-type. Thank you all Racket developers. Thank you all users of this library.
5 Changelog
This section documents all notable changes to pollen-component. It follows recommendations from Keep a CHANGELOG and uses Semantic Versioning. Each released version is a Git tag.
5.1 0.0.5 · 2018-05-03
5.1.1 Added
Added current-pollen-component-dynamic-type. (Thanks Luke Whittlesey.)
5.2 0.0.4 · 2017-03-08
5.2.1 Fixed
Fix automated tests by ommitting examples directory.
5.3 0.0.3 · 2017-02-13
5.3.1 Added
Automated tests.
5.4 0.0.2 · 2017-02-12
5.4.1 Added
Example in documentation of how to use Pollen Component with CSS-expressions and Urlang.
Acknowledgment to Greg Trzeciak.
5.5 0.0.1 · 2017-01-21
5.5.1 Added
Basic functionality.