-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A 2-D grid model for holding "cells" geared for creating tables, for various output models. Current renderers for HTML, CSV and for pretty-printed UTF-8 tables for fixed-character-cell displays (such as the Unix visual-TTY terminal model). Brief coverage in README.md, details of structure in Overview.md, there are godocs, some tests (enough to catch major issues but not great) and things work. It's feature complete for a v1, this is not a barebones MVP. There's more that could be done for a v2, but want more real-world feedback first. Bumped API to 1.0. There will be a new commit in a bit, setting up automated CI and adding badges into the README. Squashed history; original dev repo started 2016-10-03 and was worked on intermittently between then and now.
- Loading branch information
0 parents
commit 6d68737
Showing
38 changed files
with
3,586 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
language: go | ||
|
||
# Be explicit about not needing sudo, so that Travis will use container-based | ||
# infrastructure for our jobs, always, fewer heuristics. | ||
sudo: false | ||
|
||
#env: | ||
# global: | ||
# - secure: "" | ||
# # Need: COVERALLS_TOKEN | ||
|
||
matrix: | ||
allow_failures: | ||
- go: tip | ||
fast_finish: true | ||
include: | ||
- go: 1.7.3 | ||
env: UPLOAD_COVERAGE=true | ||
- go: 1.6 | ||
- go: tip | ||
|
||
branches: | ||
except: | ||
- /^(?:exp|wip)(?:[/_-].*)?$/ | ||
|
||
install: | ||
- go get -t -v -u ./... | ||
- test "${UPLOAD_COVERAGE:-false}" != "true" || go get github.com/mattn/goveralls | ||
|
||
script: | ||
- go vet ./... | ||
- go test -v ./... | ||
- test "${UPLOAD_COVERAGE:-false}" != "true" || ./CoverTest.sh | ||
|
||
# after_script: | ||
# - test "${UPLOAD_COVERAGE:-false}" != "true" || goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN | ||
|
||
#notifications: | ||
# slack: | ||
# on_success: always | ||
# secure: #... | ||
|
||
# vim: set sw=2 et : |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/bin/sh | ||
dirname="$(dirname "$0")" | ||
if [ "_$dirname" != "_" ]; then | ||
cd "${dirname:?}" | ||
fi | ||
branch="$(git symbolic-ref --short HEAD)" | ||
if [ ".$branch" = ".master" ]; then | ||
branch="" | ||
else | ||
branch=",$branch" | ||
fi | ||
printf "%s%s\n" "$(git describe --always --dirty --tags)" "$branch" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#!/bin/sh | ||
# | ||
# Relies upon: <https://github.com/wadey/gocovmerge> | ||
# | ||
# Based upon mmindenhall's solution in <https://github.com/golang/go/issues/6909> | ||
# | ||
|
||
TOP="github.com/PennockTech/tabular" | ||
|
||
progname="$(basename "$0")" | ||
trace() { printf >&2 "%s: %s\n" "$progname" "$*" ; } | ||
|
||
trace "removing old c*.out files" | ||
find . -name c\*.out -execdir rm -v {} \; | ||
|
||
trace "generating new c.partial.out files" | ||
for D in $(find . -name .git -prune -o -type d -print) | ||
do | ||
if [ $D = "." ]; then | ||
go test -covermode=count -coverprofile=c.partial.out -coverpkg ./... . | ||
continue | ||
fi | ||
( cd $D && \ | ||
go test -covermode=count -coverprofile=c.partial.out -coverpkg "$TOP,./..." . | ||
) | ||
done | ||
|
||
trace "combining coverage files -> coverage.out" | ||
gocovmerge $(find . -name .git -prune -o -name c.partial.out -print) > coverage.out | ||
|
||
trace "suggestions:" | ||
echo " go tool cover -func=coverage.out | less" | ||
echo " go tool cover -html=coverage.out" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright © 2016 Phil Pennock | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
Tabular Overview | ||
================ | ||
|
||
At the core, tabular knows nothing about rendering. The base-level library, | ||
in the repo's top-level, can be imported and used to create tables and add | ||
content. Rendering requires using another layer to convert the output for | ||
display. | ||
|
||
A `Table` is an interface. There is one core public type which implements the | ||
interface. This allows the core type to be embedded in the wrapper/display | ||
objects and for those to satisfy the table interface, thus being tables | ||
themselves. Rows and cells are not interfaces. The core public type for a | ||
table is, imaginatively, `*ATable`. Callers are advised to use the `Table` | ||
interface, if they need to care about a non-inferred type. | ||
|
||
A table consists of rows of cells and some metadata. The metadata includes | ||
virtual columns, allowing for addressing by column too. Columns are | ||
identified by the header name. There is only one (or zero) header row per | ||
table. | ||
|
||
Errors in adding data are usually not reported immediately, to let data stream | ||
in. Instead, errors accumulate in an error holder. Rows hold errors, but | ||
once a row is part of a table, its errors become the tables' errors (and the | ||
error container is diverted to be the table's). An error container can be | ||
interrogated for its list of current errors. | ||
|
||
The errors are either a list of non-nil errors, or nil. An empty list should | ||
never be returned. If a nil is returned in the list of errors then that is a | ||
bug in tabular. | ||
|
||
A row either contains cells or is a "special" row. The only type of special | ||
row is a "separator" row. The tabular layer itself doesn't know what a | ||
separator row is, beyond that it exists and a row can be one. The | ||
`AddSeparator()` table method adds one, the `IsSeparator()` row method asks a | ||
row if it is one. The `Cells()` method, which returns an array of cells, can | ||
return nil if and only if the row is special (ie, at present, a separator). A | ||
real row is always a splice of cells, even if that splice is empty. | ||
|
||
A `Cell` contains "an object". That object can be a string, something which | ||
satisfies `Stringer` or `GoStringer`, a rune, or another `Cell`. Cells can | ||
contain cells and this is intended to allow for dynamic update, based upon | ||
evaluation. | ||
|
||
If a `Cell` contains an object then various rendering layers may make use of | ||
other interfaces satisfied by that object to determine how to display it; | ||
loosely, think of "width" and "height", but this will be covered in more | ||
detail below. | ||
|
||
Cells, Rows, Columns and Tables can have "properties" set upon them. | ||
Properties are namespaced objects, very similar to Golang's net contexts. | ||
Clients of the tabular package are free to decorate items with whatever | ||
properties they want. | ||
|
||
The tabular package supports automatically updating properties at "addition" | ||
time and at "render" time. This is done by setting callbacks. Callbacks can | ||
be on a table or a row. Within the table, they can be registered for use on a | ||
table or a column, for when a row is added, or when a cell is added. | ||
|
||
The cell's location in the grid is not a property, but is available via a | ||
method call upon the cell. | ||
|
||
The callbacks and properties should not be exposed to end-users. | ||
|
||
All child objects have links back to their containers. This is used, eg, to | ||
be able to get column information for a given cell. This does mean that there | ||
are ownership loops. | ||
|
||
|
||
Sub-package Commonalities | ||
------------------------- | ||
|
||
In all cases: | ||
|
||
* There is a `Wrap()` function which takes any `tabular.Table` and returns | ||
a wrapper object for this sub-package. | ||
* There is a `New()` function which generates an empty table for this | ||
sub-package. | ||
* The wrapper objects have `Render()` and `RenderTo()` methods, and the | ||
packages have top-level functions which create a wrapper object, with | ||
default options, and calls the object methods. | ||
* The `Render()` method will return a string of the rendered text, together | ||
with an error. | ||
* The `RenderTo(io.Writer)` method uses a stream-based approach and only | ||
returns an error. | ||
* The wrapper object's type is named with a `FooTable` naming style, accepting | ||
that this causes some stuttering. This is acceptable because most callers | ||
should never need to specify the type, but instead be using the | ||
`tabular.Table` interface if they care at all beyond letting the type be | ||
inferred. With so many variants, I went for clarity over smoothness in | ||
reading out the fully-qualified type name. | ||
|
||
Because the sub-package `New()` returns an object which satisfies the | ||
`tabular.Table` interface, it should be capable of being populated like any | ||
other, and most callers with simple use-cases should be able to only import | ||
the sub-package, not `tabular` itself. | ||
|
||
It is possible to use table properties to store data, but that's more | ||
complexity than is usually warranted. If you want static attributes, put them | ||
in your wrapper object. If you want dynamic attributes, updated based upon | ||
content, _then_ use properties, and consider how to hide this from your users. | ||
|
||
|
||
Text Table Display | ||
------------------ | ||
|
||
This is the `texttable` sub-package of `tabular`. | ||
|
||
This system is designed to draw a pretty table on a cell-based display system, | ||
such as a classic Unix terminal emulator. Every "display cell" (_not_ table | ||
cell) is a fixed pixel width and height, so using box-drawing characters, | ||
everything can be made to line up. | ||
|
||
If a cell's object supports the `Height()` method then that overrides a | ||
calculation based on "newlines count + 1". If a cell's object supports the | ||
`TerminalCellWidth()` method then that overrides a calculation based on text, | ||
figuring out the longest line (multi-line supported) where length is | ||
Unicode-aware and display-width (wide char and combining char) aware. | ||
|
||
Calculations upon cells are done at _render_ time, to examine the contents and | ||
determine width and height for text-table purposes. These are stored as | ||
properties of each cell. This is a complete table sweep before printing the | ||
first line starts. Then the rendering uses the properties to size itself and | ||
print the table. | ||
|
||
TODO: The maximum widths should become column properties of the table and the | ||
height become a row property. | ||
|
||
There is a `decoration` sub-package of `texttable` which has decoration styles | ||
for rendering tables, as ASCII or as a few varieties of Unicode box-drawing. | ||
Decoration objects can be created by callers and set directly upon the table, | ||
or can be set by name. The names are maintained as a registry within the | ||
`decoration` package. Each name is a simple string, thus typos are a | ||
potential source of errors. For the styles native to the `decoration` | ||
package, package constants are exported with the names, permitting | ||
compile-time checks to catch issues. Eg, use `decoration.D_UTF8_LIGHT_CURVED` | ||
instead of `"utf8-light-curved"` if you are willing to import the `decoration` | ||
package here. There's a trade-off between provable correctness and importing | ||
more and clients get to choose the level they're happy with. | ||
|
||
|
||
HTML Table Rendering | ||
-------------------- | ||
|
||
This is the `html` sub-package of `tabular`. | ||
|
||
It does not render "separator" rows. | ||
|
||
Table `Id`, `Class` and `Caption` are top-level attributes. | ||
|
||
The table is rendered using Golang's `html/template` to handle auto-escaping | ||
of unsafe data. If the template name matters (you are using templates more | ||
generally) then you can use `TemplateName` on the `HTMLTable` object. | ||
|
||
There is an `SetRowClassGenerator()` method to let you register your own | ||
function to be used to emit a class-name on each `<tr>` of the table's body. | ||
See the package docs for more details (you get a row index and your own | ||
context for passing state). | ||
|
||
|
||
CSV Rendering | ||
------------- | ||
|
||
This is the `csv` sub-package of `tabular`. | ||
|
||
The rendering is compliant to RFC4180. All fields are always quoted. Note in | ||
particular that newlines within strings are not `\n` escaped and double-quotes | ||
are doubled for escaping, thus `""`. Both of these attributes are | ||
RFC-specified. | ||
|
||
The `CSVTable` type is designed to be extensible to change separators, | ||
escaping styles and more. The _default_ is RFC4180, and that's the only | ||
_current_ style, but we should accept PRs for any sane options, and also | ||
"family" sets, as long as well-specified. At present, only the | ||
`fieldSeparator` is called out in the struct, and there are no mutators for | ||
it. That's not a bug, just "not yet implemented, waiting for solid | ||
use-cases". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
tabular | ||
======= | ||
|
||
<!-- FIXME: add banner widgets here, once released to public --> | ||
|
||
The `tabular` package provides a Golang library for storing data in a table | ||
consisting of rows and columns. Sub-packages provide for rendering such a | ||
table as a terminal box-table (line-drawing with UTF-8 box-drawing in various | ||
styles, or ASCII), as HTML, or as CSV data. | ||
|
||
The core data model is designed to be extensible and powerful, letting such | ||
a table be embedded in various more sophisticated models. (Eg, core of a | ||
spreadsheet). Cells in the table contain arbitrary data and possess metadata | ||
in the form of "properties", modelled after the `context` package's `Context`. | ||
|
||
A table can be created from the base package and then populated, before being | ||
passed to any of the renderers, or a table can be directly created using a | ||
sub-package, such that you _probably_ won't need to import the base package | ||
directly. | ||
|
||
An overview guide to the codebase can be found in | ||
[the Overview.md](Overview.md) | ||
|
||
The usage documentation is in Godoc format. A link will be added here when | ||
this package is made publicly available. <!-- FIXME: add link to godoc here. --> | ||
|
||
This package should be installable in the usual `go get` manner. |
Oops, something went wrong.