Skip to main content

Subscribe to GoRails to get access to this episode and all other pro episodes, and new awesome content every month.

Subscribe Now
Only $19/month

Unlimited access. Cancel anytime.

44 Decorators From Scratch

Episode 174 · February 22, 2017

Learn what the Decorator design pattern does and how to implement it from scratch

Design Patterns


Transcripts

Subscribe or login to view the transcript for this episode.

Discussion


Gravatar
Nick on

Nailed it. Thanks again Chris - could never find a good explanation on Decorators, hence I never used them, will be sure to use them now!

Gravatar
Chris Oliver (167,510 XP) on

That's awesome to hear. Thanks Nick! :)


Gravatar
Patrick Berarducci (480 XP) on

Thanks, Chris, helpful as always! You also can use SimpleDelegator to handle the ActiveRecord (and delegation) issues. Here's an example https://hashrocket.com/blog...

Gravatar
Chris Oliver (167,510 XP) on

That's pretty neat. I know I've seen SimpleDelegator mentioned before in but never looked into it. I'll have to fiddle with it now!🤘

Gravatar
Patrick Berarducci (480 XP) on

Yea I learned of the technique in this context from Noel Rappin's book, Rails 4 Test Prescriptions (which is a great book, link here: https://www.amazon.com/Rail.... He uses it for presenters, which I've used and liked.


Gravatar
Francisco Quinones (7,370 XP) on

mmm... looks like a lot of dependency and complexity to keep a eye for.


Gravatar
victor hazbun (340 XP) on

When should use decorators VS helpers?


Gravatar
Scott Knight (120 XP) on

Great episode Chris! Definitely going to be using this. One thing I'm curious about, I often use partials to render collections instead of iterating through them in the view, e.g. render @users instead of @users.each do |user|... How would this work (or would it work) with the decorated user objects? I suppose you would just need to specify the partial name and local variables instead of relying on the rails shorthand of render @users right?

Gravatar
Chris Oliver (167,510 XP) on

Whoops, I had this tab open to reply to at some point...9 days later. :P

Yeah, you'd probably need to pass the partial name in to pass over the presenters that way. I'm sure there's some way you could override some things in the presenter trick Rails into rendering the proper partial name automatically. The draper gem might be able to do that and have some insights on how to pull that off with your own implementation. I know that they have a Relation-esque collection object that might be what you'd need to add to your own implementation from scratch to do that.


Gravatar
Nadav Blum (950 XP) on

Thanks. Great Episode !
I have one remark though: Is it really a good idea to use the view context in the decorator, for creating things like html tags ? Doesn't this create a coupling of the decorator with the view format? I mean you this way, you will need a different decorator for xml or json views (which do not use content_tag helpers etc...)

Gravatar
Chris Oliver (167,510 XP) on

That's a great question. If you need to build decorators that work in both cases, then yes, you'd want to do one of two things:

1. Separate decorators for JSON and HTML and wrap them accordingly depending on the response type. Downside to this is more decorators.
2. Build generic decorators and leave it up to your views. Downside to this is that part of the benefit of having the link_to's and etc in the decorator is that your view can remove logic entirely. That's probably not possible if you're not able to write logic in the decorator.


Gravatar
zillou on

Hi Chris, if we are using Rails 4+, we don't actually need to modify the `auto_load_paths` anymore, as:

> All subdirectories of app in the application and engines present at boot time. For example, app/controllers. They do not need to be the default ones, any custom directories like app/workers belong automatically to autoload_paths
> http://guides.rubyonrails.o...

Gravatar
Chris Oliver (167,510 XP) on

I thought that was the case! It didn't work for me the first time I tried it, but it could have been spring caching things or something. Thanks for sharing that. :D


Gravatar
Daniel LeGrand (1,040 XP) on

Awesome episode! One thought on collections of presenters. I found it annoying to keep writing out the collection.map { |object| ObjectPresenter.new(object) } syntax every time.

In my situation I use a BasePresenter that all my other presenters inherit from. I then add a class method "collection(...)" that allows me to create a collection of presenters with cleaner syntax.

The above syntax now looks like this:

@objects = ObjectPresenter.collection(Object.all, view_context)

class BasePresenter < SimpleDelegator
def self.collection(objects, view = nil)
objects.map { |object| new(object, view) }
end

def initialize(object, view = nil)
@object = object
@view = (view || ActionController::Base.helpers)
super(@object)
end
end


Gravatar
Rodica Trifan (1,830 XP) on

Found this very useful, thank you! If you have any more patterns wisdom, please bring it on :)


Gravatar
Mariano Giagante (1,820 XP) on

Amazing episode! I guess this kind of decorators are different to the decorators in the GoF book. Those inherit from the class they decorate to adhere the Liskov's substitution principle. I suppose this can't be done because active record naming conventions would not be compatible with this.


Login or create an account to join the conversation.