RepoPilotOpen in app →

drapergem/draper

Decorators/View-Models for Rails Applications

Healthy

Healthy across the board

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 2mo ago
  • 28+ active contributors
  • Distributed ownership (top contributor 35% of recent commits)
Show 3 more →
  • MIT licensed
  • CI configured
  • Tests present

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/drapergem/draper)](https://repopilot.app/r/drapergem/draper)

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/drapergem/draper on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: drapergem/draper

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/drapergem/draper 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 the board

  • Last commit 2mo ago
  • 28+ active contributors
  • Distributed ownership (top contributor 35% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<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 drapergem/draper repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/drapergem/draper.

What it runs against: a local clone of drapergem/draper — 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 drapergem/draper | 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 ≤ 101 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>drapergem/draper</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of drapergem/draper. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/drapergem/draper.git
#   cd draper
#
# 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 drapergem/draper and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "drapergem/draper(\\.git)?\\b" \\
  && ok "origin remote is drapergem/draper" \\
  || miss "origin remote is not drapergem/draper (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/draper/decorator.rb" \\
  && ok "lib/draper/decorator.rb" \\
  || miss "missing critical file: lib/draper/decorator.rb"
test -f "lib/draper/decoratable.rb" \\
  && ok "lib/draper/decoratable.rb" \\
  || miss "missing critical file: lib/draper/decoratable.rb"
test -f "lib/draper/collection_decorator.rb" \\
  && ok "lib/draper/collection_decorator.rb" \\
  || miss "missing critical file: lib/draper/collection_decorator.rb"
test -f "lib/draper/helper_proxy.rb" \\
  && ok "lib/draper/helper_proxy.rb" \\
  || miss "missing critical file: lib/draper/helper_proxy.rb"
test -f "lib/draper/view_context.rb" \\
  && ok "lib/draper/view_context.rb" \\
  || miss "missing critical file: lib/draper/view_context.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 101 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~71d)"
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/drapergem/draper"
  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

Draper is a Ruby gem that implements the Decorator pattern for Rails applications, wrapping ActiveRecord models with presentation-focused logic while keeping views clean and models slim. It allows developers to move view helpers and formatting logic into decorated objects that delegate to the underlying model, solving the problem of bloated models and scattered presentation logic across helpers. Single gem structure: lib/draper/ contains core modules (Decorator base class, CollectionDecorator, HelperProxy, ViewContext), delegation logic (AutomaticDelegation, Delegation), decorator association helpers (DecoratedAssociation), and Rails integration (Railtie, TaskCase). Query methods, lazy helpers, and configuration live in dedicated subdirectories. Test helpers for RSpec and Minitest are in lib/draper/test/.

👥Who it's for

Rails developers building MVC applications who need to separate presentation concerns from business logic. They want to write object-oriented view code (e.g., @article.publication_status instead of helper methods) and organize complex formatting/display logic in testable decorator classes rather than in helpers or model methods.

🌱Maturity & risk

Draper is mature and actively maintained. The gem has extensive test coverage (visible in lib/draper/test/ with RSpec and Minitest integration), a CI pipeline via GitHub Actions (.github/workflows/ci.yml), clear documentation (README with detailed examples), and follows Rails conventions (Railtie setup, Gemfile, gemspec). It's production-ready and used in active Rails applications.

Low risk overall. The project has stable dependencies (minimal external libraries visible), active GitHub Actions CI setup ensuring compatibility, and follows Ruby/Rails community standards (RuboCop linting config visible). Risk is primarily limited to the single-maintainer model typical of open-source gems—check recent commit activity in the repository. No obvious breaking changes in CHANGELOG structure.

Active areas of work

Based on file structure, the gem is in stable maintenance mode with focus on Rails compatibility (compatibility/ subdirectory handles API-only apps, Broadcastable, and GlobalID integration). The CI workflow (.github/workflows/ci.yml) validates against multiple Rails/Ruby versions. CONTRIBUTING.md and PULL_REQUEST_TEMPLATE.md indicate active community review process.

🚀Get running

git clone https://github.com/drapergem/draper.git
cd draper
bundle install
bundle exec rspec

Daily commands:

bundle exec rake
# or for RSpec specifically:
bundle exec rspec
# or with guard for continuous testing:
bundle exec guard

🗺️Map of the codebase

  • lib/draper/decorator.rb — Core Decorator class that all presenters inherit from; defines the primary API for wrapping models with presentation logic.
  • lib/draper/decoratable.rb — Mixin that extends ActiveRecord models with the .decorate method; entry point for most users.
  • lib/draper/collection_decorator.rb — Handles decoration of collections and ActiveRecord relations; implements lazy loading and query method delegation.
  • lib/draper/helper_proxy.rb — Provides access to Rails view helpers within decorators; bridges the gap between decorators and the view layer.
  • lib/draper/view_context.rb — Manages the Rails view context that decorators require to access helpers; critical for rendering logic.
  • lib/draper/railtie.rb — Rails integration point that hooks Draper into the Rails initialization lifecycle.
  • lib/draper/factory.rb — Factory pattern implementation for creating decorator instances with proper configuration and context.

🧩Components & responsibilities

  • Decorator (Ruby class with method_missing, decorator pattern) — Wraps a single model; provides presentation-specific methods and delegates unknown methods to the model.
    • Failure mode: If view context is missing, helpers will fail; if delegated method doesn't exist on model, raises NoMethodError.
  • CollectionDecorator (Enum) — Wraps ActiveRecord relations and arrays; implements lazy decoration of items and delegates query methods to relation.

🛠️How to make changes

Create a new Decorator for a Model

  1. Run the decorator generator: rails generate decorator Article (lib/generators/rails/decorator_generator.rb)
  2. This creates a new file in app/decorators/article_decorator.rb (lib/generators/rails/templates/decorator.rb)
  3. Inherit from Draper::Decorator and define presentation methods (lib/draper/decorator.rb)
  4. In controllers, call .decorate on your model: @article = Article.find(id).decorate (lib/draper/decoratable.rb)

Add Helper Access to a Decorator

  1. Draper automatically includes all Rails helpers in your decorator (lib/draper/helper_support.rb)
  2. Call helper methods directly: h.link_to(title, article_path(object)) (lib/draper/view_helpers.rb)
  3. The view context is managed automatically and lazily-loaded (lib/draper/lazy_helpers.rb)

Decorate Model Associations

  1. In your decorator, use decorated_association to auto-decorate associations (lib/draper/decorated_association.rb)
  2. Example: decorated_association :comments will automatically decorate @article.comments (lib/draper/decorator.rb)
  3. Each associated object is wrapped with its corresponding decorator (CommentDecorator) (lib/draper/collection_decorator.rb)

Test a Decorator

  1. Create test file: spec/decorators/article_decorator_spec.rb (lib/generators/rspec/templates/decorator_spec.rb)
  2. Inherit from Draper::TestCase or use RSpec integration helpers (lib/draper/test_case.rb)
  3. Test decorated methods in isolation: decorator = ArticleDecorator.new(article) (lib/draper/test/rspec_integration.rb)

🔧Why these technologies

  • Decorator Pattern — Provides a clean, OOP way to layer presentation logic on top of models without polluting them or bloating views.
  • Method Missing / Automatic Delegation — Allows decorators to transparently wrap models; when decorator doesn't implement a method, it delegates to the wrapped object.
  • Rails View Context Injection — Gives decorators access to Rails helpers (link_to, image_tag, etc.) without tight coupling to controllers/views.
  • Factory Pattern — Centralizes decorator instantiation logic, context injection, and configuration in one place.
  • Collection Decorator — Extends ActiveRecord relations with lazy loading; decorates items on-demand instead of eagerly loading everything.

⚖️Trade-offs already made

  • Automatic delegation via method_missing

    • Why: Simplifies the API—developers don't have to explicitly list delegated methods.
    • Consequence: Performance cost for missing method lookups; harder to introspect what methods are available on a decorator.
  • View context is injected at decoration time

    • Why: Decorators can call Rails helpers without explicit context passing.
    • Consequence: Decorators are tightly coupled to Rails; testing requires mock context or explicit setup.
  • Collection decorators support lazy loading

    • Why: Avoids N+1 queries and supports large result sets.
    • Consequence: Complexity in tracking when decoration actually happens; can hide performance issues if misused.
  • Global configuration via Draper.configure

    • Why: Provides convenient app-wide defaults without per-decorator boilerplate.
    • Consequence: Can lead to implicit behavior; developers must read config to understand decorator behavior.

🚫Non-goals (don't propose these)

  • Does not handle persistence or database operations (decorators are read-only view wrappers).
  • Does not provide authentication or authorization logic (separate concern).
  • Does not replace ActiveRecord models or validations.
  • Does not handle JSON serialization directly (that's typically delegated to serializers like ActiveModelSerializers).
  • Not designed for non-Rails applications (tightly coupled to Rails conventions).

🪤Traps & gotchas

  1. View context requirement: Decorators require a Rails view context to access helpers. In tests, you must set up a view context or use RSpec/Minitest integration helpers (lib/draper/test/). Calling .decorate in a context without a view will raise an error. 2. Method delegation edge cases: delegate_all forwards all unknown methods to the wrapped object, but private methods on the model will not be accessible through the decorator—this is intentional but can surprise users. 3. Circular decoration: Decorating an already-decorated object may cause unexpected behavior; Draper wraps the decorator, not the original model. Check with .decorated? before decorating. 4. Association decoration performance: lib/draper/decorated_association.rb uses lazy loading by default; accessing decorated associations in loops can trigger N+1 queries if not eager-loaded on the model.

🏗️Architecture

💡Concepts to learn

  • Decorator Pattern — Draper is a direct implementation of the Decorator design pattern; understanding this pattern is essential to using the gem effectively and knowing when decoration is the right tool vs. other approaches like helpers or model methods
  • Method Delegation / Forwarding — Draper's core mechanic is delegating unknown method calls to the wrapped object via delegate_all; understanding method_missing and metaprogramming is key to grasping how decorators transparently wrap models
  • Rails View Context — Decorators in Draper must be bound to a Rails view context to access helpers like link_to and current_user; understanding how Draper captures and manages view context is critical for testing and initialization
  • Lazy Evaluation / Lazy Loading — Draper's LazyHelpers (lib/draper/lazy_helpers.rb) and collection load strategies defer expensive computations until needed; understanding this pattern prevents N+1 queries and improves performance in decorator chains
  • Convention over Configuration (Rails idiom) — Draper follows Rails conventions heavily (ArticleDecorator decorates Article, lives in app/decorators/); understanding Rails naming conventions and Railtie hooks is essential to extending Draper or integrating it into custom Rails apps
  • Method Visibility (Public / Private / Protected) — Draper's delegation intentionally does not forward private methods from the model to the decorator; understanding Ruby's access control is important for designing decorator APIs and knowing what logic belongs where
  • ActiveRecord Associations & Eager Loading — lib/draper/decorated_association.rb handles decoration of has_many and belongs_to associations; understanding Rails associations and N+1 query prevention (includes, joins) is essential to avoiding performance pitfalls when decorating related objects
  • rails/rails — The host framework that Draper integrates with; understanding ActionView, ActionController, and Railtie is essential to using and extending Draper
  • josevalim/draper-fork — Historically a community fork exploring alternative approaches to decoration and delegation; useful for understanding design trade-offs
  • rwz/virtus — A complementary gem for attribute coercion and composition; often used alongside Draper for robust view object design
  • trailblazer/trailblazer — An alternative architectural framework that includes similar decorator/presenter concepts; relevant for developers exploring beyond Draper's scope
  • thoughtbot/active_model_serializers — A related gem for serializing models to JSON/XML; often used in Rails APIs where Draper decorators handle HTML presentation

🪄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 integration tests for DecoratedAssociation with nested decorators

The lib/draper/decorated_association.rb file handles a critical feature but lacks dedicated integration tests. Current test coverage doesn't adequately verify the interaction between DecoratedAssociation, CollectionDecorator, and nested model decorations across different Rails versions. This would prevent regressions in a heavily-used feature.

  • [ ] Create spec/draper/decorated_association_spec.rb with tests for: single association decoration, collection association decoration, lazy loading strategies, and decorator option override
  • [ ] Add tests in spec/integration/ for nested decorator chains (e.g., Article -> Comments -> Author decorators)
  • [ ] Test interaction with lib/draper/query_methods/load_strategy.rb for different loading scenarios
  • [ ] Verify behavior matches documentation in README for decorated associations

Implement missing GitHub Action workflow for Ruby version matrix testing

The existing .github/workflows/ci.yml likely tests only a limited set of Ruby versions. Draper must maintain compatibility across multiple Ruby/Rails combinations (evident from lib/draper/compatibility/ folder). Add a comprehensive matrix workflow to test against Ruby 2.7, 3.0, 3.1, 3.2, 3.3 and Rails 5.2+ to catch version-specific regressions early.

  • [ ] Review current .github/workflows/ci.yml to identify tested versions
  • [ ] Create new workflow .github/workflows/matrix-test.yml with Ruby version matrix (2.7, 3.0, 3.1, 3.2, 3.3) and Rails version matrix (5.2, 6.0, 6.1, 7.0, 7.1)
  • [ ] Add compatibility checks for lib/draper/compatibility/ modules in the matrix
  • [ ] Document supported version matrix in CONTRIBUTING.md

Add missing unit tests for ViewContext build strategies and helper proxy integration

lib/draper/view_context/build_strategy.rb and lib/draper/helper_proxy.rb are critical components that bridge Rails helpers with decorators, but lack dedicated unit test coverage. This is evidenced by the test file structure not having corresponding spec files for these modules. Comprehensive tests would ensure helper delegation, lazy_helpers, and view context building remain reliable.

  • [ ] Create spec/draper/view_context/build_strategy_spec.rb testing: strategy selection, view context building for different controller/action types, and Rails version compatibility
  • [ ] Create spec/draper/helper_proxy_spec.rb testing: method_missing delegation, lazy helper evaluation (lib/draper/lazy_helpers.rb), and isolation of helper scope
  • [ ] Add tests for lib/draper/helper_support.rb integration with the helper system
  • [ ] Verify tests cover edge cases like missing helpers, controller-less contexts, and API-only mode (lib/draper/compatibility/api_only.rb)

🌿Good first issues

  • Add comprehensive spec coverage for lib/draper/query_methods/load_strategy.rb—this file controls how decorated collections are loaded (lazy vs. eager) but appears under-tested. A new contributor could write specs validating that different load strategies produce the correct query behavior.
  • Expand compatibility layer documentation (lib/draper/compatibility/ has api_only.rb, broadcastable.rb, global_id.rb). Add real-world examples to README or CONTRIBUTING.md showing how to decorate Rails API-only models and GlobalID-enabled objects; current docs are sparse.
  • Improve error messages in lib/draper/view_context/build_strategy.rb when view context initialization fails. New contributors could add clearer exception classes and messages (e.g., when Draper.view_context is nil or misconfigured), making debugging easier for new users.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 43717f7 — Delegate missing constants to object class via const_missing (#964) (Copilot)
  • 5ac6f55 — Merge pull request #962 from chubchenko/ruby-4.0 (chubchenko)
  • 0ef01f9 — Merge pull request #961 from markokajzer/master (Alexander-Senko)
  • 96a31c8 — test: address flake when generating sid (markokajzer)
  • c1f1855 — fix: ensure turbo stream names are generated based on decorated object (markokajzer)
  • e8421b7 — Merge pull request #953 from drapergem/release-4.0.6 (seanlinsley)
  • 4c3a88f — Release v4.0.6 (seanlinsley)
  • 726ac7b — Revert "Reverted class spoofing" (seanlinsley)
  • 0fc2821 — Merge pull request #956 from y-yagi/exclude_ruby32_with_rails81 (y-yagi)
  • 472e7d9 — Temporarily exclude Ruby 3.2 with Rails 8.1 (y-yagi)

🔒Security observations

Draper is a well-structured Rails decorator gem with generally sound design. Primary security concerns are XSS prevention in view helper delegation (medium severity) and input validation in association decoration (medium severity). The codebase lacks explicit security documentation and examples. No critical vulnerabilities detected, but implementing the recommended security guidelines and documentation improvements would significantly enhance the security posture. The library itself is a presentation layer, so security largely depends on proper usage by developers integrating it into their applications.

  • Medium · Potential XSS Vulnerability in View Context Helpers — lib/draper/view_helpers.rb, lib/draper/helper_proxy.rb, lib/draper/automatic_delegation.rb. Draper is a decorator library that provides view helpers to models. The lib/draper/view_helpers.rb and lib/draper/helper_proxy.rb modules allow decorators to call Rails helper methods. If decorators use these helpers to render user-supplied content without proper escaping, XSS vulnerabilities could occur. The automatic delegation in lib/draper/automatic_delegation.rb may mask the origin of helper method calls, making it harder to track unsafe output. Fix: Ensure all user-supplied data passed to view helpers is properly escaped using Rails' h() or sanitize() methods. Document best practices in CONTRIBUTING.md about safe content rendering. Add examples in templates (lib/generators/rails/templates/decorator.rb) showing proper escaping patterns.
  • Medium · Insufficient Input Validation in Association Decoration — lib/draper/decorated_association.rb, lib/draper/decorates_assigned.rb, lib/draper/factory.rb. The lib/draper/decorated_association.rb and lib/draper/decorates_assigned.rb modules handle decoration of associated objects. Without proper validation of the decorator classes being instantiated, there's a risk of arbitrary code execution if decorator class names are user-controlled or derived from untrusted sources. Fix: Implement whitelist validation for decorator class names. Use explicit class references rather than string-based class resolution. Document security implications of using dynamic decorator assignment in the README and CONTRIBUTING guidelines.
  • Low · Missing Security Headers Documentation — README.md, CONTRIBUTING.md. As a Rails view-model library, Draper may be used in applications handling sensitive data. There is no visible security headers configuration guidance or middleware setup instructions in the repository. Fix: Add a 'Security Considerations' section to README.md covering: (1) proper XSS prevention when rendering decorated content, (2) CSRF protection with form helpers, (3) role-based authorization patterns with decorators, (4) recommended security headers for applications using Draper.
  • Low · No Dependency Pinning or Lock File Visible — Gemfile, root directory. The Gemfile is present but the Gemfile.lock is not included in the file structure. This could lead to inconsistent dependency versions across environments, potentially including vulnerable gem versions. Fix: Ensure Gemfile.lock is committed to version control and regularly updated. Use tools like Dependabot or bundler-audit to scan for vulnerable gem dependencies. Add dependency scanning to CI/CD pipeline (visible in .github/workflows/ci.yml).
  • Low · Test Fixtures May Expose Sensitive Data — spec/draper/. The spec/ directory contains test files for decorators. Test fixtures and factories could potentially contain hardcoded sensitive data or patterns that could be exploited if the test code is accidentally exposed. Fix: Review all test fixtures to ensure no hardcoded secrets, API keys, or sensitive PII are present. Use ENV variables or Rails credentials for any sensitive test data. Add test data guidelines to CONTRIBUTING.md.

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 · drapergem/draper — RepoPilot