RepoPilotOpen in app →

collectiveidea/interactor

Interactor provides a common interface for performing complex user interactions.

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 10mo ago
  • 23+ active contributors
  • Distributed ownership (top contributor 47% of recent commits)
Show 4 more →
  • MIT licensed
  • CI configured
  • Tests present
  • Slowing — last commit 10mo ago

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/collectiveidea/interactor)](https://repopilot.app/r/collectiveidea/interactor)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/collectiveidea/interactor on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: collectiveidea/interactor

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/collectiveidea/interactor shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 10mo ago
  • 23+ active contributors
  • Distributed ownership (top contributor 47% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 10mo ago

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live collectiveidea/interactor repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/collectiveidea/interactor.

What it runs against: a local clone of collectiveidea/interactor — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in collectiveidea/interactor | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 333 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>collectiveidea/interactor</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of collectiveidea/interactor. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/collectiveidea/interactor.git
#   cd interactor
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of collectiveidea/interactor and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "collectiveidea/interactor(\\.git)?\\b" \\
  && ok "origin remote is collectiveidea/interactor" \\
  || miss "origin remote is not collectiveidea/interactor (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "lib/interactor.rb" \\
  && ok "lib/interactor.rb" \\
  || miss "missing critical file: lib/interactor.rb"
test -f "lib/interactor/context.rb" \\
  && ok "lib/interactor/context.rb" \\
  || miss "missing critical file: lib/interactor/context.rb"
test -f "lib/interactor/hooks.rb" \\
  && ok "lib/interactor/hooks.rb" \\
  || miss "missing critical file: lib/interactor/hooks.rb"
test -f "lib/interactor/organizer.rb" \\
  && ok "lib/interactor/organizer.rb" \\
  || miss "missing critical file: lib/interactor/organizer.rb"
test -f "lib/interactor/error.rb" \\
  && ok "lib/interactor/error.rb" \\
  || miss "missing critical file: lib/interactor/error.rb"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 333 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~303d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/collectiveidea/interactor"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Interactor is a Ruby gem (v3.0+) that provides a standardized, single-responsibility pattern for encapsulating business logic in Rails applications. Each interactor wraps one discrete user action or workflow step, accepts a shared context object, executes its work via a call method, and communicates success/failure back through the context—eliminating scattered logic across controllers and models. Monolithic gem structure: lib/interactor.rb is the entry point, with lib/interactor/context.rb (shared state container), lib/interactor/hooks.rb (before/after lifecycle callbacks), and lib/interactor/organizer.rb (composition of multiple interactors into sequences). Test suite mirrors this in spec/ with matching spec files for each module. All core logic lives in lib/interactor/ with no separate packages or plugins.

👥Who it's for

Rails developers building maintainable applications who want to extract complex workflows (user signup, payment processing, multi-step operations) from controllers and models into reusable, testable, composable service objects. Primarily used by teams that value business logic isolation and tight context contracts.

🌱Maturity & risk

Production-ready and actively maintained. The gem has a clean test suite (spec/ directory with comprehensive specs for context, hooks, and organizers), GitHub Actions CI/CD configured in .github/workflows/tests.yml, follows standard Ruby style guide (Standard), and maintains semantic versioning in CHANGELOG.md. The codebase is small (~85KB), well-documented in README.md, and shows steady maintenance without abandonment signals.

Low risk. The gem has minimal dependencies (as indicated by typical Gemfile structure), a stable 3.0 release, clear semantic versioning in CHANGELOG.md, and comprehensive test coverage. Single maintainer (collectiveidea org) is a minor consideration, but the library's narrow, stable scope and small API surface reduce maintenance burden. No evidence of breaking changes or stalled issues in visible structure.

Active areas of work

Actively maintained for Ruby/Rails compatibility. The workflow file .github/workflows/tests.yml suggests ongoing CI validation. CHANGELOG.md indicates incremental improvements and bug fixes. No major active feature work is obvious from file structure, suggesting the library is in stable-feature maintenance mode rather than rapid development.

🚀Get running

git clone https://github.com/collectiveidea/interactor.git
cd interactor
bundle install
bundle exec rspec

Daily commands:

bundle exec rspec              # Run full test suite
bundle exec rspec spec/interactor_spec.rb  # Run specific test file
bundle exec standard          # Check Ruby style
bundle exec rake              # Run default Rake task (likely tests)

🗺️Map of the codebase

  • lib/interactor.rb — Main entry point that requires and exposes the Interactor API; every developer must understand how the gem is loaded and what it exports.
  • lib/interactor/context.rb — Core abstraction for passing state through interactors; essential for understanding how data flows and is mutated during execution.
  • lib/interactor/hooks.rb — Defines before/after/around lifecycle callbacks; critical for understanding interactor execution flow and side-effect management.
  • lib/interactor/organizer.rb — Orchestrates composition of multiple interactors into workflows; required to understand how complex multi-step operations are coordinated.
  • lib/interactor/error.rb — Defines failure semantics and error handling strategy; necessary for understanding how interactors communicate success/failure.
  • spec/integration_spec.rb — End-to-end test suite demonstrating realistic usage patterns; shows how all components work together in practice.

🧩Components & responsibilities

  • Interactor (base class) (Ruby classes, inheritance, instance methods) — Single-purpose service object; encapsulates one piece of business logic with a call method.
    • Failure mode: Raises Interactor::Failure if context.fail! is called; propagates exception up the call stack.
  • Context (Ruby Hash, dynamic attribute accessors, failure flag) — Mutable hash-like object carrying all state through a workflow; tracks success/failure status and failure messages.
    • Failure mode: Marked as failed when context.fail!(msg) is called; downstream interactors skip execution in organizer.
  • Organizer (Ruby DSL (organize method), array of interactor classes) — Orchestrates sequential execution of multiple interactors with a shared context; stops at first failure.
    • Failure mode: Halts execution and returns failed context when any interactor fails; no automatic rollback.
  • Hooks (Ruby Module (Hooks), method lookup via instance_method) — Lifecycle callbacks (before/after/around) that run at defined points during interactor execution.
    • Failure mode: Exception in hook halts execution; if exception is Interactor::Failure, context is marked failed; otherwise exception propagates.

🔀Data flow

  • Application callerInteractor.call(params) — Passes input parameters as a hash or keyword arguments.
  • Interactor.callContext instance — Creates a new context, converts params to context attributes, then passes to organizer or direct execution.
  • Interactor #call methodContext — Reads context state, executes business logic, mutates context with results, and optionally calls context.fail!(msg).
  • OrganizerChained Interactors — Passes the same context object through each interactor in sequence; halts if context.failure? becomes true.
  • Failed InteractorOrganizer — Sets context.failure? = true; organizer skips remaining interactors and returns context.
  • Organizer/InteractorCaller — Returns context object with all accumulated state; caller inspects context.failure

🛠️How to make changes

Create a new single-purpose Interactor

  1. Create a new class inheriting from Interactor and define a call method with your business logic (lib/interactor.rb)
  2. Use context.fail! to signal failure with a message if validation fails (lib/interactor/error.rb)
  3. Add test cases in spec/interactor_spec.rb covering success and failure paths (spec/interactor_spec.rb)

Compose multiple Interactors into a workflow

  1. Create an Organizer class inheriting from Interactor::Organizer (lib/interactor/organizer.rb)
  2. Define the organize method listing interactors in execution order (lib/interactor/organizer.rb)
  3. Write integration tests validating the complete workflow in spec/integration_spec.rb (spec/integration_spec.rb)

Add lifecycle hooks (before/after/around callbacks)

  1. Use before :call, after :call, or around :call in your Interactor class (lib/interactor/hooks.rb)
  2. Define the callback method (e.g., validate_input) that runs at the specified phase (lib/interactor/hooks.rb)
  3. Add test cases in spec/interactor/hooks_spec.rb to verify callback order and exception handling (spec/interactor/hooks_spec.rb)

Implement context-based data flow between Interactors

  1. Access context parameters in your interactor's call method (lib/interactor/context.rb)
  2. Assign computed results to context for downstream interactors to consume (lib/interactor/context.rb)
  3. Verify context state transitions in spec/interactor/context_spec.rb (spec/interactor/context_spec.rb)

🔧Why these technologies

  • Ruby — A dynamic, object-oriented language ideal for lightweight wrapper patterns and DSLs; enables natural syntax for before/after hooks and organizer composition.
  • Single-purpose service object pattern — Encapsulates discrete business operations with clear interfaces, making code testable, composable, and reusable across controllers, jobs, and services.
  • Shared context object — Provides a single source of truth for all state flowing through a workflow; eliminates parameter passing verbosity and makes side-effects explicit.

⚖️Trade-offs already made

  • All-or-nothing composition in Organizers (stops at first failure)

    • Why: Simplifies error handling and makes rollback semantics predictable; developers do not need to manage partial successes.
    • Consequence: Cannot continue executing subsequent interactors after a failure; workflows must be redesigned if partial execution is desired.
  • No built-in transaction/rollback mechanism

    • Why: Keeps the gem lightweight and database-agnostic; rollback logic is application-specific.
    • Consequence: Developers must manually implement rollback in around :call hooks; increases responsibility for complex workflows with side effects.
  • Direct context mutation rather than immutable data structures

    • Why: Simpler API and lower memory overhead; matches Ruby conventions.
    • Consequence: Interactors can have unintended side effects if context is shared; requires discipline in test isolation.

🚫Non-goals (don't propose these)

  • Does not provide database transaction management or automatic rollback.
  • Does not include async/background job execution; designed for synchronous in-process workflows.
  • Does not enforce permission/authorization checks; that is application responsibility.
  • Does not provide built-in logging or observability; integrates with external monitoring tools.

🪤Traps & gotchas

  1. context.fail! throws an Interactor::Failure exception internally—the call class method catches it, but if you call interactor methods directly in tests, you must handle the exception. 2) Context is mutable and shared across the entire organizer chain—modifications in one interactor are visible to all downstream interactors, so order matters. 3) Before hooks run before call, after hooks run after—they operate on the same context object, so hook execution order can matter. 4) No built-in rollback/transaction semantics—if an interactor modifies database state then fails, changes are not automatically undone.

🏗️Architecture

💡Concepts to learn

  • Service Object Pattern — Interactor is the canonical implementation of this pattern in Ruby; understanding how to encapsulate business logic in single-purpose objects is core to using this gem effectively
  • Context Object / Request Context — The shared context is the contract between interactor steps; understanding how to design a thin, immutable-where-possible context prevents state leak and makes compositions predictable
  • Callback/Hook Pattern — Before/after hooks in interactors follow the observer pattern; knowing when hooks fire relative to main logic is critical to avoid side effects and ordering bugs
  • Composition over Inheritance (Organizer Pattern) — Interactor::Organizer chains multiple interactors without subclassing; this compositional approach scales better than inheritance-based workflows
  • Railway-Oriented Programming / Either Monad — Interactor::Context.fail! and success? implement a simplified either/result monad; understanding success/failure tracks prevents silent failures and missing error handling
  • Command Pattern — Each interactor is a Command—an object that encapsulates a request as an object, allowing parameterization and queuing; this enables testability and composability
  • rails/rails — Rails controllers are the primary consumers of interactors; understanding Rails request lifecycle helps design context and error handling
  • soveran/cuba — Lightweight alternative Ruby web framework often used with service objects; shows business logic extraction patterns outside Rails
  • trailblazer/trailblazer — Competitor gem providing a more opinionated operation/workflow abstraction with built-in validation, policies, and explicit contracts; direct alternative to Interactor
  • dry-rb/dry-transaction — Functional composition tool from dry-rb ecosystem; provides step/transaction chaining similar to Organizer but with Railway-oriented error handling
  • collectiveidea/action_service — Spiritual predecessor from the same maintainer; shows evolution of service object patterns in Ruby

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive error handling and recovery tests for lib/interactor/error.rb

The repo has lib/interactor/error.rb but no dedicated spec file (spec/interactor/error_spec.rb exists in structure but likely minimal coverage). Given that error handling is critical for business logic encapsulation, this needs thorough testing of error propagation, context preservation on failure, and rollback behavior in organizers. This is high-value because interactors are meant to be reliable building blocks for complex workflows.

  • [ ] Create spec/interactor/error_spec.rb with tests for custom error types and inheritance
  • [ ] Add tests in spec/integration_spec.rb for error propagation across organizer chains
  • [ ] Document error handling patterns in a new ERRORS.md guide with code examples

Add Ruby version and dependency compatibility CI workflow

The .github/workflows/tests.yml exists but there's no matrix testing across multiple Ruby versions (3.0, 3.1, 3.2, 3.3, etc.) and Gemfile variants. As an actively maintained gem, this repo should validate compatibility across supported Ruby versions to catch breaking changes early and provide confidence to downstream users.

  • [ ] Expand .github/workflows/tests.yml to include ruby-version matrix strategy
  • [ ] Add a Gemfile.lock version and potential Gemfile-rails variant for integration testing
  • [ ] Document minimum Ruby version requirement in README.md and gemspec if not already present

Implement and test performance benchmarks for context object operations

The lib/interactor/context.rb is a core component that gets instantiated and modified frequently in chains of interactors. There are no benchmarks tracking performance of context creation, field assignment, or success/failure state transitions. This would help catch performance regressions and provide baseline metrics for optimization efforts.

  • [ ] Create spec/benchmarks/context_benchmark.rb with benchmark tests for context operations using benchmark-ips gem
  • [ ] Add benchmarks comparing organizer vs single interactor execution overhead
  • [ ] Document results in PERFORMANCE.md with recommendations for context usage patterns

🌿Good first issues

  • Add example organizer tests covering error handling and partial execution (spec/interactor/organizer_spec.rb could expand to show rollback patterns and context isolation between parallel interactors)
  • Document the before/after hook execution order with a visual diagram in README.md; currently the README shows hooks exist but not the precise timing relative to call and context state
  • Add matcher/assertion helpers in spec/support/lint.rb for common context state checks (e.g. expect(context).to be_success, expect(context).to have_attribute(:user))

Top contributors

Click to expand

📝Recent commits

Click to expand
  • c0e0079 — Release 3.2.0 (gaffneyc)
  • a5f0a20 — Merge pull request #201 from ocher/patch-1 (gaffneyc)
  • 0b4dee9 — Fix standardrb issues in #200 (gaffneyc)
  • 993d419 — Add changelog entry for pattern matching support (gaffneyc)
  • 2764aa3 — Adds support for ruby 3.0 pattern matching (#200) (bonflintstone)
  • 588a7c9 — Merge pull request #215 from taylorkearns/update-comment-typo-organize (gaffneyc)
  • d366fdd — Fix standardrb issues (gaffneyc)
  • 4def0e3 — actions: Remove v3 branch from build set as it doesn't exist (gaffneyc)
  • 616707b — Update list of Ruby versions to test against (gaffneyc)
  • ecca6af — Fix tests on newer versions of Ruby (gaffneyc)

🔒Security observations

The Interactor gem appears to be a well-maintained utility library with a secure code structure. No critical or high-severity vulnerabilities were identified based on the available file structure. The project follows good practices with GitHub Actions CI/CD, code style enforcement (StandardRB), and test coverage monitoring. Primary recommendations focus on dependency management verification and establishing a formal security policy. The absence of common injection vulnerabilities, hardcoded secrets, or infrastructure misconfigurations indicates a generally secure codebase. As a library focused on business logic encapsulation with no direct database or user input handling visible in its core components, security risk is naturally lower.

  • Low · Missing Dependency Lock File Information — Gemfile, Gemfile.lock. The dependencies/package file content is not provided, making it impossible to verify if the project uses vulnerable gem versions. The Gemfile should be analyzed to ensure all dependencies are up-to-date and free from known CVEs. Fix: Provide and review Gemfile and Gemfile.lock. Run 'bundle audit check' regularly to identify vulnerable dependencies. Implement automated dependency scanning in CI/CD pipeline.
  • Low · No SECURITY.md File — Repository root. There is no SECURITY.md file documenting the project's security policy, vulnerability disclosure process, or responsible disclosure guidelines. Fix: Create a SECURITY.md file following GitHub's security policy template to establish a clear process for reporting vulnerabilities.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · collectiveidea/interactor — RepoPilot