the great renaming: pmap + key becomes ptree + pnode

pull/9/head
Matthew Butterick 11 years ago
parent cadcfd8563
commit 2333b0b0f7

@ -12,9 +12,9 @@
[("start") `(require "server.rkt")]
[("regenerate") `(begin
;; todo: take extensions off the comand line
(displayln "Regenerate preproc & pmap files ...")
(displayln "Regenerate preproc & ptree files ...")
(require "regenerate.rkt" "pollen-file-tools.rkt" "world.rkt")
(apply regenerate-with-session (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_MAP_EXT))))]
(apply regenerate-with-session (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_TREE_EXT))))]
[("clone") (let ([target-path
(if (> (len args) 1)
(->path (get args 1))
@ -29,7 +29,7 @@
pollen-source?
preproc-source?
template-source?
pmap-source?
ptree-source?
pollen-script?
magic-directory?
racket-file?)))

@ -14,8 +14,9 @@
(williams:describe x)
x)
; debug utilities
(define months (list "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
(define (message . items)
(define (zero-fill str count)
(set! str (~a str))
@ -25,15 +26,15 @@
(define (make-date-string)
(define date (current-date))
(define date-fields (map (λ(x) (zero-fill x 2))
(list (date-month date)
(define date-fields (map (λ(x) (zero-fill x 2)) (list
(date-day date)
(date-year date)
(list-ref months (sub1 (date-month date)))
(modulo (date-hour date) 12)
(date-minute date)
(date-second date)
(if (< (date-hour date) 12) "am" "pm"))))
(apply format "[~a.~a.~a ~a:~a:~a~a]" date-fields))
; (if (< (date-hour date) 12) "am" "pm")
)))
(apply format "[~a-~a ~a:~a:~a]" date-fields))
(displayln (string-join `(,(make-date-string) ,@(map (λ(x)(if (string? x) x (~v x))) items))) (current-error-port)))

@ -1,10 +1,10 @@
#lang racket/base
(require racket/list)
(require (planet mb/pollen/tools) (planet mb/pollen/main-helper))
(require (only-in (planet mb/pollen/pmap-decode) pmap-source-decode))
(require (only-in (planet mb/pollen/predicates) pmap?))
(require (only-in (planet mb/pollen/ptree-decode) ptree-source-decode))
(require (only-in (planet mb/pollen/predicates) ptree?))
(require (only-in (planet mb/pollen/pollen-file-tools) has-ext?))
(require (only-in (planet mb/pollen/world) POLLEN_MAP_EXT))
(require (only-in (planet mb/pollen/world) POLLEN_TREE_EXT))
(provide (except-out (all-from-out racket/base) #%module-begin)
(rename-out [module-begin #%module-begin]))
@ -75,18 +75,18 @@
;; The point is just to set it up for further processing.
;; Unlike Scribble, which insists on decoding,
;; Pollen just passes through the minimally processed data.
;; one exception: if file extension marks it as pmap, send it to the pmap decoder instead.
;; one exception: if file extension marks it as ptree, send it to the ptree decoder instead.
;; this tests inner-here (which is always the file name)
;; rather than (get metas 'here) which might have been overridden.
;; Because if it's overridden to something other than *.pmap,
;; pmap processing will fail.
;; This defeats rule that pmap file suffix triggers pmap decoding.
(define here-is-pmap? (pmap-source? (->path inner-here)))
;; Because if it's overridden to something other than *.ptree,
;; ptree processing will fail.
;; This defeats rule that ptree file suffix triggers ptree decoding.
(define here-is-ptree? (ptree-source? (->path inner-here)))
(define main (apply (if here-is-pmap?
;; pmap source files will go this way,
pmap-source-decode
(define main (apply (if here-is-ptree?
;; ptree source files will go this way,
ptree-source-decode
;; ... but other files, including pollen, will go this way.
;; Root is treated as a function.
;; If it's not defined elsewhere,
@ -99,13 +99,13 @@
(module+ main
(displayln ";-------------------------")
(displayln (string-append "; pollen decoded 'main" (if here-is-pmap? " (as pmap)" "")))
(displayln (string-append "; pollen decoded 'main" (if here-is-ptree? " (as ptree)" "")))
(displayln ";-------------------------")
main
(displayln "")
(if here-is-pmap?
(displayln (format "(pmap? main) ~a" (pmap? main)))
(if here-is-ptree?
(displayln (format "(ptree? main) ~a" (ptree? main)))
(displayln (format "(tagged-xexpr? main) ~a" (tagged-xexpr? main))))
(displayln "")
(displayln ";-------------------------")

@ -1,236 +0,0 @@
#lang racket/base
(require xml xml/path racket/list racket/string racket/contract racket/match racket/set)
(require "tools.rkt" "world.rkt" "pmap-decode.rkt")
(module+ test (require rackunit))
(provide (all-defined-out))
;; function to set up the project-pmap.
;; this is to make life simpler when using map navigation functions.
;; the current main.pmap of the project is used as the default input.
;; without this, you'd have to pass it over and over.
;; which is sort of the functional lifestyle,
;; but in templates, gets tiresome and error-prone.
(define/contract (make-project-pmap)
(-> pmap?)
(define pmap-source (build-path START_DIR DEFAULT_POLLEN_MAP))
(if (file-exists? pmap-source)
;; Load it from default path.
;; dynamic require of a pmap source file gets you a full pmap.
(dynamic-require pmap-source POLLEN_ROOT)
;; ... or else synthesize it
(let* ([files (directory-list START_DIR)]
;; restrict files to those with pollen extensions
[files (map remove-ext (filter (λ(x) (has-ext? x POLLEN_SOURCE_EXT)) files))])
;; make a POLLEN_MAP_ROOT_NAME structure and convert it to a full pmap
(pmap-root->pmap (cons POLLEN_MAP_ROOT_NAME (map path->string files))))))
(define project-pmap (make-project-pmap))
;; remove parents from map (i.e., just remove attrs)
;; is not the inverse of add-parents, i.e., you do not get back your original input.
(define/contract (remove-parents mt)
(pmap? . -> . tagged-xexpr?)
(remove-attrs mt))
(module+ test
(check-equal? (remove-parents
`(pmap-main ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY ""))) (bar ((,POLLEN_MAP_PARENT_KEY ""))) (one ((,POLLEN_MAP_PARENT_KEY "")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two")))))))
'(pmap-main (foo) (bar) (one (two (three))))))
(module+ test
(let ([sample-main `(POLLEN_MAP_ROOT_NAME "foo" "bar" (one (two "three")))])
(check-equal? (pmap-root->pmap sample-main)
`(POLLEN_MAP_ROOT_NAME ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME"))) (bar ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME"))) (one ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two")))))))))
;; return the parent of a given name
(define/contract (parent element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c string? boolean?))
(and element (let ([result (se-path* `(,(->symbol element) #:parent) pmap)])
(and result (->string result))))) ; se-path* returns #f if nothing found
(module+ test
(define test-pmap-main `(pmap-main "foo" "bar" (one (two "three"))))
(define test-pmap (pmap-root->pmap test-pmap-main))
(check-equal? (parent 'three test-pmap) "two")
(check-equal? (parent "three" test-pmap) "two")
(check-false (parent 'nonexistent-name test-pmap)))
; get children of a particular element
(define/contract (children element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c list? boolean?))
;; se-path*/list returns '() if nothing found
(and element (let ([children (se-path*/list `(,(->symbol element)) pmap)])
; If there are sublists, just take first element
(and (not (empty? children)) (map (λ(i) (->string (if (list? i) (car i) i))) children)))))
(module+ test
(check-equal? (children 'one test-pmap) (list "two"))
(check-equal? (children 'two test-pmap) (list "three"))
(check-false (children 'three test-pmap))
(check-false (children 'fooburger test-pmap)))
;; find all siblings on current level: go up to parent and ask for children
(define/contract (siblings element [pmap project-pmap])
;; this never returns false: element is always a sibling of itself.
;; todo: how to use input value in contract? e.g., to check that element is part of output list
((pmap-key?) (pmap?) . ->* . (or/c list? boolean?))
(children (parent element pmap) pmap))
(module+ test
(check-equal? (siblings 'one test-pmap) '("foo" "bar" "one"))
(check-equal? (siblings 'foo test-pmap) '("foo" "bar" "one"))
(check-equal? (siblings 'two test-pmap) '("two"))
(check-false (siblings 'invalid-key test-pmap)))
(define/contract (siblings-split element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (values (or/c (listof pmap-key?) boolean?)
(or/c (listof pmap-key?) boolean?)))
(let-values ([(left right) (splitf-at (siblings element pmap)
(λ(e) (not (equal? (->string e) (->string element)))))])
(values (if (empty? left) #f left) (if (empty? (cdr right)) #f (cdr right)))))
(module+ test
(check-equal? (values->list (siblings-split 'one test-pmap)) '(("foo" "bar") #f))
(check-equal? (values->list (siblings-split 'bar test-pmap)) (list '("foo") '("one"))))
;; siblings to the left of target element (i.e., precede in map order)
(define (siblings-left element [pmap project-pmap])
(let-values ([(left right) (siblings-split element pmap)])
left))
(module+ test
(check-equal? (siblings-left 'one test-pmap) '("foo" "bar"))
(check-false (siblings-left 'foo test-pmap)))
;; siblings to the right of target element (i.e., follow in map order)
(define (siblings-right element [pmap project-pmap])
(let-values ([(left right) (siblings-split element pmap)])
right))
(module+ test
(check-false (siblings-right 'one test-pmap))
(check-equal? (siblings-right 'foo test-pmap) '("bar" "one")))
;; get element immediately to the left in map
(define/contract (sibling-previous element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c string? boolean?))
(let ([siblings (siblings-left element pmap)])
(and siblings (last siblings))))
(module+ test
(check-equal? (sibling-previous 'bar test-pmap) "foo")
(check-false (sibling-previous 'foo test-pmap)))
;; get element immediately to the right in map
(define/contract (sibling-next element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c string? boolean?))
(let ([siblings (siblings-right element pmap)])
(and siblings (first siblings))))
(module+ test
(check-equal? (sibling-next 'foo test-pmap) "bar")
(check-false (sibling-next 'one test-pmap)))
;; flatten map to sequence
(define/contract (all-pages [pmap project-pmap])
(pmap? . -> . (listof string?))
; use cdr to get rid of main-map tag at front
(map ->string (cdr (flatten (remove-parents pmap)))))
(module+ test
(check-equal? (all-pages test-pmap) '("foo" "bar" "one" "two" "three")))
;; helper function for get-previous-pages and get-next-pages
(define/contract (adjacent-pages side element [pmap project-pmap])
((symbol? pmap-key?) (pmap?) . ->* . (or/c list? boolean?))
(let ([result ((if (equal? side 'left)
takef
takef-right) (all-pages pmap)
(λ(y) (not (equal? (->string element) (->string y)))))])
(and (not (empty? result)) result)))
(module+ test
(check-equal? (adjacent-pages 'left 'one test-pmap) '("foo" "bar"))
(check-equal? (adjacent-pages 'left 'three test-pmap) '("foo" "bar" "one" "two"))
(check-false (adjacent-pages 'left 'foo test-pmap)))
;; get sequence of earlier pages
(define/contract (previous-pages element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c list? boolean?))
(adjacent-pages 'left element pmap))
(module+ test
(check-equal? (previous-pages 'one test-pmap) '("foo" "bar"))
(check-equal? (previous-pages 'three test-pmap) '("foo" "bar" "one" "two"))
(check-false (previous-pages 'foo test-pmap)))
;; get sequence of next pages
(define (next-pages element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c list? boolean?))
(adjacent-pages 'right element pmap))
(module+ test
(check-equal? (next-pages 'foo test-pmap) '("bar" "one" "two" "three"))
(check-equal? (next-pages 'one test-pmap) '("two" "three"))
(check-false (next-pages 'three test-pmap)))
;; get page immediately previous
(define/contract (previous-page element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c string? boolean?))
(let ([result (previous-pages element pmap)])
(and result (last result))))
(module+ test
(check-equal? (previous-page 'one test-pmap) "bar")
(check-equal? (previous-page 'three test-pmap) "two")
(check-false (previous-page 'foo test-pmap)))
;; get page immediately next
(define (next-page element [pmap project-pmap])
((pmap-key?) (pmap?) . ->* . (or/c string? boolean?))
(let ([result (next-pages element pmap)])
(and result (first result))))
(module+ test
(check-equal? (next-page 'foo test-pmap) "bar")
(check-equal? (next-page 'one test-pmap) "two")
(check-false (next-page 'three test-pmap)))
;; convert path to pmap-key
;; used for converting "here" values to pmap-keys
(define/contract (->pmap-key x)
(any/c . -> . pmap-key?)
(->string (remove-all-ext (last (explode-path (->path x))))))
(module+ test
(check-equal? (->pmap-key "bar") "bar")
(check-equal? (->pmap-key "foo/bar") "bar")
(check-equal? (->pmap-key "foo/bar.html") "bar")
(check-equal? (->pmap-key "/Users/this/that/foo/bar.html.pp") "bar"))
;; convert key to URL
;; = key name + suffix of template (or suffix of default template)
;; todo: finish this function, right now it just appends html
;; this would also be useful for start page (showing correct url of generated pages)
(define/contract (pmap-key->url key)
(pmap-key? . -> . string?)
(string-append key ".html"))

@ -119,13 +119,13 @@
; or a file (e.g., html) that has a pollen source file
(ormap (λ(proc) (proc (->path x))) (list pollen-source? has-pollen-source?)))
(define/contract (pmap-source? x)
(define/contract (ptree-source? x)
(any/c . -> . boolean?)
(has-ext? (->path x) POLLEN_MAP_EXT))
(has-ext? (->path x) POLLEN_TREE_EXT))
(module+ test
(check-true (pmap-source? "foo.pmap"))
(check-false (pmap-source? "pmap.bar")))
(check-true (ptree-source? "foo.ptree"))
(check-false (ptree-source? "ptree.bar")))
(define/contract (pollen-source? x)

@ -162,29 +162,29 @@
(check-false (elements-unique? "foo")))
;; certain pmap requirements are enforced at compile-time.
;; (such as pmap-keys must be valid strings, and unique.)
;; certain ptree requirements are enforced at compile-time.
;; (such as pnodes must be valid strings, and unique.)
;; otherwise this becomes a rather expensive contract
;; because every function in pmap.rkt uses it.
;; note that a pmap is just a bunch of recursively nested pmaps.
(define/contract (pmap? x)
;; because every function in ptree.rkt uses it.
;; note that a ptree is just a bunch of recursively nested ptrees.
(define/contract (ptree? x)
(any/c . -> . boolean?)
(and (match x
;; a tagged-xexpr with one attr ('parent)
;; whose subelements recursively meet the same test.
[(list (? pmap-key? tag) (? pmap-attr? attr) elements ...)
(andmap pmap? elements)]
[(list (? pnode? tag) (? ptree-attr? attr) elements ...)
(andmap ptree? elements)]
[else #f])))
(module+ test
(check-true (pmap? '(foo ((parent "bar")))))
(check-false (pmap? '(foo)))
(check-false (pmap? '(foo ((parent "bar")(hee "haw")))))
(check-true (pmap? '(foo ((parent "bar")) (hee ((parent "foo"))))))
(check-false (pmap? '(foo ((parent "bar")) (hee ((uncle "foo")))))))
;; pmap attr must be ((parent "value"))
(define/contract (pmap-attr? x)
(check-true (ptree? '(foo ((parent "bar")))))
(check-false (ptree? '(foo)))
(check-false (ptree? '(foo ((parent "bar")(hee "haw")))))
(check-true (ptree? '(foo ((parent "bar")) (hee ((parent "foo"))))))
(check-false (ptree? '(foo ((parent "bar")) (hee ((uncle "foo")))))))
;; ptree attr must be ((parent "value"))
(define/contract (ptree-attr? x)
(any/c . -> . boolean?)
(define foo 'bar)
(match x
@ -193,36 +193,36 @@
[else #f]))
(module+ test
(check-true (pmap-attr? '((parent "bar"))))
(check-false (pmap-attr? '((parent "bar") '(foo "bar"))))
(check-false (pmap-attr? '())))
(check-true (ptree-attr? '((parent "bar"))))
(check-false (ptree-attr? '((parent "bar") '(foo "bar"))))
(check-false (ptree-attr? '())))
;; pmap location must represent a possible valid filename
(define/contract (pmap-key? x #:loud [loud #f])
;; ptree location must represent a possible valid filename
(define/contract (pnode? x #:loud [loud #f])
((any/c) (#:loud boolean?) . ->* . boolean?)
;; todo: how to express the fact that the pmap-location must be
;; todo: how to express the fact that the ptree-location must be
;; a valid base name for a file?
;; however, don't restrict it to existing files
;; (author may want to use pmap as wireframe)
;; (author may want to use ptree as wireframe)
(define result
(or (eq? x #f) ; OK for map-key to be #f
(and (or (symbol? x) (string? x))
;; todo: should test be same as valid module name?
(->boolean (regexp-match #px"^[-_A-Za-z0-9]+$" (->string x))))))
(if (and (not result) loud)
(error "Not a valid pmap key:" x)
(error "Not a valid ptree key:" x)
result))
(module+ test
(check-true (pmap-key? #f))
(check-true (pmap-key? "foo-bar"))
(check-true (pmap-key? "Foo_Bar_0123"))
(check-true (pmap-key? 'foo-bar))
(check-false (pmap-key? "foo-bar.p"))
(check-false (pmap-key? "/Users/MB/foo-bar"))
(check-false (pmap-key? ""))
(check-false (pmap-key? " ")))
(check-true (pnode? #f))
(check-true (pnode? "foo-bar"))
(check-true (pnode? "Foo_Bar_0123"))
(check-true (pnode? 'foo-bar))
(check-false (pnode? "foo-bar.p"))
(check-false (pnode? "/Users/MB/foo-bar"))
(check-false (pnode? ""))
(check-false (pnode? " ")))
;; recursive whitespace test

@ -7,13 +7,13 @@
(provide (all-defined-out))
;; These functions need to be separated so that they can be accessed by pollen parser (in main.rkt)
;; Other pmap functions are in pmap.rkt.
;; pmap decoder takes pmap source and returns a full pmap structure.
;; Other ptree functions are in ptree.rkt.
;; ptree decoder takes ptree source and returns a full ptree structure.
;; recursively processes map, converting map locations & their parents into xexprs of this shape:
;; recursively processes tree, converting tree locations & their parents into xexprs of this shape:
;; '(location ((parent "parent")))
(define/contract (add-parents x [parent empty])
((tagged-xexpr?) (xexpr-tag?) . ->* . pmap?)
((tagged-xexpr?) (xexpr-tag?) . ->* . ptree?)
(match x
;; this pattern signifies next level in hierarchy
;; where first element is new parent, and rest are children.
@ -22,38 +22,38 @@
;; xexpr with tag as name, parent as attr, children as elements with tag as next parent
(make-tagged-xexpr tag attr (map (λ(c) (add-parents c tag)) children)))]
;; single map entry: convert to xexpr with parent
[else (make-tagged-xexpr (->symbol x) (make-xexpr-attr POLLEN_MAP_PARENT_KEY (->string parent)))]))
[else (make-tagged-xexpr (->symbol x) (make-xexpr-attr POLLEN_TREE_PARENT_NAME (->string parent)))]))
;; this sets default input for following functions
(define/contract (pmap-root->pmap tx)
;; (not/c pmap) prevents pmaps from being accepted as input
((and/c tagged-xexpr? (not/c pmap?)) . -> . pmap?)
(define/contract (ptree-root->ptree tx)
;; (not/c ptree) prevents ptrees from being accepted as input
((and/c tagged-xexpr? (not/c ptree?)) . -> . ptree?)
(add-parents tx))
(module+ test
(define test-pmap-main `(pmap-main "foo" "bar" (one (two "three"))))
(check-equal? (pmap-root->pmap test-pmap-main)
`(pmap-main ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY "pmap-main"))) (bar ((,POLLEN_MAP_PARENT_KEY "pmap-main"))) (one ((,POLLEN_MAP_PARENT_KEY "pmap-main")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two"))))))))
(define test-ptree-main `(ptree-main "foo" "bar" (one (two "three"))))
(check-equal? (ptree-root->ptree test-ptree-main)
`(ptree-main ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME "ptree-main"))) (bar ((,POLLEN_TREE_PARENT_NAME "ptree-main"))) (one ((,POLLEN_TREE_PARENT_NAME "ptree-main")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two"))))))))
;; contract for pmap-source-decode
(define/contract (valid-pmap-keys? x)
;; contract for ptree-source-decode
(define/contract (valid-pnodes? x)
(any/c . -> . boolean?)
(andmap (λ(x) (pmap-key? #:loud #t x)) (filter-not whitespace? (flatten x))))
(andmap (λ(x) (pnode? #:loud #t x)) (filter-not whitespace? (flatten x))))
;; contract for pmap-source-decode
(define/contract (unique-pmap-keys? x)
;; contract for ptree-source-decode
(define/contract (unique-pnodes? x)
(any/c . -> . boolean?)
;; use map ->string to make keys comparable
(elements-unique? #:loud #t (map ->string (filter-not whitespace? (flatten x)))))
(define/contract (pmap-source-decode . elements)
(() #:rest (and/c valid-pmap-keys? unique-pmap-keys?) . ->* . pmap?)
(pmap-root->pmap (decode (cons POLLEN_MAP_ROOT_NAME elements)
(define/contract (ptree-source-decode . elements)
(() #:rest (and/c valid-pnodes? unique-pnodes?) . ->* . ptree?)
(ptree-root->ptree (decode (cons POLLEN_TREE_ROOT_NAME elements)
#:xexpr-elements-proc (λ(xs) (filter-not whitespace? xs)))))

@ -0,0 +1,239 @@
#lang racket/base
(require xml xml/path racket/list racket/string racket/contract racket/match racket/set)
(require "tools.rkt" "world.rkt" "ptree-decode.rkt" "debug.rkt")
(module+ test (require rackunit))
(provide (all-defined-out))
;; function to set up the project-ptree.
;; this is to make life simpler when using tree navigation functions.
;; the current main.ptree of the project is used as the default input.
;; without this, you'd have to pass it over and over.
;; which is sort of the functional lifestyle,
;; but in templates, gets tiresome and error-prone.
(define/contract (make-project-ptree)
(-> ptree?)
(define ptree-source (build-path START_DIR DEFAULT_POLLEN_TREE))
(if (file-exists? ptree-source)
;; Load it from default path.
;; dynamic require of a ptree source file gets you a full ptree.
(begin
(message "Loading ptree file" (->string ptree-source))
(dynamic-require ptree-source POLLEN_ROOT))
;; ... or else synthesize it
(let* ([files (directory-list START_DIR)]
;; restrict files to those with pollen extensions
[files (map remove-ext (filter (λ(x) (has-ext? x POLLEN_SOURCE_EXT)) files))])
;; make a POLLEN_TREE_ROOT_NAME structure and convert it to a full ptree
(message "Generating ptree from file listing")
(ptree-root->ptree (cons POLLEN_TREE_ROOT_NAME (map path->string files))))))
(define project-ptree (make-project-ptree))
;; remove parents from tree (i.e., just remove attrs)
;; is not the inverse of add-parents, i.e., you do not get back your original input.
(define/contract (remove-parents mt)
(ptree? . -> . tagged-xexpr?)
(remove-attrs mt))
(module+ test
(check-equal? (remove-parents
`(ptree-main ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME ""))) (bar ((,POLLEN_TREE_PARENT_NAME ""))) (one ((,POLLEN_TREE_PARENT_NAME "")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two")))))))
'(ptree-main (foo) (bar) (one (two (three))))))
(module+ test
(let ([sample-main `(POLLEN_TREE_ROOT_NAME "foo" "bar" (one (two "three")))])
(check-equal? (ptree-root->ptree sample-main)
`(POLLEN_TREE_ROOT_NAME ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME"))) (bar ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME"))) (one ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two")))))))))
;; return the parent of a given name
(define/contract (parent node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c string? boolean?))
(and node (let ([result (se-path* `(,(->symbol node) #:parent) ptree)])
(and result (->string result))))) ; se-path* returns #f if nothing found
(module+ test
(define test-ptree-main `(ptree-main "foo" "bar" (one (two "three"))))
(define test-ptree (ptree-root->ptree test-ptree-main))
(check-equal? (parent 'three test-ptree) "two")
(check-equal? (parent "three" test-ptree) "two")
(check-false (parent 'nonexistent-name test-ptree)))
; get children of a particular node
(define/contract (children node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c list? boolean?))
;; se-path*/list returns '() if nothing found
(and node (let ([children (se-path*/list `(,(->symbol node)) ptree)])
; If there are sublists, just take first node
(and (not (empty? children)) (map (λ(i) (->string (if (list? i) (car i) i))) children)))))
(module+ test
(check-equal? (children 'one test-ptree) (list "two"))
(check-equal? (children 'two test-ptree) (list "three"))
(check-false (children 'three test-ptree))
(check-false (children 'fooburger test-ptree)))
;; find all siblings on current level: go up to parent and ask for children
(define/contract (siblings node [ptree project-ptree])
;; this never returns false: node is always a sibling of itself.
;; todo: how to use input value in contract? e.g., to check that node is part of output list
((pnode?) (ptree?) . ->* . (or/c list? boolean?))
(children (parent node ptree) ptree))
(module+ test
(check-equal? (siblings 'one test-ptree) '("foo" "bar" "one"))
(check-equal? (siblings 'foo test-ptree) '("foo" "bar" "one"))
(check-equal? (siblings 'two test-ptree) '("two"))
(check-false (siblings 'invalid-key test-ptree)))
(define/contract (siblings-split node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (values (or/c (listof pnode?) boolean?)
(or/c (listof pnode?) boolean?)))
(let-values ([(left right) (splitf-at (siblings node ptree)
(λ(e) (not (equal? (->string e) (->string node)))))])
(values (if (empty? left) #f left) (if (empty? (cdr right)) #f (cdr right)))))
(module+ test
(check-equal? (values->list (siblings-split 'one test-ptree)) '(("foo" "bar") #f))
(check-equal? (values->list (siblings-split 'bar test-ptree)) (list '("foo") '("one"))))
;; siblings to the left of target node (i.e., precede in tree order)
(define (siblings-left node [ptree project-ptree])
(let-values ([(left right) (siblings-split node ptree)])
left))
(module+ test
(check-equal? (siblings-left 'one test-ptree) '("foo" "bar"))
(check-false (siblings-left 'foo test-ptree)))
;; siblings to the right of target node (i.e., follow in tree order)
(define (siblings-right node [ptree project-ptree])
(let-values ([(left right) (siblings-split node ptree)])
right))
(module+ test
(check-false (siblings-right 'one test-ptree))
(check-equal? (siblings-right 'foo test-ptree) '("bar" "one")))
;; get node immediately to the left in tree
(define/contract (sibling-previous node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c string? boolean?))
(let ([siblings (siblings-left node ptree)])
(and siblings (last siblings))))
(module+ test
(check-equal? (sibling-previous 'bar test-ptree) "foo")
(check-false (sibling-previous 'foo test-ptree)))
;; get node immediately to the right in tree
(define/contract (sibling-next node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c string? boolean?))
(let ([siblings (siblings-right node ptree)])
(and siblings (first siblings))))
(module+ test
(check-equal? (sibling-next 'foo test-ptree) "bar")
(check-false (sibling-next 'one test-ptree)))
;; flatten tree to sequence
(define/contract (all-pages [ptree project-ptree])
(ptree? . -> . (listof string?))
; use cdr to get rid of root tag at front
(map ->string (cdr (flatten (remove-parents ptree)))))
(module+ test
(check-equal? (all-pages test-ptree) '("foo" "bar" "one" "two" "three")))
;; helper function for get-previous-pages and get-next-pages
(define/contract (adjacent-pages side node [ptree project-ptree])
((symbol? pnode?) (ptree?) . ->* . (or/c list? boolean?))
(let ([result ((if (equal? side 'left)
takef
takef-right) (all-pages ptree)
(λ(y) (not (equal? (->string node) (->string y)))))])
(and (not (empty? result)) result)))
(module+ test
(check-equal? (adjacent-pages 'left 'one test-ptree) '("foo" "bar"))
(check-equal? (adjacent-pages 'left 'three test-ptree) '("foo" "bar" "one" "two"))
(check-false (adjacent-pages 'left 'foo test-ptree)))
;; get sequence of earlier pages
(define/contract (previous-pages node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c list? boolean?))
(adjacent-pages 'left node ptree))
(module+ test
(check-equal? (previous-pages 'one test-ptree) '("foo" "bar"))
(check-equal? (previous-pages 'three test-ptree) '("foo" "bar" "one" "two"))
(check-false (previous-pages 'foo test-ptree)))
;; get sequence of next pages
(define (next-pages node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c list? boolean?))
(adjacent-pages 'right node ptree))
(module+ test
(check-equal? (next-pages 'foo test-ptree) '("bar" "one" "two" "three"))
(check-equal? (next-pages 'one test-ptree) '("two" "three"))
(check-false (next-pages 'three test-ptree)))
;; get page immediately previous
(define/contract (previous-page node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c string? boolean?))
(let ([result (previous-pages node ptree)])
(and result (last result))))
(module+ test
(check-equal? (previous-page 'one test-ptree) "bar")
(check-equal? (previous-page 'three test-ptree) "two")
(check-false (previous-page 'foo test-ptree)))
;; get page immediately next
(define (next-page node [ptree project-ptree])
((pnode?) (ptree?) . ->* . (or/c string? boolean?))
(let ([result (next-pages node ptree)])
(and result (first result))))
(module+ test
(check-equal? (next-page 'foo test-ptree) "bar")
(check-equal? (next-page 'one test-ptree) "two")
(check-false (next-page 'three test-ptree)))
;; convert path to pnode
;; used for converting "here" values to pnodes
(define/contract (here->pnode x)
(pathish? . -> . pnode?)
(->string (remove-all-ext (last (explode-path (->path x))))))
(module+ test
(check-equal? (here->pnode "bar") "bar")
(check-equal? (here->pnode "foo/bar") "bar")
(check-equal? (here->pnode "foo/bar.html") "bar")
(check-equal? (here->pnode "/Users/this/that/foo/bar.html.pp") "bar"))
;; convert key to URL
;; = key name + suffix of template (or suffix of default template)
;; todo: finish this function, right now it just appends html
;; this would also be useful for start page (showing correct url of generated pages)
(define/contract (pnode->url key)
(pnode? . -> . string?)
(string-append key ".html"))

@ -1,7 +1,7 @@
#lang racket/base
(require racket/list racket/path racket/port racket/system
racket/file racket/rerequire racket/contract racket/bool)
(require "world.rkt" "tools.rkt" "pmap.rkt" "readability.rkt" "template.rkt")
(require "world.rkt" "tools.rkt" "ptree.rkt" "readability.rkt" "template.rkt")
(module+ test (require rackunit))
@ -105,15 +105,16 @@
(() (#:force boolean?) #:rest (listof pathish?) . ->* . void?)
(define (&regenerate x)
(let ([path (->complete-path (->path x))])
; (message "Regenerating" (->string path))
(cond
;; this will catch pp (preprocessor) files
[(needs-preproc? path) (regenerate-with-preproc path #:force force)]
;; this will catch p files,
;; and files without extension that correspond to p files
[(needs-template? path) (regenerate-with-template path #:force force)]
;; this will catch pmap (pollen map) files
[(pmap-source? path) (let ([pmap (dynamic-require path 'main)])
(regenerate-with-pmap pmap #:force force))]
;; this will catch ptree files
[(ptree-source? path) (let ([ptree (dynamic-require path 'main)])
(regenerate-with-ptree ptree #:force force))]
[(equal? FALLBACK_TEMPLATE_NAME (->string (file-name-from-path path)))
(message "Regenerate: using fallback template")]
[(file-exists? path) 'pass-through]
@ -276,6 +277,8 @@
source-reloaded?)
(begin
(store-refresh-in-mod-dates source-path template-path)
(message "Rendering source" (->string source-path)
"with template" (->string template-path))
(let ([page-result (render-source-with-template source-path template-path)])
(display-to-file #:exists 'replace page-result output-path)
(regenerated-message (file-name-from-path output-path))))
@ -307,17 +310,17 @@
(namespace-require 'racket) ; use namespace-require for FIRST require, then eval after
;; for include-template (used below)
(eval '(require web-server/templates) (current-namespace))
;; for pmap navigation functions, and template commands
(eval '(require (planet mb/pollen/pmap)(planet mb/pollen/template)) (current-namespace))
;; for ptree navigation functions, and template commands
(eval '(require (planet mb/pollen/debug)(planet mb/pollen/ptree)(planet mb/pollen/template)) (current-namespace))
;; import source into eval space. This sets up main & metas
(eval `(require ,(path->string source-name)) (current-namespace))
(eval `(include-template #:command-char ,TEMPLATE_FIELD_DELIMITER ,(->string template-name)) (current-namespace))))
;; regenerate files listed in a pmap file
(define/contract (regenerate-with-pmap pmap #:force [force #f])
((pmap?) (#:force boolean?) . ->* . void?)
;; regenerate files listed in a ptree file
(define/contract (regenerate-with-ptree ptree #:force [force #f])
((ptree?) (#:force boolean?) . ->* . void?)
;; pass force parameter through
(for-each (λ(i) (regenerate i #:force force)) (all-pages pmap)))
(for-each (λ(i) (regenerate i #:force force)) (all-pages ptree)))

@ -79,11 +79,11 @@
;; First, generate some lists of files.
;; get lists of files by mapping a filter function for each file type
(define-values (pollen-files preproc-files pmap-files template-files)
(define-values (pollen-files preproc-files ptree-files template-files)
(let ([all-files-in-project-directory (directory-list pollen-file-root)])
(apply values
(map (λ(test) (filter test all-files-in-project-directory))
(list pollen-source? preproc-source? pmap-source? template-source?)))))
(list pollen-source? preproc-source? ptree-source? template-source?)))))
;; The actual post-preproc files may not have been generated yet
;; so calculate their names (rather than rely on directory list)
@ -123,14 +123,14 @@
`(td (a ((href ,target)) ,name))))
`(tr ,(make-link-cell 'direct) ,@(map make-link-cell routes)))
(if (andmap empty? (list pmap-files all-pollen-files all-preproc-files template-files))
(if (andmap empty? (list ptree-files all-pollen-files all-preproc-files template-files))
'(body "No files yet. Get to work!")
`(body
(style ((type "text/css")) "td a { display: block; width: 100%; height: 100%; padding: 8px; }"
"td:hover {background: #eee}")
(table ((style "font-family:Concourse T3;font-size:115%"))
;; options for pmap files and template files
,@(map (λ(file) (make-file-row file '(raw))) (append pmap-files template-files))
;; options for ptree files and template files
,@(map (λ(file) (make-file-row file '(raw))) (append ptree-files template-files))
;; options for pollen files
,@(map (λ(file) (make-file-row file '(raw source xexpr force))) post-pollen-files)
@ -151,5 +151,5 @@
(define request-url (request-uri req))
(define path (reroot-path (url->path request-url) pollen-file-root))
(define force (equal? (get-query-value request-url 'force) "true"))
(with-handlers ([exn:fail? (λ(e) (message "Default route ignoring" (url->string request-url)))])
(with-handlers ([exn:fail? (λ(e) (message "Default route ignoring" (url->string request-url) "because of error\n" (exn-message e)))])
(regenerate path #:force force)))

@ -1,14 +1,17 @@
#lang web-server
(require "start.rkt")
(require web-server/servlet-env)
(require web-server/dispatch web-server/dispatchers/dispatch)
(require xml)
(require "server-routes.rkt" "predicates.rkt" "debug.rkt")
(message "Pollen server starting...")
(message "Racket version" (version))
(message "Starting webserver")
(define (logger req)
(message (url->string (request-uri req)) "from" (request-client-ip req)))
(define client (request-client-ip req))
(when (equal? client "::1")
(set! client "localhost"))
(message "Request:" (url->string (request-uri req)) "from" client))
(define/contract (route-wrapper route-proc)
(procedure? . -> . procedure?)

@ -0,0 +1,4 @@
#lang racket/base
(require "debug.rkt")
(message "Starting Pollen")
(message "Using Racket" (version))

@ -1,5 +1,7 @@
#lang racket/base
(provide (all-defined-out))
(define POLLEN_PREPROC_EXT 'pp)
(define POLLEN_SOURCE_EXT 'p)
(define TEMPLATE_FILE_PREFIX "-")
@ -10,10 +12,10 @@
(define FALLBACK_TEMPLATE_NAME "-temp-fallback-template.html")
(define TEMPLATE_META_KEY "template")
(define POLLEN_MAP_EXT 'pmap)
(define DEFAULT_POLLEN_MAP "main.pmap")
(define POLLEN_MAP_PARENT_KEY 'parent)
(define POLLEN_MAP_ROOT_NAME 'pmap-root)
(define POLLEN_TREE_EXT 'ptree)
(define DEFAULT_POLLEN_TREE "main.ptree")
(define POLLEN_TREE_PARENT_NAME 'parent)
(define POLLEN_TREE_ROOT_NAME 'ptree-root)
(define MAIN_POLLEN_EXPORT 'main)
;(define META_POLLEN_TAG 'metas)
@ -28,14 +30,10 @@
(define OUTPUT_SUBDIR 'public)
(define RACKET_PATH "/Applications/Racket/bin/racket")
(define RACKET_PATH "/usr/bin/racket")
(define POLLEN_ROOT 'main)
; todo: this doesn't work as hoped
;(define-syntax POLLEN_ROOT_TAG
; (λ(stx) (datum->syntax stx 'main)))
; get the starting directory, which is the parent of 'run-file
(define START_DIR
(let-values ([(dir ignored also-ignored)
@ -45,4 +43,3 @@
dir)))
(provide (all-defined-out))
Loading…
Cancel
Save