Skip to content

Store proxy (tm.store_proxy) ​

Context: server and client. Thin handles for working with a store that lives in another resource (server), and for a synced store's client side (client).

Server: another resource's store ​

lua
local shops = tm.store_proxy.new('tm_shops', 'shop')     -- or tm.store_proxy('tm_shops', 'shop')
local shop, err = shops:get(12)
shops:setData(12, 'stock', 0)

new(resourceName, entityName, methods?) returns a proxy that calls exports of resourceName. Calls go through tm.exports: a resource that is still starting is waited for (when the caller can yield), and a stopped one fails fast. A failed call (resource stopped, export missing, or the export raised) returns nil, 'reason' instead of raising. Only a method name with no mapping raises, because that is a coding mistake.

Export names ​

The owner exports its store under names derived from the entity name (first letter capitalized):

Proxy methodExport called (entity shop)
get(id)getShop
getAll()getAllShop
getPublic(id, src?)getShopPublic
create(data)createShop
delete(id)deleteShop
setData(id, key, value)setShopData
setDataMany(id, values)setShopDataMany
setState(id, key, value)setShopState
setStateMany(id, values)setShopStateMany
subscribe(source)subscribeShop
unsubscribe(source)unsubscribeShop
subscribeTo(source, id)subscribeToShop
unsubscribeFrom(source, id)unsubscribeFromShop
serializeOne(id, src?)serializeShop
serializeAll(src?)serializeAllShop

Override only the names that differ; the rest keep their defaults:

lua
local shops = tm.store_proxy('tm_shops', 'shop', { get = 'fetchShop', getAll = 'fetchAllShops' })

Other proxy members ​

MemberDescription
proxy:call(method, ...)Calls any mapped method by name (proxy:call('setData', id, 'price', 99)).
proxy:isAvailable()Whether the owning resource is started.
proxy.resource, proxy.entity, proxy.methodsWhat the proxy was built with.

The owning side ​

The owner decides what to export. Export plain data, not live records with methods (a record crosses the export boundary as a copy of its fields):

lua
-- tm_shops
exports('getShop', function(id) local r = shops:get(id) return r and r:toTable() end)
exports('setShopData', function(id, key, value)
    local r = shops:get(id)
    return r ~= nil and r:setData(key, value)
end)
exports('subscribeShop', function(source) return shops:subscribe(source) end)

Client: a synced store's handle ​

lua
local shops = tm.store_proxy({
    getAllCallback = 'tm_shops:getAll',       getOneCallback = 'tm_shops:getOne',
    setDataEvent = 'tm_shops:setData',        setDataManyEvent = 'tm_shops:setDataMany',
    setStateEvent = 'tm_shops:setState',      setStateManyEvent = 'tm_shops:setStateMany',
    subscribeAllEvent = 'tm_shops:subscribe', unsubscribeAllEvent = 'tm_shops:unsubscribe',
    subscribeEvent = 'tm_shops:subscribeTo',  unsubscribeEvent = 'tm_shops:unsubscribeFrom',
    mirror = mirror,                          -- optional: a client store from tm.store.new
})
MethodDescription
get(id) / getAll()Read the mirror. nil when no mirror is configured.
loadAll() / loadOne(id)Ask the server for a snapshot (blocks, use a thread). Uses the mirror's loaders when there is a mirror, otherwise the getAllCallback / getOneCallback.
setData(id, key, value, optimistic?) / setDataMany(id, values, optimistic?)Fire the configured server event. With optimistic and a mirror, the mirror changes first. Return true when the event was sent.
setState(id, key, value) / setStateMany(id, values)Fire the configured server event.
subscribe() / unsubscribe() / subscribeTo(id) / unsubscribeFrom(id)Use the mirror's configured event, falling back to the proxy's own event. false when neither is configured.

A method whose event is not configured returns false and sends nothing.

The server handlers for these events belong to the owning resource. They must check who sent the event and validate every value before calling store:applyClientPatch or a record setter, see Security in the store docs.