Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Behavior change when decorating array #362

Closed
cheald opened this issue Dec 2, 2012 · 13 comments
Closed

Behavior change when decorating array #362

cheald opened this issue Dec 2, 2012 · 13 comments

Comments

@cheald
Copy link
Contributor

cheald commented Dec 2, 2012

In 0.18, calling something like FooDecorator.decorate([@foo]) would return an array of decorated Foos. It now tries to decorate the array itself. Is this desired behavior?

I think this arose from aliasing decorate to new. Before, it had special behavior when passed an array.

We have collection decorator, so we could make decorate use that if passed an array, or we could add something like a Decorator::decorate_all method which expects an Enumerable.

I've worked around this in my code by using [@foo].map(&:decorate), which works fine, but the semantics are different since that doesn't ensure that I'm getting an array of FooDecorator back.

@nashby
Copy link
Member

nashby commented Dec 2, 2012

Yeah, we have decorate_collection but I'm not sure why decorate no longer works with collections. Any reason for this? /cc @steveklabnik

@haines
Copy link
Contributor

haines commented Dec 2, 2012

Yep, I changed the behaviour in 7748ce5, if you use decorate_collection it will return a CollectionDecorator as you expect.

As you can see from the diff there, having decorate check if the object is a collection requires lots of nasty class-sniffing, because a simple check for respond_to? :each throws a false positive for some non-collection objects (Structs, Sequel::Models).

Apart from that implementation issue, I think it is more intention-revealing to have decorate return a singular decorator and decorate_collection return a collection decorator. In fact, the motivation for the change was that someone wanted to use a singular decorator on an Enumerable (#328), but of course decorate was decorating the items of the collection instead.

@steveklabnik This change probably needs to be documented. I can have a stab at updating the changelog and README if you want?

@cheald
Copy link
Contributor Author

cheald commented Dec 2, 2012

Oh, cool. I missed decorate_collection. Excellent - should be solved with the documentation update then. I completely agree that not bundling the behavior into #decorate is the less surprising route.

@cheald cheald closed this as completed Dec 2, 2012
@steveklabnik
Copy link
Member

Ya a changeling needs built in general. I did it sorta with my blog post,
but it's one thing to do before a real 1.0.0 release.

@nashby
Copy link
Member

nashby commented Dec 5, 2012

@steveklabnik well, I'm still not sure about this decision. It was pretty handy to decorate collections with the same decorate method but now I have to write this looong decorate_collection method name and worry about it's collection or not. What do you think? 😟

@steveklabnik
Copy link
Member

Well, the real problem is that we can't satisfy both cases automatically: If you want to create a decorator that wraps a collection, or you want a collection of decorated objects.

I am not sure of the best way to resolve this. I agree that the method name is too long, but I'm not sure what to do about it.

@jasoncodes
Copy link

The magic required to detect collections is far too fallible. This alone is enough reason for me to prefer a distinct collection method.

@cheald
Copy link
Contributor Author

cheald commented Dec 7, 2012

I agree that we need a separate method or means of invocation.

decorate_all or decorate_many perhaps. Or we could extend Array with decorate_with, so you could [@foo].decorate_with(FooDecorator), but that feels a little upside down to me.

@steveklabnik
Copy link
Member

decorate(thing, :as => :collection)? Dunno.

@semmons99
Copy link

decorate(thing, :as => :collection)

I like the above or decorate_each(collection)

@haines
Copy link
Contributor

haines commented Dec 7, 2012

In defence of my bike shed :)

  • decorate thing, as: :collection is more typing, not less, and goes from having two methods with distinct responsibilities to one with an if-statement.
  • decorate_each sounds to me like it should return an enumerator, not an enumerable... decorate_each(things) do |decorated_thing| ...
  • extending Array wouldn't work, it has to work for other types of collection too (AR lazy-loaded associations aren't arrays, for example).
  • decorate_all... actually I don't mind this one! I still slightly prefer decorate_collection is because it returns a CollectionDecorator that wraps the argument, whereas decorate_all sounds to me like it maps the argument and returns an array of decorators, which is a subtle difference.

It is a relatively long method name, for sure. But it's explicit and intention-revealing and not that long.

@cheald
Copy link
Contributor Author

cheald commented Dec 7, 2012

There's an easy fix to make everyone happy. Just jam this in an initializer:

class << Draper::Decorator; alias dc decorate_collection; end

BAM

:D

@steveklabnik
Copy link
Member

Ha!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants