Draper: presentation logic as objects, not helper soup
Decorators/View-Models for Rails Applications
At a glance
- What is it?
- Draper wraps Rails models in decorators so formatting and view logic live in testable classes instead of scattered helpers. It is a small, MIT-licensed gem with a narrow job and a few sharp edges worth knowing before you adopt it.
- Who is it for?
- Adopt Draper if your views have accumulated formatting methods and you want them in named classes with their own specs. Skip it if your presentation logic is trivial, if you rely on heavy view-component composition, or if you cannot accept that decorators wrap models and must be applied explicitly at the controller or view boundary.
- 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 169 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 November 14, 2026, and from our analysis. They are not legal advice.
Star history
- drapergem/draper5,275 ★ · -2
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| drapergem/draper | 5.3k | 5.3k |
-2 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What Draper actually replaces
The README frames the problem in terms of two bad options. Presentation logic either ends up in procedural helpers, which live in a global namespace shared by every controller and view, or it gets pushed into the model, where formatting concerns do not belong. Draper's answer is a class per model: an `ArticleDecorator` that wraps an `Article` and holds only the methods the view needs.
The README's own example is a `publication_status` helper that branches on whether an article is published. The complaint is concrete: if a `Book` later needs a slightly different date format, the helper either switches on class type or splits into `book_publication_status` and `article_publication_status`, and the global helper namespace keeps growing. A decorator turns that into a method on a class you can name, test and extend.
The audience is Rails teams that already feel this pain. If your views are thin and your helpers are few, Draper adds a layer you will not use. It is for the case where formatting, markup decisions and combined attributes have spread across helpers and models and nobody can say where a given method lives.
How delegation and the object reference work
A decorator subclasses `Draper::Decorator`. The wrapped model is reachable through `object`, with `model` as an alias, so a decorator method can call `object.published_at.strftime("%A, %B %e")` without ambiguity.
Delegation is opt-in. The README's example calls `delegate_all` inside the decorator class, which is what makes methods like `published?` from the source model available on the decorator. Without that call, only the decorator's own methods plus the `object` accessor are there. This is a deliberate design choice: you decide how much of the model surface leaks through, rather than getting everything by default.
Helpers are reachable through `h`, so `h.content_tag(:strong, "Awesome")` works inside a decorator. The README notes one limitation explicitly: `capture` is only available through `h` or `helpers`, not through the lazy mixin. That detail matters if you write decorator methods that build blocks.
Decoration happens at the boundary. In the controller you call `Article.find(params[:id]).decorate`, which infers the decorator from the object. When you want a different mapping, for instance decorating a `Widget` with a more general `ProductDecorator`, you instantiate the decorator directly instead of relying on inference.
Installing Draper and writing a first decorator
Draper is distributed as a gem. The README states that as of version 4.0.0 it officially supports Rails 5.2 and Ruby 2.4 and later, so check that floor before adding it. Add the gem to your Gemfile:
gem 'draper'Then run `bundle install` from your application directory. If you want a shared base class for generated decorators, the README gives a generator for it:
rails generate draper:installThat creates an `ApplicationDecorator` that all generated decorators inherit from. When you generate a resource, Draper hooks into the generator and produces a decorator alongside the model and controller:
rails generate resource ArticleIf the model already exists, generate the decorator on its own:
rails generate decorator ArticleDecorators live in `app/decorators` and are named for the model they wrap. A minimal one looks like this, with `delegate_all` exposing the model's methods and `object` available for anything you want to be explicit about:
# 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
endIn the controller, decorate before handing the object to the view. The README's example is `@article = Article.find(params[:id]).decorate`. From there the template uses `@article.publication_status` the same way it would call a model method. If you do not want decorators generated with every new controller, the README shows disabling the generator:
config.generators do |g|
g.decorator false
endNamespaced models follow the same directory convention as views and models. For `Admin::Catalogue`, the decorator goes in `app/decorators/admin/catalogue_decorator.rb` and is declared as `Admin::CatalogueDecorator`.
The LazyHelpers trade-off and other rough edges
The README offers `include Draper::LazyHelpers` as a convenience so you can call helper methods without the `h.` prefix. It describes the consequence in plain terms: you mix in a large number of methods and never type `h.` again. That is a real trade-off, not a free win. Mixing every Rails helper into every decorator widens the method surface and makes it harder to see where a method comes from. In a codebase with many decorators, the explicit `h.content_tag` form is easier to reason about. The README itself notes that `capture` is unavailable through the lazy path, so the convenience is not complete anyway.
Decoration is explicit. Nothing decorates a model for you unless you call `decorate` or instantiate the decorator. Objects returned from scopes or service objects need to be wrapped too; the README shows how to decorate a whole collection in one call. That means the boundary between decorated and undecorated objects is something your team has to keep consistent, and a view that assumes a decorated object will fail on a plain model.
The documentation also does not cover upgrade paths in the README itself. It points readers upgrading from a 0.x release to a wiki page, which means version migration guidance lives outside the repository's main document. If you are on an older line, plan to read that wiki page rather than expecting the README to carry you.
Where Draper is the wrong tool
Draper is a presentation layer for models. If your problem is component reuse across pages, or you need a template language with slots and composition, a decorator that wraps a single model will not get you there. Draper's unit is the model instance, not the UI fragment.
It is also the wrong choice when presentation logic is genuinely trivial. A single `full_name` method on one model does not justify a decorator class, a generator convention and a base `ApplicationDecorator`. The README's own examples are about accumulating formatting concerns, and the cost only pays off once that accumulation is real.
There is a subtler case: teams that already keep formatting in plain Ruby objects or in view objects of their own design. Draper's value is the convention (directory layout, naming, generators, `decorate` inference) plus the delegation and helper plumbing. If you already have that plumbing and it works, adding Draper means adopting its conventions for little gain.
Finally, the delegation model is a footgun if used carelessly. `delegate_all` makes the decorator look almost like the model, which is convenient until a method you thought was presentation logic turns out to be a model method passing through. Being selective about what you delegate keeps the boundary honest.
Draper compared with plain helpers and view objects
The alternative the README argues against is the Rails helper. The difference is structural: a helper is a method in a module mixed into the view context, shared across every template, while a decorator is an instance bound to one model. With helpers, the same method name must serve every class that calls it, which is why the README describes branching on input class type or duplicating methods per type. With decorators, the method belongs to `ArticleDecorator` and a `BookDecorator` can define its own version without collision.
The second alternative is putting the method on the model. That keeps the method namespaced and testable, but it puts formatting and markup decisions in the persistence layer. Draper's split is that the model stays about data and rules, and the decorator holds what the view needs. Whether that split is worth an extra class per model is a judgement call, and the honest answer is that it depends on how much presentation logic you have.
A third option is writing your own view objects with no gem at all. You would get the same object-oriented structure, but you would rebuild the parts Draper already provides: the `decorate` entry point on models, decorator inference, the `object` and `model` accessors, `h` for helpers, the generator integration and the `app/decorators` convention. That is the practical difference between Draper and a hand-rolled presenter: conventions and plumbing, not a different idea.
Maintenance, licensing and upgrade cost
Draper is MIT-licensed, which is permissive and imposes few obligations beyond retaining the licence notice. This is not legal advice; check the LICENSE file in the repository for the exact terms.
The repository is not archived. The most recent push recorded is 2026-05-29, and the latest listed release is v4.0.6 from 2025-11-18, with v4.0.5 and v4.0.4 published the same day. Those dates are recent enough that the project is not dormant, so treat upgrade expectations as something to verify from the CHANGELOG and the issue tracker rather than assume.
The practical upgrade cost is tied to the Rails and Ruby floor. The README states that version 4.0.0 and later officially support Rails 5.2 and Ruby 2.4 and above, so applications on older versions are outside the supported range. Upgrades from the 0.x line are documented on a wiki page rather than in the README, which means migration reading is a separate step. Because decorators are plain Ruby classes in your application, the gem itself carries no runtime service, no daemon and no database, so operational cost is limited to dependency updates and the code you write on top of it.
Editorial conclusion
Adopt Draper if your views have accumulated formatting methods and you want them in named classes with their own specs. Skip it if your presentation logic is trivial, if you rely on heavy view-component composition, or if you cannot accept that decorators wrap models and must be applied explicitly at the controller or view boundary. Before rolling it out, verify that your Rails and Ruby versions meet the 5.2 / 2.4 floor stated in the README, confirm whether you want the decorator generator wired into every `rails generate resource`, and check how your team feels about `include Draper::LazyHelpers` before it lands in a base decorator. The gem itself is MIT-licensed and has no runtime service to operate.
Frequently asked questions
What is Draper in Rails?
Draper adds an object-oriented presentation layer to Rails applications by wrapping models in decorators. A decorator holds view-related methods such as formatting and combined attributes, so that logic does not live in helpers or in the model itself.
How do I install Draper in a Rails app?
Add `gem 'draper'` to your Gemfile and run `bundle install`. The README states that as of version 4.0.0 Draper officially supports Rails 5.2 and Ruby 2.4 and later.
How do I generate a decorator for an existing model?
Run `rails generate decorator Article` to create the decorator for a model that already exists. Generating a resource also produces a decorator, and `rails generate draper:install` creates the `ApplicationDecorator` base class.
How do I access the wrapped model inside a Draper decorator?
Use `object`, or its alias `model`, to reach the wrapped record. The README's example calls `object.published_at.strftime("%A, %B %e")` inside a decorator method.
How can I turn off decorator generation in Rails?
Add a generator configuration block to `config/application.rb` and set `g.decorator false`. The README shows this as the way to stop Rails from generating decorator files when generating a new controller.
Official sources
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.
[](https://hysenlabs.com/projects/drapergem-draper)Star history chart for your README
[](https://hysenlabs.com/projects/drapergem-draper)Was this useful?