trevl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/NOTICE +9 -0
- data/README.md +431 -0
- data/Rakefile +10 -0
- data/assets/trendence-logo.svg +17 -0
- data/assets/trevl-logo.svg +20 -0
- data/docs/screenshots/01_bar_chart.png +0 -0
- data/docs/screenshots/02_column_chart.png +0 -0
- data/docs/screenshots/03_line_chart.png +0 -0
- data/docs/screenshots/04_pie_chart.png +0 -0
- data/docs/screenshots/05_computed_fields.png +0 -0
- data/docs/screenshots/06_postprocess_top_n.png +0 -0
- data/docs/screenshots/10_multi_series.png +0 -0
- data/examples/01_bar_chart.yml +16 -0
- data/examples/02_column_chart.yml +19 -0
- data/examples/03_line_chart.yml +21 -0
- data/examples/04_pie_chart.yml +15 -0
- data/examples/05_computed_fields.yml +26 -0
- data/examples/06_postprocess_top_n.yml +21 -0
- data/examples/07_score_kpi.yml +13 -0
- data/examples/08_table.yml +14 -0
- data/examples/09_template_inheritance.yml +25 -0
- data/examples/10_multi_series.yml +23 -0
- data/lib/trevl/auth/bearer_token.rb +23 -0
- data/lib/trevl/configuration.rb +24 -0
- data/lib/trevl/core_ext/hash.rb +37 -0
- data/lib/trevl/data_source/api.rb +86 -0
- data/lib/trevl/data_source/base.rb +19 -0
- data/lib/trevl/data_source/cube.rb +70 -0
- data/lib/trevl/data_source/static.rb +30 -0
- data/lib/trevl/data_source.rb +38 -0
- data/lib/trevl/errors.rb +9 -0
- data/lib/trevl/highcharts_asset.rb +45 -0
- data/lib/trevl/html_renderer.rb +91 -0
- data/lib/trevl/notebook/display.rb +53 -0
- data/lib/trevl/notebook.rb +41 -0
- data/lib/trevl/processor.rb +106 -0
- data/lib/trevl/renderer/data_fetcher.rb +63 -0
- data/lib/trevl/renderer/ref_parser.rb +61 -0
- data/lib/trevl/renderer/transform.rb +104 -0
- data/lib/trevl/renderer.rb +345 -0
- data/lib/trevl/schema/component.json +337 -0
- data/lib/trevl/schema/document.json +41 -0
- data/lib/trevl/template_store.rb +38 -0
- data/lib/trevl/validator.rb +96 -0
- data/lib/trevl/version.rb +5 -0
- data/lib/trevl.rb +99 -0
- data/llms.txt +171 -0
- data/notebooks/demo.ipynb +312 -0
- data/notebooks/exploration.ipynb +164 -0
- data/scripts/generate_screenshots.rb +107 -0
- metadata +137 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 18b85bde0894ec3023141a8e20862d0bc2641ece9cab486b0743d765fdae45e3
|
|
4
|
+
data.tar.gz: 57b043cd9f75abdd5a8a3b4f63276c9526c7893f059eaa7d9acd19d9c68de0df
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c06f1cffb20b1ea8185d89425395702a44c443f1f4928011876f8eccc5f04218a413c25edd610613abc35da88c9694b91a60304158f0b48a72239cdb1963d090
|
|
7
|
+
data.tar.gz: 4224f7d0f8675a6de3ceca6c3665f9df884275b01d4efe835621b10e81bd4a0e0b7708651b7953908d6e1c39b875da0f4c4f27688d1d889c8c010ebf78c6c77b
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trendence Institut GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/NOTICE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Third-party notice
|
|
2
|
+
|
|
3
|
+
This project renders charts with Highcharts, which is NOT covered by the MIT
|
|
4
|
+
licence in LICENSE and is not distributed with this project. Highcharts is
|
|
5
|
+
commercial software by Highsoft AS. Using it, whether from the CDN or from a
|
|
6
|
+
local copy, requires a valid licence from Highsoft for anything beyond the
|
|
7
|
+
uses their terms grant for free.
|
|
8
|
+
|
|
9
|
+
See https://www.highcharts.com/license
|
data/README.md
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/trevl-logo.svg" alt="TREVL" width="220" />
|
|
3
|
+
<br /><br />
|
|
4
|
+
<img src="assets/trendence-logo.svg" alt="Trendence" width="140" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h3 align="center">Write YAML, get Highcharts.</h3>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://trevl.trendence.com">DSL Documentation</a> ·
|
|
11
|
+
<a href="notebooks/demo.ipynb">Demo Notebook</a> ·
|
|
12
|
+
<a href="https://github.com/trendence/trevl">GitHub</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
[Trendence](https://www.trendence.com) is a Berlin-based HR data and analytics company. We believe data visualization should be modern, AI-ready, and accessible. That's why we've been building TREVL -- the **TR**Endence **V**isualization **L**anguage -- a custom DSL designed to make chart creation as simple as writing a few lines of YAML. A language humans easily can use and robots love.
|
|
18
|
+
|
|
19
|
+
## AI-First Design
|
|
20
|
+
|
|
21
|
+
TREVL is built for humans and machines. Every feature is designed so that LLMs can generate, validate, and iterate on visualizations autonomously:
|
|
22
|
+
|
|
23
|
+
- **`Trevl.schema_reference`** -- compact reference (~1500 tokens) optimized for system prompts
|
|
24
|
+
- **`Trevl.validate(yaml)`** -- structured error feedback for self-correction loops
|
|
25
|
+
- **`Trevl.examples`** -- 10 annotated examples for few-shot learning
|
|
26
|
+
- **`Trevl::DataSource.for("name").field_names("endpoint")`** -- discover available data fields
|
|
27
|
+
- **[`llms.txt`](llms.txt)** -- machine-readable reference in the repo root
|
|
28
|
+
- **JSON Schema** -- formal validation for editors, CI, and AI agents
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
# 1. Discover: what fields does this endpoint return?
|
|
32
|
+
Trevl::DataSource.for("myapi").field_names("salary")
|
|
33
|
+
# => ["q10", "q50", "q90"]
|
|
34
|
+
|
|
35
|
+
# 2. Reference: get the compact TREVL spec for a system prompt
|
|
36
|
+
Trevl.schema_reference
|
|
37
|
+
# => "# TREVL — TREndence Visualization Language\n..."
|
|
38
|
+
|
|
39
|
+
# 3. Examples: few-shot learning material
|
|
40
|
+
Trevl.examples.first
|
|
41
|
+
# => {name: "bar_chart", description: "Simple bar chart...", yaml: "..."}
|
|
42
|
+
|
|
43
|
+
# 4. Validate: catch errors before rendering
|
|
44
|
+
Trevl.validate(yaml).errors
|
|
45
|
+
# => ["[my_chart] Missing required field(s): api, highchartsData (at )"]
|
|
46
|
+
|
|
47
|
+
# 5. Render: YAML → Highcharts JSON
|
|
48
|
+
Trevl.render(yaml)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- **Declarative YAML** -- define charts, scores, tables, and filters without writing JavaScript
|
|
54
|
+
- **Pluggable data sources** -- REST APIs, CubeJS, static/in-memory data, or build your own
|
|
55
|
+
- **Computed fields** -- per-row JavaScript transformations via ExecJS
|
|
56
|
+
- **Postprocess** -- full-dataset transforms (sort, filter, aggregate) in JavaScript
|
|
57
|
+
- **Template inheritance** -- share chart styles with deep merge
|
|
58
|
+
- **JSON Schema validation** -- catch errors before rendering
|
|
59
|
+
- **iRuby notebooks** -- render interactive Highcharts directly in Jupyter
|
|
60
|
+
- **Fully offline** -- bundled Highcharts JS, no CDN needed
|
|
61
|
+
- **Standalone** -- no Rails, no framework dependencies
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
gem "trevl", github: "trendence/trevl"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Prerequisites:** Ruby >= 3.1, Node.js (`brew install node`) for computed fields.
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
require "trevl"
|
|
75
|
+
|
|
76
|
+
# 1. Register a data source
|
|
77
|
+
Trevl::DataSource.register("demo", Trevl::DataSource::Static.new(
|
|
78
|
+
data: {
|
|
79
|
+
"pirates" => {
|
|
80
|
+
"data" => [
|
|
81
|
+
{"name" => "Guybrush", "barrels" => 12},
|
|
82
|
+
{"name" => "LeChuck", "barrels" => 47},
|
|
83
|
+
{"name" => "Elaine", "barrels" => 3},
|
|
84
|
+
{"name" => "Stan", "barrels" => 28},
|
|
85
|
+
{"name" => "Murray", "barrels" => 0}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
))
|
|
90
|
+
|
|
91
|
+
# 2. Render
|
|
92
|
+
results = Trevl.render(<<~YAML)
|
|
93
|
+
components:
|
|
94
|
+
- id: rum_consumption
|
|
95
|
+
type: chart
|
|
96
|
+
api: demo
|
|
97
|
+
highchartsData:
|
|
98
|
+
chart:
|
|
99
|
+
type: bar
|
|
100
|
+
title:
|
|
101
|
+
text: Annual Rum Consumption by Pirate (Barrels)
|
|
102
|
+
colors: ["#8B4513"]
|
|
103
|
+
series:
|
|
104
|
+
- name: Rum (barrels)
|
|
105
|
+
data:
|
|
106
|
+
x: "$pirates.data.name"
|
|
107
|
+
y: "$pirates.data.barrels"
|
|
108
|
+
YAML
|
|
109
|
+
|
|
110
|
+
# 3. Done -- results.first["highchartsData"] is ready for Highcharts
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
<p align="center">
|
|
114
|
+
<img src="docs/screenshots/01_bar_chart.png" alt="Bar Chart" width="600" />
|
|
115
|
+
</p>
|
|
116
|
+
|
|
117
|
+
## Examples Gallery
|
|
118
|
+
|
|
119
|
+
<table>
|
|
120
|
+
<tr>
|
|
121
|
+
<td><img src="docs/screenshots/02_column_chart.png" alt="Column Chart" width="400" /></td>
|
|
122
|
+
<td><img src="docs/screenshots/03_line_chart.png" alt="Line Chart" width="400" /></td>
|
|
123
|
+
</tr>
|
|
124
|
+
<tr>
|
|
125
|
+
<td><img src="docs/screenshots/04_pie_chart.png" alt="Pie Chart" width="400" /></td>
|
|
126
|
+
<td><img src="docs/screenshots/05_computed_fields.png" alt="Computed Fields" width="400" /></td>
|
|
127
|
+
</tr>
|
|
128
|
+
<tr>
|
|
129
|
+
<td><img src="docs/screenshots/06_postprocess_top_n.png" alt="Postprocess Top-N" width="400" /></td>
|
|
130
|
+
<td><img src="docs/screenshots/10_multi_series.png" alt="Multi Series" width="400" /></td>
|
|
131
|
+
</tr>
|
|
132
|
+
</table>
|
|
133
|
+
|
|
134
|
+
See [`examples/`](examples/) for the YAML source of each chart.
|
|
135
|
+
|
|
136
|
+
## HTML Export
|
|
137
|
+
|
|
138
|
+
Render TREVL to an HTML file:
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
html = Trevl.render_to_html(yaml, width: 1000, height: 500)
|
|
142
|
+
File.write("chart.html", html)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The output is a complete HTML document that pulls Highcharts from the CDN. Point `highcharts_path` at a local copy (see [Highcharts](#highcharts)) and it is embedded inline instead, which makes the file self-contained and usable offline. Open it in any browser, or take a screenshot for AI agents:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# Grover gem (Puppeteer wrapper)
|
|
149
|
+
Grover.new(html).to_png
|
|
150
|
+
|
|
151
|
+
# Ferrum (Chrome DevTools Protocol)
|
|
152
|
+
browser = Ferrum::Browser.new
|
|
153
|
+
browser.content = html
|
|
154
|
+
browser.screenshot(path: "chart.png")
|
|
155
|
+
|
|
156
|
+
# Playwright MCP (for AI agents)
|
|
157
|
+
# browser_navigate → browser_take_screenshot
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## iRuby Notebooks
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
require "trevl"
|
|
164
|
+
require "trevl/notebook"
|
|
165
|
+
|
|
166
|
+
nb = Trevl::Notebook.new
|
|
167
|
+
|
|
168
|
+
nb.chart(<<~YAML, data: {"salary" => {"data" => [...]}})
|
|
169
|
+
components:
|
|
170
|
+
- id: chart
|
|
171
|
+
type: chart
|
|
172
|
+
api: static
|
|
173
|
+
highchartsData:
|
|
174
|
+
chart:
|
|
175
|
+
type: column
|
|
176
|
+
series:
|
|
177
|
+
- data:
|
|
178
|
+
x: "$salary.data.level"
|
|
179
|
+
y: "$salary.data.value"
|
|
180
|
+
YAML
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Highcharts is bundled -- charts render offline. See [`notebooks/demo.ipynb`](notebooks/demo.ipynb) for 5 working examples.
|
|
184
|
+
|
|
185
|
+
## Data Sources
|
|
186
|
+
|
|
187
|
+
### Static (in-memory)
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
Trevl::DataSource.register("mydata", Trevl::DataSource::Static.new(
|
|
191
|
+
data: {"endpoint" => {"data" => [...], "meta" => {...}}}
|
|
192
|
+
))
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### REST API
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
Trevl::DataSource.register("myapi", Trevl::DataSource::Api.new(
|
|
199
|
+
base_url: "https://api.example.com/v1",
|
|
200
|
+
auth: Trevl::Auth::BearerToken.new(ENV["API_TOKEN"])
|
|
201
|
+
))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### CubeJS
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
Trevl::DataSource.register("cube", Trevl::DataSource::Cube.new(
|
|
208
|
+
url: "https://cube.example.com/cubejs-api/v1",
|
|
209
|
+
token: ENV["CUBE_TOKEN"]
|
|
210
|
+
))
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Custom
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
class MySource < Trevl::DataSource::Base
|
|
217
|
+
def fetch(endpoint, params = {}, resource: nil)
|
|
218
|
+
{"data" => MyDB.query(endpoint, params), "meta" => {}}
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
Trevl::DataSource.register("db", MySource.new)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Per-render injection
|
|
226
|
+
|
|
227
|
+
Instead of registering globally, pass data directly to a render call.
|
|
228
|
+
Per-render data takes precedence over the registry and never touches shared
|
|
229
|
+
state — the right choice when the data differs per request (e.g. web apps
|
|
230
|
+
serving concurrent users).
|
|
231
|
+
|
|
232
|
+
For inline rows, pass the raw hash as `data:` — it answers any `api:` name in
|
|
233
|
+
the document, and components may omit `api:` entirely:
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
Trevl.render(yaml, data: {"scores" => rows_for_this_request})
|
|
237
|
+
Trevl.render_to_html(yaml, data: {"scores" => rows_for_this_request})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For full control (multiple sources, API/Cube instances), pass `data_sources:`
|
|
241
|
+
— entries win over `data:` for their name:
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
source = Trevl::DataSource::Api.new(base_url: "https://api.example.com/v1")
|
|
245
|
+
|
|
246
|
+
Trevl.render(yaml, data_sources: {"mydata" => source})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Validation
|
|
250
|
+
|
|
251
|
+
Validate TREVL YAML before rendering — catch errors early, not at render time.
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
result = Trevl.validate(<<~YAML)
|
|
255
|
+
components:
|
|
256
|
+
- id: my_chart
|
|
257
|
+
type: chart
|
|
258
|
+
YAML
|
|
259
|
+
|
|
260
|
+
result.valid? # => false
|
|
261
|
+
result.errors # => ["[my_chart] Missing required field(s): api, highchartsData (at )"]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Powered by [JSON Schema (draft 2020-12)](lib/trevl/schema/component.json) -- covers all 5 component types with conditional validation. The schema files work standalone in VS Code, CI pipelines, or any JSON Schema-compatible tool.
|
|
265
|
+
|
|
266
|
+
Ideal for AI agents: generate TREVL, validate, self-correct, render.
|
|
267
|
+
|
|
268
|
+
Full docs: [trevl.trendence.com/validation](https://trevl.trendence.com/validation)
|
|
269
|
+
|
|
270
|
+
## YAML Reference
|
|
271
|
+
|
|
272
|
+
### Component Types
|
|
273
|
+
|
|
274
|
+
| Type | Description |
|
|
275
|
+
|------|-------------|
|
|
276
|
+
| `chart` | Highcharts visualization (bar, column, line, pie, ...) |
|
|
277
|
+
| `score` | Single KPI value with optional unit |
|
|
278
|
+
| `table` | Data table with column definitions |
|
|
279
|
+
| `text` | Static text / HTML content |
|
|
280
|
+
| `filter` | Filter options bound to data |
|
|
281
|
+
|
|
282
|
+
### Variable References
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
"$endpoint.data.fieldName" # data row field
|
|
286
|
+
"$endpoint.meta.fieldName" # metadata field
|
|
287
|
+
"$resource.endpoint.data.fieldName" # with resource qualifier
|
|
288
|
+
"$computedFieldName" # computed field shorthand
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Computed Fields
|
|
292
|
+
|
|
293
|
+
Per-row JavaScript expressions:
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
computed:
|
|
297
|
+
- name: color
|
|
298
|
+
arguments:
|
|
299
|
+
val: "$salary.data.value"
|
|
300
|
+
code: 'val > 60000 ? "#003F85" : "#ccc"'
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Postprocess
|
|
304
|
+
|
|
305
|
+
Full-dataset JavaScript transforms:
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
postprocess: |
|
|
309
|
+
$result = $result
|
|
310
|
+
.sort((a, b) => b.value - a.value)
|
|
311
|
+
.slice(0, 10);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Templates
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
Trevl.template_store.register("blue_bar", {
|
|
318
|
+
"highchartsData" => {
|
|
319
|
+
"chart" => {"type" => "bar"},
|
|
320
|
+
"colors" => ["#003F85"]
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
```yaml
|
|
326
|
+
- id: my_chart
|
|
327
|
+
template: blue_bar
|
|
328
|
+
highchartsData:
|
|
329
|
+
title:
|
|
330
|
+
text: My Chart
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Deep merge: component overrides template at the same path.
|
|
334
|
+
|
|
335
|
+
## Auth
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
# Bearer token
|
|
339
|
+
auth = Trevl::Auth::BearerToken.new("token")
|
|
340
|
+
|
|
341
|
+
# Custom -- any object with #apply(headers, url:)
|
|
342
|
+
class MyAuth
|
|
343
|
+
def apply(headers, url: nil)
|
|
344
|
+
headers["Authorization"] = "Bearer #{fetch_token}"
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Configuration
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
Trevl.configure do |c|
|
|
353
|
+
c.logger = Logger.new($stdout, level: :info)
|
|
354
|
+
c.template_store = my_custom_store
|
|
355
|
+
end
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Highcharts
|
|
359
|
+
|
|
360
|
+
TREVL produces Highcharts configuration; it does not ship Highcharts. Highcharts is
|
|
361
|
+
commercial software by [Highsoft](https://www.highcharts.com/license) and is
|
|
362
|
+
deliberately not bundled here, so using it requires your own licence.
|
|
363
|
+
|
|
364
|
+
`Trevl.render` returns plain configuration hashes and never touches Highcharts at all.
|
|
365
|
+
Only the HTML export and the notebook display load it, and by default they reference
|
|
366
|
+
the CDN:
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
Trevl.configure do |c|
|
|
370
|
+
c.highcharts_url = "https://code.highcharts.com/11.4.0/highcharts.js" # default
|
|
371
|
+
c.highcharts_modules = ["https://code.highcharts.com/11.4.0/highcharts-more.js"]
|
|
372
|
+
end
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Set `highcharts_path` to a local file and it gets inlined instead of linked, which is
|
|
376
|
+
what you want for offline use or air-gapped rendering:
|
|
377
|
+
|
|
378
|
+
```ruby
|
|
379
|
+
Trevl.configure do |c|
|
|
380
|
+
c.highcharts_path = "/opt/highcharts/highcharts.js"
|
|
381
|
+
end
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`highcharts_modules` accepts URLs and local paths under the same rule: a path is
|
|
385
|
+
inlined, a URL is referenced.
|
|
386
|
+
|
|
387
|
+
### In a Rails app
|
|
388
|
+
|
|
389
|
+
If your app already renders charts, it almost certainly ships Highcharts through the
|
|
390
|
+
asset pipeline. In that case, do nothing: pass the hash from `Trevl.render` to your
|
|
391
|
+
existing frontend and let the bundle you already load draw it.
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
components = Trevl.render(yaml, data: {"rows" => rows})
|
|
395
|
+
# hand components.first["highchartsData"] to your Stimulus controller
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Only the server-side HTML export needs its own copy. The leanest way to give it one is
|
|
399
|
+
to reuse the file the asset pipeline already has:
|
|
400
|
+
|
|
401
|
+
```ruby
|
|
402
|
+
# config/initializers/trevl.rb
|
|
403
|
+
Trevl.configure do |c|
|
|
404
|
+
local = Rails.root.join("vendor/javascript/highcharts.js")
|
|
405
|
+
c.highcharts_path = local if local.exist?
|
|
406
|
+
end
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Without that initializer the export falls back to the CDN, which is fine for anything
|
|
410
|
+
that renders in a browser with network access.
|
|
411
|
+
|
|
412
|
+
## Development
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
bin/setup # install dependencies
|
|
416
|
+
bundle exec rspec # 140 specs
|
|
417
|
+
bundle exec standardrb # lint
|
|
418
|
+
bin/console # interactive console
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Language Specification
|
|
422
|
+
|
|
423
|
+
The full TREVL v3.0 specification lives at **[trevl.trendence.com](https://trevl.trendence.com)** -- covering component schemas, query definitions, filter operators, computed fields, postprocess patterns, template inheritance, and data source integration.
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT -- see [LICENSE](LICENSE).
|
|
428
|
+
|
|
429
|
+
Highcharts is not covered by that licence and is not distributed with this project.
|
|
430
|
+
Using it requires a licence from Highsoft; see [NOTICE](NOTICE) and
|
|
431
|
+
[highcharts.com/license](https://www.highcharts.com/license).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg aria-hidden="true" enable-background="new 0 0 733.8 140.4" version="1.1" width="733" height="140" viewBox="0 0 733 140" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g fill="#006ADF">
|
|
4
|
+
<path d="m542.9 108.6v-63.3h9s3.3-0.1 5.7 2.9 2.3 4.7 2.3 4.7 4.7-7.6 17.2-7.6 22.1 9.3 22.1 21.8v41.4h-9.7s-7.6-0.8-7.7-9.6v-26.9s0.4-11.4-9.9-11.4-11.8 11.4-11.8 14.9v33.2h-17.2z" />
|
|
5
|
+
<path d="m332.9 108.6v-63.3h9s3.3-0.1 5.7 2.9 2.3 4.7 2.3 4.7 4.7-7.6 17.2-7.6 22.1 9.3 22.1 21.8v41.4h-9.7s-7.6-0.8-7.7-9.6v-26.9s0.4-11.4-9.9-11.4-11.8 11.4-11.8 14.9v33.2h-17.2z" />
|
|
6
|
+
<path d="m731.7 83.2s0.4-1.3 0.4-6-1.2-14.1-8.8-22.6-16.8-10.2-23.1-10.2-15.7 2.1-23.2 10.8-9.8 20.1-7.6 30.2 8.3 17.7 19.8 22.3 24.6 1.2 30.8-2.7 8.7-7.4 8.7-7.4l-11.7-9s-5.4 6.2-14 6.2c0 0-8.8 0.4-13.5-4.3-3.8-3.9-3.6-7.2-3.6-7.2h45.8zm-31.4-23.9c11.6 0 13.9 10.5 13.9 10.5h-27.6s2-10.5 13.7-10.5z" />
|
|
7
|
+
<path d="m532.7 83s0.4-1.3 0.4-6-1.2-14.1-8.8-22.6-16.8-10.2-23.1-10.2-15.7 2.1-23.2 10.8-9.8 20.1-7.6 30.3 8.3 17.7 19.8 22.3 24.6 1.2 30.8-2.7 8.7-7.4 8.7-7.4l-11.7-9.1s-5.4 6.2-14 6.2c0 0-8.8 0.4-13.5-4.3-3.8-3.9-3.6-7.2-3.6-7.2h45.8zm-31.4-24c11.6 0 13.9 10.5 13.9 10.5h-27.6s2-10.5 13.7-10.5z" />
|
|
8
|
+
<path d="m323.4 83.2s0.4-1.3 0.4-6-1.2-14.1-8.8-22.6-16.8-10.1-23-10.1-15.7 2.1-23.2 10.8-9.8 20.1-7.6 30.2 8.3 17.7 19.8 22.3 24.6 1.2 30.8-2.7 8.7-7.4 8.7-7.4l-11.6-9.2s-5.4 6.2-14 6.2c0 0-8.8 0.4-13.5-4.3-3.8-3.9-3.6-7.2-3.6-7.2h45.6zm-31.4-23.9c11.6 0 13.9 10.5 13.9 10.5h-27.6s2.1-10.5 13.7-10.5z" />
|
|
9
|
+
<path d="m428.8 45.5c5.8 0 11.2 1.6 15.9 4.3v-31.3h16.7l0.1 90.5h-8.2s-5.3 0-7.5-4.7c-4.9 3.1-10.7 4.9-17 4.9-17.6 0-31.9-14.3-31.9-31.8 0.1-17.6 14.4-31.9 31.9-31.9zm0 47.9c8.9 0 16.1-7.2 16.1-16.1s-7.2-16.1-16.1-16.1-16.1 7.2-16.1 16.1 7.3 16.1 16.1 16.1z" />
|
|
10
|
+
<path d="m173.4 45.6v-16.8h17.7v16.8h10.2v13.1h-10.3v28.3s-0.4 5.4 5.2 5.4h8v16.4h-12.9s-8.5 0.5-14.1-5.6c-4.3-4.7-3.9-12.4-3.9-12.4v-32h-10.3v-13.2h10.4z" />
|
|
11
|
+
<path d="m256.7 62.4l-2.4 0.1s-10.3 0.1-14.8 6.1c-4.3 5.7-3.5 12.4-3.5 12.4v27.4h-17.6v-63h9.6s2.7-0.2 5.2 3c2.5 3.1 2.1 5.6 2.1 5.6s1-3.6 5.8-6.5 10.2-2.7 10.2-2.7h5.6v17.4l-0.2 0.2z" />
|
|
12
|
+
<path d="m651.6 87.2c-2.9 3.9-7.6 6.4-12.9 6.4-8.9 0-16.1-7.2-16.1-16.1s7.2-16.1 16.1-16.1c5.4 0 10.2 2.7 13.2 6.8l12.9-9c-5.8-8.2-15.3-13.6-26.1-13.6-17.6 0-31.8 14.3-31.8 31.8 0 17.6 14.3 31.8 31.8 31.8 10.5 0 19.8-5.1 25.6-12.9l-12.7-9.1z" />
|
|
13
|
+
<circle cx="69.7" cy="69.5" r="13.4" />
|
|
14
|
+
<path d="m69.7 28.8c-22.5 0-40.7 18.2-40.7 40.7 0 8.1 2.4 15.7 6.5 22 0.1 0.1 0.1 0.2 0.2 0.3 0 0 0 0.1 0.1 0.1 1.5 2.2 4 3.6 6.9 3.6 4.6 0 8.3-3.7 8.3-8.3 0-1.7-0.5-3.2-1.4-4.6 0-0.1-0.1-0.1-0.1-0.2-2.4-3.7-3.8-8.2-3.8-13 0-13.3 10.8-24.1 24.1-24.1s24.1 10.8 24.1 24.1-10.8 24.1-24.1 24.1c-0.9 0-1.7 0-2.5-0.1h-0.1-0.1-0.8c-4.6 0-8.3 3.7-8.3 8.3 0 4.3 3.2 7.8 7.3 8.3h0.4c1.3 0.1 2.7 0.2 4.1 0.2 22.5 0 40.7-18.2 40.7-40.7s-18.3-40.7-40.8-40.7z" />
|
|
15
|
+
<path d="m69.7 0.6c-6.6 0-13 1-19.1 2.7-0.5 0.1-0.9 0.2-1.3 0.4-3.3 1.2-5.7 4.4-5.7 8.1 0 4.8 3.9 8.7 8.7 8.7 0.7 0 1.3-0.1 1.9-0.2 0.4-0.1 0.7-0.2 1-0.3 4.6-1.3 9.5-2.1 14.5-2.1 28.5 0 51.6 23.1 51.6 51.6s-23.1 51.6-51.6 51.6-51.6-23.1-51.6-51.6c0-14.7 6.2-28 16.1-37.4 0.2-0.1 0.3-0.3 0.5-0.4 1.5-1.6 2.4-3.7 2.4-6 0-4.8-3.9-8.7-8.7-8.7-2 0-3.9 0.7-5.4 1.9-0.4 0.3-0.8 0.7-1.1 1-12.9 12.5-21.1 30.1-21.1 49.6 0 38 30.8 68.9 68.9 68.9s68.9-30.8 68.9-68.9-30.8-68.9-68.9-68.9z" />
|
|
16
|
+
</g>
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 36" width="92" height="36">
|
|
2
|
+
<!-- TREVL logo: letters in Trendence blue, E = colorful bar chart -->
|
|
3
|
+
|
|
4
|
+
<!-- T - Trendence blue -->
|
|
5
|
+
<text x="0" y="27" font-family="Inter, -apple-system, sans-serif" font-size="28" font-weight="700" fill="#006ADF" letter-spacing="-1">T</text>
|
|
6
|
+
|
|
7
|
+
<!-- R - Trendence blue -->
|
|
8
|
+
<text x="17" y="27" font-family="Inter, -apple-system, sans-serif" font-size="28" font-weight="700" fill="#006ADF" letter-spacing="-1">R</text>
|
|
9
|
+
|
|
10
|
+
<!-- E as three horizontal bars in brand colors -->
|
|
11
|
+
<rect x="38" y="7" width="18" height="5" rx="1.5" fill="#8B3A9F"/>
|
|
12
|
+
<rect x="38" y="14" width="12" height="5" rx="1.5" fill="#E8922F"/>
|
|
13
|
+
<rect x="38" y="21" width="16" height="5" rx="1.5" fill="#E85D3A"/>
|
|
14
|
+
|
|
15
|
+
<!-- V - Trendence blue -->
|
|
16
|
+
<text x="58" y="27" font-family="Inter, -apple-system, sans-serif" font-size="28" font-weight="700" fill="#006ADF" letter-spacing="-1">V</text>
|
|
17
|
+
|
|
18
|
+
<!-- L - Trendence blue -->
|
|
19
|
+
<text x="76" y="27" font-family="Inter, -apple-system, sans-serif" font-size="28" font-weight="700" fill="#006ADF" letter-spacing="-1">L</text>
|
|
20
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Simple bar chart. Use when you want horizontal bars comparing categories.
|
|
2
|
+
components:
|
|
3
|
+
- id: rum_consumption
|
|
4
|
+
type: chart
|
|
5
|
+
api: demo
|
|
6
|
+
highchartsData:
|
|
7
|
+
chart:
|
|
8
|
+
type: bar
|
|
9
|
+
title:
|
|
10
|
+
text: Annual Rum Consumption by Pirate (Barrels)
|
|
11
|
+
colors: ["#8B4513"]
|
|
12
|
+
series:
|
|
13
|
+
- name: Rum (barrels)
|
|
14
|
+
data:
|
|
15
|
+
x: "$pirates.data.name"
|
|
16
|
+
y: "$pirates.data.barrels"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Vertical column chart. Use for comparing values across categories side by side.
|
|
2
|
+
components:
|
|
3
|
+
- id: treasure_haul
|
|
4
|
+
type: chart
|
|
5
|
+
api: demo
|
|
6
|
+
highchartsData:
|
|
7
|
+
chart:
|
|
8
|
+
type: column
|
|
9
|
+
title:
|
|
10
|
+
text: Treasure Haul by Quarter (Doubloons)
|
|
11
|
+
colors: ["#DAA520"]
|
|
12
|
+
plotOptions:
|
|
13
|
+
column:
|
|
14
|
+
borderRadius: 4
|
|
15
|
+
series:
|
|
16
|
+
- name: Doubloons
|
|
17
|
+
data:
|
|
18
|
+
x: "$loot.data.quarter"
|
|
19
|
+
y: "$loot.data.doubloons"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Line chart for time series. Use when showing trends over time.
|
|
2
|
+
components:
|
|
3
|
+
- id: monkey_sightings
|
|
4
|
+
type: chart
|
|
5
|
+
api: demo
|
|
6
|
+
highchartsData:
|
|
7
|
+
chart:
|
|
8
|
+
type: line
|
|
9
|
+
title:
|
|
10
|
+
text: Three-Headed Monkey Sightings per Month
|
|
11
|
+
xAxis:
|
|
12
|
+
type: category
|
|
13
|
+
yAxis:
|
|
14
|
+
title:
|
|
15
|
+
text: Sightings
|
|
16
|
+
series:
|
|
17
|
+
- name: Sightings
|
|
18
|
+
color: "#2E7D32"
|
|
19
|
+
data:
|
|
20
|
+
x: "$monkeys.data.month"
|
|
21
|
+
y: "$monkeys.data.sightings"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Pie chart for proportions. Use when showing parts of a whole.
|
|
2
|
+
components:
|
|
3
|
+
- id: mutiny_vote
|
|
4
|
+
type: chart
|
|
5
|
+
api: demo
|
|
6
|
+
highchartsData:
|
|
7
|
+
chart:
|
|
8
|
+
type: pie
|
|
9
|
+
title:
|
|
10
|
+
text: Mutiny Board Election Results
|
|
11
|
+
series:
|
|
12
|
+
- name: Votes
|
|
13
|
+
data:
|
|
14
|
+
name: "$candidates.data.candidate"
|
|
15
|
+
y: "$candidates.data.votes"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Computed fields with conditional coloring. Use when you need per-row logic.
|
|
2
|
+
# Arguments bind $references to JS variables. Code is a JS expression.
|
|
3
|
+
# Computed field results are accessible as row fields via $endpoint.data.fieldName.
|
|
4
|
+
components:
|
|
5
|
+
- id: insult_mastery
|
|
6
|
+
type: chart
|
|
7
|
+
api: demo
|
|
8
|
+
computed:
|
|
9
|
+
- name: color
|
|
10
|
+
arguments:
|
|
11
|
+
val: "$pirates.data.skill"
|
|
12
|
+
code: 'val > 80 ? "#2E7D32" : val > 50 ? "#E65100" : "#C62828"'
|
|
13
|
+
highchartsData:
|
|
14
|
+
chart:
|
|
15
|
+
type: bar
|
|
16
|
+
title:
|
|
17
|
+
text: Insult Sword Fighting Skill Rating
|
|
18
|
+
plotOptions:
|
|
19
|
+
bar:
|
|
20
|
+
colorByPoint: true
|
|
21
|
+
series:
|
|
22
|
+
- name: Skill
|
|
23
|
+
data:
|
|
24
|
+
x: "$pirates.data.name"
|
|
25
|
+
y: "$pirates.data.skill"
|
|
26
|
+
color: "$pirates.data.color"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Postprocess: sort and take top N. Use when you need to filter/sort the full dataset.
|
|
2
|
+
# $result is the data array. Reassign it with the transformed result.
|
|
3
|
+
components:
|
|
4
|
+
- id: top_islands
|
|
5
|
+
type: chart
|
|
6
|
+
api: demo
|
|
7
|
+
postprocess: |
|
|
8
|
+
$result = $result
|
|
9
|
+
.sort(function(a, b) { return b.buried - a.buried; })
|
|
10
|
+
.slice(0, 5);
|
|
11
|
+
highchartsData:
|
|
12
|
+
chart:
|
|
13
|
+
type: bar
|
|
14
|
+
title:
|
|
15
|
+
text: Top 5 Islands by Buried Treasure
|
|
16
|
+
colors: ["#DAA520"]
|
|
17
|
+
series:
|
|
18
|
+
- name: Chests buried
|
|
19
|
+
data:
|
|
20
|
+
x: "$islands.data.name"
|
|
21
|
+
y: "$islands.data.buried"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Score component for a single KPI value. Use for dashboards with key numbers.
|
|
2
|
+
components:
|
|
3
|
+
- id: crew_morale
|
|
4
|
+
type: score
|
|
5
|
+
api: demo
|
|
6
|
+
display:
|
|
7
|
+
value: "$morale.data.score"
|
|
8
|
+
unit: "%"
|
|
9
|
+
header:
|
|
10
|
+
title: Crew Morale
|
|
11
|
+
format:
|
|
12
|
+
minimumFractionDigits: 0
|
|
13
|
+
maximumFractionDigits: 0
|