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
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 method | Export 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:
local shops = tm.store_proxy('tm_shops', 'shop', { get = 'fetchShop', getAll = 'fetchAllShops' })Other proxy members
| Member | Description |
|---|---|
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.methods | What 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):
-- 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
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
})| Method | Description |
|---|---|
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.