intertwingly

It’s just data

Writebook on Juntos


Writebook is Basecamp's collaborative book publishing application. It's a real Rails 8 app with models, concerns, controllers, views, Stimulus controllers, Turbo Streams, Active Storage, full-text search, and session-based authentication.

Ruby2JS transpiles Ruby to JavaScript. Juntos takes that further: it transpiles an entire Rails application — models, controllers, views, routes, migrations, tests — into a standalone JavaScript project that runs on Node.js, Deno, Cloudflare Workers, or in the browser.

The developer writes Ruby. The deployment runs JavaScript. No Ruby runtime needed in production.

If the transpiler can handle Writebook, it can handle most Rails applications.

The approach

Rather than trying to predict what features would be needed, we used Writebook to find out. The process was iterative: eject the application, try to start it, fix whatever breaks, repeat. Each fix addressed a general gap in the transpiler — not a Writebook-specific hack.

Starting from npx juntos eject, we worked through:

  1. Transform failures — files that couldn't be transpiled at all
  2. Syntax errors — valid Ruby patterns producing invalid JavaScript
  3. Import resolution — missing imports, wrong paths, circular dependencies
  4. Runtime errors — code that transpiles correctly but fails at execution

What we fixed along the way

Every fix was a general improvement to Ruby2JS or Juntos. Writebook surfaced the issues; the solutions apply to any Rails app.

Transpiler fixes

Concern handling

Routes and controllers

Runtime additions

Infrastructure

Where it stands

After two days of work:

$ cd writebook/ejected && node main.js
Initializing database...
Running migrations...
Ran 5 migration(s)
Running seeds...
Starting server on http://localhost:3000

The ejected server starts, initializes a SQLite database, runs all 5 migrations, executes seeds, and responds to HTTP requests. The book index route dispatches to the controller, which queries the database and calls the view renderer.

The first page render hits a cache is not defined error — a Rails view helper that needs a JavaScript equivalent. This is the next thing to fix: view helpers that reference Rails framework methods.

What's next

The remaining work falls into clear categories:

View helperscache, content_for, simple_format, and other Rails view methods need JavaScript implementations or stubs.

App-specific filter — Writebook uses define_method in one concern and attachment_reflections for dynamic iteration. A Writebook-specific filter handles these by expanding them at transpile time using information available in the source.

Missing controllers — some resource declarations reference controllers that don't exist in the app (Rails would return 404). The eject pipeline should detect and skip these.

Authentication flowhas_secure_password is implemented, but the session management, cookie handling, and before_action authentication checks need wiring through the request context.

The bigger picture

Writebook proved that the transpiler handles real Rails patterns. Every issue we found was a general gap, not a fundamental limitation. The architecture works: Ruby source → AST transformation → JavaScript output. Concerns mix in, callbacks register, associations resolve, routes dispatch.

The interesting insight is about metaprogramming. Ruby does it at runtime; JavaScript can't. But every metaprogramming pattern in Writebook uses information available at transpile time — declared attachments, literal symbol arguments, known concern structure. A transpile-time filter can expand these patterns just as effectively as Ruby's runtime, and the developer can inspect the result.

The full source is at github.com/ruby2js/ruby2js. Juntos documentation is at ruby2js.com/docs/juntos.