Skip to content

Commit 77c98ca

Browse files
committed
feat: add deep freeze flag, misc refactors
1 parent 708bc55 commit 77c98ca

22 files changed

Lines changed: 185 additions & 337 deletions

benches/patch.bench.luau

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
local CharmSync = require("../packages/charm-sync/src")
1+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
2+
3+
local CharmSync = require(ReplicatedStorage.packages["charm-sync"].src)
24

35
local patch = CharmSync.patch
46

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
local core = require("../../core")
2+
local suite = require("../../../../../test/suite")
3+
4+
local atom = core.atom
5+
local effect = core.effect
6+
local test = suite.test
7+
8+
test("should accept equality function", function()
9+
local calls = {}
10+
local src = atom(0, function(current, incoming)
11+
return current == incoming or incoming % 2 == 1
12+
end)
13+
14+
effect(function()
15+
table.insert(calls, src())
16+
end)
17+
18+
src(2)
19+
src(3)
20+
assert(src() == 2, "should skip odd value")
21+
22+
src(4)
23+
src(5)
24+
assert(src() == 4, "should skip odd value")
25+
assert(table.concat(calls) == "024", `should only call on even updates, got {table.concat(calls)}`)
26+
end)
27+
28+
test("should enforce immutability", function()
29+
local src = atom({ nested = {} })
30+
assert(not pcall(function()
31+
src().nested.a = 1
32+
end), "should not allow mutation of initial value")
33+
34+
src({ nested = { a = 1 } })
35+
assert(not pcall(function()
36+
src().nested.a = 2
37+
end), "should not allow mutation of new value")
38+
end)
39+
40+
return {}

packages/charm/src/__tests__/core/computed.spec.luau

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
local core = require("../../core")
22
local suite = require("../../../../../test/suite")
33

4-
local computed = core.computed
54
local atom = core.atom
5+
local computed = core.computed
6+
local effect = core.effect
67
local test = suite.test
78

89
test("should correctly propagate changes through computed core", function()
@@ -80,4 +81,27 @@ test("should not update if the atom value is reverted", function()
8081
assert(times == 1, "computed ran when it should not")
8182
end)
8283

84+
test("should accept equality function", function()
85+
local calls = {}
86+
local src = atom(0)
87+
local c1 = computed(function()
88+
return src()
89+
end, function(current, incoming)
90+
return current == incoming or incoming % 2 == 1
91+
end)
92+
93+
effect(function()
94+
table.insert(calls, c1())
95+
end)
96+
97+
src(2)
98+
src(3)
99+
assert(c1() == 2, "should skip odd value")
100+
101+
src(4)
102+
src(5)
103+
assert(c1() == 4, "should skip odd value")
104+
assert(table.concat(calls) == "024", `should only call on even updates, got {table.concat(calls)}`)
105+
end)
106+
83107
return {}

packages/charm/src/__tests__/core/gc.spec.luau

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,44 @@ local effectScope = core.effectScope
88
local test = suite.test
99

1010
--- Creates a weak reference to a value.
11-
--- @return Function to dereference the value.
12-
local function weakRef<T>(value: T): () -> T?
13-
local ref = setmetatable({ value = value }, { __mode = "v" })
14-
return function()
15-
return ref.value
16-
end
11+
local function weakRef<T>(value: T): { value: T? }
12+
return setmetatable({ value = value }, { __mode = "v" }) :: any
1713
end
1814

19-
--- Triggers garbage collection by allocating excessive memory.
15+
--- Triggers garbage collection by creating a large number of tables.
2016
local function gc()
2117
for _ = 1, 1000 do
2218
local _ = table.create(100, 0)
2319
end
2420
end
2521

2622
--- In alien-signals, a computed value unlinks from dependencies when its last
27-
--- parent scope is disposed. Calling a computed value outside of a scope links
28-
--- it to dependencies, but the dependencies hold strong references to the
29-
--- computed value, so it won't GC until called in a scope and disposed.
23+
--- parent scope is disposed. However, calling a computed value outside of a
24+
--- scope still links it to dependencies, which act as strong references.
25+
--- Computed values called in this way won't GC until unlinked from a scope.
3026
---
3127
--- This function releases a computed value by linking it to a scope and then
3228
--- immediately closing it, causing alien-signals to unlink the computed value
33-
--- and allow it to be garbage collected.
34-
local function release(getter: () -> ())
29+
--- from its dependencies and allow it to be garbage collected.
30+
local function unlink(getter: () -> ())
3531
effectScope(function()
3632
getter()
3733
end)()
3834
end
3935

4036
test("should release computed cache", function()
4137
local srcRef = weakRef({})
42-
local src = atom(srcRef())
43-
38+
local src = atom(srcRef.value)
4439
local c = computed(function()
4540
return src()
4641
end)
4742

4843
c() -- cache src value
4944
src(nil) -- release value
50-
c = release(c) -- release computed
45+
c = unlink(c) -- release computed
5146

5247
gc()
53-
assert(srcRef() == nil, "should release computed cache")
48+
assert(srcRef.value == nil, "should release computed cache")
5449
end)
5550

5651
test("should not release effect for atom", function()
@@ -61,7 +56,6 @@ test("should not release effect for atom", function()
6156
effectCalls += 1
6257
src()
6358
end)
64-
6559
assert(effectCalls == 1, "should call effect once")
6660

6761
gc()

packages/charm/src/__tests__/core/issue_48.spec.luau

Lines changed: 0 additions & 113 deletions
This file was deleted.

packages/charm/src/__tests__/core/untrack.spec.luau

Lines changed: 0 additions & 23 deletions
This file was deleted.

packages/charm/src/__tests__/listen.spec.luau

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,21 @@ test("should track inner subs", function()
9898
assert(table.concat(order) == "", `bad order: {table.concat(order)}`)
9999
end)
100100

101+
test("should skip redundant updates", function()
102+
local order = {}
103+
local a = atom(0)
104+
105+
listen(function(): "even" | "odd"
106+
return if a() % 2 == 0 then "even" else "odd"
107+
end, function(value)
108+
table.insert(order, value)
109+
end)
110+
111+
a(1)
112+
a(3)
113+
a(4)
114+
a(6)
115+
assert(table.concat(order) == "evenoddeven", `bad order: {table.concat(order)}`)
116+
end)
117+
101118
return {}

0 commit comments

Comments
 (0)