-
Notifications
You must be signed in to change notification settings - Fork 526
Upgrading to 1.0
A lot has changed in Draper 1.0, both under the hood and in the API. You can check out the change log or the compare view to see the extent of the changes, but if you just want to get up and running, here's a quick guide to how to upgrade from Draper 0.x.
You'll need to update your decorators' superclass (don't forget that you can have an ApplicationDecorator
and inherit from that!).
You might not have been inheriting from this class yourself, but doing so is a great way to add methods to your decorated collections (e.g. for pagination).
It's included by default into ActiveRecord::Base
and Mongoid::Document
, so if your models inherit from one of those, you're already set. If you want to decorate plain old Ruby objects, you should include this module manually to get a decorate
instance method.
To regain this behavior you need to add delegate_all
to the top of your decorator. Newly-generated decorators will have this by default.
If you were previously controlling which methods were delegated using allows
, denies
, or denies_all
, these methods have now been removed. Now, to control delegation don't add the delegate_all
. For denies_all
-style behavior, that's it, you're done!
To bring some delegation back into play, you can use the standard Module.delegate
from Active Support. We've overridden it to make to: :source
the default, so you can just write delegate :foo, :bar
where you might have had allows :foo, :bar
. Unfortunately, if you were using denies
, you're out of luck (unless you override method_missing
yourself) - try switching to a whitelist approach instead.
delegate_all
will also try to send missing class methods to the model class. If you follow the naming convention of SomeModelDecorator
decorating SomeModel
, Draper infers the model class from the decorator class and you don't need to put decorates :some_model
at the top any more. If you don't follow this convention, you'll still need the decorates
if you want the class methods to be delegated.
Previously, when you did ProductDecorator.decorate(Product.all)
, Draper guessed that what you actually wanted was a collection of ProductDecorator
s. Now, you need to tell it explicitly by doing ProductDecorator.decorate_collection(Product.all)
.
Note that for Active Record queries you can do Product.where(color: "red").decorate
instead. However, in Rails 3 all
returns an array, not a query, so Product.all.decorate
won't work (but you can instead do Product.scoped.decorate
or Product.popular.decorate
). Since Rails 4, all
returns a query so Product.all.decorate
will work.
Without the collection-decorating magic, Decorator.decorate
is now a simple alias for Decorator.new
. Even more tersely, though, you can use the decorate
instance method on models - Product.find(params[:id]).decorate
.
Draper no longer decorates ProductDecorator.find(id)
and other finder methods out of the box. Other methods of decorating found objects (e.g. Product.find(id).decorate
) should be preferred, but if you want to get this functionality back you can add decorates_finders
to your decorator definition.
A ProductDecorator
used to magically get a product
method that returned the underlying model. That isn't generated any more - it's now just called source
(and aliased to model
). You can get your product
method back by adding an alias_method :product, :source
.
A DecoratedEnumerableProxy
proxied missing methods to the source collection much like a regular decorator. However, this could lead to inconsistent state if the method modified the collection (e.g. build
) because the collection of decorators was not updated to match.
The new CollectionDecorator
is designed to avoid these inconsistencies by never delegating methods to the source - all array methods are delegated to the decorated collection, not the source collection. However, sometimes you do need access to the underlying collection (e.g. when using pagination gems that add methods to ActiveRecord::Relation
). In this case you can access the protected source
method in a custom subclass of CollectionDecorator
(e.g. a PaginatingDecorator
). See the README for examples.
This change will break your application if you were previously doing something like this:
@article = Article.find(params[:id]).decorate
# ArticleDecorator decorates_association :comments
@article.comments.build
# => NoMethodError
We would suggest that you avoid using decorators in this way - they are designed to interact with the view, not to be modified in the controller. Make sure decoration is the last thing that happens before you hand off to the view.
Decorators are now fussy about the options you give them. In 0.18 you could save extra contextual information by doing Decorator.decorate(model, role: "admin")
and then accessing it through the saved options
hash.
In 1.0 you should use the :context
option: Decorator.decorate(model, context: {role: "admin"})
. The hash is then available through the context
method, as you might expect.