gregoryv/draw

View on GitHub
docs/package.go

Summary

Maintainability
B
4 hrs
Test Coverage
// Package docs provides the content of github pages at https://gregoryv.github.io/draw/
package docs

import (
    "github.com/gregoryv/draw/design"
    "github.com/gregoryv/draw/shape"
    . "github.com/gregoryv/web"
    "github.com/gregoryv/web/files"
    "github.com/gregoryv/web/toc"
)

func NewProjectsPage() *Page {
    page := NewPage(Html(
        Head(
            Meta(Charset("utf-8")),
            Meta(Name("viewport"), Content("width=device-width, initial-scale=1")),
            Style(
                Theme(),
            ),
        ),
        Body(
            NewProjectArticle(),
        )))
    return page
}

func NewProjectArticle() *Element {
    nav := Nav(H4("Table of contents"))
    article := Article(
        H1("Draw - programming software design diagrams"),

        P(`This module provides an API to program software design
        diagrams using the Go programming language.`),

        P(`Drawing diagrams by hand, though very intuitive and easy to
        create are rarely maintained over time. If we want to keep
        them up to date with our code, we need another
        way.<br>Autogenerated diagrams from source code is one way to
        go about it. However then the problem becomes one of making it
        informative enough for the inteded audience, ie. it's more
        difficult to filter out unwanted elements or place them in a
        logical order depending on the discussion/purpose.<br> We can
        also resort to using text formats and process those into
        diagrams, eg. mscgen or graphviz. These are great tools which
        bring design diagrams closer to the source code and benefit
        from the same revision control as other code. The problem of
        maintaining diagrams however remains, refactoring code doesn't
        touch the diagrams.`),

        P(`This module uses the benefits of the text base diagram
        formats by using the Go programming language. Ie. not another
        format rather it provides an API for programming
        diagrams. Developers can refer to types directly from the
        modules they want to describe. Whatever information can be
        gathered from the source code is used, which allows the
        programmer to focus on creating a diagram visually suitable
        for their reader.`),

        Div(Class("left"),
            nav,
        ),
        Div(Class("right"),
            H2("Install"),
            Pre(
                Code(
                    "    go get ",
                    A(Href("https://github.com/gregoryv/draw"),
                        "github.com/gregoryv/draw/"),
                    "...",
                ),
            ),

            H2("API documentation"),
            Ul(
                Li(
                    A(Href(godoc("github.com/gregoryv/draw")), "draw"),
                ),
                Li(
                    A(Href(godoc("github.com/gregoryv/draw/shape")),
                        "draw/shape"),
                    " - SVG shapes",
                ),
                Li(
                    A(Href(godoc("github.com/gregoryv/draw/design")),
                        "draw/design"),
                    " - software design diagrams",
                ),
            ),
            H2("About"),
            Img(Src("me_circle.png"), Class("me")),
            P(
                `Written by `, A(Href("https://github.com/gregoryv"), gregory), Br(),
                A(Href("#license"), "MIT License"),
            ),
        ),
        Br(Attr("clear", "all")),

        H2("Quick start"),

        P(`Each diagram is a Go type specifically designed to provide
        an easy and intuitive way of "programming" diagrams. Elements
        are either fixed strings or taken from the source code by
        using concrete instances of real types. This allows for
        refactoring to also update diagrams within a package.`, Br(),
            `Once you selected which elements to include, position them
        relative to each other in the diagram. Relative placement has
        the benefit of adaptive diagrams once you add more methods or
        fields to your structs. It works for most cases, eliminating
        manual updates.`),

        ExampleSmallClassDiagram().Inline(),

        LoadFile("small_example.go", 1, -1),

        P(`Once a diagram is done, you can render the SVG in different ways`),
        Ul(
            Li(Code("SaveAs(filename)")),
            Li(Code("Inline()"), " - returns SVG as string with all classes replaced with styling"),
            Li(Code("WriteSVG(io.Writer)")),
        ),

        P(`These pages for instance are generated using the `,
            A(Href("https://github.com/gregoryv/web"),
                `github.com/gregoryv/web`), ` package using the Inline()
        method and looks something like the below code`),

        LoadFile("doc_example_test.go", 1, -1),

        P(`Styling is currently provided by `,
            Code("draw.ClassAttributes"), ` and can be changed to some
            degree. For now font size and family should not be changed
            as size of shapes will not adapt to the styling
            values. The idea is however that the default styling
            should be left alone.`),

        P(`There are more design diagram types available, take a look
        below.  If you are missing something that could benefit the
        community, please let me know.`),

        H2("Diagrams"),
        H3("Class"),

        P(`In class diagrams the author wants to convey design
           relations between various entities. However the relations
           and most of the element naming can be generated from the
           source code. The author should add what is needed for a
           clear picture, ie. selecting entities to show and position
           them in a perceptible manner.`),

        ExampleClassDiagram().Inline(), Br(),
        "Source: ", A(Href("class_example.go"), "class_example.go"),

        P(`Records describe each entity using package name and
           type. Methods and fields are shown only by name if
           visible. Details such as arguments and return values are
           left to the API documentation. Relations are automatically
           rendererd between entities if there is one.`),

        H3("Activity"),

        P(`Activity diagrams start in one position and show states and
        activities transitioning from one stated to another ending in
        one or more exits.`),

        ExampleActivityDiagram().Inline(),
        LoadFile("activity_example.go", 1, -1),

        H3("Sequence"),

        P(`Sequence diagrams are ment to describe a sequence of
        events, specifically calling methods or remote API calls. I've
        tried to emphasize the horizontal arrows over vertical lines
        and keep visual effects to a minimum. For now there is only
        one arrow variation. I found that embedding information by the
        subtle head variations and arrow line styling is hard to
        read.`),

        ExampleSequenceDiagram().Inline(),
        LoadFile("sequence_example.go", 1, -1),

        H3("Gantt chart"),
        ExampleGanttChart().Inline(),

        H3("Generic"),
        ExampleDiagram().Inline(),

        H2("Shapes"),
        AllShapes().Inline(),

        H2("Changelog"),
        LoadFile("../changelog.md", 7, -1),

        H2("License"),
        LoadFile("../LICENSE"),
    )
    toc.MakeTOC(nav, article, "h2", "h3")
    return article
}

func godoc(pkg string) string {
    return "https://godoc.org/" + pkg
}

const gregory = "Gregory Vin&ccaron;i&cacute;"

func AllShapes() *design.Diagram {
    d := design.NewDiagram()
    vspace := 60

    actorLbl := shape.NewLabel("Actor")
    actor := shape.NewActor()

    d.Place(actorLbl).At(20, 20)
    d.Place(actor).RightOf(actorLbl, vspace+40)

    lastLabel := actorLbl
    var last shape.Shape = actor
    add := func(txt string, s shape.Shape) {
        label := shape.NewLabel(txt)
        d.Place(label, s).Below(lastLabel, vspace)
        d.VAlignCenter(last, s)
        d.HAlignCenter(label, s)
        lastLabel = label
        last = s
    }

    add("Arrow", shape.NewLine(240, 0, 300, 0))
    add("Circle", shape.NewCircle(20))
    add("Component", shape.NewComponent("Component"))
    add("Cylinder", shape.NewCylinder(30, 40))
    add("Database", shape.NewDatabase("database"))
    add("Diamond", shape.NewDiamond())
    add("Dot", shape.NewDot())
    add("ExitDot", shape.NewExitDot())
    add("Internet", shape.NewInternet())
    add("Label", shape.NewLabel("label-text"))
    add("Line", shape.NewLine(240, 0, 300, 0))
    add("Note", shape.NewNote("This describes\nsomething..."))

    rec := shape.NewRecord("record")
    rec.Fields = []string{"fields"}
    rec.Methods = []string{"methods"}
    add("Record", rec)

    add("Rect", shape.NewRect("a rectangle"))
    add("State", shape.NewState("active"))
    add("Triangle", shape.NewTriangle())

    return d
}

// LoadFile returns a pre web element wrapping the contents from the
// given file. If to == -1 all lines to the end of file are returned.
func LoadFile(filename string, span ...int) *Element {
    from, to := 0, -1
    if len(span) == 2 {
        from, to = span[0], span[1]
    }
    v := files.MustLoadLines(filename, from, to)
    class := "srcfile"
    if from == 0 && to == -1 {
        class += " complete"
    }
    return Pre(Class(class), Code(Class("go"), v))
}