From 8f89b8171f41ffa12fc1be0cac1bbfa2203f08fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Sat, 23 Nov 2024 00:43:24 +0100 Subject: [PATCH] refactor app macros --- src/ReactiveTools.jl | 298 +++++++++++++------------------------- src/Stipple.jl | 2 +- src/Tools.jl | 36 ++++- src/stipple/reactivity.jl | 180 +++++------------------ 4 files changed, 174 insertions(+), 342 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index eda58fa3..d0241e3e 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -5,10 +5,7 @@ using MacroTools using MacroTools: postwalk using OrderedCollections import Genie -import Stipple: deletemode!, parse_expression!, init_storage - -# definition of variables -export @readonly, @private, @in, @out, @jsfn, @readonly!, @private!, @in!, @out!, @jsfn!, @mixin +import Stipple: deletemode!, parse_expression!, parse_expression, init_storage, striplines, striplines! #definition of handlers/events export @onchange, @onbutton, @event, @notify @@ -33,9 +30,6 @@ export @before_create, @created, @before_mount, @mounted, @before_update, @updat export DEFAULT_LAYOUT, Page -export @onchangeany # deprecated - -const REACTIVE_STORAGE = LittleDict{Module,LittleDict{Symbol,Expr}}() const HANDLERS = LittleDict{Module,Vector{Expr}}() const TYPES = LittleDict{Module,Union{<:DataType,Nothing}}() @@ -427,7 +421,7 @@ function update_storage(m::Module) # end end -import Stipple: @vars, @add_vars +import Stipple: @vars macro vars(expr) init_storage(__module__) @@ -438,13 +432,6 @@ macro vars(expr) REACTIVE_STORAGE[__module__] end -macro add_vars(expr) - init_storage(__module__) - REACTIVE_STORAGE[__module__] = Stipple.merge_storage(REACTIVE_STORAGE[__module__], @eval(__module__, Stipple.@var_storage($expr)); context = __module__) - - update_storage(__module__) -end - macro model() esc(quote Stipple.@type() |> Base.invokelatest @@ -475,16 +462,11 @@ end ``` """ macro app(expr) - delete_bindings!(__module__) - delete_handlers!(__module__) - - init_handlers(__module__) - init_storage(__module__) - + modelname = Symbol(model_typename(__module__)) + storage = Stipple.init_storage() quote - $expr - - Stipple.ReactiveTools.@handlers + Stipple.ReactiveTools.@app $modelname $expr __GF_AUTO_HANDLERS__ + Stipple.ReactiveTools.TYPES[$__module__] = $modelname end |> esc end @@ -523,23 +505,6 @@ function binding(expr::Expr, storage::LittleDict{Symbol, Expr}, @nospecialize(mo storage end -# this macro needs to run in a macro where `expr`is already defined -macro report_val() - quote - val = expr isa Symbol ? expr : expr.args[2] - issymbol = val isa Symbol - :(if $issymbol - if isdefined(@__MODULE__, $(QuoteNode(val))) - $val - else - @info(string("Warning: Variable '", $(QuoteNode(val)), "' not yet defined")) - end - else - Stipple.Observables.to_value($val) - end) |> esc - end |> esc -end - # this macro needs to run in a macro where `expr`is already defined macro define_var() quote @@ -550,136 +515,46 @@ macro define_var() end |> esc end -# works with -# @in a = 2 -# @in a::Vector = [1, 2, 3] -# @in a::Vector{Int} = [1, 2, 3] - -# the @in, @out and @private macros below are defined so a docstring can be attached -# the actual macro definition is done in the for loop further down -""" -```julia -@in(expr) -``` - -Declares a reactive variable that is public and can be written to from the UI. - -**Usage** -```julia -@app begin - @in N = 0 -end -``` -""" -macro in end - -""" -```julia -@out(expr) -``` - -Declares a reactive variable that is public and readonly. - -**Usage** -```julia -@app begin - @out N = 0 -end -``` -""" -macro out end - -""" -```julia -@private(expr) -``` - -Declares a non-reactive variable that cannot be accessed by UI code. - -**Usage** -```julia -@app begin - @private N = 0 -end -``` -""" -macro private end - -for (fn, mode) in [(:in, :PUBLIC), (:out, :READONLY), (:jsnfn, :JSFUNCTION), (:private, :PRIVATE)] - fn! = Symbol(fn, "!") - Core.eval(@__MODULE__, quote - - macro $fn!(expr) - binding(expr isa Symbol ? expr : copy(expr), __module__, $mode; source = __source__) - esc(:($expr)) - end - - macro $fn!(flag, expr) - flag != :non_reactive && return esc(:(ReactiveTools.$fn!($flag, _, $expr))) - binding(expr isa Symbol ? expr : copy(expr), __module__, $mode; source = __source__, reactive = false) - esc(:($expr)) - end - - macro $fn(location, flag, expr) - reactive = flag != :non_reactive - ex = [expr isa Symbol ? expr : copy(expr)] - loc = location isa Symbol ? QuoteNode(location) : location - - quote - local location = isdefined($__module__, $loc) ? eval($loc) : $loc - local storage = location isa DataType ? Stipple.model_to_storage(location) : location isa LittleDict ? location : Stipple.init_storage() - - Stipple.ReactiveTools.binding($ex[1], storage, $$mode; source = $__source__, reactive = $reactive, m = $__module__) - location isa DataType || location isa Symbol ? eval(:(Stipple.@type($$loc, $storage))) : location - end |> esc +function parse_expression_macro(expr::Expr, storage::LittleDict, m::Module) + expr.head == :macrocall || return expr + flag = :nothing + fn = Symbol(String(expr.args[1])[2:end]) + mode = Dict(:in => PUBLIC, :out => READONLY, :jsnfn => JSFUNCTION, :private => PRIVATE, :mixin => 0)[fn] + + source = filter(x -> x isa LineNumberNode, expr.args) + expr = striplines!(copy(expr)) + params = expr.args[2:end] + + if fn != :mixin + location = :nothing + if length(params) == 2 + location, expr = params + elseif length(params) == 3 + location, flag, expr = params + # elseif length(params) == 4 + # location, flag, _, expr = params + else + error("2, 3, or 4 arguments expected, found $(length(params))") end - macro $fn(expr) - binding(expr isa Symbol ? expr : copy(expr), __module__, $mode; source = __source__) - @report_val() - end + reactive = flag != :non_reactive + ex = [expr isa Symbol ? expr : copy(expr)] - macro $fn(flag, expr) - flag != :non_reactive && return esc(:(ReactiveTools.@fn($flag, _, $expr))) - binding(expr isa Symbol ? expr : copy(expr), __module__, $mode; source = __source__, reactive = false) - @report_val() + Stipple.ReactiveTools.binding(ex[1], storage, mode; source, reactive, m) + elseif fn == :mixin + prefix = "" + expr = params[end] + mixin = if hasproperty(expr, :head) && expr.head == :(::) + prefix = string(expr.args[1]) + expr.args[2] + else + expr end - end) -end - -macro mixin(expr, prefix = "", postfix = "") - # if prefix is not a String then call the @mixin version for generic model types - prefix isa String || return quote - @mixin $expr $prefix $postfix "" - end - - storage = init_storage(__module__) - - Stipple.ReactiveTools.update_storage(__module__) - Core.eval(__module__, quote - Stipple.ReactiveTools.@mixin $storage $expr $prefix $postfix - end) - quote end -end - -macro mixin(location, expr, prefix, postfix) - if hasproperty(expr, :head) && expr.head == :(::) - prefix = string(expr.args[1]) - expr = expr.args[2] + mixin_storage = Stipple.model_to_storage(@eval(m, $mixin), prefix, "") + merge!(storage, Stipple.merge_storage(storage, mixin_storage; context = m)) + else + error("Unknown macro @$fn") end - loc = location isa Symbol ? QuoteNode(location) : location - - x = Core.eval(__module__, expr) - quote - local location = $loc isa Symbol && isdefined($__module__, $loc) ? $__module__.$(loc isa QuoteNode ? loc.value : loc) : $loc - local storage = location isa DataType ? Stipple.model_to_storage(location) : location isa LittleDict ? location : Stipple.init_storage() - M = $x isa DataType ? $x : typeof($x) # really needed? - local mixin_storage = Stipple.model_to_storage(M, $(QuoteNode(prefix)), $postfix) - - merge!(storage, Stipple.merge_storage(storage, mixin_storage; context = @__MODULE__)) - location isa DataType || location isa Symbol ? eval(:(Stipple.@type($$loc, $storage))) : location - mixin_storage - end |> esc end #===# @@ -785,14 +660,10 @@ macro app(typename, expr, handlers_fn_name = :handlers) end macro handlers() - handlers = init_handlers(__module__) - + modelname = Symbol(model_typename(__module__)) quote - function __GF_AUTO_HANDLERS__(__model__) - $(handlers...) - - return __model__ - end + Stipple.ReactiveTools.TYPES[$__module__] = Stipple.@type + Stipple.ReactiveTools.@handlers $modelname Stipple.ReactiveTools.REACTIVE_STORAGE[$__module__] __GF_AUTO_HANDLERS__ end |> esc end @@ -807,6 +678,29 @@ macro handlers(expr) end |> esc end +""" + get_varnames(app_expr::Vector, context::Module) + +Return a list of all non-internal variable names used in a vector of var definitions lines. +""" +function get_varnames(app_expr::Vector, context::Module) + varnames = copy(Stipple.AUTOFIELDS) + app_expr = striplines(app_expr) + for ex in app_expr + if ex.args[1] ∈ [Symbol("@in"), Symbol("@out"), Symbol("@jsfunction"), Symbol("@private")] + res = Stipple.parse_expression(ex) + push!(varnames, res isa Symbol ? res : res[1]) + elseif ex.args[1] == Symbol("@mixin") + prefix, mixin = ex.args[end] isa Symbol ? Any[nothing, ex.args[end]] : ex.args[end].args + fnames = setdiff(@eval(context, collect($mixin isa LittleDict ? keys($mixin) : propertynames($mixin()))), Stipple.AUTOFIELDS, Stipple.INTERNALFIELDS) + prefix === nothing || (fnames = Symbol.(prefix, fnames)) + append!(varnames, fnames) + end + end + @assert(length(unique(varnames)) == length(varnames), "Duplicate field names detected") + varnames +end + macro handlers(typename, expr, handlers_fn_name = :handlers) newtype = endswith(String(typename), "_!_") newtype && (typename = Symbol(String(typename)[1:end-3])) @@ -814,7 +708,7 @@ macro handlers(typename, expr, handlers_fn_name = :handlers) expr = wrap(expr, :block) i_start = 1 handlercode = [] - initcode = quote end + initcode = [] for (i, ex) in enumerate(expr.args) if ex isa Expr @@ -822,24 +716,24 @@ macro handlers(typename, expr, handlers_fn_name = :handlers) ex_index = .! isa.(ex.args, LineNumberNode) if sum(ex_index) < 4 pos = findall(ex_index)[2] - insert!(ex.args, pos, typename) + insert!(ex.args, pos, :__storage__) end push!(handlercode, expr.args[i_start:i]...) else if ex.head == :macrocall && ex.args[1] in Symbol.(["@in", "@out", "@private", "@readonly", "@jsfn", "@mixin"]) ex_index = isa.(ex.args, Union{Symbol, Expr}) pos = findall(ex_index)[2] - sum(ex_index) == 2 && ex.args[1] != Symbol("@mixin") && insert!(ex.args, pos, :_) - insert!(ex.args, pos, :__storage__) + sum(ex_index) == 2 && ex.args[1] != Symbol("@mixin") && insert!.(Ref(ex.args), pos, [:_]) + insert!(ex.args, pos, typename) end - push!(initcode.args, expr.args[i_start:i]...) + push!(initcode, expr.args[i_start:i]...) end i_start = i + 1 end end # model_to_storage is only needed when we add variables to an existing type. - no_new_vars = findfirst(x -> x isa Expr, initcode.args) === nothing + no_new_vars = findfirst(x -> x isa Expr, initcode) === nothing # if we redefine a type newtype is true if isdefined(__module__, typename) && no_new_vars && ! newtype # model is already defined and no variables are added and we are not redefining a model type @@ -850,40 +744,41 @@ macro handlers(typename, expr, handlers_fn_name = :handlers) else Stipple.init_storage() end - initcode = quote - # define a local variable __storage__ with the value of storage - # that will be used by the macro afterwards - __storage__ = $storage - # add more definitions to __storage___ - $(initcode.args...) - end - - # needs to be executed before evaluation of handler code - # because the handler code depends on the model fields. - @eval __module__ begin - # execution of initcode will fill up the __storage__ - $initcode - Stipple.@type($typename, values(__storage__)) - end + + varnames = get_varnames(initcode, __module__) end + filter!(x -> !isa(x, LineNumberNode), initcode) + parse_expression_macro.(initcode, Ref(storage), Ref(__module__)) + initcode_final = :(Stipple.@type($typename, $storage)) + handlercode_final = [] + d = LittleDict(varnames .=> varnames) + d_expr = :($d) for ex in handlercode if ex isa Expr + replace!(ex.args, :__storage__ => d_expr) push!(handlercode_final, @eval(__module__, $ex)) else push!(handlercode_final, ex) end end + + # println("initcode: ", initcode) + # println("initcode_final: ", initcode_final) + # println("handlercode: ", handlercode) + # println("handlercode_final: ", handlercode_final) quote + $(initcode_final) Stipple.ReactiveTools.delete_events($typename) - + function $handlers_fn_name(__model__) $(handlercode_final...) __model__ end + Stipple.ReactiveTools.HANDLERS_FUNCTIONS[$typename] = $handlers_fn_name ($typename, $handlers_fn_name) end |> esc end @@ -1003,6 +898,17 @@ function get_known_vars(M::Module) reactive_vars, non_reactive_vars end +function get_known_vars(storage::LittleDict) + reactive_vars = Symbol[] + non_reactive_vars = Symbol[] + for (k, v) in storage + k in Stipple.INTERNALFIELDS && continue + is_reactive = v isa Symbol ? true : startswith(string(Stipple.split_expr(v)[2]), r"(Stipple\.)?R(eactive)?($|{)") + push!(is_reactive ? reactive_vars : non_reactive_vars, k) + end + reactive_vars, non_reactive_vars +end + function get_known_vars(::Type{M}) where M<:ReactiveModel CM = Stipple.get_concrete_type(M) reactive_vars = Symbol[] @@ -1045,7 +951,7 @@ macro onchange(var, expr) end macro onchange(location, vars, expr) - loc::Union{Module, Type{<:M}} where M<:ReactiveModel = @eval __module__ $location + loc::Union{Module, Type{<:M}, LittleDict} where M<:ReactiveModel = @eval __module__ $location vars = wrap(vars, :tuple) expr = wrap(expr, :block) diff --git a/src/Stipple.jl b/src/Stipple.jl index 71ec16ac..367face3 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -1257,7 +1257,7 @@ using Stipple.ReactiveTools @stipple_precompile begin ui() = [cell("hello"), row("world"), htmldiv("Hello World")] - @app PrecompileApp begin + @eval @app PrecompileApp begin @in demo_i = 1 @out demo_s = "Hi" diff --git a/src/Tools.jl b/src/Tools.jl index 0f918d70..1ef4e477 100644 --- a/src/Tools.jl +++ b/src/Tools.jl @@ -210,4 +210,38 @@ macro stipple_precompile(workload) $workload end end -end \ No newline at end of file +end + +""" + striplines!(ex::Union{Expr, Vector}) + +Remove all line number nodes from an expression or vector of expressions. See also `striplines`. +""" +function striplines!(ex::Expr) + for i in reverse(eachindex(ex.args)) + if isa(ex.args[i], LineNumberNode) + deleteat!(ex.args, i) + elseif isa(ex.args[i], Expr) + striplines!(ex.args[i]) + end + end + ex +end + +function striplines!(exprs::Vector) + for i in reverse(eachindex(exprs)) + if isa(exprs[i], LineNumberNode) + deleteat!(exprs, i) + elseif isa(exprs[i], Expr) + striplines!(exprs[i]) + end + end + exprs +end + +""" + striplines(ex::Union{Expr, Vector}) + +Return a copy of an expression with all line number nodes removed. See also `striplines!`. +""" +striplines(ex) = striplines!(copy(ex)) \ No newline at end of file diff --git a/src/stipple/reactivity.jl b/src/stipple/reactivity.jl index 89ed9057..7c266a33 100644 --- a/src/stipple/reactivity.jl +++ b/src/stipple/reactivity.jl @@ -153,10 +153,8 @@ end """ abstract type ReactiveModel end -export @vars, @add_vars, @define_mixin, @clear_cache, clear_cache, @clear_route, clear_route +export @vars, @define_mixin, @clear_cache, clear_cache, @clear_route, clear_route -# deprecated -export @reactive, @reactive!, @old_reactive, @old_reactive! export ChannelName, getchannel const ChannelName = String @@ -184,15 +182,6 @@ const INTERNALFIELDS = [CHANNELFIELDNAME, :modes__] # not DRY but we need a refe ws_disconnected::Stipple.R{Bool} = false end -@mix Stipple.@with_kw mutable struct old_reactive - Stipple.@reactors -end - - -@mix Stipple.@kwredef mutable struct old_reactive! - Stipple.@reactors -end - function split_expr(expr) expr.args[1] isa Symbol ? (expr.args[1], nothing, expr.args[2]) : (expr.args[1].args[1], expr.args[1].args[2], expr.args[2]) end @@ -261,16 +250,17 @@ function parse_expression!(expr::Expr, @nospecialize(mode) = nothing, source = n source = (source !== nothing ? String(strip(string(source), collect("#= "))) : "") + # args[end] instead of args[2] because of potential LineNumberNode var = expr.args[1] if !isnothing(mode) mode = mode isa Symbol && ! isdefined(m, mode) ? :(Stipple.$mode) : mode type = if isa(var, Expr) && var.head == Symbol("::") # change type T to type R{T} - var.args[2] = :($Rtype{$(var.args[2])}) + var.args[end] = :($Rtype{$(var.args[end])}) else try # add type definition `::R{T}` to the var where T is the type of the default value - T = @eval m typeof($(expr.args[2])) + T = @eval(m === nothing ? @__MODULE__() : m, typeof($(expr.args[end]))) expr.args[1] = :($var::$Rtype{$T}) Rtype catch ex @@ -279,13 +269,13 @@ function parse_expression!(expr::Expr, @nospecialize(mode) = nothing, source = n :($Rtype{Any}) end end - expr.args[2] = :($type($(expr.args[2]), $mode, false, false, $source)) + expr.args[end] = :($type($(expr.args[end]), $mode, false, false, $source)) end # if no type is defined, set the type of the default value if expr.args[1] isa Symbol try - T = @eval m typeof($(expr.args[2])) + T = @eval m typeof($(expr.args[end])) expr.args[1] = :($(expr.args[1])::$T) catch ex # if the default value is not defined, we can't infer the type @@ -296,26 +286,14 @@ function parse_expression!(expr::Expr, @nospecialize(mode) = nothing, source = n expr.args[1].args[1], expr end -macro var_storage(expr, new_inputmode = :auto) +parse_expression(expr::Expr, mode = nothing, source = nothing, m = nothing) = parse_expression!(copy(expr), mode, source, m) + +macro var_storage(expr) m = __module__ if expr.head != :block expr = quote $expr end end - if new_inputmode == :auto - new_inputmode = true - for e in expr.args - e isa LineNumberNode && continue - e.args[1] isa Symbol && continue - - type = e.args[1].args[2] - if startswith(string(type), r"(Stipple\.)?R(eactive)?($|{)") - new_inputmode = false - break - end - end - end - storage = init_storage() source = nothing @@ -327,34 +305,17 @@ macro var_storage(expr, new_inputmode = :auto) mode = :PUBLIC reactive = true if e.head == :(=) - var, ex = if new_inputmode - #check whether flags are set - if e.args[end] isa Expr && e.args[end].head == :tuple - flags = e.args[end].args[2:end] - if length(flags) > 0 && flags[1] ∈ [:READONLY, :PRIVATE, :JSFUNCTION, :NON_REACTIVE] - newmode = intersect(setdiff(flags, [:NON_REACTIVE]), [:READONLY, :PRIVATE, :JSFUNCTION]) - length(newmode) > 0 && (mode = newmode[end]) - reactive = :NON_REACTIVE ∉ flags - e.args[end] = e.args[end].args[1] - end - end - var, ex = parse_expression!(e, reactive ? mode : nothing, source, m) - else - var = e.args[1] - if var isa Symbol - reactive = false - else - type = var.args[2] - reactive = startswith(string(type), r"(Stipple\.)?R(eactive)?($|{)") - var = var.args[1] - end - if occursin(Stipple.SETTINGS.private_pattern, string(var)) - mode = :PRIVATE - elseif occursin(Stipple.SETTINGS.readonly_pattern, string(var)) - mode = :READONLY - end - var, e + #check whether flags are set + if e.args[end] isa Expr && e.args[end].head == :tuple + flags = e.args[end].args[2:end] + if length(flags) > 0 && flags[1] ∈ [:READONLY, :PRIVATE, :JSFUNCTION, :NON_REACTIVE] + newmode = intersect(setdiff(flags, [:NON_REACTIVE]), [:READONLY, :PRIVATE, :JSFUNCTION]) + length(newmode) > 0 && (mode = newmode[end]) + reactive = :NON_REACTIVE ∉ flags + e.args[end] = e.args[end].args[1] + end end + var, ex = parse_expression!(e, reactive ? mode : nothing, source, m) # prevent overwriting of control fields var ∈ keys(Stipple.init_storage()) && continue if reactive == false @@ -412,19 +373,26 @@ macro type(modelname, storage) modelconst = Symbol(modelname, '!') modelconst_qn = QuoteNode(modelconst) + output = quote end + output.args = @eval __module__ collect(values($storage)) + output_qn = QuoteNode(output) + + is_called_by_revise = length(output.args) <= 1 + is_called_by_revise && error("No fields defined in model $modelname") + quote abstract type $modelname <: Stipple.ReactiveModel end - local output = quote end - output.args = collect(values($storage)) + # Revise seems to call the macro line by line internally for code tracking purposes. # Interstingly, Revise will not populate output.args in that case and will generate an empty model. # We use this to our advantage and prevent additional model generation when length(output.args) <= 1. - local is_called_by_revise = length(output.args) <= 1 - eval(quote - $is_called_by_revise || Stipple.@kwredef mutable struct $$modelconst_qn <: $$modelname - $output - end - end) + local is_called_by_revise = length($(output.args)) <= 1 + is_called_by_revise && error("No variables defined") + + @eval Stipple.@kwredef mutable struct $modelconst <: $modelname + $output + end + $modelname(; kwargs...) = $modelconst(; kwargs...) Stipple.get_concrete_type(::Type{$modelname}) = $modelconst @@ -446,51 +414,13 @@ end e::String = "private", NON_REACTIVE, PRIVATE end ``` -This macro replaces the old `@reactive!` and doesn't need the Reactive in the declaration. -Instead the non_reactives are marked by a flag. The old declaration syntax is still supported -to make adaptation of old code easier. -``` -@vars HHModel begin - a::R{Int} = 1 - b::R{Float64} = 2 - c::String = "Hello" - d_::String = "readonly" - e__::String = "private" -end -``` -by - -```julia -@reactive! mutual struct HHModel <: ReactiveModel - a::R{Int} = 1 - b::R{Float64} = 2 - c::String = "Hello" - d_::String = "readonly" - e__::String = "private" -end -``` - -Old syntax is still supported by @vars and can be forced by the `new_inputmode` argument. - """ -macro vars(modelname, expr, new_inputmode = :auto) +macro vars(modelname, expr) quote - Stipple.@type($modelname, values(Stipple.@var_storage($expr, $new_inputmode))) + Stipple.@type($modelname, values(Stipple.@var_storage($expr))) end |> esc end -macro add_vars(modelname, expr, new_inputmode = :auto) - storage = @eval(__module__, Stipple.@var_storage($expr, $new_inputmode)) - new_storage = if isdefined(__module__, modelname) - old_storage = @eval(__module__, Stipple.model_to_storage($modelname)) - ReactiveTools.merge_storage(old_storage, storage; context = __module__) - else - storage - end - - esc(:(Stipple.@type $modelname $new_storage)) -end - macro define_mixin(mixin_name, expr) storage = @eval(__module__, Stipple.@var_storage($expr)) delete!.(Ref(storage), [:channel__, Stipple.AUTOFIELDS...]) @@ -502,44 +432,6 @@ macro define_mixin(mixin_name, expr) end |> esc end -macro reactive!(expr) - warning = """@reactive! is deprecated, please replace use `@vars` instead. - - In case of errors, please replace `@reactive!` by `@old_reactive!` and open an issue at - https://github.com/GenieFramework/Stipple.jl. - - If you use `@old_reactive!`, make sure to call `accessmode_from_pattern!()`, because the internals for - accessmode have changed, e.g. - ``` - model = init(MyDashboard) |> accessmode_from_pattern! |> handlers |> ui |> html - ``` - """ - @warn warning - output = @eval(__module__, values(Stipple.@var_storage($(expr.args[3]), false))) - expr.args[3] = quote $(output...) end - - esc(:(Stipple.@kwredef $expr)) -end - -macro reactive(expr) - warning = """@reactive is deprecated, please replace use `@vars` instead. - - In case of errors, please replace `@reactive` by `@old_reactive!` and open an issue at - https://github.com/GenieFramework/Stipple.jl. - If you use `@old_reactive!`, make sure to call `accessmode_from_pattern!()`, because the internals for - accessmode have changed, e.g. - ``` - model = init(MyDashboard) |> accessmode_from_pattern! |> handlers |> ui |> html - ``` - - """ - @warn warning - output = @eval(__module__, values(Stipple.@var_storage($(expr.args[3]), false))) - expr.args[3] = quote $(output...) end - - esc(:(Base.@kwdef $expr)) -end - #===# mutable struct Settings