creating extensions

There are both Connection and Server extensions. Here is how to make them.

creating connection extensions

Toolips.AbstractConnectionType

abstract type AbstractConnection

Connections are passed through function routes and can have Servables written to it.

Consistencies

  • routes::Dict - A {String, Function} dictionary that the server references to

direct incoming connections.

  • http::Any - Usually an HTTP.Stream, however can be anything that is binded to

the Base.write method.

  • extensions::Dict - A {Symbol, ServerExtension} dictionary that can be used to

access ServerExtensions.

Abstract Connections must have the extensions Dict, the routing Dict, and some sort of writable stream called http. This needs to be binded to Base.write. A good example of this is Toolips.SpoofStream and Toolips.SpoofConnection, which can be used to write connection output to a string.

mutable struct SpoofStream
    text::String
    SpoofStream() = new("")
end

The http value can be anything, so in this case it will be a SpoofStream. The SpoofStream contains only a string, text. This is then binded to the write method:

write(s::SpoofStream, e::Any) = s.text = s.text * string(e)
write(c::SpoofStream, s::Servable) = s.f(c)

Finally, we make our connection, using SpoofStream as HTTP.

mutable struct SpoofConnection <: AbstractConnection
    routes::Dict
    http::SpoofStream
    extensions::Dict
    function SpoofConnection(r::Dict, http::SpoofStream, extensions::Dict)
        new(r, SpoofStream(), extensions)
    end
    SpoofConnection() = new(Dict(), SpoofStream(), Dict())
end

creating server extensions

Toolips.ServerExtensionType

abstract type ServerExtension

Server extensions are loaded into the server on startup, and can have a few different abilities according to their type field's value. This value can be either a Symbol or a Vector of Symbols.

Consistencies

  • type::T where T == Vector{Symbol} || T == Symbol. The type can be :routing,

:func, :connection, or any combination inside of a Vector{Symbol}. :routing ServerExtensions must have an f() function that takes two dictionaries; e.g. f(r::Dict{String, Function}, e::Dict{Symbol, ServerExtension}) The first Dict is the dictionary of routes, the second is the dictionary of server extensions. :func server extensions will be ran everytime the server is routed. They will need to have the same f function, but taking a single argument as a connection. Lastly, :connection extensions are simply pushed to the connection.

Server extensions are a little bit more intense. There are three types of server extensions, :func, :routing, and :connection. The type field can be either a Vector{Symbol}, or a single symbol – and a combination of each of these can be written. A :func extension is one that holds a function that is ran every time a Connection is routed. A :func extension requires that the function f(::AbstractConnection) or f(::Connection) exists inside of it. Here is an example:

import Toolips: ServerExtension

mutable struct MyExtension <: ServerExtension
    f::Function
    function MyExtension()
        f(c::Connection) = begin
            write!(c, "Hello!")
        end
    end
end

Each time the server is routed, there will now be "Hello!" written to the top of the page. A :routing extension is similar, but we will want to have the f function instead take two dictionaries. The dictionaries are specifically of type Dict{String, Function}, and Dict{Symbol, ServerExtension}. A great example of this is the Toolips.Files extension:

mutable struct Files <: ServerExtension
    type::Symbol
    directory::String
    f::Function
    function Files(directory::String = "public")
        f(r::Dict, e::Dict) = begin
            l = length(directory) + 1
            for path in route_from_dir(directory)
                push!(r, path[l:length(path)] => c::Connection -> write!(c, File(path)))
            end
        end
        new(:routing, directory, f)
    end
end

Finally, there is also a :connection extension. These are extensions that are to be pushed into the Connection's extensions field. Nothing extra needs to be done to these types of extensions. A great example of this is the Toolips.Logger:

mutable struct Logger <: ServerExtension
    type::Symbol
    out::String
    levels::Dict
    log::Function
    prefix::String
    timeformat::String
    writeat::Int64
    function Logger(levels::Dict{Any, Crayon} = Dict(
    1 => Crayon(foreground = :light_cyan),
    2 => Crayon(foreground = :light_yellow),
    3 => Crayon(foreground = :yellow, bold = true),
    4 => Crayon(foreground = :red, bold = true),
    :time_crayon => Crayon(foreground = :magenta, bold = true),
     :message_crayon => Crayon(foreground  = :light_blue, bold = true)
    );
    out::String = pwd() * "/logs/log.txt", prefix::String = "🌷 toolips> ",
                    timeformat::String = "YYYY:mm:dd:HH:MM", writeat::Int64 = 2)

        log(level::Int64, message::String) = _log(level, message, levels, out,
                                                prefix, timeformat, writeat)
        log(message::String) = _log(1, message, levels, out, prefix, timeformat,
        writeat)
        log(c::Connection, message::String) = _log(c, message)
        # These bindings are left open-ended for extending via
                                            # import Toolips._log
        log(level::Int64, message::Any) = _log(level, a, levels, out, prefix,
                                            timeformat)
        new(:connection, out::String, levels::Dict, log::Function,
                    prefix::String, timeformat::String, writeat::Int64)::Logger
    end
end

toolips internals

If you're looking at the internals, you are probably good enough at reading documentation... Here are the doc-strings, my friend. Thank you for contributing.

Base.writeMethod

Internals

write(s::SpoofStream, e::Any) -> _


A binding to Base.write that allows one to write to SpoofStream.text.

example

s = SpoofStream()
write(s, "hi")
println(s.text)
    hi
Base.writeMethod

Internals

write(s::SpoofStream, e::Servable) -> _


A binding to Base.write that allows one to write a Servable to SpoofStream.text.

example

s = SpoofStream()
write(s, p("hello"))
println(s.text)
    <p id = "hello"></p>
Toolips.create_serverdepsFunction

Internals

create_serverdeps(name::String, inc::String) -> _


Creates the essential portions of the webapp file structure, where name is the project's name and inc is any extensions or strings to incorporate at the top of the file.

example

create_serverdeps("ToolipsApp")
Toolips.serverfuncdefsFunction

Core

serverfuncdefs(::AbstractVector, ::String, ::Integer,

::Dict) -> (::Function, ::Function, ::Function)

This method is used internally by a constructor to generate the functions add, start, and remove for the ServerTemplate.

example

Toolips._startFunction

Core - Internals

_start(routes::AbstractVector, ip::String, port::Integer,

extensions::Dict, c::Type) -> ::WebServer

This is an internal function for the ServerTemplate. This function is binded to the ServerTemplate.start field.

example

st = ServerTemplate()
st.start()
Toolips.generate_routerFunction

Core - Internals

generate_router(routes::AbstractVector, server::Any, extensions::Dict,

        conn::Type)

This method is used internally by the _start method. It returns a closure function that both routes and calls functions.

example

server = Sockets.listen(Sockets.InetAddr(parse(IPAddr, ip), port))
if has_extension(extensions, Logger)
    extensions[Logger].log(1,
     "Toolips Server starting on port " * string(port))
end
routefunc, rdct, extensions = generate_router(routes, server, extensions,
                                                Connection)
@async HTTP.listen(routefunc, ip, port, server = server)
Toolips._logFunction

Extensions

_log(level::Int64, message::String, levels::Dict, out::String) -> _


Binded call for the field log() inside of Logger(). See ?(Logger) for more details on the field log. All arguments are fields of that type. Return is a printout into the REPL as well as an append to the log file, provided by the out URI. –––––––––

example (Closure from Logger)

log(level::Int64, message::String) = _log(level, message, levels, out)
log(message::String) = _log(1, message, levels, out)

Extensions

_log(http::HTTP.Stream, message::String) -> _


Binded call for the field log() inside of Logger(). This will log both to the JavaScript/HTML console. –––––––––

example (Closure from Logger)

log(http::HTTP.Stream, message::String) = _log(http, message)
Base.stringFunction

Internals

string(r::Vector{UInt8}) -> ::String


Turns a vector of UInt8s into a string.

Interface

string(c::Component) -> ::String


Shows c as a string representation of itself.

example

c = divider("example", align = "center")
string(c)
    "divider: align = center"
Toolips.SpoofConnectionType

SpoofConnection <: AbstractConnection

  • routes::Dict
  • http::SpoofStream
  • extensions::Dict Builds a fake connection with a SpoofStream. Useful if you want to write

a Servable without a server.

example

fakec = SpoofConnection()
servable = Component()
# write!(::AbstractConnection, ::Servable):
write!(fakec, servable)

field info

  • routes::Dict - A dictionary of routes, usually left empty.
  • http::SpoofStream - A fake http stream that instead writes output to a string.
  • extensions::Dict - A dictionary of extensions, usually empty.

constructors

  • SpoofStream(r::Dict, http::SpoofStream, extensions::Dict)
  • SpoofStream()
Toolips.SpoofStreamType

SpoofStream

  • text::String The SpoofStream allows us to fake a connection by building a SpoofConnection

which will write to the SpoofStream.text field whenever write! is called. This is useful for testing, or just writing servables into a string.

example

stream = SpoofStream()
write(stream, "hello!")
println(stream.text)

    hello!
conn = SpoofConnection()
servab = Component()
write!(conn, servab)

field info

  • text::String - The text written to the stream.

constructors

  • SpoofStream()
Toolips.route_from_dirFunction

Extensions

routefromdir(dir::String) -> ::Vector{String}


Recursively appends filenames for a directory AND all subsequent directories.

example

x::Vector{String} = route_from_dir("mypath")
Base.showMethod

Interface

show(t::Base.TTY, x::Component) -> _


Shows a component as markdown in a terminal.

example

# In the terminal, elsewhere the component will show as HTML.
show(x)
Base.showMethod

Interface

show(x::Component) -> _


Shows a component as HTML.

example

show(x)
Toolips.show_logFunction

Extensions

show_log(level::Int64, message::String, levels::Dict{Any, Crayon},

            prefix::String, time::Any)

Prints a log to the screen.

example

show_log(1, "hello!", levels, "toolips> ", now()

[2022:05:23:22:01] toolips> hello!
Toolips.@L_strMacro

Interface

L_str(s::String) -> ::String


Creates a literal string

example

x = 5
L"dollar_signx" # pretend dollar_sign is a dollar sign.
Toolips.has_extensionMethod

Internals

has_extension(d::Dict, t::Type) -> ::Bool


Checks if d has an extension of type t.

example

if has_extension(d, Logger)
    d[:Logger].log("it has a logger, I think.")
end
Toolips.argsplitFunction

Internals

argsplit(args::Vector{AbstractString}) -> ::Dict{Symbol, Any}


Used by the getargs method to parse GET arguments into a Dict.

example

argsplit(["c=5", "b=8"])
    Dict(:c => 5, :b => 8)
Base.stringMethod

Internals

string(r::Vector{UInt8}) -> ::String


Turns a vector of UInt8s into a string.

Toolips.showchildrenFunction

Internals

showchildren(x::Component) -> ::String


Get the children of x as a markdown string.

example

c = divider("example")
child = p("mychild")
push!(c, child)
s = showchildren(c)
println(s)
"##### children
|-- mychild