Decorate scoped associations #128

Closed
javierv opened this Issue Feb 10, 2012 · 7 comments

Projects

None yet

6 participants

@javierv
javierv commented Feb 10, 2012

I've started experimenting with Draper, and looks great. Thanks!

I've just seen automatically decorate associations has been added, and it works great. For example, this works as expected:

unscoped_comments = PostDecorator.find(1).comments
unscoped_comments.map(&:some_comment_decorator_method)

However, it doesn't work if we use some scopes:

scoped_comments = PostDecorator.find(1).comments.order("created_at DESC")
scoped_comments.map(&:some_comment_decorator_method)

The reason apparently is:

unscoped_comments.class   => Draper::DecoratedEnumerableProxy
scoped_comments.class     => ActiveRecord::Relation

My feature request would be the second case to automatically work, although I don't know if that's out of the scope of this project.

Thanks!

@michaelfairley
Contributor

I'm not sure there's a great way to do this (but hopefully I'm wrong).

Compare PostDecorator.find(1).comments.size and PostDecorator.find(1).comments.order("created_at DESC"). In once instance, we want draper to give you the result of calling the method on the decorated class, and in the other we want draper to re-decorate the result.

Draper would need a mechanism (decorates_scope :order perhaps) for it be told that all calls to order should be redecorated. And then it need to have that called on it for all ActiveRecord::Relation methods, and any custom scopes.

An option for how you can deal with this without waiting for a change to draper (this is roughly what the hypothetical decorates_scope would do):

# In CommentDecorator
def order(*args)
  self.class.decorate(model.order(*args))
end
@linuxonrails

I have the same problem: Decorator doesn't work 100% with associations.

PostDecorator.last.user.class
 => UserDecorator

@posts = PostDecorator.decorate(Post.public.lastest)
> @post.first.user.class
 => UserDecorator

But @posts.first.summary don't work (for example)

class UserDecorator < ApplicationDecorator
  decorates :user
  def name
    mode.username
  end
end

class PostDecorator < ApplicationDecorator
  decorates :post
  decorates_association :user

  def summary
    model.user.name # ERROR! mode.user is not an UserDecorator :-(
  end
end
@michaelfairley
Contributor

@linuxonrails, if you do user.name instead of model.user.name, it should work. model is the underlying model, so it has no knowledge of any of the decorator related stuff.

@linuxonrails

Very thanks michaelfairley! It works!!!

@adamcrown

Also, calling scopes on the decorator class directly returns undecorated results.

class User < ActiveRecord::Base
  scope :admins, where(:admin => true)
end

class UserDecorator < ApplicationDecorator
  decorates :users
  decorates_association :admins
end

UserDecorator.admins.first.class #=> User

I'm assuming that this is because decorates_association is decorating instance methods but not class methods.

@rboyd rboyd pushed a commit to rboyd/draper that referenced this issue Jun 15, 2012
Robert Boyd decorate scoped associations #128 eb27430
@rboyd
rboyd commented Jun 15, 2012

I have a small one-liner patch to lib/draper/base.rb that would allow decorates_association to take a :scope option. Pull request for consideration to follow.

Contrived example: decorate a Company that has-many associated Employees and we'd like to order them by birthdate:

class CompanyDecorator < Draper::Base
  decorates_association :employees, scope: :by_birthdate
end

in Employee:
scope :by_birthdate, order('birthdate')
@steveklabnik
Member

This should be closed by #223, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment