Liking cljdoc? Tell your friends :D

co.multiply.tropical.storage

State the browser owns and keeps in localStorage, such as a draft the user is writing: a value of any size, which outlasts closing what shows it, a page load, and the server restarting or being deployed again, in the one browser, while the server stores none of it.

defstorage declares a state, with its default and how its values become JSON and come back. Any island takes it up with use-storage, with an item for one per item, such as a suggestion's id, and a default of its own if the item has one:

(defstorage edits
  "Edits to a suggestion, before the user acts on it."
  {:default {} :valid? map?})

(defisland rework
  [{:keys [id text]}]
  (let [draft (use-storage edits id {:default {"text" text}})
        start (use-action :start
                (fn [_ value]
                  (when-some [{t "text"} (decode edits value)]
                    (start! id t))
                  (forget [[edits id]]))
                {:value (ui/signal draft)})]
    [:form
     [:textarea {:data-bind (ui/path draft "text")}]
     [:button {:type "button" :data-on:click start} "Start"]]))

How it holds:

  • The browser keeps each value in a Datastar signal under _storage, local to the page, so no action posts it unless one passes it as its value. An island that uses a state declares the signal on its root, only if missing: as the browser's entry has it, or as the default. A later render, or the island mounting again, leaves what the user did.
  • The element the app mounts in carries ui/page-attrs (ring/mount adds it), whose handler writes each change into the browser's entry, while the value differs from the default the island declared it with: only an edit is kept, and a default the server revised shows through where there is none.
  • The server sees a value only when an action passes it, or through observe. The first frame renders the default, and the browser fills in its own as its script starts, through the elements' bindings (data-bind, data-text, ui/attr).
  • An entry is the user's: its key holds a digest of the session's user, and a page loaded for another drops it. A page also drops entries older than their state's :max-age-ms, stored under another :version, or of a state no longer declared. When the browser's storage is full, the oldest entries go first.

Values are JSON, since the browser changes them itself, in expressions and bindings. :encode turns a Clojure value into one, and :decode turns one back; a state checks, as it is declared, that its default comes back as it went. A value only ever round-tripped, which the browser never reads, can be a string, such as Transit, at the cost of binding to it.

An action sets entries with patch, and drops them with forget, in the browser that made it, whether or not anything on the page uses them.

State the browser owns and keeps in `localStorage`, such as a draft the user
is writing: a value of any size, which outlasts closing what shows it, a page
load, and the server restarting or being deployed again, in the one browser,
while the server stores none of it.

`defstorage` declares a state, with its default and how its values become
JSON and come back. Any island takes it up with `use-storage`, with an item
for one per item, such as a suggestion's id, and a default of its own if the
item has one:

    (defstorage edits
      "Edits to a suggestion, before the user acts on it."
      {:default {} :valid? map?})

    (defisland rework
      [{:keys [id text]}]
      (let [draft (use-storage edits id {:default {"text" text}})
            start (use-action :start
                    (fn [_ value]
                      (when-some [{t "text"} (decode edits value)]
                        (start! id t))
                      (forget [[edits id]]))
                    {:value (ui/signal draft)})]
        [:form
         [:textarea {:data-bind (ui/path draft "text")}]
         [:button {:type "button" :data-on:click start} "Start"]]))

How it holds:

- The browser keeps each value in a Datastar signal under `_storage`, local
  to the page, so no action posts it unless one passes it as its value. An
  island that uses a state declares the signal on its root, only if missing:
  as the browser's entry has it, or as the default. A later render, or the
  island mounting again, leaves what the user did.
- The element the app mounts in carries `ui/page-attrs` (`ring/mount` adds
  it), whose handler writes each change into the browser's entry, while the
  value differs from the default the island declared it with: only an edit
  is kept, and a default the server revised shows through where there is
  none.
- The server sees a value only when an action passes it, or through
  `observe`. The first frame renders the default, and the browser fills
  in its own as its script starts, through the elements' bindings
  (`data-bind`, `data-text`, `ui/attr`).
- An entry is the user's: its key holds a digest of the session's user, and
  a page loaded for another drops it. A page also drops entries older than
  their state's `:max-age-ms`, stored under another `:version`, or of a
  state no longer declared. When the browser's storage is full, the oldest
  entries go first.

Values are JSON, since the browser changes them itself, in expressions and
bindings. `:encode` turns a Clojure value into one, and `:decode` turns one
back; a state checks, as it is declared, that its default comes back as it
went. A value only ever round-tripped, which the browser never reads, can
be a string, such as Transit, at the cost of binding to it.

An action sets entries with `patch`, and drops them with `forget`, in the
browser that made it, whether or not anything on the page uses them.
raw docstring

decodeclj

(decode state value)

value, the JSON of one of state's values as an action got it, as the state's value: decoded, and nil unless it is valid. It came from the browser, so it is untrusted, as signals are.

`value`, the JSON of one of `state`'s values as an action got it, as the
state's value: decoded, and nil unless it is valid. It came from the
browser, so it is untrusted, as signals are.
sourceraw docstring

defstoragecljmacro

(defstorage state-name & decl)

Defines a state kept in the browser's localStorage: (defstorage name doc? opts). opts:

  • :default what the state is until the user changes it; an island may give one of its own (use-storage).
  • :valid? what a value from the browser must be, once decoded. Defaults to some?.
  • :encode a fn of a value to JSON: maps, vectors, strings, numbers and booleans, with no nil. Defaults to identity.
  • :decode a fn of JSON, as parsed, with string keys, back to a value. Defaults to identity.
  • :version an integer or a string. Change it when the shape of the values changes: entries stored under another are dropped. Defaults to 1.
  • :max-age-ms how long an entry is kept since it was last written. Defaults to max-age-ms.
Defines a state kept in the browser's `localStorage`:
`(defstorage name doc? opts)`. `opts`:

- `:default`    what the state is until the user changes it; an island may
                give one of its own (`use-storage`).
- `:valid?`     what a value from the browser must be, once decoded. Defaults
                to `some?`.
- `:encode`     a fn of a value to JSON: maps, vectors, strings, numbers and
                booleans, with no nil. Defaults to `identity`.
- `:decode`     a fn of JSON, as parsed, with string keys, back to a value.
                Defaults to `identity`.
- `:version`    an integer or a string. Change it when the shape of the
                values changes: entries stored under another are dropped.
                Defaults to 1.
- `:max-age-ms` how long an entry is kept since it was last written.
                Defaults to `max-age-ms`.
sourceraw docstring

forgetclj

(forget entries)

The signals that drop entries, for an action's handler to return, merged with its own. Each of entries is [state item], or [state] for a state used without an item:

(fn [_ value] (start! value) (forget [[edits id]]))

The browser that made the action drops them, whether or not the page uses them, and an island using one goes back to the default it declared.

The signals that drop entries, for an action's handler to return, merged
with its own. Each of `entries` is `[state item]`, or `[state]` for a state
used without an item:

    (fn [_ value] (start! value) (forget [[edits id]]))

The browser that made the action drops them, whether or not the page uses
them, and an island using one goes back to the default it declared.
sourceraw docstring

max-age-msclj

How long an entry is kept since it was last written, unless its state says: 30 days.

How long an entry is kept since it was last written, unless its state says:
30 days.
sourceraw docstring

observeclj

(observe handle)

The browser's value of handle's entry, as use-storage gives the handle, for the server to render from: pending until the browser has sent it, then the latest, decoded. The island renders again when it changes. A value that isn't valid reads as the handle's default.

(let [draft (use-storage edits id {:default {"text" text}})
      v     (observe draft)]
  [:p (if (= pending v) "…" (str (word-count (get v "text")) " words"))])

The browser sends it once the page has the island, so the first frame never has it, and doesn't wait for it, then each change, a moment after the user stops typing, for as long as the island reads it. Where the page shows the value through bindings, it needs no reading: read it for what only the server can render from it, such as a row per item, or a count.

The browser's value of `handle`'s entry, as `use-storage` gives the handle,
for the server to render from: `pending` until the browser has sent it,
then the latest, decoded. The island renders again when it changes. A
value that isn't valid reads as the handle's default.

    (let [draft (use-storage edits id {:default {"text" text}})
          v     (observe draft)]
      [:p (if (= pending v) "…" (str (word-count (get v "text")) " words"))])

The browser sends it once the page has the island, so the first frame
never has it, and doesn't wait for it, then each change, a moment after the
user stops typing, for as long as the island reads it. Where the page shows
the value through bindings, it needs no reading: read it for what only the
server can render from it, such as a row per item, or a count.
sourceraw docstring

patchclj

(patch entries)

The signals that set entries, for an action's handler to return, merged with its own. Each of entries is [state item value], or [state value] for a state used without an item. The browser that made the action keeps them, whether or not the page uses them, and islands using them follow.

A map is merged into the browser's value, as any signal patch is: a key the patch leaves out stays as it was. A value that isn't valid for its state, or can't be kept as JSON, throws.

The signals that set entries, for an action's handler to return, merged
with its own. Each of `entries` is `[state item value]`, or `[state value]`
for a state used without an item. The browser that made the action keeps
them, whether or not the page uses them, and islands using them follow.

A map is merged into the browser's value, as any signal patch is: a key the
patch leaves out stays as it was. A value that isn't valid for its state, or
can't be kept as JSON, throws.
sourceraw docstring

pendingclj

What observe returns until the browser has sent its value: the same as co.multiply.tropical.hooks/pending.

What `observe` returns until the browser has sent its value: the same as
`co.multiply.tropical.hooks/pending`.
sourceraw docstring

use-storageclj

(use-storage state)
(use-storage state item)
(use-storage state item opts)

Takes up state in this island, or the one for item, such as a suggestion's id: declares its signal on the island's root, unless the page has it, from the browser's entry, or the default if there is none. Returns a handle for the helpers in co.multiply.tropical.ui and for observe. A hook: call it during the render.

With {:default v}, the island gives a default of its own, for a state whose starting point is the item's, such as the text of a suggestion. The browser keeps an entry only while its value differs from the default it was declared with.

The server doesn't know the browser's value. What the first frame shows of it is the default, which the browser replaces as its script starts: bind what shows it (data-bind, data-text, ui/attr). For the server to render from it, read it with observe.

Takes up `state` in this island, or the one for `item`, such as a
suggestion's id: declares its signal on the island's root, unless the page
has it, from the browser's entry, or the default if there is none. Returns a
handle for the helpers in `co.multiply.tropical.ui` and for `observe`. A
hook: call it during the render.

With `{:default v}`, the island gives a default of its own, for a state whose
starting point is the item's, such as the text of a suggestion. The browser
keeps an entry only while its value differs from the default it was
declared with.

The server doesn't know the browser's value. What the first frame shows of
it is the default, which the browser replaces as its script starts: bind
what shows it (`data-bind`, `data-text`, `ui/attr`). For the server to render
from it, read it with `observe`.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close