draper
draper copied to clipboard
Collection decorator unable to access decorator of collection objects
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's
Event::ActiveRecord_Relationand not the expected
EventDecorator`.
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.