๐Ÿ“–

Writebook (37signals)

ONCE brand publishing tool โ€” textbook for delegated_type and edit history management

GitHub: projectcues/writebook (ONCE official code)

Writebook is a web-based publishing tool released under 37signals' ONCE brand. You can write books, manage chapters, and publish online.

Please refer to the Korean version for the detailed directory structure and pattern analysis, including: delegated_type for polymorphic content management (Page/Section/Picture), Edit records for tracking edit history, Access records for permission management (reader/editor), DB-based Session model, the same Authentication concern pattern as Campfire, and CRUD mapping patterns for Publication, Bookmark, and Move resources.

Architecture Diagram

delegated_type Structure (ERD)

๐Ÿ“š Book
title, theme, published, slug
has_many :leaves
๐Ÿƒ Leaf delegated_type :leafable
book_id, title, position, status, leafable_type, leafable_id
delegated_type :leafable, types: [Page, Section, Picture]
Key point: Unlike <strong>STI</strong> (single table), each type has <strong>its own table</strong>
Leaf serves as common interface (title, position, status)

State = Record (Edit / Access / Session)

โœ๏ธ Edit
leaf_id
action (revision|trash)
leafable_type, leafable_id
created_at
Record created on each edit
Browse history with previous / next
๐Ÿ”‘ Access
user_id
book_id
level (reader|editor)
Exists = Access granted
Authorization without Pundit
๐Ÿ” Session
user_id
token (secure_token)
user_agent, ip_address
last_active_at
Sessions are also DB records
Session management without Devise

Authentication Flow (Same as Campfire)

๐ŸŒ Browser Request
Authentication concern
before_action :require_authentication
authentication.rb ↗
session_token in cookie
No cookie
restore_authentication
Session.find_by(token:)
โ†’ session.resume
โ†’ Current.user = session.user
request_authentication
redirect_to new_session_url
Key point: <strong>Exact same pattern</strong> as Campfire โ€” auth structure reused internally at 37signals

Campfire vs Writebook

Purpose
Real-time chat
Web publishing
Auth
Authentication
Same
Polymorphism
STI (Room)
delegated_type
History
None
Edit records
Access control
Membership
Access (reader/editor)
Real-time
Action Cable 6 channels
None
Job queue
Solid Queue
Resque + Redis

Key Points

1

Open projectcues/writebook repository on GitHub

2

config/routes.rb โ†’ check resource structure and nesting patterns

3

app/models/leaf.rb โ†’ understand delegated_type pattern

4

app/models/leafable.rb โ†’ analyze polymorphic interface module

5

app/models/edit.rb โ†’ check edit history management pattern

6

app/models/access.rb โ†’ check access permission record pattern

7

app/models/session.rb โ†’ check DB-based session management

8

app/controllers/concerns/authentication.rb โ†’ compare with Campfire pattern

9

app/controllers/books/publications_controller.rb โ†’ check CRUD mapping

Pros

  • Learn real-world delegated_type pattern usage
  • See edit history management implemented as records
  • Understand consistent 37signals patterns by comparing with Campfire
  • Access control implemented as records (without Pundit)
  • Production implementation of DB-based session management
  • Elegant slug-based URL routing implementation

Cons

  • Uses Resque+Redis โ€” older stack, not Solid Queue
  • No Action Cable โ€” Campfire is better for learning real-time features
  • Writebook is a relatively simple app (CRUD-centric)
  • Private ONCE license โ€” fork/modification restrictions

Use Cases

delegated_type: Leaf โ†’ Page/Section/Picture (polymorphic content) Edit record: track edit history as records (previous/next navigation) Access record: User-Book permission management (reader/editor) Session record: DB-based session management (user_agent, ip tracking) Publication CRUD: publish = Publication resource update Move CRUD: page reorder = Move resource create Authentication concern: same auth pattern reused from Campfire