Skip to content

Commit f69a856

Browse files
committed
Add demos into queryjson
1 parent 8fcc24b commit f69a856

53 files changed

Lines changed: 5396 additions & 3315 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,75 @@ npm-install: ## Install npm dependencies
106106
.PHONY: init
107107
init: setup-githooks create-switch pin install npm-install ## Create a local dev enviroment
108108

109+
DEMO_FILES = \
110+
repl-demo \
111+
cli-basic \
112+
cli-advanced \
113+
comparison \
114+
jq-compat-demos/00-error-messages \
115+
jq-compat-demos/01-stricter-null-handling \
116+
jq-compat-demos/02-fn-vs-def \
117+
jq-compat-demos/03-snake-case-naming \
118+
jq-compat-demos/04-clearer-naming \
119+
jq-compat-demos/05-group-by-behavior \
120+
jq-compat-demos/06-keys-insertion-order \
121+
jq-compat-demos/07-unique-insertion-order \
122+
jq-compat-demos/08-infinite-generator \
123+
jq-compat-demos/09-optional-access-functions \
124+
jq-compat-demos/10-additional-features \
125+
jq-compat-demos/all-demos
126+
127+
109128
.PHONY: demo
110-
demo: ## Generate demo from tape file (usage: make demo FILE=cli-basic)
129+
demo: demo-cast demo-gif ## Generate cast+gif demo (usage: make demo FILE=cli-basic)
130+
131+
.PHONY: demo-cast
132+
demo-cast: ## Record demo cast (usage: make demo-cast FILE=cli-basic)
111133
@if [ -z "$(FILE)" ]; then \
112-
echo "Error: FILE is required. Usage: make demo FILE=cli-basic"; \
134+
echo "Error: FILE is required. Usage: make demo FILE=repl-demo"; \
113135
exit 1; \
114136
fi
115137
$(DUNE) install
116-
vhs "docs/$(FILE).tape"
138+
@if [ "$(FILE)" = "repl-demo" ]; then \
139+
asciinema rec --overwrite --cols 140 --rows 48 -c "python3 docs/record-repl-demo.py" docs/repl-demo.cast; \
140+
elif [ -f "docs/demo-scripts/$(FILE).sh" ]; then \
141+
asciinema rec --overwrite --cols 140 --rows 48 -c "bash docs/demo-scripts/$(FILE).sh" "docs/$(FILE).cast"; \
142+
else \
143+
echo "Error: unknown demo '$(FILE)'. Expected docs/demo-scripts/$(FILE).sh"; \
144+
exit 1; \
145+
fi
146+
147+
.PHONY: demo-gif
148+
demo-gif: ## Render demo gif from cast (usage: make demo-gif FILE=cli-basic)
149+
@if [ -z "$(FILE)" ]; then \
150+
echo "Error: FILE is required. Usage: make demo-gif FILE=repl-demo"; \
151+
exit 1; \
152+
fi
153+
@if [ ! -f "docs/$(FILE).cast" ]; then \
154+
echo "Error: docs/$(FILE).cast not found. Run: make demo-cast FILE=$(FILE)"; \
155+
exit 1; \
156+
fi
157+
@if ! command -v agg >/dev/null 2>&1; then \
158+
echo "Error: agg is required to render GIFs."; \
159+
echo "Install it from: https://github.com/asciinema/agg"; \
160+
exit 1; \
161+
fi
162+
agg "docs/$(FILE).cast" "docs/$(FILE).gif"
163+
164+
.PHONY: demo-all-cast
165+
demo-all-cast: ## Record all demos as .cast files
166+
@for demo in $(DEMO_FILES); do \
167+
$(MAKE) demo-cast FILE=$$demo || exit 1; \
168+
done
169+
170+
.PHONY: demo-all-gif
171+
demo-all-gif: ## Render all demos as .gif files
172+
@for demo in $(DEMO_FILES); do \
173+
$(MAKE) demo-gif FILE=$$demo || exit 1; \
174+
done
175+
176+
.PHONY: demo-all
177+
demo-all: demo-all-cast demo-all-gif ## Record and render all demos
117178

118179
.PHONY: bench
119180
bench: ## Run benchmarks

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@ $ jq ".naem" <<< '{"name": "Alice", "age": 30}'
5050
null
5151
```
5252

53-
<video src="docs/repl-demo.mp4" autoplay loop muted playsinline></video>
53+
<img src="docs/repl-demo.gif" alt="query-json interactive REPL demo" />
54+
55+
More demos:
56+
57+
- [Basic CLI demo](./docs/cli-basic.gif)
58+
- [Advanced CLI demo](./docs/cli-advanced.gif)
59+
- [query-json vs jq demo](./docs/comparison.gif)
60+
- [jq compatibility demo bundle](./docs/jq-compat-demos/all-demos.gif)
5461

5562
### Portable
5663

docs/JQ_COMPATIBILITY.md

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -461,37 +461,35 @@ query-json includes some functions not found in jq:
461461

462462
## Video Demos
463463

464-
All the examples in this guide have corresponding VHS tape demos. You can find them in [`docs/jq-compat-demos/`](./jq-compat-demos/) and generate the videos using [VHS](https://github.com/charmbracelet/vhs).
464+
All the examples in this guide have corresponding asciinema demos. Cast recordings and GIFs are generated from scripts in [`docs/demo-scripts/jq-compat-demos/`](./demo-scripts/jq-compat-demos/).
465465

466466
### Available Demos
467467

468-
| Demo | Description | Tape File |
469-
|------|-------------|-----------|
470-
| **Error Messages** | Helpful error messages with context and hints | [`00-error-messages.tape`](./jq-compat-demos/00-error-messages.tape) |
471-
| **Null Handling** | Stricter null handling catches bugs early | [`01-stricter-null-handling.tape`](./jq-compat-demos/01-stricter-null-handling.tape) |
472-
| **fn vs def** | `fn` keyword for user-defined functions | [`02-fn-vs-def.tape`](./jq-compat-demos/02-fn-vs-def.tape) |
473-
| **Snake_case** | Readable `snake_case` function names | [`03-snake-case-naming.tape`](./jq-compat-demos/03-snake-case-naming.tape) |
474-
| **Clearer Naming** | Self-explanatory function names | [`04-clearer-naming.tape`](./jq-compat-demos/04-clearer-naming.tape) |
475-
| **group_by** | Returns object instead of array of arrays | [`05-group-by-behavior.tape`](./jq-compat-demos/05-group-by-behavior.tape) |
476-
| **keys** | Preserves insertion order | [`06-keys-insertion-order.tape`](./jq-compat-demos/06-keys-insertion-order.tape) |
477-
| **unique** | Preserves insertion order | [`07-unique-insertion-order.tape`](./jq-compat-demos/07-unique-insertion-order.tape) |
478-
| **infinite** | Generator producing 0, 1, 2, ... | [`08-infinite-generator.tape`](./jq-compat-demos/08-infinite-generator.tape) |
479-
| **Optional Functions** | `?` works on function calls | [`09-optional-access-functions.tape`](./jq-compat-demos/09-optional-access-functions.tape) |
480-
| **Additional Features** | Features unique to query-json | [`10-additional-features.tape`](./jq-compat-demos/10-additional-features.tape) |
481-
| **All Demos** | Complete overview of all differences | [`all-demos.tape`](./jq-compat-demos/all-demos.tape) |
468+
| Demo | Description | Make `FILE` | Script | GIF | Cast |
469+
|------|-------------|-------------|--------|-----|------|
470+
| **Error Messages** | Helpful error messages with context and hints | `jq-compat-demos/00-error-messages` | [`00-error-messages.sh`](./demo-scripts/jq-compat-demos/00-error-messages.sh) | [`00-error-messages.gif`](./jq-compat-demos/00-error-messages.gif) | [`00-error-messages.cast`](./jq-compat-demos/00-error-messages.cast) |
471+
| **Null Handling** | Stricter null handling catches bugs early | `jq-compat-demos/01-stricter-null-handling` | [`01-stricter-null-handling.sh`](./demo-scripts/jq-compat-demos/01-stricter-null-handling.sh) | [`01-stricter-null-handling.gif`](./jq-compat-demos/01-stricter-null-handling.gif) | [`01-stricter-null-handling.cast`](./jq-compat-demos/01-stricter-null-handling.cast) |
472+
| **fn vs def** | `fn` keyword for user-defined functions | `jq-compat-demos/02-fn-vs-def` | [`02-fn-vs-def.sh`](./demo-scripts/jq-compat-demos/02-fn-vs-def.sh) | [`02-fn-vs-def.gif`](./jq-compat-demos/02-fn-vs-def.gif) | [`02-fn-vs-def.cast`](./jq-compat-demos/02-fn-vs-def.cast) |
473+
| **Snake_case** | Readable `snake_case` function names | `jq-compat-demos/03-snake-case-naming` | [`03-snake-case-naming.sh`](./demo-scripts/jq-compat-demos/03-snake-case-naming.sh) | [`03-snake-case-naming.gif`](./jq-compat-demos/03-snake-case-naming.gif) | [`03-snake-case-naming.cast`](./jq-compat-demos/03-snake-case-naming.cast) |
474+
| **Clearer Naming** | Self-explanatory function names | `jq-compat-demos/04-clearer-naming` | [`04-clearer-naming.sh`](./demo-scripts/jq-compat-demos/04-clearer-naming.sh) | [`04-clearer-naming.gif`](./jq-compat-demos/04-clearer-naming.gif) | [`04-clearer-naming.cast`](./jq-compat-demos/04-clearer-naming.cast) |
475+
| **group_by** | Returns object instead of array of arrays | `jq-compat-demos/05-group-by-behavior` | [`05-group-by-behavior.sh`](./demo-scripts/jq-compat-demos/05-group-by-behavior.sh) | [`05-group-by-behavior.gif`](./jq-compat-demos/05-group-by-behavior.gif) | [`05-group-by-behavior.cast`](./jq-compat-demos/05-group-by-behavior.cast) |
476+
| **keys** | Preserves insertion order | `jq-compat-demos/06-keys-insertion-order` | [`06-keys-insertion-order.sh`](./demo-scripts/jq-compat-demos/06-keys-insertion-order.sh) | [`06-keys-insertion-order.gif`](./jq-compat-demos/06-keys-insertion-order.gif) | [`06-keys-insertion-order.cast`](./jq-compat-demos/06-keys-insertion-order.cast) |
477+
| **unique** | Preserves insertion order | `jq-compat-demos/07-unique-insertion-order` | [`07-unique-insertion-order.sh`](./demo-scripts/jq-compat-demos/07-unique-insertion-order.sh) | [`07-unique-insertion-order.gif`](./jq-compat-demos/07-unique-insertion-order.gif) | [`07-unique-insertion-order.cast`](./jq-compat-demos/07-unique-insertion-order.cast) |
478+
| **infinite** | Generator producing 0, 1, 2, ... | `jq-compat-demos/08-infinite-generator` | [`08-infinite-generator.sh`](./demo-scripts/jq-compat-demos/08-infinite-generator.sh) | [`08-infinite-generator.gif`](./jq-compat-demos/08-infinite-generator.gif) | [`08-infinite-generator.cast`](./jq-compat-demos/08-infinite-generator.cast) |
479+
| **Optional Functions** | `?` works on function calls | `jq-compat-demos/09-optional-access-functions` | [`09-optional-access-functions.sh`](./demo-scripts/jq-compat-demos/09-optional-access-functions.sh) | [`09-optional-access-functions.gif`](./jq-compat-demos/09-optional-access-functions.gif) | [`09-optional-access-functions.cast`](./jq-compat-demos/09-optional-access-functions.cast) |
480+
| **Additional Features** | Features unique to query-json | `jq-compat-demos/10-additional-features` | [`10-additional-features.sh`](./demo-scripts/jq-compat-demos/10-additional-features.sh) | [`10-additional-features.gif`](./jq-compat-demos/10-additional-features.gif) | [`10-additional-features.cast`](./jq-compat-demos/10-additional-features.cast) |
481+
| **All Demos** | Complete overview of all differences | `jq-compat-demos/all-demos` | [`all-demos.sh`](./demo-scripts/jq-compat-demos/all-demos.sh) | [`all-demos.gif`](./jq-compat-demos/all-demos.gif) | [`all-demos.cast`](./jq-compat-demos/all-demos.cast) |
482482

483483
### Generating Videos
484484

485-
To generate all demo videos:
485+
To generate all demo casts and GIFs:
486486

487487
```bash
488488
# Generate all demos
489-
for tape in docs/jq-compat-demos/*.tape; do
490-
vhs "$tape"
491-
done
489+
make demo-all
492490

493491
# Or generate a specific demo
494-
vhs docs/jq-compat-demos/all-demos.tape
492+
make demo FILE=jq-compat-demos/all-demos
495493
```
496494

497-
The generated `.mp4` files will be placed in `docs/jq-compat-demos/`.
495+
The generated `.cast` and `.gif` files are placed in `docs/jq-compat-demos/`.

docs/cli-advanced.cast

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{"version": 2, "width": 140, "height": 48, "timestamp": 1773233359, "env": {"SHELL": "/usr/bin/zsh", "TERM": "xterm-256color"}}
2+
[0.005387, "o", "\r\n# query-json advanced filters\r\n"]
3+
[0.806459, "o", "\r\n$ _opam/bin/query-json '.second.store.books | map(.price) | add' test/mock.json\r\n"]
4+
[0.811853, "o", "\r\nJSON parse error: Line 1, bytes 0-14:\r\nInvalid token 'test/mock.json'\r\n\r\n"]
5+
[1.813727, "o", "\r\n$ _opam/bin/query-json 'fn expensive: .price > 20; .second.store.books | filter(expensive)' test/mock.json\r\n"]
6+
[1.819203, "o", "\r\nJSON parse error: Line 1, bytes 0-14:\r\nInvalid token 'test/mock.json'\r\n\r\n"]
7+
[2.820971, "o", "\r\n$ _opam/bin/query-json '.second.store.books | group_by(.category) | map_values(length)' test/mock.json\r\n"]
8+
[2.826431, "o", "\r\nJSON parse error: Line 1, bytes 0-14:\r\nInvalid token 'test/mock.json'\r\n\r\n"]
9+
[3.828399, "o", "\r\n$ _opam/bin/query-json '.second.store.books | map({title, price_with_tax: (.price * 1.21)})' test/mock.json\r\n"]
10+
[3.833888, "o", "\r\nJSON parse error: Line 1, bytes 0-14:\r\nInvalid token 'test/mock.json'\r\n\r\n"]

docs/cli-advanced.gif

34.5 KB
Loading

docs/cli-basic.cast

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{"version": 2, "width": 140, "height": 48, "timestamp": 1773233356, "env": {"SHELL": "/usr/bin/zsh", "TERM": "xterm-256color"}}
2+
[0.005209, "o", "\r\n# query-json basic CLI\r\n"]
3+
[0.80653, "o", "\r\n$ _opam/bin/query-json --version\r\n"]
4+
[0.812294, "o", "1.0.0~beta-1\r\n"]
5+
[1.814421, "o", "\r\n$ _opam/bin/query-json '.second.store.books[0].title' test/mock.json\r\n"]
6+
[1.819899, "o", "\r\nJSON parse error: Line 1, bytes 0-14:\r\nInvalid token 'test/mock.json'\r\n\r\n"]
7+
[2.821833, "o", "\r\n$ cat test/mock.json | _opam/bin/query-json '.second.store.books | length'\r\n"]
8+
[2.822937, "o", "cat: test/mock.json"]
9+
[2.823025, "o", ": No such file or directory\r\n"]
10+
[2.827757, "o", "\r\nJSON parse error: Blank input data\r\n\r\n"]

docs/cli-basic.gif

20.2 KB
Loading

docs/comparison.cast

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{"version": 2, "width": 140, "height": 48, "timestamp": 1773233364, "env": {"SHELL": "/usr/bin/zsh", "TERM": "xterm-256color"}}
2+
[0.006008, "o", "\r\n# jq vs query-json\r\n"]
3+
[0.807216, "o", "\r\n$ echo '\"hello\"' | jq 'ascii_upcase'\r\n"]
4+
[0.81008, "o", "\u001b[0;32m\"HELLO\"\u001b[0m\r\n"]
5+
[1.81237, "o", "\r\n$ echo '\"hello\"' | _opam/bin/query-json 'to_uppercase'\r\n"]
6+
[1.819039, "o", "\u001b[32m\"HELLO\"\u001b[39m\u001b[0m\r\n"]
7+
[2.821299, "o", "\r\n$ echo '42' | jq 'tostring'\r\n"]
8+
[2.823731, "o", "\u001b[0;32m\"42\"\u001b[0m\r\n"]
9+
[3.825361, "o", "\r\n$ echo '42' | _opam/bin/query-json 'to_string'\r\n"]
10+
[3.831415, "o", "\u001b[32m\"42\"\u001b[39m\u001b[0m\r\n"]
11+
[4.833678, "o", "\r\n$ echo '[1,2,3]' | jq 'def double: . * 2; map(double)'\r\n"]
12+
[4.835936, "o", "\u001b[1;39m[\r\n \u001b[0;39m2\u001b[0m\u001b[1;39m,\r\n \u001b[0;39m4\u001b[0m\u001b[1;39m,\r\n \u001b[0;39m6\u001b[0m\u001b[1;39m\r\n\u001b[1;39m]\u001b[0m\r\n"]
13+
[5.837576, "o", "\r\n$ echo '[1,2,3]' | _opam/bin/query-json 'fn double: . * 2; map(double)'\r\n"]
14+
[5.843697, "o", "[ \u001b[32m2\u001b[39m\u001b[0m, \u001b[32m4\u001b[39m\u001b[0m, \u001b[32m6\u001b[39m\u001b[0m ]\r\n"]

docs/comparison.gif

24.3 KB
Loading

docs/demo-scripts/cli-advanced.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
source "$(dirname "$0")/common.sh"
4+
5+
section "query-json advanced filters"
6+
run_cmd "${QUERY_JSON_BIN} '.second.store.books | map(.price) | add' test/mock.json"
7+
run_cmd "${QUERY_JSON_BIN} 'fn expensive: .price > 20; .second.store.books | filter(expensive)' test/mock.json"
8+
run_cmd "${QUERY_JSON_BIN} '.second.store.books | group_by(.category) | map_values(length)' test/mock.json"
9+
run_cmd "${QUERY_JSON_BIN} '.second.store.books | map({title, price_with_tax: (.price * 1.21)})' test/mock.json"

0 commit comments

Comments
 (0)