drapergem / draper

Decorators/View-Models for Rails Applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Collection decorator unable to access decorator of collection objects

unikitty37 opened this issue · comments

Rails: 5.1.2
Draper: 3.0.0

I have an EventDecorator and an EventsDecorator on the Event model.

class EventDecorator < ApplicationDecorator
  def short_listing
    'short' # simplified for demo purposes :)
  end
end
class EventsDecorator < Draper::CollectionDecorator
  include Draper::LazyHelpers

  def front_page_card
    if object.empty?
      '<div>No upcoming events</div>'
    else
       object.reduce('') { |list, event| "#{list}<div>#{event.short_listing}</div>" }
    end
  end
end

and I am accessing them by decorating the collection in the controller:

@upcoming_events = Event.upcoming(limit: 3).decorate

I would expect that, when I render @upcoming_events.front_page_card in the view, I would get three divs, all containing the word 'short'. What I actually get is undefined method short_listing' for #Event:0x007fe7d09af5c8, and using the web console to inspect object.classshows that it'sEvent::ActiveRecord_Relationand not the expectedEventDecorator`.

For added confusion, running object.reduce('') { |list, event| "#{list}<div>#{event.short_listing}</div>" } in the rails console correctly assigns the EventDecorator class to each event object in the reduce block, and returns the expected formatted list:

irb(main):014:0> object.reduce('') { |list, event| "#{list}<div>#{event.short_listing}</div>" }
=> "<div>short</div><div>short</div><div>short</div>"

What could be going wrong here?

Changing event.short_listing to event.decorate.short_listing seems to fix the problem, but I don't understand why the behaviour is different when front_page_card is called from a view renderer rather than from the rails console.

The documentation certainly seems to imply that I shouldn't need to explicitly decorate event here…

Hi. it looks not a draper bug.
Issues should be for bug report and suggesting new features. So If you have question of how to use, I recommend posting to Q&A site ( such as StackOverflow

BTW, documentation says you should write like this.

@upcoming_events = EventsDecorator.decorate(Event.upcoming(limit: 3))

Please try 😄
Regards.

Afraid following the documentation does not work — I still get the NoMethodError if I call

@upcoming_events = EventsDecorator.decorate(Event.upcoming(limit: 3))

in the controller — I can call collection decorator methods such as @upcoming_events.front_page_card just fine, but those methods cannot then call a decorator method defined in EventDecorator, such as short_listing without explicitly calling decorate on the event first — so the event.short_listing above needs to be replaced with event.decorate.short_listing.

The documentation says "Draper decorates each item by calling the decorate method", which implies I don't need to do this — and that is what happens at the rails console, just not in the renderer. Seems inconsistent…

I got bitten by the same bug with draper 4.0.1.

Specifically, one need to call decorate on collection members, from a Draper::CollectionDecorator.

It's counter-intuitive and, as said before, contrary to the doc. I would at least update the doc with a warning.

Regards.