Skip to main content

rails-wasm-author-constraints

Use this skill whenever checking if a Rails feature or gem works in WASM, or understanding what's real vs. conceptual in lessons. Trigger on: 'does X work in WASM', 'can I use this gem', 'gem compatibility', 'WASM limitation', 'what works', 'supported features', 'PGLite', 'threading', 'Net::HTTP', 'ActionCable', 'background jobs', 'can I teach X', 'bundle install in WASM', 'conceptual vs real', or any Rails capability question — even without mentioning WASM. Authoritative compatibility matrix: which features work, which are shimmed, which are impossible, plus gem tiers, PGLite behavior, boot timing, and conceptual-vs-real operations. General Rails knowledge is insufficient here. Do NOT use for file organization (use rails-file-management) or frontmatter (use tutorial-lesson-config).

Source facts

Repository
palkan/action_policy
Last source activity
March 20, 2026 at 18:05
Detected SKILL.md language
English
Stars
1,565
Forks
89

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
rails-wasm-author-constraints
description
Use this skill whenever checking if a Rails feature or gem works in WASM, or understanding what's real vs. conceptual in lessons. Trigger on: 'does X work in WASM', 'can I use this gem', 'gem compatibility', 'WASM limitation', 'what works', 'supported features', 'PGLite', 'threading', 'Net::HTTP', 'ActionCable', 'background jobs', 'can I teach X', 'bundle install in WASM', 'conceptual vs real', or any Rails capability question — even without mentioning WASM. Authoritative compatibility matrix: which features work, which are shimmed, which are impossible, plus gem tiers, PGLite behavior, boot timing, and conceptual-vs-real operations. General Rails knowledge is insufficient here. Do NOT use for file organization (use rails-file-management) or frontmatter (use tutorial-lesson-config).
# Rails WASM Author Constraints What works, what doesn't, and how to design lessons around the WASM environment. ## Quick Reference: What Can I Teach? | Rails Feature | Works? | Notes | |---------------|--------|-------| | ActiveRecord (CRUD, queries, scopes) | Yes | Full support via PGLite | | Migrations & schema management | Yes | Standard `rails db:migrate` | | Controllers & routing | Yes | Full support | | Views (ERB templates) | Yes | Full support | | Form helpers & validations | Yes | Full support | | Model associations | Yes | `has_many`, `belongs_to`, etc. | | Scaffolding & generators | Yes | `rails generate scaffold`, etc. | | Asset pipeline (Propshaft) | Yes | Importmaps, Stimulus, Turbo | | Rails console | Yes | Interactive IRB via custom bridge | | Authentication (basic) | Yes | `has_secure_password`, session-based | | ActionMailer (define mailers) | Partial | Can define/configure but delivery is a no-op | | Active Storage (upload) | Partial | Upload works but image processing is a no-op | | Background jobs | No | Single-threaded; Solid Queue won't process | | ActionCable / WebSockets | No | No `IO.select`, no real socket support | | External HTTP requests | No | No outbound networking from Ruby — sockets are unimplemented at the WASI level | | Threads / parallel processing | No | `Thread.new` uses fibers (cooperative, single-threaded) | | System commands from Ruby | No | `system()`, backticks, `Open3` are non-functional | ## Hard Limitations These are **impossible to work around** in the WASM environment. Do not write lessons that depend on them: ### No Outbound Networking 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: - Call external APIs from Ruby - Download files from the internet - Connect to external databases or services **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. ### No Process Spawning `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. ### No Threading `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. ### No IO.select The `poll_oneoff` WASI syscall is unimplemented. This breaks gems like nio4r, Puma, and ActionCable's EventMachine adapter. ### No chmod/fchmod POSIX permission calls are stubbed. Avoid `FileUtils.chmod` in tutorial code. The `rails new` generator is pre-patched to handle this. ## Gem Compatibility ### Adding Gems 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. ### Compatibility Tiers | Tier | Description | Examples | |------|-------------|---------| | **Works** | Pure Ruby gems, no native extensions | `devise`, `friendly_id`, `pagy`, `pundit`, `draper`, `kaminari` | | **Shimmed** | Has native extensions but already patched | `nokogiri` (stub), `io-console` (stub), `bcrypt` | | **Needs testing** | May work if extension compiles for WASM | Test with `bin/build-wasm` | | **Won't work** | Requires unsupported syscalls or networking | `pg` (native), `mysql2`, `redis`, `sidekiq`, `puma` | ### Pre-Shimmed Gems These gems are already handled by the WASM runtime: | Gem | Behavior | |-----|----------| | `nokogiri` | 165-line minimal stub; CSS selectors return `[]`; sufficient for sanitization but not real HTML parsing | | `io-console` | Stubbed; `winsize` returns `[80, 24]`; `raw` yields without change | | `nio4r` | `.so` stripped; loads as empty shim | | `date`, `psych`, `bigdecimal` | `.so` stripped; Ruby falls back to pure-Ruby stdlib | | `sqlite3` (native) | Replaced by PGLite adapter | ### Gem Build Workflow ```bash # 1. Edit the Gemfile vim ruby-wasm/Gemfile # 2. Rebuild the WASM binary (takes several minutes) bin/build-wasm # 3. Test your tutorial locally npm run dev ``` ## Database: PGLite The database is **PGLite** — an in-browser PostgreSQL implementation compiled to WASM. ### What Works - Standard ActiveRecord operations: `create`, `find`, `where`, `update`, `destroy` - Migrations: `rails db:migrate`, `rails db:rollback` - Seeds: `rails db:seed` - PostgreSQL-compatible SQL syntax - Multiple databases (development, test) - Associations, joins, aggregations - Indexes and constraints ### What to Know | Behavior | Detail | |----------|--------| | **Data does not survive page reloads** | WebContainer filesystem is in-memory; refreshing the browser resets everything | | **Database adapter** | `pglite` (auto-configured by wasmify-rails; authors don't need to set this up) | | **Setup command** | `node scripts/rails.js db:prepare` in `prepareCommands` | | **Location** | `pgdata/<dbname>/` in WebContainer filesystem | | **Performance** | Slower than native PostgreSQL; acceptable for tutorial-sized datasets | ### Lesson Design Implications - Always include `['node scripts/rails.js db:prepare', 'Prepare development database']` in `prepareCommands` for lessons that use the database - Provide seeds in templates so users start with data - Don't rely on data from a previous lesson persisting — each lesson should set up its own state via migrations + seeds ## Boot Timing The WASM runtime takes time to load. Set expectations for tutorial users: | Phase | Typical Duration | What Happens | |-------|-----------------|--------------| | `npm install` | 10-30s | Downloads ~80MB WASM binary + dependencies | | WASM compile | 2-5s | Browser compiles the binary | | Rails bootstrap | 2-5s | Loads Rails framework from embedded VFS | | Command execution | Varies | User's command runs | **Total first-load time: 15-40 seconds** depending on network and browser. ### Author Tips for Boot Experience - Include a note in early lessons: "The Ruby runtime takes a moment to load — this is normal!" - Use `prepareCommands` with descriptive labels so users see progress - The `['output', 'Setup Logs']` terminal panel shows boot details - Subsequent lesson navigation is faster if the WebContainer is already booted ## Auto-Authentication 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`). **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). This requires the Rails 8 `Authentication` concern and a `User` model with an `email_address` column. ## Integration Testing Rails integration tests work in the WASM runtime with important limitations. ### What Works - `ActionDispatch::IntegrationTest` with `get`, `post`, `patch`, `delete` - `assert_response`, `assert_redirected_to` - `assert_difference`, `assert_no_difference` - Fixtures (`fixtures :all`) for test data - Cookie-based session management for authentication ### What Does NOT Work **Nokogiri-dependent assertions are unavailable.** The Nokogiri gem is a minimal stub in WASM — CSS selectors return `[]` and HTML parsing is non-functional. This means these Rails test helpers will not work: - `assert_select` — relies on Nokogiri CSS selectors - `css_select` — same - `assert_dom` — same - Any assertion that parses response HTML into a DOM ### Best Practices **Use `response.body` string matching instead of DOM assertions:** ```ruby # BAD — uses Nokogiri under the hood assert_select "h1", text: "Tickets" assert_select ".alert--error", text: /blank/ assert_select "form" # GOOD — plain string matching assert_includes response.body, "Tickets" assert_includes response.body, "blank" assert_includes response.body, "<form" ``` **Define an `assert_text` helper in `test_helper.rb`:** ```ruby class ActionDispatch::IntegrationTest def assert_text(text) assert_includes response.body, text end end ``` **Use cookie-based sign_in without making HTTP requests:** ```ruby class ActionDispatch::IntegrationTest private def sign_in(user) Current.session = user.sessions.create! ActionDispatch::TestRequest.create.cookie_jar.tap do |cookie_jar| cookie_jar.signed[:session_id] = Current.session.id cookies[:session_id] = cookie_jar[:session_id] end end end ``` **Disable test parallelization** — we don't have threads or processes: ```ruby module ActiveSupport class TestCase # parallelize(workers: 1) fixtures :all end end ``` ## Filesystem Boundaries 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. ## Conceptual vs. Real Operations Some tutorial operations are **conceptual** — they teach the correct pattern but the actual execution requires something different in the WASM environment: | Operation in lesson content | Reality | What the author must do | |-----------------------------|---------|------------------------| | "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 | | "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 | | "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 | | "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 |
View on GitHub