Skip to content

Refactor workspace management into dedicated WorkspaceStack class - #1259

Draft
st0012 wants to merge 5 commits into
masterfrom
claude/irb-context-workspace-coupling-no0j43
Draft

st0012 wants to merge 5 commits into
masterfrom
claude/irb-context-workspace-coupling-no0j43

Conversation

@st0012

@st0012 st0012 commented Oct 1, 2026

Copy link
Copy Markdown
Member

Summary

This PR refactors IRB's workspace management by extracting workspace stack logic into a dedicated WorkspaceStack class. This improves code organization, encapsulation, and makes workspace operations more explicit and testable.

Key Changes

  • New WorkspaceStack class (lib/irb/workspace_stack.rb):

    • Manages the stack of workspaces with a fixed home workspace at the bottom
    • Provides methods: push, pop, swap, replace, and with (context manager)
    • Automatically loads helper methods when workspaces become current
    • Includes Enumerable for iteration support
  • Refactored Context class:

    • Replaced @workspace_stack array with @workspaces (WorkspaceStack instance)
    • Added from_binding parameter to initialize to track if context was started by Binding#irb
    • Moved from_binding? logic from IRB class to Context
    • Added home_workspace accessor to get the initial workspace
    • Integrated change_workspace and push_workspace methods directly into Context (previously in extension files)
    • Moved evaluate method logic to IRB class as evaluate_statement
  • Refactored WorkSpace class:

    • Extracted binding creation logic into class method base_binding
    • Simplified initialize to use the new base_binding method
  • Updated IRB class:

    • Moved from_binding to a method that delegates to @context.from_binding?
    • Removed manual helper method loading (now handled by WorkspaceStack)
    • Moved statement evaluation logic to new evaluate_statement method
    • Simplified suspend_workspace to use WorkspaceStack#with
    • Improved suspend_input_method to use direct property assignment instead of instance_eval
  • Removed extension files:

    • lib/irb/ext/change-ws.rb - functionality moved to Context
    • lib/irb/ext/workspaces.rb - functionality moved to Context
    • Removed unused old method from lib/irb/ext/loader.rb
  • Added comprehensive tests (test/irb/test_workspace_stack.rb):

    • Tests for push/pop operations
    • Tests for swap functionality
    • Tests for replace and with (context manager) operations
    • Tests for helper method loading on workspace changes
  • Minor improvements:

    • Removed unnecessary initialize method from Measure command
    • Updated command implementations to use new workspaces accessor

Notable Implementation Details

  • The WorkspaceStack ensures the home workspace is never removed from the stack
  • Helper methods are loaded into a workspace's main object when it becomes current, centralizing this responsibility
  • The with method provides exception-safe temporary workspace switching
  • All workspace operations now go through WorkspaceStack, making it the single source of truth for workspace management

https://claude.ai/code/session_0175QjMQGeUPByLSqoL6ye5U

Prototype for untangling Irb, Context and WorkSpace:

- Add WorkspaceStack, which owns the workspace stack and its invariants
  (fixed home workspace, never empty, helper methods loaded whenever a
  workspace becomes current). Context delegates to it, and the methods
  that ext/workspaces.rb and ext/change-ws.rb used to monkey patch into
  Context now live in Context itself.
- Fix `chws` with no argument not returning home after `pushws`, caused
  by home_workspace being memoized lazily from the current workspace.
- Stop reading Context's ivars from the outside (pushws, Irb's
  suspend_input_method).
- Move from_binding onto Context and statement dispatch onto Irb, which
  passes itself to commands. Internal code no longer reaches Irb via
  Context#irb (only multi-irb still does).
- Extract WorkSpace.base_binding from WorkSpace#initialize.
- Remove the dead Context#workspace_home reader and IrbLoader#old.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175QjMQGeUPByLSqoL6ye5U
Measure's initialize override was a no-op, and it was the only thing
that forced the setter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175QjMQGeUPByLSqoL6ye5U
Context#irb is always the Irb running the command, so injecting it
separately added an API without changing behavior.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175QjMQGeUPByLSqoL6ye5U
debug.gem prepends a patch to IRB::Context#evaluate to restart its
threads around each evaluation, so moving dispatch to Irb broke the
debugger's IRB console.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0175QjMQGeUPByLSqoL6ye5U

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants