fix reloading behavior (closes #64)

pull/84/head
Matthew Butterick 9 years ago
parent c62bdbbe72
commit 61d7b222bd

@ -1,11 +1,11 @@
#lang racket/base
(require racket/path racket/file file/cache sugar/coerce "project.rkt" "world.rkt" racket/rerequire "debug.rkt")
(require racket/file file/cache sugar/coerce "project.rkt" "world.rkt" racket/rerequire)
;; The cache is a hash with paths as keys.
;; The cache values are also hashes, with key/value pairs for that path.
(provide reset-cache cached-require paths->key path->hash)
(provide (all-from-out racket/rerequire))
(provide reset-cache cached-require paths->key)
(define (get-cache-dir)
(build-path (world:current-project-root) (world:current-cache-dir-name)))
@ -18,19 +18,29 @@
(define (paths->key source-path [template-path #f])
;; key is list of file + mod-time pairs
(define path-strings (map (compose1 ->string ->complete-path)
(append (list source-path)
(if template-path (list template-path) null)
(or (get-directory-require-files source-path) null))))
(append (list source-path)
(if template-path (list template-path) null)
(or (get-directory-require-files source-path) null))))
(map cons path-strings (map file-or-directory-modify-seconds path-strings)))
(define (update-directory-requires source-path)
(define directory-require-files (get-directory-require-files source-path))
(and directory-require-files (map dynamic-rerequire directory-require-files))
(void))
(define (path->hash path)
(dynamic-rerequire path)
(hash (world:current-main-export) (dynamic-require path (world:current-main-export))
(world:current-meta-export) (dynamic-require path (world:current-meta-export))))
;; new namespace forces dynamic-require to re-instantiate 'path'
;; otherwise it gets cached in current namespace.
(parameterize ([current-namespace (make-base-namespace)])
(hash (world:current-main-export) (dynamic-require path (world:current-main-export))
(world:current-meta-export) (dynamic-require path (world:current-meta-export)))))
(define (cached-require path-string subkey)
(define (cached-require path-string subkey)
(define path (with-handlers ([exn:fail? (λ _ (error 'cached-require (format "~a is not a valid path" path-string)))])
(->complete-path path-string)))
@ -40,11 +50,13 @@
(cond
[(world:current-compile-cache-active)
(define pickup-file (build-path (get-cache-dir) "pickup.rktd"))
(cache-file pickup-file #:exists-ok? #t
(paths->key path)
(get-cache-dir)
(λ _ (write-to-file (path->hash path) pickup-file #:exists 'replace))
#:max-cache-size (world:current-compile-cache-max-size))
(let ([key (paths->key path)])
(cache-file pickup-file
#:exists-ok? #t
key
(get-cache-dir)
(λ _ (write-to-file (path->hash path) pickup-file #:exists 'replace))
#:max-cache-size (world:current-compile-cache-max-size)))
(hash-ref (file->value pickup-file) subkey)]
[else ; cache inactive
(dynamic-require path subkey)]))
[else (parameterize ([current-namespace (make-base-namespace)])
(dynamic-require path subkey))]))

@ -6,5 +6,5 @@
(define update-implies '("txexpr" "sugar"))
(define scribblings '(("scribblings/pollen.scrbl" (multi-page))))
(define raco-commands '(("pollen" (submod pollen/command raco) "issue Pollen command" #f)))
(define compile-omit-paths '("tests" "tools"))
(define test-omit-paths '("tests/data" "tools"))
(define compile-omit-paths '("test" "tools"))
(define test-omit-paths '("test/data" "tools"))

@ -1,5 +1,5 @@
#lang racket/base
(require racket/file racket/path racket/match)
(require racket/file racket/rerequire racket/path racket/match)
(require sugar/coerce sugar/test sugar/define sugar/container sugar/file sugar/len)
(require "file.rkt" "cache.rkt" "world.rkt" "debug.rkt" "pagetree.rkt" "project.rkt" "template.rkt")

@ -11,40 +11,22 @@
The slowest part of a @racket[render] is parsing and decoding the source file. Often, previewing a single source file necessarily means decoding others (for instance templates, or other source files that are linked into the main source file). But usually, only one source file is changing at a time. Therefore, Pollen stores copies of the exports of source files — namely, whatever is stored in @code[(format "~a" world:main-export)] and @code[(format "~a" world:meta-export)] — in the cache so they can be reused.
@defparam[current-cache hash hash?]{A parameter that refers to the current cache. It is initialized with @racket[make-cache].
The cache is a hash table that uses the complete path of a source file as its keys. The value associated with each of these keys is a subcache — another hash table with keys @racket['doc], @racket['metas] (for storing the exports of the source file) and @racket['mod-time] (for storing the modification time, provided by @racket[file-or-directory-modify-seconds]).}
@defproc[
(cached-require
[source-path pathish?]
[key (or/c 'doc 'metas 'mod-time)])
[key (or/c 'doc 'metas)])
(or/c txexpr? hash? integer?)]
Similar to @racket[(dynamic-require _source-path _key)], except that it first tries to retrieve the requested value out of @racket[current-cache]. If it's not there, or out of date, @racket[dynamic-require] is used to update the value.
Similar to @racket[(dynamic-require _source-path _key)], except that it first tries to retrieve the requested value out of the cache. If it's not there, or out of date, @racket[dynamic-require] is used to update the value.
The only keys supported are @racket['doc], @racket['metas], and @racket['mod-time].
The only keys supported are @racket['doc] and @racket['metas].
If you want the speed benefit of the cache, you should @bold{always} use @racket[cached-require] to get data from Pollen source files. That doesn't mean you can't still use functions like @racket[require], @racket[local-require], and @racket[dynamic-require]. They'll just be slower.
@defproc[
(make-cache)
hash?]
Initializes @racket[current-cache].
@defproc[
(reset-cache)
void?]
Clears @racket[current-cache]. When only the nuclear option will do.
Clears the cache. When only the nuclear option will do.
@defproc[
(cache-ref
[source-path pathish?])
hash?]
Returns the subcache associated with the key @racket[_source-path], which will itself be a hash table. See @racket[current-cache].

@ -0,0 +1,3 @@
#lang racket/base
(provide id)
(define (id) "second")

@ -0,0 +1,2 @@
#lang pollen
(id)

@ -0,0 +1 @@
◊(format "~a" doc)

@ -0,0 +1,42 @@
#lang at-exp racket/base
(require rackunit racket/runtime-path pollen/render racket/file racket/system)
;; define-runtime-path only allowed at top level
(define-runtime-path rerequire-dir "data/rerequire")
(define-runtime-path directory-require.rkt "data/rerequire/directory-require.rkt")
(define-runtime-path pre.txt.pp "data/rerequire/pre.txt.pp")
(define-runtime-path pre.txt "data/rerequire/pre.txt")
(define-runtime-path template.txt "data/rerequire/template.txt")
(define-runtime-path markup.txt.pm "data/rerequire/markup.txt.pm")
(define-runtime-path markup.txt "data/rerequire/markup.txt")
(copy-file markup.txt.pm pre.txt.pp #t)
;; test makes sure that file render changes after directory-require changes
(parameterize ([current-output-port (open-output-string)])
(display-to-file @string-append{#lang racket/base
(provide id)
(define (id) "first")} directory-require.rkt #:exists 'replace)
(render-to-file-if-needed markup.txt.pm)
(check-equal? (file->string markup.txt) "rootfirst")
(render-to-file-if-needed pre.txt.pp)
(check-equal? (file->string pre.txt) "first")
(sleep 1)
(display-to-file @string-append{#lang racket/base
(provide id)
(define (id) "second")} directory-require.rkt #:exists 'replace)
(render-to-file-if-needed markup.txt.pm)
(check-equal? (file->string markup.txt) "rootsecond")
(render-to-file-if-needed pre.txt.pp)
(check-equal? (file->string pre.txt) "second"))
(delete-file pre.txt.pp)
(delete-file pre.txt)
(delete-file markup.txt)
(delete-directory/files (build-path (current-directory) "pollen-cache"))
Loading…
Cancel
Save