From 53e8eddda7ef62fcc8ead54f1606bc6218d54ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Franc=CC=A7ois=20Pe=CC=81russe?= Date: Fri, 13 Feb 2026 13:51:05 -0500 Subject: [PATCH 1/4] Documentation for the upcoming storage API --- docs/source/api/storage.rst | 193 ++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 194 insertions(+) create mode 100644 docs/source/api/storage.rst diff --git a/docs/source/api/storage.rst b/docs/source/api/storage.rst new file mode 100644 index 0000000..cea8a75 --- /dev/null +++ b/docs/source/api/storage.rst @@ -0,0 +1,193 @@ +storage +======= + +Simple key/value storage backed by JSON and the built-in ``string.read`` / ``string.save`` functions. The default storage is per-project (stored under ``asset``). A device-wide store is available under ``storage.global`` (stored under ``asset.documents``). + +All writes are immediate. Keys must be strings, and values must be JSON-encodable (``nil``, ``boolean``, ``number``, ``string``, ``table``). + +.. lua:module:: storage + +.. lua:function:: get(key [, default]) + + Gets a value for a key, returning ``default`` if the key does not exist. + + :param key: Key to read + :type key: string + :param default: Value to return when key is missing + :type default: any + :return: Stored value or default + :rtype: any + + .. code-block:: lua + + -- Default project storage + storage:set("highScore", 9001) + print(storage:get("highScore", 0)) + + -- Missing key uses default + print(storage:get("missing", 42)) + +.. lua:function:: set(key, value) + + Sets a key to a value. Passing ``nil`` deletes the key. + + :param key: Key to write + :type key: string + :param value: Value to store (JSON-encodable) + :type value: any + :return: True on success, false on failure + :rtype: boolean + + .. code-block:: lua + + storage:set("musicEnabled", true) + storage:set("volume", 0.8) + storage:set("temp", nil) -- deletes the key + +.. lua:function:: has(key) + + Checks if a key exists. + + :param key: Key to check + :type key: string + :return: True if the key exists, false otherwise + :rtype: boolean + + .. code-block:: lua + + if storage:has("playerName") then + print("Player name saved") + end + +.. lua:function:: delete(key) + + Removes a key. This is a synonym for ``set(key, nil)``. + + :param key: Key to remove + :type key: string + :return: True on success, false on failure + :rtype: boolean + + .. code-block:: lua + + storage:delete("tutorialSeen") + +.. lua:function:: keys() + + Returns a sorted array of all keys in the store. + + :return: Sorted list of keys + :rtype: table + + .. code-block:: lua + + for _, key in ipairs(storage:keys()) do + print(key) + end + +.. lua:function:: clear() + + Removes all keys from the store. + + :return: True on success, false on failure + :rtype: boolean + + .. code-block:: lua + + storage:clear() + +.. lua:function:: key() + + Returns the underlying asset key for the storage file. + + :return: Asset key used for the store file + :rtype: assetKey + + .. code-block:: lua + + print(storage:key()) + +.. lua:function:: namespace(prefix) + + Returns a view of the store that prefixes all keys with ``prefix``. A ``.`` is added automatically if missing. + + :param prefix: Namespace prefix (for example, ``"prefs"``) + :type prefix: string + :return: Namespaced storage view + :rtype: table + + .. code-block:: lua + + local prefs = storage:namespace("prefs") + prefs:set("audioMuted", true) -- stored as "prefs.audioMuted" + print(prefs:get("audioMuted")) + +.. lua:attribute:: storage.project: storage + + Default project-scoped storage (stored under ``asset``). This is the same store used by the top-level ``storage`` functions. + +.. lua:attribute:: storage.global: storage + + Default device-wide storage (stored under ``asset.documents``). + +.. lua:function:: store(name [, opts]) + + Returns a named store. Stores are cached by name and scope, so repeated calls return the same store instance. + + :param name: Store name (used to derive the file name) + :type name: string + :param opts: Options table + :type opts: table + :return: Storage instance + :rtype: table + + **Options** + + * ``scope``: ``"project"`` (default) or ``"global"`` + + .. code-block:: lua + + local settings = storage.store("settings") + local globalPrefs = storage.store("prefs", { scope = "global" }) + +.. lua:function:: projectStore(name) + + Convenience wrapper for ``storage.store(name, { scope = "project" })``. + + :param name: Store name + :type name: string + :return: Storage instance + :rtype: table + +.. lua:function:: globalStore(name) + + Convenience wrapper for ``storage.store(name, { scope = "global" })``. + + :param name: Store name + :type name: string + :return: Storage instance + :rtype: table + +**Usage Patterns** + +.. code-block:: lua + + -- Separate storage for different systems + local prefs = storage:namespace("prefs") + local stats = storage:namespace("stats") + + prefs:set("musicEnabled", true) + stats:set("gamesPlayed", 12) + + -- Project vs global + local projectStore = storage.project + local globalStore = storage.global + + projectStore:set("level", 3) + globalStore:set("unlocked", { "sword", "shield" }) + +**Notes** + +* Keys must be strings +* Values must be JSON-encodable +* Writes are immediate diff --git a/docs/source/index.rst b/docs/source/index.rst index b3297ab..09962f2 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -72,6 +72,7 @@ Codea 4 api/pick api/viewer api/device + api/storage Indices and tables ================== From d52f0a024fc33a2cdebaf5ed315fc1cebde65274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Franc=CC=A7ois=20Pe=CC=81russe?= Date: Fri, 13 Feb 2026 15:33:52 -0500 Subject: [PATCH 2/4] Async save updates. --- docs/source/api/storage.rst | 24 ++++++++++++------------ docs/source/api/string.rst | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/docs/source/api/storage.rst b/docs/source/api/storage.rst index cea8a75..4ad7054 100644 --- a/docs/source/api/storage.rst +++ b/docs/source/api/storage.rst @@ -1,9 +1,9 @@ storage ======= -Simple key/value storage backed by JSON and the built-in ``string.read`` / ``string.save`` functions. The default storage is per-project (stored under ``asset``). A device-wide store is available under ``storage.global`` (stored under ``asset.documents``). +Simple key/value storage backed by JSON and the built-in ``string.read`` / ``string.saveAsync`` functions. The default storage is per-project (stored under ``asset``). A device-wide store is available under ``storage.global`` (stored under ``asset.documents``). -All writes are immediate. Keys must be strings, and values must be JSON-encodable (``nil``, ``boolean``, ``number``, ``string``, ``table``). +Writes are asynchronous by default. Use ``storage:flush()`` when you need to ensure data is persisted immediately. Keys must be strings, and values must be JSON-encodable (``nil``, ``boolean``, ``number``, ``string``, ``table``). .. lua:module:: storage @@ -35,9 +35,6 @@ All writes are immediate. Keys must be strings, and values must be JSON-encodabl :type key: string :param value: Value to store (JSON-encodable) :type value: any - :return: True on success, false on failure - :rtype: boolean - .. code-block:: lua storage:set("musicEnabled", true) @@ -65,9 +62,6 @@ All writes are immediate. Keys must be strings, and values must be JSON-encodabl :param key: Key to remove :type key: string - :return: True on success, false on failure - :rtype: boolean - .. code-block:: lua storage:delete("tutorialSeen") @@ -89,13 +83,19 @@ All writes are immediate. Keys must be strings, and values must be JSON-encodabl Removes all keys from the store. - :return: True on success, false on failure - :rtype: boolean - .. code-block:: lua storage:clear() +.. lua:function:: flush() + + Writes any pending changes synchronously. + + .. code-block:: lua + + storage:set("level", 3) + storage:flush() + .. lua:function:: key() Returns the underlying asset key for the storage file. @@ -190,4 +190,4 @@ All writes are immediate. Keys must be strings, and values must be JSON-encodabl * Keys must be strings * Values must be JSON-encodable -* Writes are immediate +* Writes are asynchronous; call ``storage:flush()`` to force persistence diff --git a/docs/source/api/string.rst b/docs/source/api/string.rst index 76cbb8c..66d8ea7 100644 --- a/docs/source/api/string.rst +++ b/docs/source/api/string.rst @@ -36,4 +36,24 @@ Used to interact with text files. .. helptext:: save the text to a file :param assetKey assetKey: The asset key of the file. - :param string text: The text to save. \ No newline at end of file + :param string text: The text to save. + +.. lua:function:: saveAsync(assetKey, text [, callback]) + + Saves text to a file asynchronously. Multiple calls to the same file are coalesced and only the latest content is written. + + The optional callback is called from the main thread with ``(ok, err)``. ``err`` is ``nil`` on success. + + .. helptext:: save the text to a file asynchronously + + :param assetKey assetKey: The asset key of the file. + :param string text: The text to save. + :param function callback: Optional completion callback ``function(ok, err)``. + + .. code-block:: lua + + string.saveAsync(asset.documents .. "Config.json", json.encode(config), function(ok, err) + if not ok then + print("Save failed:", err) + end + end) From 9dee2b47db96eee59d6ccc2bad7b40997072bdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Franc=CC=A7ois=20Pe=CC=81russe?= Date: Fri, 13 Feb 2026 15:52:38 -0500 Subject: [PATCH 3/4] Add missing line --- docs/source/api/storage.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/api/storage.rst b/docs/source/api/storage.rst index 4ad7054..ed53f95 100644 --- a/docs/source/api/storage.rst +++ b/docs/source/api/storage.rst @@ -35,6 +35,7 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens :type key: string :param value: Value to store (JSON-encodable) :type value: any + .. code-block:: lua storage:set("musicEnabled", true) @@ -62,6 +63,7 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens :param key: Key to remove :type key: string + .. code-block:: lua storage:delete("tutorialSeen") From b98f0c2b41d7c4c50fde340a9bae67a2b3d473a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Franc=CC=A7ois=20Pe=CC=81russe?= Date: Sun, 15 Feb 2026 10:29:00 -0500 Subject: [PATCH 4/4] update docs following changes to the api --- docs/source/api/storage.rst | 107 +++++++++++++++--------------------- docs/source/api/string.rst | 17 ++---- 2 files changed, 49 insertions(+), 75 deletions(-) diff --git a/docs/source/api/storage.rst b/docs/source/api/storage.rst index ed53f95..700b936 100644 --- a/docs/source/api/storage.rst +++ b/docs/source/api/storage.rst @@ -1,46 +1,31 @@ storage ======= -Simple key/value storage backed by JSON and the built-in ``string.read`` / ``string.saveAsync`` functions. The default storage is per-project (stored under ``asset``). A device-wide store is available under ``storage.global`` (stored under ``asset.documents``). +Simple key/value storage. -Writes are asynchronous by default. Use ``storage:flush()`` when you need to ensure data is persisted immediately. Keys must be strings, and values must be JSON-encodable (``nil``, ``boolean``, ``number``, ``string``, ``table``). +Saving happens in the background, so your project can keep running smoothly. If you need to make sure your changes are saved right away (for example, before quitting or switching levels), call storage:flush(). -.. lua:module:: storage - -.. lua:function:: get(key [, default]) +Storage uses string keys (like "highScore"), and the value you save must be either boolean, number, string, or a table containing only those kinds of values. If you store nil for a given key, then that key/value pair will be removed from storage. - Gets a value for a key, returning ``default`` if the key does not exist. +You can read and write values directly using property-style access (``storage.highScore = 100`` and ``print(storage.highScore)``). - :param key: Key to read - :type key: string - :param default: Value to return when key is missing - :type default: any - :return: Stored value or default - :rtype: any +The default storage is per-project. A device-wide store is available under ``storage.global``. - .. code-block:: lua +.. lua:module:: storage - -- Default project storage - storage:set("highScore", 9001) - print(storage:get("highScore", 0)) +**Basic Usage** - -- Missing key uses default - print(storage:get("missing", 42)) +.. code-block:: lua -.. lua:function:: set(key, value) + -- Default project storage + storage.highScore = 9001 + print(storage.highScore or 0) - Sets a key to a value. Passing ``nil`` deletes the key. + -- Missing key uses default + print(storage.missing or 42) - :param key: Key to write - :type key: string - :param value: Value to store (JSON-encodable) - :type value: any - - .. code-block:: lua - - storage:set("musicEnabled", true) - storage:set("volume", 0.8) - storage:set("temp", nil) -- deletes the key + -- Remove a key + storage.temp = nil .. lua:function:: has(key) @@ -59,11 +44,11 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens .. lua:function:: delete(key) - Removes a key. This is a synonym for ``set(key, nil)``. + Removes a key. :param key: Key to remove :type key: string - + .. code-block:: lua storage:delete("tutorialSeen") @@ -95,10 +80,10 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens .. code-block:: lua - storage:set("level", 3) + storage.level = 3 storage:flush() -.. lua:function:: key() +.. lua:function:: asset() Returns the underlying asset key for the storage file. @@ -107,7 +92,7 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens .. code-block:: lua - print(storage:key()) + print(storage:asset()) .. lua:function:: namespace(prefix) @@ -121,8 +106,8 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens .. code-block:: lua local prefs = storage:namespace("prefs") - prefs:set("audioMuted", true) -- stored as "prefs.audioMuted" - print(prefs:get("audioMuted")) + prefs.audioMuted = true -- stored as "prefs.audioMuted" + print(prefs.audioMuted) .. lua:attribute:: storage.project: storage @@ -132,44 +117,42 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens Default device-wide storage (stored under ``asset.documents``). -.. lua:function:: store(name [, opts]) +.. lua:function:: projectStore(name) - Returns a named store. Stores are cached by name and scope, so repeated calls return the same store instance. + Convenience wrapper for ``storage(name, "project")``. - :param name: Store name (used to derive the file name) + :param name: Store name :type name: string - :param opts: Options table - :type opts: table :return: Storage instance :rtype: table - **Options** - - * ``scope``: ``"project"`` (default) or ``"global"`` - - .. code-block:: lua - - local settings = storage.store("settings") - local globalPrefs = storage.store("prefs", { scope = "global" }) - -.. lua:function:: projectStore(name) +.. lua:function:: globalStore(name) - Convenience wrapper for ``storage.store(name, { scope = "project" })``. + Convenience wrapper for ``storage(name, "global")``. :param name: Store name :type name: string :return: Storage instance :rtype: table -.. lua:function:: globalStore(name) +.. lua:currentmodule:: None - Convenience wrapper for ``storage.store(name, { scope = "global" })``. +.. lua:function:: storage(name [, scope]) - :param name: Store name + Returns a named store. Stores are cached by name and scope, so repeated calls return the same store instance. + + :param name: Store name (used to derive the file name) :type name: string + :param scope: Store scope (``"project"`` or ``"global"``) + :type scope: string :return: Storage instance :rtype: table + .. code-block:: lua + + local settings = storage("settings") + local globalPrefs = storage("prefs", "global") + **Usage Patterns** .. code-block:: lua @@ -178,18 +161,18 @@ Writes are asynchronous by default. Use ``storage:flush()`` when you need to ens local prefs = storage:namespace("prefs") local stats = storage:namespace("stats") - prefs:set("musicEnabled", true) - stats:set("gamesPlayed", 12) + prefs.musicEnabled = true + stats.gamesPlayed = 12 -- Project vs global local projectStore = storage.project local globalStore = storage.global - projectStore:set("level", 3) - globalStore:set("unlocked", { "sword", "shield" }) + projectStore.level = 3 + globalStore.unlocked = { "sword", "shield" } **Notes** * Keys must be strings -* Values must be JSON-encodable -* Writes are asynchronous; call ``storage:flush()`` to force persistence +* Values must be (``nil``, ``boolean``, ``number``, ``string``, ``table``) +* Storage saves in the background, call storage:flush() if you need to write storage immediately (for example, before quitting) diff --git a/docs/source/api/string.rst b/docs/source/api/string.rst index 66d8ea7..3692d8c 100644 --- a/docs/source/api/string.rst +++ b/docs/source/api/string.rst @@ -29,22 +29,13 @@ Used to interact with text files. :return: The contents of the file. :rtype: string -.. lua:function:: save(assetKey, text) +.. lua:function:: save(assetKey, text [, callback]) Saves text to a file. - .. helptext:: save the text to a file - - :param assetKey assetKey: The asset key of the file. - :param string text: The text to save. + If a callback is supplied, saving will happen in the background. Multiple calls to save the same file are grouped and only the latest save will be performed. -.. lua:function:: saveAsync(assetKey, text [, callback]) - - Saves text to a file asynchronously. Multiple calls to the same file are coalesced and only the latest content is written. - - The optional callback is called from the main thread with ``(ok, err)``. ``err`` is ``nil`` on success. - - .. helptext:: save the text to a file asynchronously + .. helptext:: save the text to a file :param assetKey assetKey: The asset key of the file. :param string text: The text to save. @@ -52,7 +43,7 @@ Used to interact with text files. .. code-block:: lua - string.saveAsync(asset.documents .. "Config.json", json.encode(config), function(ok, err) + string.save(asset.documents .. "Config.json", json.encode(config), function(ok, err) if not ok then print("Save failed:", err) end