--[[ 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