Skip to content

Commit 70803ae

Browse files
committed
resources
1 parent af02e0a commit 70803ae

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

src/Resources.jl

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module Resources
2+
using ..Signals
3+
export Resource
4+
5+
struct Resource
6+
id::Symbol
7+
value::Signal
8+
loader::Function
9+
isloading::Signal{Bool}
10+
sources::Vector{Signal}
11+
error::Signal
12+
end
13+
14+
function Resource(loader::Function, sources::Vector{<:Signal}, id = :none)
15+
value = Signal(nothing)
16+
isloading = Signal(false)
17+
error = Signal(nothing)
18+
19+
resource = Resource(id, value, loader, isloading, sources, error)
20+
21+
# Create computed signal to track dependencies
22+
trigger = computed(() -> begin
23+
# Read all source signals
24+
map(s -> s(), sources)
25+
nothing
26+
end)
27+
28+
effect_fn = () -> begin
29+
isloading() && return
30+
isloading(true)
31+
try
32+
result = loader()
33+
value(result)
34+
error(nothing)
35+
catch e
36+
error(e)
37+
value(nothing)
38+
finally
39+
isloading(false)
40+
end
41+
end
42+
push!(trigger.effects, effect_fn)
43+
44+
resource
45+
end
46+
47+
function (resource::Resource)()
48+
resource.value()
49+
end
50+
51+
loading(resource::Resource) = resource.isloading()
52+
value(resource::Resource) = resource.value()
53+
54+
end

src/Signals.jl

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ end
2323
COMPUTED_DEPS::Set{Signal} = Set{Signal}()
2424

2525
Signal(x) = Signal(x, nothing, true, nothing, Set{Signal}(), Set{Function}())
26+
Signal(::Nothing) = Signal{Union{Any,Nothing}}(nothing, nothing, true, nothing, Set{Signal}(), Set{Function}())
2627
Signal(x, id::Union{Symbol,Nothing}) = Signal(x, id, true, nothing, Set{Signal}(), Set{Function}())
2728
Signal(f::Function, id::Union{Symbol,Nothing}=nothing) = Signal(f(), id, true, f, Set{Signal}(), Set{Function}())
2829

src/StateSignals.jl

+4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
module StateSignals
22
include("Signals.jl")
3+
include("Resources.jl")
34

45
using .Signals
6+
using .Resources
57
export Signal, computed, effect, invalidate, pull!
68
export CONTEXT_SIGNALS
79

10+
export Resource
11+
812
end

test/test-resources.jl

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Test
2+
using StateSignals
3+
4+
@testset "Basic Resource Operations" begin
5+
# Test resource creation
6+
s = Signal(0)
7+
r = Resource([s]) do
8+
s() * 2
9+
end
10+
11+
@test r() === nothing # Initial value should be nothing
12+
@test r.isloading() == false # Should not be loading initially
13+
14+
# Test resource loading
15+
s(5)
16+
sleep(0.1)
17+
@test r() == 10 # Should compute s() * 2
18+
@test r.isloading() == false # Should be done loading
19+
end
20+
21+
@testset "Resource Loading State" begin
22+
s = Signal(0)
23+
loading_states = []
24+
25+
r = Resource([s]) do
26+
# Track loading state changes
27+
push!(loading_states, r.isloading())
28+
sleep(0.1) # Simulate async work
29+
s() * 2
30+
end
31+
32+
s(5)
33+
sleep(0.2) # Wait for async operation
34+
35+
@test loading_states[1] == true # Should be loading during computation
36+
@test r.isloading() == false # Should be done loading after computation
37+
@test r() == 10 # Should have correct final value
38+
end
39+
40+
@testset "Multiple Dependencies" begin
41+
a = Signal(2)
42+
b = Signal(3)
43+
44+
r = Resource([a, b]) do
45+
a() * b()
46+
end
47+
48+
@test r() === nothing # Initial value
49+
50+
a(4) # Should trigger resource update
51+
sleep(0.1)
52+
@test r() == 12 # 4 * 3
53+
54+
b(5) # Should trigger another update
55+
sleep(0.1)
56+
@test r() == 20 # 4 * 5
57+
end
58+
59+
@testset "Error Handling" begin
60+
s = Signal(0)
61+
r = Resource([s]) do
62+
if s() < 0
63+
error("Value cannot be negative")
64+
end
65+
s() * 2
66+
end
67+
68+
# Initial state
69+
@test r() === nothing
70+
@test r.error() === nothing
71+
@test r.isloading() == false
72+
73+
# Test error condition
74+
s(-1)
75+
sleep(0.1)
76+
@test r() === nothing # Value should be reset
77+
@test r.error() isa ErrorException # Should have captured the error
78+
@test r.error().msg == "Value cannot be negative"
79+
@test r.isloading() == false # Should not be loading after error
80+
end

test/test-signals.jl

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ end
4444
@test tracked_value[] == 1 # Initial effect run
4545

4646
s(2) # Should trigger effect
47+
sleep(0.1)
4748
@test tracked_value[] == 2
4849
end
4950

0 commit comments

Comments
 (0)