@tutorialkit-rb/cli 0.1.5 → 0.1.7
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.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/template/.claude/skills/rails-file-management/SKILL.md +211 -0
- package/template/.claude/skills/rails-lesson-recipes/SKILL.md +415 -0
- package/template/.claude/skills/rails-wasm-author-constraints/SKILL.md +181 -0
- package/template/.claude/skills/tutorial-content-structure/SKILL.md +377 -0
- package/template/.claude/skills/tutorial-lesson-config/SKILL.md +389 -0
- package/template/.claude/skills/tutorial-quickstart/SKILL.md +440 -0
- package/template/.github/workflows/deploy.yml +85 -0
- package/template/.gitignore +10 -0
- package/template/CLAUDE.md +47 -0
- package/template/astro.config.ts +12 -0
- package/template/bin/build-pack +84 -0
- package/template/bin/build-ruby-base +180 -0
- package/template/bin/link-local +13 -0
- package/template/bin/unlink-local +7 -0
- package/template/cors-proxy/README.md +63 -0
- package/template/cors-proxy/package-lock.json +1504 -0
- package/template/cors-proxy/package.json +12 -0
- package/template/cors-proxy/src/index.ts +87 -0
- package/template/cors-proxy/wrangler.toml +7 -0
- package/template/netlify.toml +9 -0
- package/template/package.json +12 -4
- package/template/ruby-wasm/Gemfile +6 -0
- package/template/ruby-wasm/Gemfile.base +19 -0
- package/template/ruby-wasm/Gemfile.base.lock +50 -0
- package/template/ruby-wasm/Gemfile.lock +8 -0
- package/template/ruby-wasm/bin/pack-gems +368 -0
- package/template/ruby-wasm/package.json +6 -0
- package/template/src/components/FileManager.tsx +33 -0
- package/template/src/components/HeadTags.astro +6 -6
- package/template/src/components/HelpDropdown.tsx +1 -1
- package/template/src/components/RailsPathLinkHandler.tsx +2 -2
- package/template/src/components/ShellConfigurator.tsx +6 -1
- package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +4 -4
- package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +4 -4
- package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +2 -2
- package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/.tk-config.json +3 -0
- package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/controllers/http_demo_controller.rb +65 -0
- package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/views/http_demo/index.html.erb +172 -0
- package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/config/routes.rb +8 -0
- package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/content.md +97 -0
- package/template/src/content/tutorial/9-outbound-http/meta.md +4 -0
- package/template/src/content/tutorial/meta.md +5 -0
- package/template/src/middleware.ts +14 -0
- package/template/src/templates/default/lib/boot-progress.js +49 -0
- package/template/src/templates/default/lib/http-bridge.js +55 -0
- package/template/src/templates/default/lib/patches/http_bridge.rb +167 -0
- package/template/src/templates/default/lib/rails.js +52 -5
- package/template/src/templates/default/lib/server.js +33 -1
- package/template/src/templates/default/package.json +4 -1
- package/template/src/templates/default/scripts/rails.js +1 -1
- package/template/src/templates/default/scripts/smoke-test.js +349 -0
- package/template/src/templates/default/scripts/wasi-loader.mjs +10 -0
- package/template/src/templates/default/workspace/_debug_app/.github/dependabot.yml +12 -0
- package/template/src/templates/default/workspace/_debug_app/.github/workflows/ci.yml +51 -0
- package/template/src/templates/default/workspace/_debug_app/.ruby-version +1 -0
- package/template/src/templates/default/workspace/_debug_app/Gemfile +37 -0
- package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/application.html.erb +1 -2
- package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/manifest.json.erb +2 -2
- package/template/src/templates/default/workspace/_debug_app/bin/dev +2 -0
- package/template/src/templates/default/workspace/_debug_app/bin/rake +4 -0
- package/template/src/templates/default/workspace/_debug_app/bin/setup +34 -0
- package/template/src/templates/default/workspace/_debug_app/config/application.rb +30 -0
- package/template/src/templates/default/workspace/_debug_app/config/cable.yml +10 -0
- package/template/src/templates/default/workspace/_debug_app/config/credentials.yml.enc +1 -0
- package/template/src/templates/default/workspace/_debug_app/config/master.key +1 -0
- package/template/src/templates/default/workspace/_debug_app/config/routes.rb +14 -0
- package/template/src/templates/default/workspace/_debug_app/test/models/.keep +0 -0
- package/template/src/templates/default/workspace/_debug_app/test/test_helper.rb +15 -0
- package/template/src/templates/default/workspace/_debug_app/tmp/.keep +0 -0
- package/template/src/templates/default/workspace/_debug_app/tmp/pids/.keep +0 -0
- package/template/src/templates/default/workspace/_debug_app/tmp/storage/.keep +0 -0
- package/template/src/templates/default/workspace/_debug_app/vendor/.keep +0 -0
- package/template/src/templates/rails-app/workspace/README.md +24 -0
- package/template/src/templates/rails-app/workspace/Rakefile +6 -0
- package/template/src/templates/rails-app/workspace/app/assets/images/.keep +0 -0
- package/template/src/templates/rails-app/workspace/app/assets/stylesheets/application.css +534 -0
- package/template/src/templates/rails-app/workspace/app/controllers/application_controller.rb +2 -0
- package/template/src/templates/rails-app/workspace/app/helpers/application_helper.rb +2 -0
- package/template/src/templates/rails-app/workspace/app/jobs/application_job.rb +7 -0
- package/template/src/templates/rails-app/workspace/app/mailers/application_mailer.rb +4 -0
- package/template/src/templates/rails-app/workspace/app/models/application_record.rb +3 -0
- package/template/src/templates/rails-app/workspace/app/models/concerns/.keep +0 -0
- package/template/src/templates/rails-app/workspace/app/views/layouts/application.html.erb +38 -0
- package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.html.erb +13 -0
- package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.text.erb +1 -0
- package/template/src/templates/rails-app/workspace/bin/rails +4 -0
- package/template/src/templates/rails-app/workspace/{store/config → config}/application.rb +1 -1
- package/template/src/templates/rails-app/workspace/config/boot.rb +3 -0
- package/template/src/templates/rails-app/workspace/config/database.yml +32 -0
- package/template/src/templates/rails-app/workspace/config/environment.rb +5 -0
- package/template/src/templates/rails-app/workspace/config/environments/development.rb +69 -0
- package/template/src/templates/rails-app/workspace/config/environments/production.rb +89 -0
- package/template/src/templates/rails-app/workspace/config/environments/test.rb +54 -0
- package/template/src/templates/rails-app/workspace/config/initializers/assets.rb +7 -0
- package/template/src/templates/rails-app/workspace/config/initializers/content_security_policy.rb +25 -0
- package/template/src/templates/rails-app/workspace/config/initializers/filter_parameter_logging.rb +8 -0
- package/template/src/templates/rails-app/workspace/config/initializers/inflections.rb +16 -0
- package/template/src/templates/rails-app/workspace/config/locales/en.yml +31 -0
- package/template/src/templates/rails-app/workspace/config/puma.rb +41 -0
- package/template/src/templates/rails-app/workspace/{store/config → config}/routes.rb +1 -1
- package/template/src/templates/rails-app/workspace/config/storage.yml +34 -0
- package/template/src/templates/rails-app/workspace/config.ru +6 -0
- package/template/src/templates/rails-app/workspace/db/seeds.rb +9 -0
- package/template/src/templates/rails-app/workspace/log/.keep +0 -0
- package/template/src/templates/rails-app/workspace/public/400.html +114 -0
- package/template/src/templates/rails-app/workspace/public/404.html +114 -0
- package/template/src/templates/rails-app/workspace/public/406-unsupported-browser.html +114 -0
- package/template/src/templates/rails-app/workspace/public/422.html +114 -0
- package/template/src/templates/rails-app/workspace/public/500.html +114 -0
- package/template/src/templates/rails-app/workspace/public/icon.png +0 -0
- package/template/src/templates/rails-app/workspace/public/icon.svg +3 -0
- package/template/src/templates/rails-app/workspace/public/robots.txt +1 -0
- package/template/src/templates/rails-app/workspace/script/.keep +0 -0
- package/template/src/templates/rails-app/workspace/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/test/controllers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/test/helpers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/test/integration/.keep +0 -0
- package/template/src/templates/rails-app/workspace/tmp/.keep +0 -0
- package/template/src/templates/rails-app/workspace/tmp/cache/.keep +0 -0
- package/template/src/templates/rails-app/workspace/tmp/pids/.keep +0 -0
- package/template/src/templates/rails-app/workspace/tmp/sockets/.keep +0 -0
- package/template/src/templates/rails-app/workspace/tmp/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/vendor/javascripts/.keep +0 -0
- package/template/tsconfig.json +3 -1
- package/template/uno.config.ts +17 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/README.md +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/Rakefile +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/images/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/stylesheets/application.css +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/controllers/application_controller.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store/app/models → default/workspace/_debug_app/app/controllers}/concerns/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/helpers/application_helper.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/jobs/application_job.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/mailers/application_mailer.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/models/application_record.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store/log → default/workspace/_debug_app/app/models/concerns}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.html.erb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.text.erb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/service-worker.js +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/bin/rails +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/boot.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/database.yml +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environment.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/development.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/production.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/test.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/assets.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/content_security_policy.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/filter_parameter_logging.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/inflections.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/locales/en.yml +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/puma.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/storage.yml +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config.ru +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/db/seeds.rb +0 -0
- /package/template/src/templates/{rails-app/workspace/store/script → default/workspace/_debug_app/lib/tasks}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/storage → default/workspace/_debug_app/log}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/400.html +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/404.html +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/406-unsupported-browser.html +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/422.html +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/500.html +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.png +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.svg +0 -0
- /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/robots.txt +0 -0
- /package/template/src/templates/{rails-app/workspace/store/test/controllers → default/workspace/_debug_app/script}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/test/helpers → default/workspace/_debug_app/storage}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/test/integration → default/workspace/_debug_app/test/controllers}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/tmp → default/workspace/_debug_app/test/fixtures/files}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/tmp/pids → default/workspace/_debug_app/test/helpers}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/tmp/storage → default/workspace/_debug_app/test/integration}/.keep +0 -0
- /package/template/src/templates/{rails-app/workspace/store/vendor/javascripts → default/workspace/_debug_app/test/mailers}/.keep +0 -0
- /package/template/src/templates/rails-app/workspace/{store/.ruby-version → .ruby-version} +0 -0
- /package/template/src/templates/rails-app/workspace/{store/Gemfile → Gemfile} +0 -0
- /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/application.js +0 -0
- /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/application.js +0 -0
- /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/index.js +0 -0
- /package/template/src/templates/rails-app/workspace/{store/bin → bin}/importmap +0 -0
- /package/template/src/templates/rails-app/workspace/{store/config → config}/cable.yml +0 -0
- /package/template/src/templates/rails-app/workspace/{store/config → config}/credentials.yml.enc +0 -0
- /package/template/src/templates/rails-app/workspace/{store/config → config}/importmap.rb +0 -0
- /package/template/src/templates/rails-app/workspace/{store/config → config}/master.key +0 -0
- /package/template/src/templates/rails-app/workspace/{store/test → test}/test_helper.rb +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-wasm-author-constraints
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill whenever checking if a Rails feature or gem works in WASM, or understanding
|
|
5
|
+
what's real vs. conceptual in lessons. Trigger on: 'does X work in WASM', 'can I use this
|
|
6
|
+
gem', 'gem compatibility', 'WASM limitation', 'what works', 'supported features', 'PGLite',
|
|
7
|
+
'threading', 'Net::HTTP', 'ActionCable', 'background jobs', 'can I teach X', 'bundle
|
|
8
|
+
install in WASM', 'conceptual vs real', or any Rails capability question — even without
|
|
9
|
+
mentioning WASM. Authoritative compatibility matrix: which features work, which are shimmed,
|
|
10
|
+
which are impossible, plus gem tiers, PGLite behavior, boot timing, and conceptual-vs-real
|
|
11
|
+
operations. General Rails knowledge is insufficient here. Do NOT use for file organization
|
|
12
|
+
(use rails-file-management) or frontmatter (use tutorial-lesson-config).
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Rails WASM Author Constraints
|
|
16
|
+
|
|
17
|
+
What works, what doesn't, and how to design lessons around the WASM environment.
|
|
18
|
+
|
|
19
|
+
## Quick Reference: What Can I Teach?
|
|
20
|
+
|
|
21
|
+
| Rails Feature | Works? | Notes |
|
|
22
|
+
|---------------|--------|-------|
|
|
23
|
+
| ActiveRecord (CRUD, queries, scopes) | Yes | Full support via PGLite |
|
|
24
|
+
| Migrations & schema management | Yes | Standard `rails db:migrate` |
|
|
25
|
+
| Controllers & routing | Yes | Full support |
|
|
26
|
+
| Views (ERB templates) | Yes | Full support |
|
|
27
|
+
| Form helpers & validations | Yes | Full support |
|
|
28
|
+
| Model associations | Yes | `has_many`, `belongs_to`, etc. |
|
|
29
|
+
| Scaffolding & generators | Yes | `rails generate scaffold`, etc. |
|
|
30
|
+
| Asset pipeline (Propshaft) | Yes | Importmaps, Stimulus, Turbo |
|
|
31
|
+
| Rails console | Yes | Interactive IRB via custom bridge |
|
|
32
|
+
| Authentication (basic) | Yes | `has_secure_password`, session-based |
|
|
33
|
+
| ActionMailer (define mailers) | Partial | Can define/configure but delivery is a no-op |
|
|
34
|
+
| Active Storage (upload) | Partial | Upload works but image processing is a no-op |
|
|
35
|
+
| Background jobs | No | Single-threaded; Solid Queue won't process |
|
|
36
|
+
| ActionCable / WebSockets | No | No `IO.select`, no real socket support |
|
|
37
|
+
| External HTTP requests | No | No outbound networking from Ruby — sockets are unimplemented at the WASI level |
|
|
38
|
+
| Threads / parallel processing | No | `Thread.new` uses fibers (cooperative, single-threaded) |
|
|
39
|
+
| System commands from Ruby | No | `system()`, backticks, `Open3` are non-functional |
|
|
40
|
+
|
|
41
|
+
## Hard Limitations
|
|
42
|
+
|
|
43
|
+
These are **impossible to work around** in the WASM environment. Do not write lessons that depend on them:
|
|
44
|
+
|
|
45
|
+
### No Outbound Networking
|
|
46
|
+
|
|
47
|
+
Socket operations (`TCPSocket`, `UDPSocket`, and all socket classes) fail because the underlying WASI syscalls are unimplemented. `Net::HTTP` and `open-uri` will raise errors when attempting connections. You cannot:
|
|
48
|
+
- Call external APIs from Ruby
|
|
49
|
+
- Download files from the internet
|
|
50
|
+
- Connect to external databases or services
|
|
51
|
+
|
|
52
|
+
**Workaround for lessons:** If you want to teach API consumption, focus on the controller/model patterns and mock the responses. Show the code structure without executing real HTTP calls.
|
|
53
|
+
|
|
54
|
+
### No Process Spawning
|
|
55
|
+
|
|
56
|
+
`system()`, backticks, `exec`, `fork`, and `Open3` are non-functional. The `rails new` generator works because it's been patched, but arbitrary shell commands from tutorial code will not work.
|
|
57
|
+
|
|
58
|
+
### No Threading
|
|
59
|
+
|
|
60
|
+
`Thread.new` is shimmed to use `Fiber.new` (cooperative, single-threaded). Code that relies on parallel execution behaves differently. Background job processing and concurrent operations don't work as expected.
|
|
61
|
+
|
|
62
|
+
### No IO.select
|
|
63
|
+
|
|
64
|
+
The `poll_oneoff` WASI syscall is unimplemented. This breaks gems like nio4r, Puma, and ActionCable's EventMachine adapter.
|
|
65
|
+
|
|
66
|
+
### No chmod/fchmod
|
|
67
|
+
|
|
68
|
+
POSIX permission calls are stubbed. Avoid `FileUtils.chmod` in tutorial code. The `rails new` generator is pre-patched to handle this.
|
|
69
|
+
|
|
70
|
+
## Gem Compatibility
|
|
71
|
+
|
|
72
|
+
### Adding Gems
|
|
73
|
+
|
|
74
|
+
Authors **can add gems** to their tutorial by editing `ruby-wasm/Gemfile` and running `bin/build-wasm` to rebuild the WASM binary. This bakes all gems into the binary at build time.
|
|
75
|
+
|
|
76
|
+
### Compatibility Tiers
|
|
77
|
+
|
|
78
|
+
| Tier | Description | Examples |
|
|
79
|
+
|------|-------------|---------|
|
|
80
|
+
| **Works** | Pure Ruby gems, no native extensions | `devise`, `friendly_id`, `pagy`, `pundit`, `draper`, `kaminari` |
|
|
81
|
+
| **Shimmed** | Has native extensions but already patched | `nokogiri` (stub), `io-console` (stub), `bcrypt` |
|
|
82
|
+
| **Needs testing** | May work if extension compiles for WASM | Test with `bin/build-wasm` |
|
|
83
|
+
| **Won't work** | Requires unsupported syscalls or networking | `pg` (native), `mysql2`, `redis`, `sidekiq`, `puma` |
|
|
84
|
+
|
|
85
|
+
### Pre-Shimmed Gems
|
|
86
|
+
|
|
87
|
+
These gems are already handled by the WASM runtime:
|
|
88
|
+
|
|
89
|
+
| Gem | Behavior |
|
|
90
|
+
|-----|----------|
|
|
91
|
+
| `nokogiri` | 165-line minimal stub; CSS selectors return `[]`; sufficient for sanitization but not real HTML parsing |
|
|
92
|
+
| `io-console` | Stubbed; `winsize` returns `[80, 24]`; `raw` yields without change |
|
|
93
|
+
| `nio4r` | `.so` stripped; loads as empty shim |
|
|
94
|
+
| `date`, `psych`, `bigdecimal` | `.so` stripped; Ruby falls back to pure-Ruby stdlib |
|
|
95
|
+
| `sqlite3` (native) | Replaced by PGLite adapter |
|
|
96
|
+
|
|
97
|
+
### Gem Build Workflow
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 1. Edit the Gemfile
|
|
101
|
+
vim ruby-wasm/Gemfile
|
|
102
|
+
|
|
103
|
+
# 2. Rebuild the WASM binary (takes several minutes)
|
|
104
|
+
bin/build-wasm
|
|
105
|
+
|
|
106
|
+
# 3. Test your tutorial locally
|
|
107
|
+
npm run dev
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Database: PGLite
|
|
111
|
+
|
|
112
|
+
The database is **PGLite** — an in-browser PostgreSQL implementation compiled to WASM.
|
|
113
|
+
|
|
114
|
+
### What Works
|
|
115
|
+
|
|
116
|
+
- Standard ActiveRecord operations: `create`, `find`, `where`, `update`, `destroy`
|
|
117
|
+
- Migrations: `rails db:migrate`, `rails db:rollback`
|
|
118
|
+
- Seeds: `rails db:seed`
|
|
119
|
+
- PostgreSQL-compatible SQL syntax
|
|
120
|
+
- Multiple databases (development, test)
|
|
121
|
+
- Associations, joins, aggregations
|
|
122
|
+
- Indexes and constraints
|
|
123
|
+
|
|
124
|
+
### What to Know
|
|
125
|
+
|
|
126
|
+
| Behavior | Detail |
|
|
127
|
+
|----------|--------|
|
|
128
|
+
| **Data does not survive page reloads** | WebContainer filesystem is in-memory; refreshing the browser resets everything |
|
|
129
|
+
| **Database adapter** | `pglite` (auto-configured by wasmify-rails; authors don't need to set this up) |
|
|
130
|
+
| **Setup command** | `node scripts/rails.js db:prepare` in `prepareCommands` |
|
|
131
|
+
| **Location** | `pgdata/<dbname>/` in WebContainer filesystem |
|
|
132
|
+
| **Performance** | Slower than native PostgreSQL; acceptable for tutorial-sized datasets |
|
|
133
|
+
|
|
134
|
+
### Lesson Design Implications
|
|
135
|
+
|
|
136
|
+
- Always include `['node scripts/rails.js db:prepare', 'Prepare development database']` in `prepareCommands` for lessons that use the database
|
|
137
|
+
- Provide seeds in templates so users start with data
|
|
138
|
+
- Don't rely on data from a previous lesson persisting — each lesson should set up its own state via migrations + seeds
|
|
139
|
+
|
|
140
|
+
## Boot Timing
|
|
141
|
+
|
|
142
|
+
The WASM runtime takes time to load. Set expectations for tutorial users:
|
|
143
|
+
|
|
144
|
+
| Phase | Typical Duration | What Happens |
|
|
145
|
+
|-------|-----------------|--------------|
|
|
146
|
+
| `npm install` | 10-30s | Downloads ~80MB WASM binary + dependencies |
|
|
147
|
+
| WASM compile | 2-5s | Browser compiles the binary |
|
|
148
|
+
| Rails bootstrap | 2-5s | Loads Rails framework from embedded VFS |
|
|
149
|
+
| Command execution | Varies | User's command runs |
|
|
150
|
+
|
|
151
|
+
**Total first-load time: 15-40 seconds** depending on network and browser.
|
|
152
|
+
|
|
153
|
+
### Author Tips for Boot Experience
|
|
154
|
+
|
|
155
|
+
- Include a note in early lessons: "The Ruby runtime takes a moment to load — this is normal!"
|
|
156
|
+
- Use `prepareCommands` with descriptive labels so users see progress
|
|
157
|
+
- The `['output', 'Setup Logs']` terminal panel shows boot details
|
|
158
|
+
- Subsequent lesson navigation is faster if the WebContainer is already booted
|
|
159
|
+
|
|
160
|
+
## Auto-Authentication
|
|
161
|
+
|
|
162
|
+
The runtime includes an auto-login patch: if `tmp/authenticated-user.txt` exists in the Rails app root, the first HTTP request auto-authenticates the user found by `User.find_by(email_address: <file contents>)`. The file should contain a single email address (e.g., `admin@example.com`).
|
|
163
|
+
|
|
164
|
+
**To use:** Place `workspace/tmp/authenticated-user.txt` in `_files/` or a template, containing the email of a seeded user. The first request calls `start_new_session_for(user)`, creating a session cookie for all subsequent requests. Runs once per VM lifetime (the `$__pre_authenticated` global flag prevents repeat attempts).
|
|
165
|
+
|
|
166
|
+
This requires the Rails 8 `Authentication` concern and a `User` model with an `email_address` column.
|
|
167
|
+
|
|
168
|
+
## Filesystem Boundaries
|
|
169
|
+
|
|
170
|
+
Ruby code can only access `/workspace` (the WASI preopen). Attempting to access paths outside this boundary raises `Errno::ENOENT`. Tutorial code should never navigate above `/workspace` with `Dir.chdir("..")` or absolute paths outside the preopen.
|
|
171
|
+
|
|
172
|
+
## Conceptual vs. Real Operations
|
|
173
|
+
|
|
174
|
+
Some tutorial operations are **conceptual** — they teach the correct pattern but the actual execution requires something different in the WASM environment:
|
|
175
|
+
|
|
176
|
+
| Operation in lesson content | Reality | What the author must do |
|
|
177
|
+
|-----------------------------|---------|------------------------|
|
|
178
|
+
| "Add `gem 'devise'` to your Gemfile" | Gems are baked into the WASM binary at build time | The gem must already be in `ruby-wasm/Gemfile` and rebuilt with `bin/build-wasm` **before** the tutorial is published |
|
|
179
|
+
| "Run `bundle install`" | `bundle install` is a no-op in WASM — all gems come from the binary | Include it for pedagogical completeness; it will appear to succeed |
|
|
180
|
+
| "Run `rails server`" | The Rails server runs through `node scripts/rails.js server` | Use `node scripts/rails.js server` in frontmatter `mainCommand`; in lesson content, show `rails server` since that's what the terminal wrapper understands |
|
|
181
|
+
| "Edit `database.yml`" | Database adapter is PGLite, auto-configured by wasmify-rails | Pre-configure in the template; showing a `database.yml` edit is fine for teaching but won't change the runtime behavior |
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tutorial-content-structure
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill whenever organizing tutorial content into parts, chapters, and lessons.
|
|
5
|
+
Trigger when the user says 'create a lesson', 'create a chapter', 'create a part', 'add
|
|
6
|
+
a lesson', 'tutorial structure', 'content.md', 'meta.md', 'lesson ordering', 'callout',
|
|
7
|
+
':::tip', ':::info', ':::warn', 'code import', 'expressive code', or asks about the
|
|
8
|
+
content hierarchy, directory naming, or markdown features — even if they don't explicitly
|
|
9
|
+
mention content structure. This skill documents TutorialKit's specific directory conventions,
|
|
10
|
+
metadata file roles (meta.md vs content.md), numeric prefix ordering, the recommended
|
|
11
|
+
tutorial root config for Rails, callout syntax with all attributes, code block file imports,
|
|
12
|
+
and expressive code features. These are framework-specific patterns that cannot be inferred
|
|
13
|
+
from general knowledge. Do NOT use for frontmatter option reference (use
|
|
14
|
+
tutorial-lesson-config) or file/template organization (use rails-file-management).
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Tutorial Content Structure
|
|
18
|
+
|
|
19
|
+
How to organize interactive Ruby on Rails tutorials using TutorialKit's content hierarchy.
|
|
20
|
+
|
|
21
|
+
## Content Hierarchy
|
|
22
|
+
|
|
23
|
+
Tutorials use a directory-based hierarchy under `src/content/tutorial/`:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/content/tutorial/
|
|
27
|
+
meta.md # Tutorial root config (type: tutorial)
|
|
28
|
+
1-getting-started/ # Part
|
|
29
|
+
meta.md # Part config (type: part)
|
|
30
|
+
1-first-rails-app/ # Lesson (or Chapter, if it contains lessons)
|
|
31
|
+
content.md # Lesson content (type: lesson)
|
|
32
|
+
_files/ # Initial code files
|
|
33
|
+
_solution/ # Solution code files
|
|
34
|
+
2-rails-console/
|
|
35
|
+
content.md
|
|
36
|
+
_files/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The full hierarchy is **Tutorial > Parts > Chapters > Lessons**, but you can skip levels:
|
|
40
|
+
|
|
41
|
+
| Structure | When to Use |
|
|
42
|
+
|-----------|-------------|
|
|
43
|
+
| Parts > Chapters > Lessons | Large tutorials with major sections and subsections |
|
|
44
|
+
| Parts > Lessons | Medium tutorials grouped by topic |
|
|
45
|
+
| Lessons only | Small tutorials or quick guides |
|
|
46
|
+
|
|
47
|
+
## Directory Naming
|
|
48
|
+
|
|
49
|
+
Directories use **numeric prefixes** for ordering: `1-basics/`, `2-controllers/`, `3-views/`.
|
|
50
|
+
|
|
51
|
+
- The number determines display order in navigation
|
|
52
|
+
- The text after the number becomes part of the URL slug
|
|
53
|
+
- Use kebab-case: `1-creating-your-first-rails-app/`
|
|
54
|
+
|
|
55
|
+
## Metadata Files
|
|
56
|
+
|
|
57
|
+
### `meta.md` — Tutorial Root
|
|
58
|
+
|
|
59
|
+
Every tutorial needs a root `meta.md` at `src/content/tutorial/meta.md`. This sets **inherited defaults** for all lessons:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
---
|
|
63
|
+
type: tutorial
|
|
64
|
+
prepareCommands:
|
|
65
|
+
- ['npm install', 'Preparing Ruby runtime']
|
|
66
|
+
terminalBlockingPrepareCommandsCount: 1
|
|
67
|
+
previews: false
|
|
68
|
+
filesystem:
|
|
69
|
+
watch: ['/*.json', '/workspace/**/*']
|
|
70
|
+
terminal:
|
|
71
|
+
open: true
|
|
72
|
+
activePanel: 0
|
|
73
|
+
panels:
|
|
74
|
+
- type: terminal
|
|
75
|
+
id: 'cmds'
|
|
76
|
+
title: 'Command Line'
|
|
77
|
+
allowRedirects: true
|
|
78
|
+
- ['output', 'Setup Logs']
|
|
79
|
+
---
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Important for Rails tutorials:** The config above is the recommended baseline. It sets up `npm install` as a blocking prepare command (downloads the ~80MB WASM runtime), watches `/workspace/**/*` for file changes, and configures a persistent terminal with setup log output.
|
|
83
|
+
|
|
84
|
+
### `meta.md` — Parts and Chapters
|
|
85
|
+
|
|
86
|
+
Parts and chapters each get a `meta.md`:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
---
|
|
90
|
+
type: part
|
|
91
|
+
title: Getting Started with Rails
|
|
92
|
+
---
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
---
|
|
97
|
+
type: chapter
|
|
98
|
+
title: Models and Databases
|
|
99
|
+
---
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Parts and chapters can also set inherited configuration (same options as tutorial root). Any setting here overrides the tutorial default for all lessons within.
|
|
103
|
+
|
|
104
|
+
### `content.md` — Lessons
|
|
105
|
+
|
|
106
|
+
Each lesson directory contains a `content.md` with frontmatter and markdown body:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
---
|
|
110
|
+
type: lesson
|
|
111
|
+
title: Creating your first Rails app
|
|
112
|
+
editor: false
|
|
113
|
+
custom:
|
|
114
|
+
shell:
|
|
115
|
+
workdir: "/workspace"
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
# Your lesson content here
|
|
119
|
+
|
|
120
|
+
Write instructions, explanations, and code examples in standard markdown.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Explicit Ordering
|
|
124
|
+
|
|
125
|
+
Instead of relying on numeric prefixes, you can explicitly order children in `meta.md`:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
---
|
|
129
|
+
type: tutorial
|
|
130
|
+
parts:
|
|
131
|
+
- getting-started # matches folder name (without numeric prefix)
|
|
132
|
+
- controllers
|
|
133
|
+
- views
|
|
134
|
+
---
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
---
|
|
139
|
+
type: part
|
|
140
|
+
title: Getting Started
|
|
141
|
+
lessons:
|
|
142
|
+
- creating-your-first-rails-app
|
|
143
|
+
- rails-console
|
|
144
|
+
---
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
When using explicit ordering, folder names don't need numeric prefixes.
|
|
148
|
+
|
|
149
|
+
## Markdown Features
|
|
150
|
+
|
|
151
|
+
### Callouts
|
|
152
|
+
|
|
153
|
+
```markdown
|
|
154
|
+
:::tip
|
|
155
|
+
Rails follows RESTful conventions, making your applications predictable.
|
|
156
|
+
:::
|
|
157
|
+
|
|
158
|
+
:::info
|
|
159
|
+
You can customize the application Rails generates by using flags.
|
|
160
|
+
:::
|
|
161
|
+
|
|
162
|
+
:::warn
|
|
163
|
+
This operation will reset the database.
|
|
164
|
+
:::
|
|
165
|
+
|
|
166
|
+
:::danger
|
|
167
|
+
Never expose your secret_key_base in production.
|
|
168
|
+
:::
|
|
169
|
+
|
|
170
|
+
:::success
|
|
171
|
+
Your Rails app is running!
|
|
172
|
+
:::
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Callouts support optional attributes:
|
|
176
|
+
|
|
177
|
+
```markdown
|
|
178
|
+
:::tip{title="Pro Tip"}
|
|
179
|
+
Custom title for the callout.
|
|
180
|
+
:::
|
|
181
|
+
|
|
182
|
+
:::info{noBorder=true}
|
|
183
|
+
Borderless callout style.
|
|
184
|
+
:::
|
|
185
|
+
|
|
186
|
+
:::warn{hideTitle=true}
|
|
187
|
+
Only the message, no title bar.
|
|
188
|
+
:::
|
|
189
|
+
|
|
190
|
+
:::danger{hideIcon=true}
|
|
191
|
+
Title shown but no icon.
|
|
192
|
+
:::
|
|
193
|
+
|
|
194
|
+
:::tip{class="my-custom-class"}
|
|
195
|
+
Custom CSS class on the callout container.
|
|
196
|
+
:::
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
| Attribute | Effect |
|
|
200
|
+
|-----------|--------|
|
|
201
|
+
| `title` | Custom title text (replaces default like "Tip", "Info") |
|
|
202
|
+
| `noBorder` | `"true"` removes the left border |
|
|
203
|
+
| `hideTitle` | `"true"` hides the entire title bar |
|
|
204
|
+
| `hideIcon` | `"true"` hides the icon but keeps the title |
|
|
205
|
+
| `class` | Additional CSS classes on the callout container |
|
|
206
|
+
|
|
207
|
+
### Code Block File Imports
|
|
208
|
+
|
|
209
|
+
Inline the contents of lesson files directly in your markdown:
|
|
210
|
+
|
|
211
|
+
~~~markdown
|
|
212
|
+
```file:/workspace/app/controllers/products_controller.rb
|
|
213
|
+
```
|
|
214
|
+
~~~
|
|
215
|
+
|
|
216
|
+
This renders the contents of the file from the lesson's `_files/` directory. For solution files:
|
|
217
|
+
|
|
218
|
+
~~~markdown
|
|
219
|
+
```solution:/workspace/app/controllers/products_controller.rb
|
|
220
|
+
```
|
|
221
|
+
~~~
|
|
222
|
+
|
|
223
|
+
### Expressive Code Attributes
|
|
224
|
+
|
|
225
|
+
Code blocks support highlighting, line annotations, and framing:
|
|
226
|
+
|
|
227
|
+
~~~markdown
|
|
228
|
+
```ruby title="app/models/product.rb" showLineNumbers
|
|
229
|
+
class Product < ApplicationRecord
|
|
230
|
+
validates :name, presence: true # [!code highlight]
|
|
231
|
+
validates :price, numericality: { greater_than: 0 }
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
~~~
|
|
235
|
+
|
|
236
|
+
Available attributes:
|
|
237
|
+
|
|
238
|
+
| Attribute | Example | Effect |
|
|
239
|
+
|-----------|---------|--------|
|
|
240
|
+
| `title` | `title="config/routes.rb"` | Shows filename header |
|
|
241
|
+
| `showLineNumbers` | `showLineNumbers` | Display line numbers |
|
|
242
|
+
| `ins={lines}` | `ins={2-3}` | Mark lines as inserted (green) |
|
|
243
|
+
| `del={lines}` | `del={5}` | Mark lines as deleted (red) |
|
|
244
|
+
| `{lines}` | `{1,3-5}` | Highlight specific lines |
|
|
245
|
+
| `collapse={range}` | `collapse={1-5}` | Collapse line range |
|
|
246
|
+
| `frame="terminal"` | `frame="terminal"` | Terminal-style frame |
|
|
247
|
+
|
|
248
|
+
### Rails path links
|
|
249
|
+
|
|
250
|
+
Inline code matching Rails path patterns (`app/`, `db/`, `config/`, `test/`) is automatically converted into clickable buttons that open the file in the editor:
|
|
251
|
+
|
|
252
|
+
```markdown
|
|
253
|
+
Open `app/models/user.rb` to see the User model.
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
This is handled by the `remarkRailsPathLinks` plugin. Only paths starting with `app/`, `db/`, `config/`, or `test/` are matched.
|
|
257
|
+
|
|
258
|
+
### Links
|
|
259
|
+
|
|
260
|
+
Standard markdown links work. For linking to the running app preview:
|
|
261
|
+
|
|
262
|
+
```markdown
|
|
263
|
+
Visit [the home page](http://localhost:3000) to see the result.
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### MDX Support
|
|
267
|
+
|
|
268
|
+
Rename `content.md` to `content.mdx` to use MDX features (component imports, JSX in markdown).
|
|
269
|
+
|
|
270
|
+
## Styling Rails Views in Lessons
|
|
271
|
+
|
|
272
|
+
The `rails-app` template includes a CSS design system with BEM components and CSS custom properties. When writing ERB views in `_files/` or `_solution/`, use these classes for consistent styling without adding custom CSS.
|
|
273
|
+
|
|
274
|
+
### Common Patterns
|
|
275
|
+
|
|
276
|
+
**Page with a list (index view):**
|
|
277
|
+
|
|
278
|
+
```erb
|
|
279
|
+
<div class="page-header">
|
|
280
|
+
<h1>Tickets</h1>
|
|
281
|
+
<%= link_to "New Ticket", new_ticket_path, class: "btn btn--primary" %>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<div class="card">
|
|
285
|
+
<table class="table">
|
|
286
|
+
<thead>
|
|
287
|
+
<tr>
|
|
288
|
+
<th>Title</th>
|
|
289
|
+
<th>Status</th>
|
|
290
|
+
<th></th>
|
|
291
|
+
</tr>
|
|
292
|
+
</thead>
|
|
293
|
+
<tbody>
|
|
294
|
+
<% @tickets.each do |ticket| %>
|
|
295
|
+
<tr>
|
|
296
|
+
<td><%= link_to ticket.title, ticket %></td>
|
|
297
|
+
<td><span class="badge badge--primary"><%= ticket.status %></span></td>
|
|
298
|
+
<td class="inline-actions">
|
|
299
|
+
<%= link_to "Edit", edit_ticket_path(ticket), class: "btn btn--small" %>
|
|
300
|
+
</td>
|
|
301
|
+
</tr>
|
|
302
|
+
<% end %>
|
|
303
|
+
</tbody>
|
|
304
|
+
</table>
|
|
305
|
+
</div>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Detail page (show view):**
|
|
309
|
+
|
|
310
|
+
```erb
|
|
311
|
+
<div class="page-header">
|
|
312
|
+
<h1><%= @ticket.title %></h1>
|
|
313
|
+
<div class="inline-actions">
|
|
314
|
+
<%= link_to "Edit", edit_ticket_path(@ticket), class: "btn btn--small" %>
|
|
315
|
+
<%= link_to "Back", tickets_path, class: "btn btn--small" %>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="card mb-md">
|
|
320
|
+
<div class="card__body">
|
|
321
|
+
<p><%= @ticket.description %></p>
|
|
322
|
+
<p class="text-muted text-sm">
|
|
323
|
+
Created by <%= @ticket.user.name %> ·
|
|
324
|
+
<span class="badge badge--primary"><%= @ticket.status %></span>
|
|
325
|
+
</p>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Form:**
|
|
331
|
+
|
|
332
|
+
```erb
|
|
333
|
+
<div class="card">
|
|
334
|
+
<div class="card__header">
|
|
335
|
+
<h2>New Ticket</h2>
|
|
336
|
+
</div>
|
|
337
|
+
<div class="card__body">
|
|
338
|
+
<%= form_with model: @ticket do |form| %>
|
|
339
|
+
<div class="form__group">
|
|
340
|
+
<%= form.label :title, class: "form__label" %>
|
|
341
|
+
<%= form.text_field :title, class: "input" %>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<div class="form__group">
|
|
345
|
+
<%= form.label :description, class: "form__label" %>
|
|
346
|
+
<%= form.text_area :description, class: "input" %>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<div class="form__actions">
|
|
350
|
+
<%= form.submit "Create", class: "btn btn--primary" %>
|
|
351
|
+
<%= link_to "Cancel", tickets_path, class: "btn" %>
|
|
352
|
+
</div>
|
|
353
|
+
<% end %>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Flash messages** are handled automatically by the layout. Controllers just use `redirect_to ..., notice: "..."` or `alert: "..."`.
|
|
359
|
+
|
|
360
|
+
### Quick Reference
|
|
361
|
+
|
|
362
|
+
| Need | Classes |
|
|
363
|
+
|------|---------|
|
|
364
|
+
| Primary action button | `btn btn--primary` |
|
|
365
|
+
| Destructive action | `btn btn--danger` |
|
|
366
|
+
| Small inline button | `btn btn--small` |
|
|
367
|
+
| Text input / textarea / select | `input` |
|
|
368
|
+
| Wrap content in a box | `card` > `card__body` |
|
|
369
|
+
| Box with title | `card` > `card__header` + `card__body` |
|
|
370
|
+
| Status label | `badge badge--primary` (or `--success`, `--danger`, `--warning`) |
|
|
371
|
+
| Data table | `table` |
|
|
372
|
+
| Title + action row | `page-header` |
|
|
373
|
+
| Form field wrapper | `form__group` > `form__label` + `input` |
|
|
374
|
+
| Muted helper text | `text-muted text-sm` |
|
|
375
|
+
| Row of buttons/links | `inline-actions` |
|
|
376
|
+
|
|
377
|
+
See the `tutorial-quickstart` skill for the full CSS custom properties reference and the complete component list.
|