SyntaxGameServer/RCCService2018/content/LuaPackages/PurchasePrompt/Thunk.lua

85 lines
2.4 KiB
Lua

--[[
An upgraded version of Rodux's thunk middleware that describes a table
format for thunks. This allows them to be named (via the `type` field)
and gives us cleaner methods for dependency injection via providing
services to our middleware that can be threaded into any thunks that
might request them
]]
local Symbol = require(script.Parent.Symbol)
local Thunk = {}
local ThunkTag = Symbol.named("ThunkTag")
function Thunk.middleware(services)
services = services or {}
return function(nextDispatch, store)
--[[
This middleware doesn't need to do anything during initialization
so we go straight to returning the wrapped dispatch function
]]
return function(action)
if action[ThunkTag] == true then
local injectedServices = {}
for _, service in pairs(action.requiredServices) do
local providedService = services[service]
if providedService == nil then
error((
"Service with key %s is a dependency but was not provided"
):format(service))
end
injectedServices[service] = providedService
end
--[[
By convention, we return the result of our thunk operation.
This value is not guaranteed to have any particular form or
meaning, but it prevents our middleware from conditionally returning,
which is a dangerous pattern in Lua.
]]
return action(store, injectedServices)
else
return nextDispatch(action)
end
end
end
end
function Thunk.new(name, requiredServices, onInvoke)
assert(typeof(name) == "string", "Bad arg #1: name must be a string")
assert(requiredServices == nil or typeof(requiredServices) == "table",
"Bad arg #2: requiredServices must be a table or nil")
assert(typeof(onInvoke) == "function", "Bad arg #3: onInvoke must be a function")
requiredServices = requiredServices or {}
return setmetatable({
[ThunkTag] = true,
type = name,
requiredServices = requiredServices,
}, {
__call = function(self, ...)
onInvoke(...)
end,
})
end
function Thunk.test(thunk, store, providedServices)
assert(typeof(thunk) == "table" and thunk[ThunkTag] == true,
"Test Error - Bad arg #1: Must provide a valid thunk")
if #thunk.requiredServices > 0 then
for _, service in ipairs(thunk.requiredServices) do
assert(providedServices[service] ~= nil,
"Test Error - Bad arg #3: Missing required service "..tostring(service))
end
end
return thunk(store, providedServices)
end
return Thunk