Model or dataset
drapergem/draper avatar
drapergem/draper

Draper: presentation logic with a class of its own

Decorators/View-Models for Rails Applications

5,277 stars523 forksRubyMIT

At a glance

What is it?
Draper is the Rails gem that wraps models in decorators, also called view models or presenters, so presentation logic lives in testable classes instead of procedural helpers or fattened models. Version 4 supports Rails 5.2 and Ruby 2.4 and later, under MIT.
Who is it for?
Use Draper when your Rails views are drifting into helper sprawl and you want that logic in named, unit-testable classes that wrap models. Stay with plain helpers when the logic is genuinely trivial, and consider ViewComponent when the unit you want to encapsulate is template plus logic rather than a decorated model.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 123 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An object-oriented layer where helpers used to live

Draper's opening argument is architectural: Rails applications accumulate presentation logic, and without a dedicated home it tangles into procedural helpers or adds bulk to models. Draper decorators wrap models with presentation-related logic so this layer can be organized and tested effectively, which is the whole pitch in one sentence. The shape is conventional object orientation: an Article model gets an ArticleDecorator, the controller decorates the record before handing it to the view, and the view uses the decorator exactly as it used the model:

ruby
# app/controllers/articles_controller.rb
def show
  @article = Article.find(params[:id]).decorate
end

Nothing about the template changes on day one. What changes is where the next piece of display logic goes: onto the decorator, as a method with a class, a file and a spec, rather than into the global helper namespace. That last property is the quiet payoff of the whole arrangement, because an ordinary class can be unit-tested without rendering anything, which is the testing half of the README's organize and test claim.

The helper that would have grown into two, then ten

The README's worked example is the moment every Rails project recognizes. You write a publication_status helper that formats an article's published_at with strftime, and it works, but it lives in a nebulous namespace spread across all controllers and views. Then a Book needs the same status with slightly different date formatting, and the options become switching on the input class, which the README calls poor Ruby style, or splitting into article_publication_status and book_publication_status, and continuing to grow a pile of prefixed names you have to remember. The template you actually want reads:

erb
<%= @article.publication_status %>

The alternative home, the Article model, is wrong in the other direction, because the method is presentation-centric and does not belong there. Draper's answer is the third place, a decorator that owns the method.

delegate_all, object, and a method that formats a date

A decorator is small by design. It inherits from Draper::Decorator, lives in app/decorators, and is named for the model it decorates. The canonical example is fifteen lines:

ruby
# app/decorators/article_decorator.rb
class ArticleDecorator < Draper::Decorator
  delegate_all

  def publication_status
    if published?
      "Published at #{published_at}"
    else
      "Unpublished"
    end
  end

  def published_at
    object.published_at.strftime("%A, %B %e")
  end
end

The moving parts are two. delegate_all makes the wrapped Article's methods, like published?, available directly on the decorator. And object, with model as an alias, is the wrapped instance itself, reached explicitly when a decorator method needs to override what delegation would forward. The published_at override shows the standard idiom: the decorator redefines the name, other decorator methods calling it receive the formatted version, and object retains the untouched original for anything that needs it. The README also collects the vocabulary you may have met elsewhere, presenter, exhibit, view model, and notes the ideal uses: formatting complex data, composing representations like a name from first_name and last_name, and marking up attributes with semantic HTML such as turning a url field into a hyperlink.

One gem line, three generators, one opt-out

Installation is a Gemfile entry and a bundle install, with version 4.0.0 officially supporting Rails 5.2 and Ruby 2.4 and later:

ruby
  gem 'draper'

Generators do the scaffolding. rails generate draper:install creates an ApplicationDecorator that all generated decorators inherit. With Draper installed, generating a resource produces its decorator for free, and an existing model gets one with rails generate decorator Article. Teams that do not want decorator files attached to controller generation can turn the behavior off in config/application.rb:

ruby
config.generators do |g|
  g.decorator false
end

Namespaced models follow the convention you would expect: Admin::Catalogue finds its decorator at app/decorators/admin/catalogue_decorator.rb, mirroring the layout used for views and models.

h for helpers, LazyHelpers for the impatient

Decorators do not abolish helpers, they coexist with them, and Rails' own helpers remain reachable inside a decorator through the h method:

ruby
class ArticleDecorator < Draper::Decorator
  def emphatic
    h.content_tag(:strong, "Awesome")
  end
end

If typing h. repeatedly wears you down, one include mixes everything in:

ruby
include Draper::LazyHelpers

The README's characterization of the result is honest to the point of comedy, you will mix in a bazillion methods and never type h. again, with the documented caveat that the capture method remains reachable only through h or helpers. The design trade is explicit: h keeps the boundary between decorator methods and helper methods visible in the code, while LazyHelpers optimizes for brevity at the cost of that visibility.

decorate, explicit wrapping, and collections

Putting a decorator to work offers a gradient of control. The simple path is calling decorate on the model itself, which infers the decorator from the object's class:

ruby
@article = Article.first.decorate

When the inference is wrong for the job, say a Widget should be presented through a more general ProductDecorator, the decorator can be instantiated directly or through its own decorate class method:

ruby
@widget = ProductDecorator.new(Widget.first)
# or, equivalently
@widget = ProductDecorator.decorate(Widget.first)

Collections get the same treatment with the documented promise of decorating all elements in one fell swoop, so index actions do not become per-row ceremonies and the view's iteration code stays identical to the undecorated version. The choice between inferred and explicit decoration also scales with team size, since an explicit decorator class documents intent where inference leaves the reader to recall the naming convention. The pattern that emerges is deliberate: decoration follows the object graph from controller inward, and the view never knows whether it holds a model or a decorator, only that the methods it calls exist.

Three patch releases in one day, and a quiet 2026

Maintenance has a distinct texture here. Versions 4.0.4, 4.0.5 and 4.0.6 were all released on 2025-11-18, a same-day patch run of the kind projects do when a fix matters more than ceremony, and the last push to the repository came on 2026-05-29. That is a mature gem in maintenance mode rather than active feature development, which suits its subject: the decorator pattern it implements has been stable for over a decade, and for a team choosing today that age reads as a feature, with the pattern settled, the edge cases long found, and the gem's remaining job being compatibility with new Rails releases rather than invention. Repository hygiene is classical Ruby, rspec, rubocop, Code Climate, YARD docs, a Guardfile, a CHANGELOG and a CONTRIBUTING guide. The license is MIT, and the upgrade path from ancient 0.x releases is documented in the wiki for the long-running applications this gem tends to live in.

Editorial conclusion

Use Draper when your Rails views are drifting into helper sprawl and you want that logic in named, unit-testable classes that wrap models. Stay with plain helpers when the logic is genuinely trivial, and consider ViewComponent when the unit you want to encapsulate is template plus logic rather than a decorated model. Verify first that your Rails and Ruby versions sit at or above the 5.2 and 2.4 floors Draper 4 declares, and decide your delegation policy, delegate_all or explicit, before the decorator count grows.

Frequently asked questions

What is Draper in Rails?

Draper is an MIT-licensed Ruby gem that adds decorators, also called view models or presenters, to Rails applications. A decorator wraps a model and holds only presentation logic, replacing procedural helper methods with object-oriented classes.

How do you install Draper?

Add gem 'draper' to your Gemfile and run bundle install. Version 4.0.0 and later officially support Rails 5.2 and Ruby 2.4 and newer.

How do you create a decorator with Draper?

Run rails generate decorator Article to create ArticleDecorator for an existing model, or rails generate draper:install to create an ApplicationDecorator that generated decorators inherit. Decorators live in app/decorators and are named for the model they decorate.

Official sources

  1. drapergem/draper on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/drapergem-draper.svg)](https://hysenlabs.com/projects/drapergem-draper)