1 Reactive Model
(require unlike-assets/reactive) | |
package: unlike-assets-lib |
The reactive model uses my kinda-ferpy library to track dependencies between assets and build them both lazily and asynchronously. This model is best for those who prefer functional programming and don’t want to deal with the underlying graph.
This module is experimental. Use the imperative model if you need a stable interface. Once this stabilizes, I suggest you prefer it over the imperative model.
1.1 Reactive API Reference
procedure
(u/a-build-system? p) → boolean?
p : any/c
procedure
(live-build? p) → boolean?
p : any/c
procedure
(start-live-build! key #:sample! sample! #:build! build! [ #:suppress? suppress?]) → live-build? key : string? sample! : (-> any/c) build! : (-> any/c any/c) suppress? : (-> any/c any/c any/c?) = eq?
When you apply start-live-build!, you also apply sample!, then build!. build! will run on its own thread.
Specifically, this procedure creates two stateful cells:
%S, which holds the last known value of (sample!).
%B, which is an async cell created using (make-stateful-cell/async #:dependencies (list %S) (lambda () (build! (%S)))).
Each time you apply LB, it will apply sample! and compare the returned value to %S using suppress?. If suppress? returns a false value, then %S will update to the new value and cause %B to update.
(LB) will return %B. You can depend on this cell to monitor when the build thread restarts.
Why have stop? at all? Because (%B) returns a procedure that waits for the build thread to finish. This means ((%B)) waits for the build output. If that build output is a procedure–which is normal when resolving dependencies–then getting to the final value means writing a comical expression with at least three bracket pairs: (((%B))). stop? allows you to search for a common representation of build output.
procedure
(make-u/a-build-system key->live-build [ known]) → u/a-build-system? key->live-build : (-> any/c u/a-build-system? live-build?) known : (and/c hash? (not/c immutable?)) = (make-hash)
(S) evaluates to known.
(S key) will return a live build with the given key. If the build does not already exist in known, then it will first be added using (key->live-build key S). Subsequent applications of S to key will return the same reference to the live build without consulting key->live-build. Notice that S can be invoked recursively in the body of key->live-build. This allows one live build to depend on others. Be warned that if a cycle forms, then the build will not terminate.
(S key stop?) behaves like (S key), but will also apply the live build procedure LB to stop? (See start-live-build!).
(S key stop? make-alias) is useful for clients that want to refer to a live build by the name of its product. If an asset "main.css.rkt" produces "f871a234.css", then (S "f871a234.css") will return the same live build that produced it. Note that this does NOT return the same version of the asset the name implies! It returns the living build and therefore only the latest version, even if the name is outdated.
1.2 Reactive Assets as Modules
Under the reactive model, assets are living hashes that act as restricted "modules". When combined with make-u/a-build-system, you can make procure procedures. Procure procedures load non-Racket resources as if they were dynamic Racket modules with reload support enabled.
procedure
(make-asset h) → procedure?
h : (and/c immutable? hash?)
syntax
(asset pair ...)
pair = [id expr]
(asset [media-type #"text/html"] [version '(1 3)])
procedure
(make-asset-contract #:allow-missing-keys? allow-missing-keys? pair) → flat-contract? allow-missing-keys? : any/c pair : (cons/c symbol? flat-contract?)
If allow-missing-keys? is a true value, then the contract will not break if a key is not defined in the underlying asset’s hash.
syntax
(asset/c pair ... maybe-optional-pairs)
pair = [id contract-expr] maybe-optional-pairs =
| #:optional | pair | ...
(or/c (make-asset-contract #:weak? #f (list (cons 'media-type bytes?) (cons 'writer (-> output-port? any)))) (make-asset-contract #:weak? #t (list (cons 'alias string?))))
(asset/c [media-type bytes?] [writer (-> output-port? any)] #:optional [alias string?])
procedure
(make-u/a-procure-procedure S) → (->* (string?) #:rest symbol?)
S : u/a-build-system?
(P key) is equivalent to (S key stateful-cell?). This is a weak procurement because it does not wait for the result of a build, but it does start the build in the background.
(P key sym) is equivalent to ((S key asset?) sym). This is a strong procurement because it starts a build, waits for the result V, and returns (V sym).
(P key sym . syms) is equivalent to (apply values (cons (P key sym) (map (lambda (s) (P key s)) syms))).
; Strong procurement (define html-formatted-markdown (P "index.md" 'html)) ; Weak procurement (define build-cell (P "index.md")) ; Srong procurement with multiple values. (define-values (html-formatted-markdown output-file) (P "index.md" 'html 'out-file))
1.3 Example: Living Values based on Files
(require racket/runtime-path) ; Base change detection on file modification seconds. (define (start-live-file-build! key build!) (start-live-build! key #:sample! (λ () (file-or-directory-modify-seconds key)) #:suppress? = #:build! build!)) (define (start-live-line-count key u/a) (start-live-file-build! key (λ (mtime) (length (file->lines key))))) (define (start-live-mtime key u/a) (start-live-file-build! key identity)) (define (key->live-build key u/a) (define start! (if (string-suffix? key ".txt") start-live-line-count start-live-mtime)) (start! key u/a)) (define sys (make-u/a-build-system key->live-build)) ; This will return the current modification time of dummy.bin. (sys "dummy.bin" number?) ; This will return the current number of lines in passage.txt. (sys "passage.txt" number?)