diff --git a/src/ModelStorage.jl b/src/ModelStorage.jl index eb38fd3..0f5f032 100644 --- a/src/ModelStorage.jl +++ b/src/ModelStorage.jl @@ -1,4 +1,68 @@ module ModelStorage +using JSON3 +using Stipple +import Stipple: INTERNALFIELDS, AUTOFIELDS, Reactive + +const DEFAULT_EXCLUDE = vcat(INTERNALFIELDS, AUTOFIELDS) + +""" + model_values(model::M; fields::Vector{Symbol} = Symbol[], exclude::Vector{Symbol} = Symbol[], json::Bool = false) where M + +Exports the values of reactive fields from a Stipple model. Returns either a Dict of field-value pairs or a JSON string +if json=true. + +### Example + + @app TestApp2 begin + @in i = 100 + @out s = "Hello" + @private x = 4 + end + + model = @init TestApp2 + exported_values = Stipple.ModelStorage.model_values(model) +""" +function model_values(model::M; fields::Vector{Symbol} = Symbol[], exclude::Vector{Symbol} = Symbol[], json::Bool = false) where M + field_list = isempty(fields) ? fieldnames(M) : fields + excluded_fields = vcat(DEFAULT_EXCLUDE, exclude) + + field_dict = Dict(field => getfield(model, field)[] for field in field_list + if field ∉ excluded_fields && getfield(model, field) isa Stipple.Reactive) + + json ? JSON3.write(field_dict) : field_dict +end + +""" + load_model_values!(model::M, values::Dict{Symbol, Any}) where M + load_model_values!(model::M, values::String) where M + +Loads values into the fields of a ReactiveModel. Accepts either a Dict of field-value pairs or a JSON string. + +### Example + + values_dict = Dict(:i => 20, :s => "world", :x => 5) + Stipple.ModelStorage.load_model_values!(model, values_dict) +""" +function load_model_values!(model::M, values::Dict{Symbol, Any}) where M + model_field_list = fieldnames(M) + excluded_fields = DEFAULT_EXCLUDE + + for (field, value) in values + if field ∉ excluded_fields && field ∈ model_field_list + model_field = getfield(model, field) + + if model_field isa Reactive + model_field[] = value + end + end + end + + return model +end + +function load_model_values!(model::M, values::String) where M + load_model_values!(model, Dict(JSON3.read(values))) +end module Sessions @@ -61,4 +125,4 @@ end end # module Sessions -end # module ModelStorage \ No newline at end of file +end # module ModelStorage diff --git a/src/Stipple.jl b/src/Stipple.jl index b2e0dc3..d86a0eb 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -103,10 +103,9 @@ export setchannel, getchannel isempty(methods(notify, Observables)) && (Base.notify(observable::AbstractObservable) = Observables.notify!(observable)) include("ParsingTools.jl") -use_model_storage() && include("ModelStorage.jl") include("NamedTuples.jl") - include("stipple/reactivity.jl") +use_model_storage() && include("ModelStorage.jl") include("stipple/json.jl") include("stipple/undefined.jl") include("stipple/assets.jl") diff --git a/test/runtests.jl b/test/runtests.jl index 22e1754..9af0559 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -122,7 +122,7 @@ end @eval @debounce TestApp i 101 @eval @debounce TestApp (a, b, c) 101 @test Stipple.DEBOUNCE[TestApp][:i] == 101 - + @eval @clear_debounce TestApp @test haskey(Stipple.DEBOUNCE, TestApp) == false end @@ -154,7 +154,7 @@ end @eval @debounce i3 101 @eval @debounce (a, b, c) 101 @test Stipple.DEBOUNCE[Stipple.@type()][:i3] == 101 - + @eval @clear_debounce @test haskey(Stipple.DEBOUNCE, Stipple.@type()) == false end @@ -195,7 +195,7 @@ end module App2 using Stipple, Stipple.ReactiveTools - + @app begin @in i2 = 102 end @@ -205,7 +205,7 @@ end end end - + @testset "Multipage Reactive API (implicit)" begin @eval p1 = @page("/app1", "hello", model = App1) @eval p2 = @page("/app2", "world", model = App2) @@ -435,7 +435,7 @@ end @test row(@gutter :sm [ cell("Hello", sm = 2, md = 8) cell("World", sm = 10, md = 4) - ]).data == "
" * + ]).data == "
" * "
Hello
World
" end @@ -572,25 +572,25 @@ end c::Int d::Int end - + struct T2 a::Int b::T1 end - + t2 = T2(1, T1(2, 3)) t2_dict = JSON3.read(Stipple.json(t2), Dict) - + Base.@kwdef struct T3 c::Int = 1 d::Int = 3 end - + Base.@kwdef struct T4 a::Int = 1 b::T3 = T3() end - + @test Stipple.stipple_parse(T2, t2_dict) == T2(1, T1(2, 3)) @test Stipple.stipple_parse(T3, Dict()) == T3(1, 3) @test Stipple.stipple_parse(T4, Dict()) == T4(1, T3(1, 3)) @@ -605,4 +605,36 @@ end @test Stipple.stipple_parse(Union{Nothing, SubString}, "hi") == SubString("hi") end @test Stipple.stipple_parse(Union{Nothing, String}, nothing) === nothing -end \ No newline at end of file +end + +@testset "Exporting and loading model field values" begin + @app TestApp2 begin + @in i = 100 + @out s = "Hello" + @private x = 4 + end + + model = @init TestApp2 + + exported_values = Stipple.ModelStorage.model_values(model) + @test exported_values[:i] == 100 + @test exported_values[:s] == "Hello" + @test exported_values[:x] == 4 + + values_json = JSON3.write(exported_values) + exported_values_json = Stipple.ModelStorage.model_values(model, json = true) + @test values_json == exported_values_json + + values_dict = Dict(:i => 20, :s => "world", :x => 5) + Stipple.ModelStorage.load_model_values!(model, values_dict) + @test model.i[] == 20 + @test model.s[] == "world" + @test model.x[] == 5 + + values_json = Dict(:i => 30, :s => "zero", :x => 50) |> JSON3.write |> string + Stipple.ModelStorage.load_model_values!(model, values_json) + @test model.i[] == 30 + @test model.s[] == "zero" + @test model.x[] == 50 + +end