Skip to content

Commit

Permalink
Merge pull request #183 from Sleitnick/bugfix/silo-combine
Browse files Browse the repository at this point in the history
Add to combined actions and check for conflicts
  • Loading branch information
Sleitnick authored Jan 6, 2024
2 parents 81e0d1f + 3a34248 commit 936cf43
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 37 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4

- name: Install Aftman
uses: ok-nick/setup-aftman@v0
uses: ok-nick/setup-aftman@v0.4.2

- name: Lint
run: |
Expand All @@ -32,5 +32,5 @@ jobs:
- uses: JohnnyMorganz/stylua-action@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: v0.18.2
version: v0.19.1
args: --check ./modules
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
| [Ser](https://sleitnick.github.io/RbxUtil/api/Ser) | `Ser = "sleitnick/[email protected]"` | Ser class for serialization and deserialization |
| [Shake](https://sleitnick.github.io/RbxUtil/api/Shake) | `Shake = "sleitnick/[email protected]"` | Shake class for making things shake |
| [Signal](https://sleitnick.github.io/RbxUtil/api/Signal) | `Signal = "sleitnick/[email protected]"` | Signal class |
| [Silo](https://sleitnick.github.io/RbxUtil/api/Silo) | `Silo = "sleitnick/silo@0.1.2"` | State container class |
| [Silo](https://sleitnick.github.io/RbxUtil/api/Silo) | `Silo = "sleitnick/silo@0.2.0"` | State container class |
| [Streamable](https://sleitnick.github.io/RbxUtil/api/Streamable) | `Streamable = "sleitnick/[email protected]"` | Streamable class and StreamableUtil |
| [Symbol](https://sleitnick.github.io/RbxUtil/api/Symbol) | `Symbol = "sleitnick/[email protected]"` | Symbol |
| [TableUtil](https://sleitnick.github.io/RbxUtil/api/TableUtil) | `TableUtil = "sleitnick/[email protected]"` | Table utility functions |
Expand Down
10 changes: 5 additions & 5 deletions modules/silo/Util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ local Util = {}
Util.None = newproxy()

-- Recursive table freeze.
function Util.DeepFreeze(tbl: AnyTable): AnyTable
function Util.DeepFreeze<T>(tbl: AnyTable): AnyTable
table.freeze(tbl)
for _, v in pairs(tbl) do
for _, v in tbl do
if type(v) == "table" then
Util.DeepFreeze(v)
end
Expand All @@ -22,9 +22,9 @@ function Util.DeepFreeze(tbl: AnyTable): AnyTable
end

-- Recursive table copy.
function Util.DeepCopy(tbl: AnyTable): AnyTable
function Util.DeepCopy<T>(tbl: AnyTable): AnyTable
local newTbl = table.clone(tbl)
for k, v in pairs(newTbl) do
for k, v in newTbl do
if type(v) == "table" then
newTbl[k] = Util.DeepCopy(v)
end
Expand All @@ -36,7 +36,7 @@ end
-- Similar to the spread operator in JavaScript.
function Util.Extend(original: AnyTable, extension: AnyTable): AnyTable
local t = Util.DeepCopy(original)
for k, v in pairs(extension) do
for k, v in extension do
if type(v) == "table" then
if type(original[k]) == "table" then
t[k] = Util.Extend(original[k], v)
Expand Down
73 changes: 45 additions & 28 deletions modules/silo/init.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
--!strict

-- Silo
-- Stephen Leitnick
-- April 29, 2022
Expand Down Expand Up @@ -32,8 +30,18 @@ type Action<A> = {
Payload: A,
}

local Util = require(script.Util)
export type Silo<S, A> = {
Actions: { [string]: <A>(value: A) -> () },

GetState: (self: Silo<S, A>) -> State<S>,
Dispatch: (self: Silo<S, A>, action: Action<A>) -> (),
ResetToDefaultState: (self: Silo<S, A>) -> (),
Subscribe: (self: Silo<S, A>, subscriber: (newState: State<S>, oldState: State<S>) -> ()) -> () -> (),
Watch: <T>(self: Silo<S, A>, selector: (State<S>) -> T, onChange: (T) -> ()) -> () -> (),
}

local TableWatcher = require(script.TableWatcher)
local Util = require(script.Util)

--[=[
@class Silo
Expand Down Expand Up @@ -75,33 +83,35 @@ Silo.__index = Silo
payload (no need to pass state). The `SetKills` modifier is then used
as the `SetKills` action to be dispatched.
]=]
function Silo.new<S>(defaultState: State<S>, modifiers: { Modifier<S> }?)
function Silo.new<S, A>(defaultState: State<S>, modifiers: { [string]: Modifier<S> }?): Silo<S, A>
local self = setmetatable({}, Silo)

self._DefaultState = Util.DeepFreeze(Util.DeepCopy(defaultState))
self._State = Util.DeepFreeze(Util.DeepCopy(defaultState))
self._Modifiers = {}
self._Modifiers = {} :: { [string]: any }
self._Dispatching = false
self._Parent = self
self._Subscribers = {}

self.Actions = {}

-- Create modifiers and action creators:
for actionName, modifier in pairs(modifiers or {}) do
self._Modifiers[actionName] = function(state: State<S>, payload: any)
-- Create a watcher to virtually watch for state mutations:
local watcher = TableWatcher(state)
modifier(watcher, payload)
-- Apply state mutations into new state table:
return watcher()
end
if modifiers then
for actionName, modifier in modifiers do
self._Modifiers[actionName] = function(state: State<S>, payload: any)
-- Create a watcher to virtually watch for state mutations:
local watcher = TableWatcher(state)
modifier(watcher :: any, payload)
-- Apply state mutations into new state table:
return watcher()
end

self.Actions[actionName] = function(payload)
return {
Name = actionName,
Payload = payload,
}
self.Actions[actionName] = function(payload)
return {
Name = actionName,
Payload = payload,
}
end
end
end

Expand All @@ -113,10 +123,10 @@ end
@return Silo
Constructs a new silo as a combination of other silos.
]=]
function Silo.combine<S>(silos, initialState: State<S>?)
function Silo.combine<S, A>(silos: { [string]: Silo<unknown, unknown> }, initialState: State<S>?): Silo<S, A>
-- Combine state:
local state = {}
for name, silo in pairs(silos) do
for name, silo in silos do
if silo._Dispatching then
error("cannot combine silos from a modifier", 2)
end
Expand All @@ -126,27 +136,31 @@ function Silo.combine<S>(silos, initialState: State<S>?)
local combinedSilo = Silo.new(Util.Extend(state, initialState or {}))

-- Combine modifiers and actions:
for name, silo in pairs(silos) do
for name, silo in silos do
silo._Parent = combinedSilo
for actionName, modifier in pairs(silo._Modifiers) do
for actionName, modifier in silo._Modifiers do
-- Prefix action name to keep it unique:
local fullActionName = name .. "/" .. actionName
local fullActionName = `{name}/{actionName}`
combinedSilo._Modifiers[fullActionName] = function(s, payload)
-- Extend the top-level state from the sub-silo state modification:
return Util.Extend(s, {
[name] = modifier((s :: { [string]: any })[name], payload),
})
end
end
for actionName in pairs(silo.Actions) do
for actionName in silo.Actions do
if combinedSilo.Actions[actionName] ~= nil then
error(`duplicate action name {actionName} found when combining silos`, 2)
end
-- Update the action creator to include the correct prefixed action name:
local fullActionName = name .. "/" .. actionName
local fullActionName = `{name}/{actionName}`
silo.Actions[actionName] = function(p)
return {
Name = fullActionName,
Payload = p,
}
end
combinedSilo.Actions[actionName] = silo.Actions[actionName]
end
end

Expand Down Expand Up @@ -197,7 +211,7 @@ function Silo:Dispatch<A>(action: Action<A>)
self._State = Util.DeepFreeze(newState)

-- Notify subscribers of state change:
for _, subscriber in ipairs(self._Subscribers) do
for _, subscriber in self._Subscribers do
subscriber(newState, oldState)
end
end
Expand Down Expand Up @@ -312,10 +326,13 @@ function Silo:ResetToDefaultState()
if self._DefaultState ~= oldState then
self._State = Util.DeepFreeze(Util.DeepCopy(self._DefaultState))

for _, subscriber in ipairs(self._Subscribers) do
for _, subscriber in self._Subscribers do
subscriber(self._State, oldState)
end
end
end

return Silo
return {
new = Silo.new,
combine = Silo.combine,
}
2 changes: 1 addition & 1 deletion modules/silo/wally.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sleitnick/silo"
description = "State container class"
version = "0.1.2"
version = "0.2.0"
license = "MIT"
authors = ["Stephen Leitnick"]
registry = "https://github.com/UpliftGames/wally-index"
Expand Down

0 comments on commit 936cf43

Please sign in to comment.