From a8530d08daac551adb7e06e7a303630c61e0b3f0 Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 17 Apr 2020 17:30:05 +0300 Subject: [PATCH 1/6] Refactor Dash.jl to support same parameters as Dash for Python & R --- src/ComponentPackages.jl | 11 ++ src/Dash.jl | 10 +- src/app.jl | 155 +++++++++++++++++++++------ src/config.jl | 50 +++++++++ src/handlers.jl | 69 +++--------- src/index_page.jl | 125 ++++++++++++++++++++++ src/utils.jl | 28 ++++- test/config_functional.jl | 207 ++++++++++++++++++++++++++++++++++++ test/core.jl | 14 +-- test/dash_creation.jl | 122 +++++++++++++++++++++ test/dev.jl | 2 + test/images/test_images.png | Bin 0 -> 8926 bytes test/runtests.jl | 3 + test/utils.jl | 19 ++++ 14 files changed, 710 insertions(+), 105 deletions(-) create mode 100644 src/config.jl create mode 100644 src/index_page.jl create mode 100644 test/config_functional.jl create mode 100644 test/dash_creation.jl create mode 100644 test/images/test_images.png create mode 100644 test/utils.jl diff --git a/src/ComponentPackages.jl b/src/ComponentPackages.jl index 24b1c37..6e53ee4 100644 --- a/src/ComponentPackages.jl +++ b/src/ComponentPackages.jl @@ -43,6 +43,17 @@ module ComponentPackages ) end + function components_js_sources(prefix = ""; debug=false) + type = debug ? :dev : :prod + result = [] + + for p in values(_components_packages) + for script in p.scripts[type] + push!(result, "$(prefix)_dash-component-suites/$(p.package_name)/$(script)") + end + end + return result + end diff --git a/src/Dash.jl b/src/Dash.jl index 26024e3..3eb519a 100644 --- a/src/Dash.jl +++ b/src/Dash.jl @@ -1,4 +1,5 @@ module Dash +components = [1,2,3,4,5] import HTTP, JSON2 using MacroTools include("ComponentPackages.jl") @@ -15,11 +16,17 @@ export dash, Component, Front, @use, <|, @callid_str, CallbackId, callback!, run_server, PreventUpdate, no_update, @wildprop ComponentPackages.@reg_components() +include("utils.jl") +include("config.jl") include("app.jl") +include("index_page.jl") include("handlers.jl") -include("utils.jl") +macro test() + +end + @doc """ module Dash @@ -91,4 +98,5 @@ function run_server(app::DashApp, host = HTTP.Sockets.localhost, port = 8080; de HTTP.serve(handler, host, port) end + end # module \ No newline at end of file diff --git a/src/app.jl b/src/app.jl index b2381f1..332c454 100644 --- a/src/app.jl +++ b/src/app.jl @@ -1,5 +1,25 @@ const IdProp = Tuple{Symbol, Symbol} +const default_index = """ + + + {%metas%} + {%title%} + {%favicon%} + {%css%} + + + {%app_entry%} + + +""" + + + struct CallbackId state ::Vector{IdProp} input ::Vector{IdProp} @@ -34,6 +54,27 @@ mutable struct Layout component::Union{Nothing, Component} end +const ExternalSrcType = Union{String, Dict{String, String}} + +struct DashConfig + external_stylesheets ::Vector{ExternalSrcType} + external_scripts ::Vector{ExternalSrcType} + url_base_pathname ::Union{String, Nothing} #TODO This looks unused + requests_pathname_prefix ::String + routes_pathname_prefix ::String + assets_folder ::String + assets_url_path ::String + assets_ignore ::String + serve_locally ::Bool + suppress_callback_exceptions ::Bool + eager_loading ::Bool + meta_tags ::Vector{Dict{String, String}} + index_string ::Union{String, Nothing} + assets_external_path ::Union{String, Nothing} + include_assets_files ::Bool + show_undo_redo ::Bool +end + """ struct DashApp <: Any @@ -41,26 +82,13 @@ Representation of Dash application """ struct DashApp name ::String + config ::DashConfig layout ::Layout callbacks ::Dict{Symbol, Callback} - external_stylesheets ::Vector{String} - external_scripts ::Vector{String} - url_base_pathname ::String - assets_folder ::String - callable_components ::Dict{Symbol, Component} - function DashApp(name::String; - external_stylesheets ::Vector{String} = Vector{String}(), - external_scripts ::Vector{String} = Vector{String}(), - url_base_pathname="/", - assets_folder::String = "assets") - - new(name, Layout(nothing), Dict{Symbol, Callback}(), - external_stylesheets, external_scripts, - url_base_pathname, assets_folder, - Dict{Symbol, Component}() - - ) - end + callable_components ::Dict{Symbol, Component} + + DashApp(name::String, config::DashConfig) = new(name, config, Layout(nothing), Dict{Symbol, Callback}(), Dict{Symbol, Component}()) + end function layout!(app::DashApp, component::Component) @@ -78,7 +106,7 @@ function Base.getproperty(app::DashApp, name::Symbol) name == :layout ? getlayout(app) : Base.getfield(app, name) end - + @@ -108,29 +136,86 @@ end """ function dash(name::String; - external_stylesheets ::Vector{String} = Vector{String}(), - external_scripts ::Vector{String} = Vector{String}(), - url_base_pathname="/", - assets_folder::String = "assets") - result = DashApp(name, - external_stylesheets=external_stylesheets, - external_scripts=external_scripts, - url_base_pathname=url_base_pathname, - assets_folder = assets_folder - ) + external_stylesheets = ExternalSrcType[], + external_scripts = ExternalSrcType[], + url_base_pathname = nothing, + requests_pathname_prefix = nothing, + routes_pathname_prefix = nothing, + assets_folder = "assets", + assets_url_path = "assets", + assets_ignore = "", + serve_locally = true, + suppress_callback_exceptions = false, + eager_loading = false, + meta_tags = Dict{Symbol, String}[], + index_string = default_index, + assets_external_path = nothing, + include_assets_files = true, + show_undo_redo = false + + ) + + + config = DashConfig( + external_stylesheets, + external_scripts, + pathname_configs( + url_base_pathname, + requests_pathname_prefix, + routes_pathname_prefix + )..., + absolute_assets_path(assets_folder), + lstrip(assets_url_path, '/'), + assets_ignore, + serve_locally, + suppress_callback_exceptions, + eager_loading, + meta_tags, + index_string, + assets_external_path, + include_assets_files, + show_undo_redo + ) + + result = DashApp(name, config) return result end -function dash(layout_maker::Function, name::String; - external_stylesheets ::Vector{String} = Vector{String}(), - external_scripts ::Vector{String} = Vector{String}(), - url_base_pathname="/", - assets_folder::String = "assets") +function dash(layout_maker ::Function, name; + external_stylesheets = ExternalSrcType[], + external_scripts = ExternalSrcType[], + url_base_pathname = nothing, + requests_pathname_prefix = nothing, + routes_pathname_prefix = nothing, + assets_folder = "assets", + assets_url_path = "assets", + assets_ignore = "", + serve_locally = true, + suppress_callback_exceptions = false, + eager_loading = false, + meta_tags = Dict{Symbol, String}[], + index_string = default_index, + assets_external_path = nothing, + include_assets_files = true, + show_undo_redo = false + ) result = dash(name, external_stylesheets=external_stylesheets, external_scripts=external_scripts, url_base_pathname=url_base_pathname, - assets_folder = assets_folder + requests_pathname_prefix = requests_pathname_prefix, + routes_pathname_prefix = routes_pathname_prefix, + assets_folder = assets_folder, + assets_url_path = assets_url_path, + assets_ignore = assets_ignore, + serve_locally = serve_locally, + suppress_callback_exceptions = suppress_callback_exceptions, + eager_loading = eager_loading, + meta_tags = meta_tags, + index_string = index_string, + assets_external_path = assets_external_path, + include_assets_files = include_assets_files, + show_undo_redo = show_undo_redo ) layout!(result, layout_maker()) return result diff --git a/src/config.jl b/src/config.jl new file mode 100644 index 0000000..170a65c --- /dev/null +++ b/src/config.jl @@ -0,0 +1,50 @@ +function absolute_assets_path(assets_path::AbstractString) + #TODO app path, enviroment variable etc.. + return joinpath(pwd(), assets_path) +end + +function pathname_configs(url_base_pathname, requests_pathname_prefix, routes_pathname_prefix) + + raise_error = (s) -> error(""" + $s This is ambiguous. + To fix this, set `routes_pathname_prefix` instead of `url_base_pathname`. + + Note that `requests_pathname_prefix` is the prefix for the AJAX calls that + originate from the client (the web browser) and `routes_pathname_prefix` is + the prefix for the API routes on the backend (this flask server). + `url_base_pathname` will set `requests_pathname_prefix` and + `routes_pathname_prefix` to the same value. + If you need these to be different values then you should set + `requests_pathname_prefix` and `routes_pathname_prefix`, + not `url_base_pathname`. + """) + + + if !isnothing(url_base_pathname) && !isnothing(requests_pathname_prefix) + raise_error("You supplied `url_base_pathname` and `requests_pathname_prefix`") + end + + if !isnothing(url_base_pathname) && !isnothing(routes_pathname_prefix) + raise_error("You supplied `url_base_pathname` and `routes_pathname_prefix`") + end + + if !isnothing(url_base_pathname) && isnothing(routes_pathname_prefix) + routes_pathname_prefix = url_base_pathname + elseif isnothing(routes_pathname_prefix) + routes_pathname_prefix = "/" + end + + !startswith(routes_pathname_prefix, "/") && error("routes_pathname_prefix` needs to start with `/`") + !endswith(routes_pathname_prefix, "/") && error("routes_pathname_prefix` needs to end with `/`") + + if isnothing(requests_pathname_prefix) + requests_pathname_prefix = routes_pathname_prefix + end + + !startswith(requests_pathname_prefix, "/") && + error("requests_pathname_prefix` needs to start with `/`") + !endswith(requests_pathname_prefix, routes_pathname_prefix) && + error("requests_pathname_prefix` needs to end with `routes_pathname_prefix`") + + return (url_base_pathname, requests_pathname_prefix, routes_pathname_prefix) +end \ No newline at end of file diff --git a/src/handlers.jl b/src/handlers.jl index 3be6934..cb2f9ca 100644 --- a/src/handlers.jl +++ b/src/handlers.jl @@ -1,49 +1,4 @@ -function index_page(app::DashApp; debug = false) - metas = ["""""", """"""] - title = app.name - css = join(map(app.external_stylesheets) do s - """""" - end, "\n" - ) - external_scripts = join(map(app.external_scripts) do s - """""" - end, "\n") - - app_entry = """ -
-
- Loading... -
-
- """ - config = ( - url_base_pathname = "$(app.url_base_pathname)", - requests_pathname_prefix = "$(app.url_base_pathname)", - ui = true, - props_check = debug, - show_undo_redo = false - ) - - scripts = ComponentPackages.components_js_include(app.url_base_pathname, debug = debug) - """ - - - $(join(metas, "")) - $(title) - $(css) - - - $(app_entry) - - - """ -end function dependencies_json(app::DashApp) id_prop_named(p::IdProp) = (id = p[1], property = p[2]) @@ -108,8 +63,9 @@ function process_callback(app::DashApp, body::String) end function process_assets(app::DashApp, path) - assets_path = "$(app.url_base_pathname)assets/" - filename = joinpath(app.assets_folder, replace(path, assets_path=>"")) + assets_path = "$(app.config.routes_pathname_prefix)" * strip(app.config.assets_url_path, '/') * "/" + + filename = joinpath(app.config.assets_folder, replace(path, assets_path=>"")) try return HTTP.Response(200, [], body = read(filename)) catch @@ -119,22 +75,24 @@ end function make_handler(app::DashApp; debug::Bool = false) - function (req::HTTP.Request) + index_string::String = index_page(app, debug = debug) + + return function (req::HTTP.Request) uri = HTTP.URI(req.target) - ComponentPackages.@register_js_sources(uri.path, app.url_base_pathname) - if uri.path == "$(app.url_base_pathname)" - return HTTP.Response(200, index_page(app, debug = debug)) + ComponentPackages.@register_js_sources(uri.path, app.config.routes_pathname_prefix) + if uri.path == "$(app.config.routes_pathname_prefix)" + return HTTP.Response(200, index_string) end - if uri.path == "$(app.url_base_pathname)_dash-layout" + if uri.path == "$(app.config.routes_pathname_prefix)_dash-layout" return HTTP.Response(200, ["Content-Type" => "application/json"], body = JSON2.write(app.layout)) end - if uri.path == "$(app.url_base_pathname)_dash-dependencies" + if uri.path == "$(app.config.routes_pathname_prefix)_dash-dependencies" return HTTP.Response(200, ["Content-Type" => "application/json"], body = dependencies_json(app)) end - if startswith(uri.path, "$(app.url_base_pathname)assets/") + if startswith(uri.path, "$(app.config.routes_pathname_prefix)assets/") return process_assets(app, uri.path) end - if uri.path == "$(app.url_base_pathname)_dash-update-component" && req.method == "POST" + if uri.path == "$(app.config.routes_pathname_prefix)_dash-update-component" && req.method == "POST" try return HTTP.Response(200, ["Content-Type" => "application/json"], body = JSON2.write( @@ -151,4 +109,5 @@ function make_handler(app::DashApp; debug::Bool = false) end return HTTP.Response(404) end + end \ No newline at end of file diff --git a/src/index_page.jl b/src/index_page.jl new file mode 100644 index 0000000..ea300b1 --- /dev/null +++ b/src/index_page.jl @@ -0,0 +1,125 @@ +make_css_tag(url::String) = """""" +make_css_tag(dict::Dict{String, String}) = formattag("link", dict, opened = true) + +make_scipt_tag(url::String) = """""" +make_scipt_tag(dict::Dict{String, String}) = formattag("script", dict) + +function metas_html(app::DashApp) + meta_tags = app.config.meta_tags + has_ie_compat = any(meta_tags) do tag + get(tag, "http-equiv", "") == "X-UA-Compatible" + end + has_charset = any(tag -> haskey(tag, "charset"), meta_tags) + + result = String[] + !has_ie_compat && push!(result, "") + !has_charset && push!(result, "") + + append!(result, formattag.("meta", meta_tags, opened = true)) + return join(result, "\n ") + +end + +css_html(app::DashApp) = join(map(make_css_tag, app.config.external_stylesheets), "\n ") + +app_entry_html() = """ +
+
+ Loading... +
+
+""" + +function config_html(app::DashApp; debug = false) + config = ( + url_base_pathname = app.config.url_base_pathname, + requests_pathname_prefix = app.config.requests_pathname_prefix, + ui = true, + props_check = debug, + show_undo_redo = app.config.show_undo_redo, + suppress_callback_exceptions = app.config.suppress_callback_exceptions + ) + return """""" +end + + + +function scripts_html(app::DashApp; debug = false) + scripts = app.config.external_scripts + append!(scripts, + ComponentPackages.components_js_sources(app.config.requests_pathname_prefix, debug = debug) + ) + return join(map(make_scipt_tag, scripts), "\n ") +end + +renderer_html() = """""" + +favicon_html(app::DashApp) = "" + +#= +def _walk_assets_directory(self): + walk_dir = self.config.assets_folder + slash_splitter = re.compile(r"[\\/]+") + ignore_str = self.config.assets_ignore + ignore_filter = re.compile(ignore_str) if ignore_str else None + + for current, _, files in os.walk(walk_dir): + if current == walk_dir: + base = "" + else: + s = current.replace(walk_dir, "").lstrip("\\").lstrip("/") + splitted = slash_splitter.split(s) + if len(splitted) > 1: + base = "/".join(slash_splitter.split(s)) + else: + base = splitted[0] + + if ignore_filter: + files_gen = (x for x in files if not ignore_filter.search(x)) + else: + files_gen = files + + for f in sorted(files_gen): + path = "/".join([base, f]) if base else f + + full = os.path.join(current, f) + + if f.endswith("js"): + self.scripts.append_script(self._add_assets_resource(path, full)) + elif f.endswith("css"): + self.css.append_css(self._add_assets_resource(path, full)) + elif f == "favicon.ico": + self._favicon = path +=# + +function find_assets_files(app::DashApp) + result = (css = String[], js = String[], favicon = nothing) + + if app.config.include_assets_files + for (base, dirs, files) in walkdir(app.config.assets_folder) + if !isempty(files) + relative = (base == app.config.assets_folder) ? "" : relpath(base, app.config.assets_folder) + relative_uri = join(splitpath(relative), "/") * "/" + + + end + + + end + end + return result +end + +function index_page(app::DashApp; debug = false) + + return interpolate_string(app.config.index_string, + metas = metas_html(app), + title = app.name, + favicon = favicon_html(app), + css = css_html(app), + app_entry = app_entry_html(), + config = config_html(app, debug = debug), + scripts = scripts_html(app, debug = debug), + renderer = renderer_html() + ) +end \ No newline at end of file diff --git a/src/utils.jl b/src/utils.jl index e0a2b6d..1cff6e9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,4 +1,28 @@ using MacroTools + +function formattag(name ::String, attributes::Dict{String, String}, inner::String = ""; opened = false, closed = false) + attrs_string = join( + ["$k=\"$v\"" for (k, v) in attributes], + " " + ) + tag = "<$name $attrs_string" + if closed + tag *= "/>" + elseif opened + tag *= ">" + else + tag *= ">$inner" + end +end + +function interpolate_string(s::String; kwargs...) + result = s + for (k, v) in kwargs + result = replace(result, "{%$(k)%}" => v) + end + return result +end + macro wildprop(e) if e.head != :(=) || length(e.args) != 2 || !(e.args[1] isa AbstractString) error("expected AbstractString = value") @@ -24,6 +48,7 @@ function parse_props(s) end end + """ @callid_str" @@ -45,4 +70,5 @@ macro callid_str(s) output = parse_props(strip(m[:output])) state = isnothing(m[:state]) ? Vector{IdProp}() : parse_props(strip(m[:state])) return CallbackId(state, input, output) -end \ No newline at end of file +end + diff --git a/test/config_functional.jl b/test/config_functional.jl new file mode 100644 index 0000000..3e3d420 --- /dev/null +++ b/test/config_functional.jl @@ -0,0 +1,207 @@ +using Test +using Dash +using HTTP + + +@testset "external_stylesheets" begin + app = dash("test app") + index_page = Dash.index_page(app) + + @test isnothing(findfirst("link rel=\"stylesheet\"", index_page)) + + app = dash("test app"; external_stylesheets = ["https://test.css"]) + index_page = Dash.index_page(app) + @test !isnothing(findfirst("", index_page)) + + app = dash("test app"; external_stylesheets = [ + "https://test.css", + Dict("href" => "https://test2.css", "rel" => "stylesheet") + ] + ) + index_page = Dash.index_page(app) + + @test !isnothing(findfirst("", index_page)) + @test !isnothing(findfirst("href=\"https://test2.css\"", index_page)) + +end + +@testset "external_scripts" begin + + + app = dash("test app"; external_scripts = ["https://test.js"]) + index_page = Dash.index_page(app) + + @test !isnothing(findfirst("""""", index_page)) + + app = dash("test app"; external_scripts = [ + "https://test.js", + Dict("src" => "https://test2.js", "crossorigin" => "anonymous") + ]) + index_page = Dash.index_page(app) + + @test !isnothing(findfirst("""""", index_page)) + + @test !isnothing(findfirst("""""", index_page)) + +end + +@testset "url paths" begin + app = dash("test app"; requests_pathname_prefix = "/reg/prefix/", routes_pathname_prefix = "/prefix/") + index_page = Dash.index_page(app) + + @test !isnothing(findfirst("""requests_pathname_prefix":"/reg/prefix/""", index_page)) + handler = Dash.make_handler(app) + request = HTTP.Request("GET", "/prefix/") + response = handler(request) + @test response.status == 200 + + request = HTTP.Request("GET", "/prefix/_dash-layout") + response = handler(request) + @test response.status == 200 + + request = HTTP.Request("GET", "/prefix/_dash-dependencies") + response = handler(request) + @test response.status == 200 + +end + +@testset "assets paths" begin + app = dash("test app") + res = Dash.process_assets(app, "/assets/test.png") + @test res.status == 200 + res = Dash.process_assets(app, "/assets/test3.png") + @test res.status == 404 + res = Dash.process_assets(app, "/images/test.png") + @test res.status == 404 + + app = dash("test app", url_base_pathname = "/test/") + res = Dash.process_assets(app, "/assets/test.png") + @test res.status == 404 + res = Dash.process_assets(app, "/test/assets/test.png") + @test res.status == 200 + res = Dash.process_assets(app, "/images/test.png") + @test res.status == 404 + + app = dash("test app", assets_url_path = "ass") + res = Dash.process_assets(app, "/ass/test.png") + @test res.status == 200 + res = Dash.process_assets(app, "/ass/test3.png") + @test res.status == 404 + res = Dash.process_assets(app, "/assets/test3.png") + @test res.status == 404 + res = Dash.process_assets(app, "/images/test.png") + @test res.status == 404 + + app = dash("test app", assets_folder = "images") + res = Dash.process_assets(app, "/assets/test.png") + @test res.status == 404 + res = Dash.process_assets(app, "/assets/test_images.png") + @test res.status == 200 + res = Dash.process_assets(app, "/images/test.png") + @test res.status == 404 +end + +@testset "suppress_callback_exceptions" begin + app = dash("test app") + index_page = Dash.index_page(app) + @test !isnothing(findfirst("\"suppress_callback_exceptions\":false", index_page)) + @test isnothing(findfirst("\"suppress_callback_exceptions\":true", index_page)) + + app = dash("test app", suppress_callback_exceptions = true) + index_page = Dash.index_page(app) + @test isnothing(findfirst("\"suppress_callback_exceptions\":false", index_page)) + @test !isnothing(findfirst("\"suppress_callback_exceptions\":true", index_page)) +end + +@testset "meta_tags" begin + app = dash("test app") + index_page = Dash.index_page(app) + + @test !isnothing( + findfirst( + "", + index_page) + ) + + @test !isnothing( + findfirst( + "", + index_page) + ) + + app = dash("test app", meta_tags = [Dict("type" => "tst", "rel" => "r")]) + index_page = Dash.index_page(app) + + @test !isnothing( + findfirst( + "", + index_page) + ) + + @test !isnothing( + findfirst( + "", + index_page) + ) + + @test !isnothing( + findfirst( + Dash.formattag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), + index_page) + ) + + app = dash("test app", meta_tags = [Dict("charset" => "Win1251"), Dict("type" => "tst", "rel" => "r")]) + index_page = Dash.index_page(app) + + @test isnothing( + findfirst( + "", + index_page) + ) + @test !isnothing( + findfirst( + "", + index_page) + ) + + @test !isnothing( + findfirst( + Dash.formattag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), + index_page) + ) + + app = dash("test app", meta_tags = [Dict("http-equiv" => "X-UA-Compatible", "content" => "IE"), Dict("type" => "tst", "rel" => "r")]) + index_page = Dash.index_page(app) + @test isnothing( + findfirst( + "", + index_page) + ) + @test !isnothing( + findfirst( + Dash.formattag("meta", Dict("http-equiv" => "X-UA-Compatible", "content" => "IE"), opened = true), + index_page) + ) + +end + +@testset "index_string" begin + index_string = "test test test, {%metas%},{%title%},{%favicon%},{%css%},{%app_entry%},{%config%},{%scripts%},{%renderer%}" + app = dash("test", index_string = index_string) + index_page = Dash.index_page(app) + @test startswith(index_page, "test test test,") + +end + +@testset "show_undo_redo" begin + + app = dash("test") + + index_page = Dash.index_page(app) + @test !isnothing(findfirst("\"show_undo_redo\":false", index_page)) + + app = dash("test", show_undo_redo = true) + + index_page = Dash.index_page(app) + @test !isnothing(findfirst("\"show_undo_redo\":true", index_page)) +end \ No newline at end of file diff --git a/test/core.jl b/test/core.jl index f45b453..a2129e6 100644 --- a/test/core.jl +++ b/test/core.jl @@ -51,18 +51,6 @@ end end -@testset "Dash creation" begin - app = dash("test app"; external_stylesheets=["https://test.css"], url_base_pathname = "/") do - html_div(id = "test-div") - end - @test app.name == "test app" - @test app.external_stylesheets == ["https://test.css"] - @test app.url_base_pathname == "/" - @test app.layout.type == "Div" - @test app.layout.props[:id] == "test-div" - @test length(app.callable_components) == 1 - @test haskey(app.callable_components, Symbol("test-div")) -end @testset "callid" begin id = callid"id1.prop1 => id2.prop2" @@ -278,7 +266,7 @@ end html_img(src = "assets/test.png") end end - @test app.assets_folder == "assets" + @test app.config.assets_folder == joinpath(pwd(),"assets") handler = Dash.make_handler(app) request = HTTP.Request("GET", "/assets/test.png") response = handler(request) diff --git a/test/dash_creation.jl b/test/dash_creation.jl new file mode 100644 index 0000000..42c95d2 --- /dev/null +++ b/test/dash_creation.jl @@ -0,0 +1,122 @@ +using Test +using Dash + + +@testset "default args" begin + app = dash("test app") + @test app.name == "test app" + @test isempty(app.config.external_stylesheets) + @test isempty(app.config.external_scripts) + #@test app.config.url_base_pathname == "/" + @test app.config.requests_pathname_prefix == "/" + @test app.config.routes_pathname_prefix == "/" + @test app.config.assets_folder == joinpath(pwd(), "assets") + @test app.config.assets_url_path == "assets" + @test app.config.assets_ignore == "" + + @test app.config.serve_locally == true + @test app.config.suppress_callback_exceptions == false + @test app.config.eager_loading == false + + @test isempty(app.config.meta_tags) + @test app.config.index_string == Dash.default_index + @test app.config.assets_external_path == nothing + + @test app.config.include_assets_files == true + @test app.config.show_undo_redo == false +end + +@testset "setted args" begin + app = dash("test app"; external_stylesheets=["https://test.css"]) + @test app.config.external_stylesheets == ["https://test.css"] + + app = dash("test app"; external_stylesheets=[Dict("url" => "https://test.css", "integrity" => "integrity")]) + @test app.config.external_stylesheets == [ + Dict("url" => "https://test.css", "integrity" => "integrity") + ] + + app = dash("test app"; external_scripts = ["http://test.js"]) + @test app.config.external_scripts == ["http://test.js"] + + app = dash("test app"; external_scripts=[Dict("url" => "https://test.js", "integrity" => "integrity")]) + @test app.config.external_scripts == [ + Dict("url" => "https://test.js", "integrity" => "integrity") + ] + + app = dash("test app"; url_base_pathname = "/base/") + @test app.config.url_base_pathname == "/base/" + @test app.config.requests_pathname_prefix == "/base/" + @test app.config.routes_pathname_prefix == "/base/" + + app = dash("test app"; requests_pathname_prefix = "/prefix/") + @test app.config.requests_pathname_prefix == "/prefix/" + @test app.config.routes_pathname_prefix == "/" + + app = dash("test app"; routes_pathname_prefix = "/prefix/") + @test app.config.requests_pathname_prefix == "/prefix/" + @test app.config.routes_pathname_prefix == "/prefix/" + + app = dash("test app"; requests_pathname_prefix = "/reg/prefix/", routes_pathname_prefix = "/prefix/") + + @test app.config.requests_pathname_prefix == "/reg/prefix/" + @test app.config.routes_pathname_prefix == "/prefix/" + + @test_throws ErrorException app = dash("test app"; url_base_pathname = "/base") + @test_throws ErrorException app = dash("test app"; url_base_pathname = "base/") + @test_throws ErrorException app = dash("test app"; url_base_pathname = "/", routes_pathname_prefix = "/prefix/") + @test_throws ErrorException app = dash("test app"; requests_pathname_prefix = "/prefix") + @test_throws ErrorException app = dash("test app"; routes_pathname_prefix = "/prefix") + @test_throws ErrorException app = dash("test app"; requests_pathname_prefix = "prefix/") + @test_throws ErrorException app = dash("test app"; routes_pathname_prefix = "prefix/") + @test_throws ErrorException app = dash("test app"; requests_pathname_prefix = "/reg/prefix/", routes_pathname_prefix = "/ix/") + + + app = dash("test app"; assets_folder = "images") + @test app.config.assets_folder == joinpath(pwd(), "images") + + app = dash("test app"; assets_url_path = "/images") + @test app.config.assets_url_path == "images" + + app = dash("test app"; assets_ignore = "ignore") + @test app.config.assets_ignore == "ignore" + + app = dash("test app"; serve_locally = false) + @test app.config.serve_locally == false + + app = dash("test app"; suppress_callback_exceptions = true) + @test app.config.suppress_callback_exceptions == true + + app = dash("test app"; eager_loading = false) + @test app.config.eager_loading == false + + app = dash("test app"; meta_tags = [Dict(["name"=>"test", "content" => "content"])]) + @test app.config.meta_tags == [Dict(["name"=>"test", "content" => "content"])] + + app = dash("test app"; index_string = "") + @test app.config.index_string == "" + + app = dash("test app"; assets_external_path = "external") + @test app.config.assets_external_path == "external" + + app = dash("test app"; include_assets_files = true) + @test app.config.include_assets_files == true + + app = dash("test app"; show_undo_redo = false) + @test app.config.show_undo_redo == false + +end + + +@testset "old Dash creation" begin + app = dash("test app"; external_stylesheets=["https://test.css"], url_base_pathname = "/") do + html_div(id = "test-div") + end + @test app.name == "test app" + @test app.config.external_stylesheets == ["https://test.css"] + @test app.config.url_base_pathname == "/" + @test app.layout.type == "Div" + @test app.layout.props[:id] == "test-div" + @test length(app.callable_components) == 1 + @test haskey(app.callable_components, Symbol("test-div")) +end + diff --git a/test/dev.jl b/test/dev.jl index 0d9e6ec..01bb4ac 100644 --- a/test/dev.jl +++ b/test/dev.jl @@ -11,6 +11,8 @@ styles = ( ), ) + + app = dash("Dash Layout", external_stylesheets=external_stylesheets) app.layout = html_div() do dcc_graph( diff --git a/test/images/test_images.png b/test/images/test_images.png new file mode 100644 index 0000000000000000000000000000000000000000..83f519a5687aa882030cacfd4fa1759c8e2f3c18 GIT binary patch literal 8926 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 KNTX2=3jaTUxn48? literal 0 HcmV?d00001 diff --git a/test/runtests.jl b/test/runtests.jl index 099cb39..1a6391f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,6 @@ +include("dash_creation.jl") +include("config_functional.jl") +include("utils.jl") include("core.jl") include("types.jl") #include("dev.jl") \ No newline at end of file diff --git a/test/utils.jl b/test/utils.jl new file mode 100644 index 0000000..95451b5 --- /dev/null +++ b/test/utils.jl @@ -0,0 +1,19 @@ +using Test +using Dash +@testset "interpolate_string" begin + test_str = """ + {%head%} + blah blah blah blah blah blah blah blah blah blah + {%middle%} + da da da da da da da da da da da da da {%da%} da + end + """ + inter = Dash.interpolate_string(test_str, head="hd", middle = :mmmm, da = 10) + @test inter == """ + hd + blah blah blah blah blah blah blah blah blah blah + mmmm + da da da da da da da da da da da da da 10 da + end + """ +end From 122c49ff7481e51162b28eeee5cd8de53689906e Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 17 Apr 2020 20:56:31 +0300 Subject: [PATCH 2/6] docstr fix --- src/app.jl | 89 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/src/app.jl b/src/app.jl index 332c454..2bc79a1 100644 --- a/src/app.jl +++ b/src/app.jl @@ -114,27 +114,86 @@ end dash(name::String; external_stylesheets ::Vector{String} = Vector{String}(), url_base_pathname::String="/") dash(layout_maker::Function, name::String; external_stylesheets ::Vector{String} = Vector{String}(), url_base_pathname::String="/") -Construct a dash app using callback for layout creation +Construct a dash app # Arguments - `layout_maker::Function` - function for layout creation. Must has signature ()::Component -- `name::String` - Dashboard name -- `external_stylesheets::Vector{String} = Vector{String}()` - vector of external css urls -- `external_scripts::Vector{String} = Vector{String}()` - vector of external js scripts urls -- `url_base_pathname::String="/"` - base url path for dashboard, default "/" +- `name::String` - The name of your application - `assets_folder::String` - a path, relative to the current working directory, -for extra files to be used in the browser. Default `"assets"` + for extra files to be used in the browser. Default ``'assets'``. -# Examples -```jldoctest -julia> app = dash("Test") do - html_div() do - html_h1("Test Dashboard") - end -end -``` -""" +- `assets_url_path::String` - The local urls for assets will be: + ``requests_pathname_prefix * assets_url_path * "/" * asset_path`` + where ``asset_path`` is the path to a file inside ``assets_folder``. + Default ``'assets'`. + + +- `assets_ignore::String` - [IN DEVELOPMENT] A regex, as a string to pass to ``Regex``, for + assets to omit from immediate loading. Ignored files will still be + served if specifically requested. You cannot use this to prevent access + to sensitive files. + :type assets_ignore: string + +- `assets_external_path::String` - [IN DEVELOPMENT] an absolute URL from which to load assets. + Use with ``serve_locally=false``. Dash can still find js and css to + automatically load if you also keep local copies in your assets + folder that Dash can index, but external serving can improve + performance and reduce load on the Dash server. + + +- `include_assets_files::Bool` - [IN DEVELOPMENT] Default ``true``, set to ``False`` to prevent + immediate loading of any assets. Assets will still be served if + specifically requested. You cannot use this to prevent access + to sensitive files. + + +- `url_base_pathname::String`: A local URL prefix to use app-wide. + Default ``'/'``. Both `requests_pathname_prefix` and + `routes_pathname_prefix` default to `url_base_pathname`. + +- `requests_pathname_prefix::String`: A local URL prefix for file requests. + Defaults to `url_base_pathname`, and must end with + `routes_pathname_prefix` + + +- `routes_pathname_prefix::String`: A local URL prefix for JSON requests. + Defaults to ``url_base_pathname``, and must start and end + with ``'/'``. + +- `serve_locally`: [IN DEVELOPMENT] If ``true`` (default), assets and dependencies + (Dash and Component js and css) will be served from local URLs. + If ``false`` we will use CDN links where available. + +- `meta_tags::Vector{Dict{String, String}}`: html tags to be added to the index page. + Each dict should have the attributes and values for one tag, eg: + ``Dict("name"=>"description", "content" => "My App")`` + + +- `index_string::String`: Override the standard Dash index page. + Must contain the correct insertion markers to interpolate various + content into it depending on the app config and components used. + See https://dash.plotly.com/external-resources for details. + + +- `external_scripts::Vector`: Additional JS files to load with the page. + Each entry can be a String (the URL) or a Dict{String, String} with ``src`` (the URL) + and optionally other ``""" -make_scipt_tag(dict::Dict{String, String}) = formattag("script", dict) +make_script_tag(url::String) = """""" +make_script_tag(dict::Dict{String, String}) = format_tag("script", dict) function metas_html(app::DashApp) meta_tags = app.config.meta_tags @@ -122,4 +122,4 @@ function index_page(app::DashApp; debug = false) scripts = scripts_html(app, debug = debug), renderer = renderer_html() ) -end \ No newline at end of file +end From c3bdc1a728c6c2dc4febc449beb1c85cf8ec911e Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 24 Apr 2020 15:50:11 +0300 Subject: [PATCH 5/6] Update src/utils.jl Co-Authored-By: Ryan Patrick Kyle --- src/utils.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 1cff6e9..53d3e94 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,6 +1,6 @@ using MacroTools -function formattag(name ::String, attributes::Dict{String, String}, inner::String = ""; opened = false, closed = false) +function format_tag(name ::String, attributes::Dict{String, String}, inner::String = ""; opened = false, closed = false) attrs_string = join( ["$k=\"$v\"" for (k, v) in attributes], " " @@ -71,4 +71,3 @@ macro callid_str(s) state = isnothing(m[:state]) ? Vector{IdProp}() : parse_props(strip(m[:state])) return CallbackId(state, input, output) end - From f4cceac69d86d0771d05cba19c2fe5ff6c6e2b5b Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 24 Apr 2020 17:12:17 +0300 Subject: [PATCH 6/6] fixes to PR --- src/Dash.jl | 5 ---- src/app.jl | 27 +++++++++++++++---- src/index_page.jl | 57 ++------------------------------------- test/config_functional.jl | 6 ++--- 4 files changed, 27 insertions(+), 68 deletions(-) diff --git a/src/Dash.jl b/src/Dash.jl index 3eb519a..962f19c 100644 --- a/src/Dash.jl +++ b/src/Dash.jl @@ -1,5 +1,4 @@ module Dash -components = [1,2,3,4,5] import HTTP, JSON2 using MacroTools include("ComponentPackages.jl") @@ -23,10 +22,6 @@ include("index_page.jl") include("handlers.jl") -macro test() - -end - @doc """ module Dash diff --git a/src/app.jl b/src/app.jl index 2bc79a1..5adf7ad 100644 --- a/src/app.jl +++ b/src/app.jl @@ -78,7 +78,9 @@ end """ struct DashApp <: Any -Representation of Dash application +Representation of Dash application. + +Not meant to be constructed directly, use `dash` function instead. """ struct DashApp name ::String @@ -111,13 +113,28 @@ end """ - dash(name::String; external_stylesheets ::Vector{String} = Vector{String}(), url_base_pathname::String="/") - dash(layout_maker::Function, name::String; external_stylesheets ::Vector{String} = Vector{String}(), url_base_pathname::String="/") + dash(name::String; + external_stylesheets, + external_scripts, + url_base_pathname, + requests_pathname_prefix, + routes_pathname_prefix, + assets_folder, + assets_url_path, + assets_ignore, + serve_locally, + suppress_callback_exceptions, + eager_loading , + meta_tags, + index_string, + assets_external_path, + include_assets_files, + show_undo_redo + ) Construct a dash app # Arguments -- `layout_maker::Function` - function for layout creation. Must has signature ()::Component - `name::String` - The name of your application - `assets_folder::String` - a path, relative to the current working directory, for extra files to be used in the browser. Default ``'assets'``. @@ -148,7 +165,7 @@ Construct a dash app - `url_base_pathname::String`: A local URL prefix to use app-wide. - Default ``'/'``. Both `requests_pathname_prefix` and + Default ``nothing``. Both `requests_pathname_prefix` and `routes_pathname_prefix` default to `url_base_pathname`. diff --git a/src/index_page.jl b/src/index_page.jl index 8140a79..1336949 100644 --- a/src/index_page.jl +++ b/src/index_page.jl @@ -15,7 +15,7 @@ function metas_html(app::DashApp) !has_ie_compat && push!(result, "") !has_charset && push!(result, "") - append!(result, formattag.("meta", meta_tags, opened = true)) + append!(result, format_tag.("meta", meta_tags, opened = true)) return join(result, "\n ") end @@ -49,66 +49,13 @@ function scripts_html(app::DashApp; debug = false) append!(scripts, ComponentPackages.components_js_sources(app.config.requests_pathname_prefix, debug = debug) ) - return join(map(make_scipt_tag, scripts), "\n ") + return join(map(make_script_tag, scripts), "\n ") end renderer_html() = """""" favicon_html(app::DashApp) = "" -#= -def _walk_assets_directory(self): - walk_dir = self.config.assets_folder - slash_splitter = re.compile(r"[\\/]+") - ignore_str = self.config.assets_ignore - ignore_filter = re.compile(ignore_str) if ignore_str else None - - for current, _, files in os.walk(walk_dir): - if current == walk_dir: - base = "" - else: - s = current.replace(walk_dir, "").lstrip("\\").lstrip("/") - splitted = slash_splitter.split(s) - if len(splitted) > 1: - base = "/".join(slash_splitter.split(s)) - else: - base = splitted[0] - - if ignore_filter: - files_gen = (x for x in files if not ignore_filter.search(x)) - else: - files_gen = files - - for f in sorted(files_gen): - path = "/".join([base, f]) if base else f - - full = os.path.join(current, f) - - if f.endswith("js"): - self.scripts.append_script(self._add_assets_resource(path, full)) - elif f.endswith("css"): - self.css.append_css(self._add_assets_resource(path, full)) - elif f == "favicon.ico": - self._favicon = path -=# - -function find_assets_files(app::DashApp) - result = (css = String[], js = String[], favicon = nothing) - - if app.config.include_assets_files - for (base, dirs, files) in walkdir(app.config.assets_folder) - if !isempty(files) - relative = (base == app.config.assets_folder) ? "" : relpath(base, app.config.assets_folder) - relative_uri = join(splitpath(relative), "/") * "/" - - - end - - - end - end - return result -end function index_page(app::DashApp; debug = false) diff --git a/test/config_functional.jl b/test/config_functional.jl index 3e3d420..b53b6ab 100644 --- a/test/config_functional.jl +++ b/test/config_functional.jl @@ -146,7 +146,7 @@ end @test !isnothing( findfirst( - Dash.formattag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), + Dash.format_tag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), index_page) ) @@ -166,7 +166,7 @@ end @test !isnothing( findfirst( - Dash.formattag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), + Dash.format_tag("meta", Dict("type" => "tst", "rel" => "r"), opened = true), index_page) ) @@ -179,7 +179,7 @@ end ) @test !isnothing( findfirst( - Dash.formattag("meta", Dict("http-equiv" => "X-UA-Compatible", "content" => "IE"), opened = true), + Dash.format_tag("meta", Dict("http-equiv" => "X-UA-Compatible", "content" => "IE"), opened = true), index_page) )