Skip to content

Commit

Permalink
store handlers with models, cleanup CHANNELFIELD and ChannelName plus…
Browse files Browse the repository at this point in the history
… fixes
  • Loading branch information
hhaensel committed Dec 20, 2024
1 parent de0e2e8 commit 934e9b7
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/ModelStorage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function init_from_storage( t::Type{M};

if field isa Reactive
# restore fields only if a stored model exists, if the field is not part of the internal fields and is not write protected
if isnothing(stored_model) || f [Stipple.CHANNELFIELDNAME, Stipple.AUTOFIELDS...] ||
if isnothing(stored_model) || f [Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS...] ||
Stipple.isprivate(f, model) || ! hasproperty(stored_model, f) || ! hasproperty(model, f)
else
# restore field value from stored model
Expand Down
36 changes: 10 additions & 26 deletions src/ReactiveTools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using MacroTools
using MacroTools: postwalk
using OrderedCollections
import Genie
import Stipple: deletemode!, parse_expression!, parse_expression, init_storage, striplines, striplines!
import Stipple: deletemode!, parse_expression!, parse_expression, init_storage, striplines, striplines!, postwalk!

#definition of handlers/events
export @onchange, @onbutton, @event, @notify
Expand All @@ -27,16 +27,8 @@ export @methods, @watch, @computed, @client_data, @add_client_data

export @before_create, @created, @before_mount, @mounted, @before_update, @updated, @activated, @deactivated, @before_destroy, @destroyed, @error_captured

function postwalk!(f::Function, expr::Expr)
ex = postwalk(f, expr)
expr.head = ex.head
expr.args = ex.args
end

export DEFAULT_LAYOUT, Page

const HANDLERS_FUNCTIONS = LittleDict{Type{<:ReactiveModel},Function}()

function DEFAULT_LAYOUT(; title::String = "Genie App",
meta::D = Dict(),
head_content::Union{AbstractString, Vector} = "",
Expand Down Expand Up @@ -125,7 +117,7 @@ macro appname()
end

function Stipple.setmode!(expr::Expr, mode::Int, fieldnames::Symbol...)
fieldname in [Stipple.CHANNELFIELDNAME, :modes__] && return
fieldname in [Stipple.INTERNALFIELDS..., :modes__] && return
expr.args[2] isa Expr && expr.args[2].args[1] == :(Stipple._deepcopy) && (expr.args[2] = expr.args[2].args[2])

d = if expr.args[2] isa LittleDict
Expand Down Expand Up @@ -397,7 +389,6 @@ end
"""
macro app(expr = Expr(:block))
modelname = model_typename(__module__)
storage = Stipple.init_storage()
quote
Stipple.ReactiveTools.@app $modelname $expr __GF_AUTO_HANDLERS__
$modelname
Expand Down Expand Up @@ -509,16 +500,14 @@ macro init(args...)
if called_without_type
typename = model_typename(__module__)
insert!(init_args, Stipple.has_parameters(init_args) ? 2 : 1, typename)
else
typename = init_args[type_pos]
end

quote
Stipple.ReactiveTools.init_model($(init_args...))
end |> esc
end

function init_model(M::Type{<:ReactiveModel}, args...; context = nothing, kwargs...)
function init_model(M::Type{<:ReactiveModel}, args...; kwargs...)
m = __module__ = parentmodule(M)

initfn = begin
Expand All @@ -536,14 +525,11 @@ function init_model(M::Type{<:ReactiveModel}, args...; context = nothing, kwargs
end
end

handlersfn = if context !== nothing && isdefined(m, :__GF_AUTO_HANDLERS__)
m.__GF_AUTO_HANDLERS__
else
Stipple.ReactiveTools.HANDLERS_FUNCTIONS[M]
model = initfn(M, args...; kwargs...)
for h in model.handlers__
model |> h
end

model = initfn(M, args...; kwargs...) |> handlersfn

# Update the model in all pages where it has been set as instance of an app.
# Where it has been set as ReactiveModel type, no change is required
for p in Stipple.Pages._pages
Expand All @@ -553,13 +539,12 @@ function init_model(M::Type{<:ReactiveModel}, args...; context = nothing, kwargs
end

function init_model(m::Module, args...; kwargs...)
init_model(@eval(m, Stipple.@type), args...; context = m, kwargs...)
init_model(@eval(m, Stipple.@type), args...; kwargs...)
end

macro app(typename, expr, handlers_fn_name = :handlers)
macro app(typename, expr, handlers_fn_name = Symbol(typename, :_handlers))
quote
Stipple.ReactiveTools.@handlers $typename $expr $handlers_fn_name
Stipple.ReactiveTools.HANDLERS_FUNCTIONS[$typename] = $handlers_fn_name
$typename, $handlers_fn_name
end |> esc
end
Expand Down Expand Up @@ -625,7 +610,7 @@ function add_brackets!(expr, varnames)
expr
end

macro handlers(typename, expr, handlers_fn_name = :handlers)
macro handlers(typename, expr, handlers_fn_name = Symbol(typename, :_handlers))
expr = wrap(expr, :block)
i_start = 1
handlercode = []
Expand All @@ -650,7 +635,7 @@ macro handlers(typename, expr, handlers_fn_name = :handlers)
end
end

storage = Stipple.init_storage()
storage = Stipple.init_storage(isempty(handlercode) ? nothing : handlers_fn_name)
varnames = get_varnames(initcode, __module__)

add_brackets!.(initcode, Ref(varnames))
Expand Down Expand Up @@ -689,7 +674,6 @@ macro handlers(typename, expr, handlers_fn_name = :handlers)

__model__
end
Stipple.ReactiveTools.HANDLERS_FUNCTIONS[$typename] = $handlers_fn_name
($typename, $handlers_fn_name)
end |> esc
end
Expand Down
13 changes: 7 additions & 6 deletions src/Stipple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ function channeldefault(::Type{M}) where M<:ReactiveModel
use_model_storage() || return nothing

stored_model = Stipple.ModelStorage.Sessions.GenieSession.get(model_id, nothing)
stored_model === nothing ? nothing : getfield(stored_model, Stipple.CHANNELFIELDNAME)
stored_model === nothing ? nothing : getchannel(stored_model)
end

@nospecialize
Expand Down Expand Up @@ -446,7 +446,7 @@ end

function setmode!(dict::AbstractDict, mode, fieldnames::Symbol...)
for fieldname in fieldnames
fieldname in [Stipple.CHANNELFIELDNAME, :modes__] && continue
fieldname Stipple.INTERNALFIELDS && continue
mode == PUBLIC || mode == :PUBLIC ? delete!(dict, fieldname) : dict[fieldname] = Core.eval(Stipple, mode)
end
dict
Expand All @@ -456,12 +456,13 @@ function deletemode!(modes, fieldnames::Symbol...)
setmode!(modes, PUBLIC, fieldnames...)
end

function init_storage()
ch = channelfactory()
function init_storage(handler::Union{Nothing, Symbol, Expr} = nothing)
handlers = handler === nothing ? :(Function[]) : :(Function[$handler])

LittleDict{Symbol, Expr}(
CHANNELFIELDNAME => :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = Stipple.channelfactory()),
:channel__ => :(channel__::String = Stipple.channelfactory()),
:modes__ => :(modes__::Stipple.LittleDict{Symbol,Int} = Stipple.LittleDict{Symbol,Int}()),
:handlers__ => :(handlers__::Vector{Function} = $handlers),
:isready => :(isready::Stipple.R{Bool} = false),
:isprocessing => :(isprocessing::Stipple.R{Bool} = false),
:fileuploads => :(fileuploads::Stipple.R{Dict{AbstractString,AbstractString}} = Dict{AbstractString,AbstractString}()),
Expand Down Expand Up @@ -1191,7 +1192,7 @@ macro mixin_old(expr, prefix = "", postfix = "")
values = getfield.(Ref(mix), fnames)
output = quote end
for (f, type, v) in zip(Symbol.(pre, fnames, post), fieldtypes(get_concrete_type(T)), values)
f in Symbol.(prefix, [:channel__, :modes__, AUTOFIELDS...], postfix) && continue
f in Symbol.(prefix, [INTERNALFIELDS..., AUTOFIELDS...], postfix) && continue
v_copy = Stipple._deepcopy(v)
push!(output.args, v isa Symbol ? :($f::$type = $(QuoteNode(v))) : :($f::$type = $v_copy))
end
Expand Down
12 changes: 12 additions & 0 deletions src/Tools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,18 @@ Return a copy of an expression with all line number nodes removed. See also `str
"""
striplines(ex; recursive::Bool = false) = striplines!(copy(ex); recursive)


"""
postwalk!(f::Function, expr::Expr)
Inplace version of MacroTools.postwalk()
"""
function postwalk!(f::Function, expr::Expr)
ex = MacroTools.postwalk(f, expr)
expr.head = ex.head
expr.args = ex.args
end

"""
debug(field::Reactive; listener::Int = 0)
debug(field::Reactive, value; listener::Int = 0)
Expand Down
2 changes: 1 addition & 1 deletion src/stipple/reactive_props.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
isprivate(field::Reactive) = field.r_mode == PRIVATE

function isprivate(fieldname::Symbol, model::M)::Bool where {M<:ReactiveModel}
fieldname in [Stipple.CHANNELFIELDNAME, :modes__] && return true
fieldname INTERNALFIELDS && return true

field = getfield(model, fieldname)
field isa Reactive ? isprivate(field) : get(model.modes__, fieldname, 0) == PRIVATE
Expand Down
33 changes: 23 additions & 10 deletions src/stipple/reactivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,26 +155,24 @@ abstract type ReactiveModel end

export @vars, @define_mixin, @clear_cache, clear_cache, @clear_route, clear_route

export ChannelName, getchannel

const ChannelName = String
const CHANNELFIELDNAME = :channel__
export getchannel

function getchannel(m::M) where {M<:ReactiveModel}
getfield(m, CHANNELFIELDNAME)
getfield(m, :channel__)
end


function setchannel(m::M, value) where {M<:ReactiveModel}
setfield!(m, CHANNELFIELDNAME, ChannelName(value))
setfield!(m, :channel__, String(value))
end

const AUTOFIELDS = [:isready, :isprocessing, :fileuploads, :ws_disconnected] # not DRY but we need a reference to the auto-set fields
const INTERNALFIELDS = [CHANNELFIELDNAME, :modes__] # not DRY but we need a reference to the auto-set fields
const INTERNALFIELDS = [:channel__, :modes__, :handlers__] # not DRY but we need a reference to the auto-set fields

@pour reactors begin
channel__::Stipple.ChannelName = Stipple.channelfactory()
modes__::LittleDict{Symbol, Int} = LittleDict(:modes__ => PRIVATE, :channel__ => PRIVATE)
handlers__::Vector{Function} = Function[]
modes__::LittleDict{Symbol, Int} = LittleDict(:modes__ => PRIVATE, :channel__ => PRIVATE, :handlers__ => PRIVATE)
isready::Stipple.R{Bool} = false
isprocessing::Stipple.R{Bool} = false
channel_::String = "" # not sure what this does if it's empty
Expand All @@ -192,7 +190,7 @@ function model_to_storage(::Type{T}, prefix = "", postfix = "") where T# <: Reac
values = getfield.(Ref(M()), fields)
storage = LittleDict{Symbol, Expr}()
for (f, type, v) in zip(fields, fieldtypes(M), values)
f = f in [:channel__, :modes__, AUTOFIELDS...] ? f : Symbol(prefix, f, postfix)
f = f in [INTERNALFIELDS..., AUTOFIELDS...] ? f : Symbol(prefix, f, postfix)
storage[f] = v isa Symbol ? :($f::$type = $(QuoteNode(v))) : :($f::$type = Stipple._deepcopy($v))
end
# fix channel field, which is not reconstructed properly by the code above
Expand All @@ -217,6 +215,21 @@ function merge_storage(storage_1::AbstractDict, storage_2::AbstractDict; keep_ch
setmode!(modes, get(m2, field, PUBLIC), field)
end
end

if haskey(storage_1, :handlers__) && haskey(storage_2, :handlers__)
for storage in (storage_1, storage_2)
haskey(storage, :handlers__) && postwalk!(storage[:handlers__]) do ex
MacroTools.@capture(ex, Stipple._deepcopy(x_)) ? x : ex
end
end
h1 = find_assignment(storage_1[:handlers__]).args[end]
h2 = find_assignment(storage_2[:handlers__]).args[end]

h1 isa Expr && (h1 = h1.args)
h2 isa Expr && (h2 = h2.args[2:end])
append!(h1, h2)
delete!(storage_2, :handlers__)
end
storage = merge(storage_1, storage_2)
storage[:modes__] = :(modes__::Stipple.LittleDict{Symbol, Int} = $modes)

Expand Down Expand Up @@ -371,7 +384,7 @@ macro var_storage(expr)
end
var, ex = parse_expression!(e, reactive ? mode : nothing, source, m, let_block, required_vars)
# prevent overwriting of control fields
var keys(Stipple.init_storage()) && continue
var [INTERNALFIELDS..., AUTOFIELDS...] && continue
if reactive == false
Stipple.setmode!(storage[:modes__], Core.eval(Stipple, mode), var)
end
Expand Down
2 changes: 1 addition & 1 deletion src/stipple/rendering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function Stipple.render(app::M)::Dict{Symbol,Any} where {M<:ReactiveModel}
for field in fieldnames(typeof(app))
f = getfield(app, field)

field != CHANNELFIELDNAME && occursin(SETTINGS.private_pattern, String(field)) && continue
field != :channel__ && occursin(SETTINGS.private_pattern, String(field)) && continue
f isa Reactive && f.r_mode == PRIVATE && continue

result[field] = Stipple.jsrender(f, field)
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ end
end
end

@eval model = TestApp |> init |> handlers
@eval model = TestApp |> init |> TestApp_handlers
@test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post)

# check reactivity
Expand Down

0 comments on commit 934e9b7

Please sign in to comment.