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

Changed partial rendering to allow collections which don't implement #to_ary. #25912

Conversation

stevenharman
Copy link
Contributor

An optimization was introduced in 27f4ffd which will call #to_ary on the collection to prevent unnecessary queries for ActiveRecord scopes/relations. If the given collection did not respond to #to_ary, an empty collection was returned. That meant you couldn't use collections built from Enumerator nor Enumerable.

With this change, #collection_from_options will attempt the optimization, but fall back to passing along the given collection, as-is. This is an alternative to #22005 which will raise an exception if you attempt to use something like an Enumerator or Enumerable instance.

@rails-bot
Copy link

r? @schneems

(@rails-bot has picked a reviewer for you, use r? to override)

y.yield(Customer.new("david"))
y.yield(Customer.new("mary"))
end
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", collection: customers)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ruby 1.9 hash syntax here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! That's a hard one to break. I'll push a new commit momentarily.

@stevenharman stevenharman force-pushed the fix_render_partial_collection_to_allow_custom_collection branch from db1e227 to a0e0cf9 Compare July 21, 2016 19:46
stevenharman referenced this pull request Jul 25, 2016
This transforms for instance scoped objects into arrays and avoid unneeded queries

[#5958 state:committed]
@rafaelfranca
Copy link
Member

Action view tests seems to be broken.

@stevenharman stevenharman force-pushed the fix_render_partial_collection_to_allow_custom_collection branch from a0e0cf9 to ebd8c69 Compare July 25, 2016 21:03
An optimization was introduced in
rails@27f4ffd
which tried to `#to_ary` the collection to prevent unnecessary queries
for ActiveRecord scopes/relations. If the given collection did not
respond to `#to_ary`, and empty collection was returned. That meant you
couldn't use collections built from `Enumerator` nor `Enumerable`.

With this change, `#collection_from_options` will attempt the
optimization, but fall back to passing along the given collection,
as-is.
This will ensure we attempt to render an empty collection, meaning we
don't actually render anything at all. Allowing `nil` or a falsey value
through results in calling `render_partial` rather than
`render_collection`, which isn't what we want.
@stevenharman stevenharman force-pushed the fix_render_partial_collection_to_allow_custom_collection branch from ebd8c69 to e4a4936 Compare July 26, 2016 14:39
@stevenharman
Copy link
Contributor Author

I've fixed the broken ActiveView test. I'm happy to squash this all into a single commit if y'all are happy with it.

@spastorino
Copy link
Contributor

Let's also change to_ary to to_a since we are explicitly converting it to an Array.

@spastorino
Copy link
Contributor

And there's no need to check respond_to, this should be enough ...

collection = @options[:collection]
collection ? collection.to_a : []

@stevenharman
Copy link
Contributor Author

stevenharman commented Jul 26, 2016

I wonder, will that have the desired consequence? More things implement to_a than to_ary. Enumerator, for example doesn't implement to_ary. Nor does Enumerator::Lazy, and the to_a would force evaluating the entire thing, no? Maybe that's OK because presumably you'd not be passing some sort of infinite collection here.

My understanding of the original optimization was to cause an ActiveRecord scope to evaluate and then prevent extra DB calls. In that case, the to_ary is probably well-suited because Array-like things should implement it. And less Array-like things (Enumerators, for example) won't.

@matthewd
Copy link
Member

Yeah, the original intention was basically to pre-buffer the each, for the benefit of AR scopes. to_a will still do that, and will also work for anything else enumerable, which is what we want.

Otherwise we're likely to run into an issue with our need for size anyway.

As you note, we know it's not infinite, because we know we're about to call each, and collect the results until it terminates of its own accord. So this feels like the best balance of API to impose, IMO.

We can safely assume we're not dealing with an infinite collection as
we're about to call `each` on it and collect the results until it
terminates on its own. Given that, `to_a` is implemented by the normal
Array-like objects, and less Array-like objects like `Enumerator` and
`Enumerator::Lazy`.
@stevenharman stevenharman force-pushed the fix_render_partial_collection_to_allow_custom_collection branch from 3ee41ed to 87899cf Compare July 26, 2016 15:33
@spastorino spastorino merged commit 49315e2 into rails:master Jul 26, 2016
@stevenharman
Copy link
Contributor Author

Many thanks for the feedback and help, @spastorino and @rafaelfranca. If I might ask one more thing... would you be open to me back porting this to 5-0-stable and 4-2-stable? It is a bug fix, albeit is minor one.

spastorino added a commit that referenced this pull request Jul 26, 2016
…tion_to_allow_custom_collection

Changed partial rendering to allow collections which don't implement `#to_ary`.
@spastorino
Copy link
Contributor

I've backported to 5-0-stable, I don't think it worth backporting to 4-2-stable.

@stevenharman
Copy link
Contributor Author

I was mostly interested in 4-2-stable b/c we're still running 4.2.x, and that's where I discovered this issue. 😄

glebm added a commit to thredded/thredded that referenced this pull request Dec 26, 2016
The Rails PR that introduced the backwards-incompatible change that
prompted this:
rails/rails#25912
rails/rails@87899cf

The Rails changelog entry is under 5.0.1.rc1:
https://github.com/rails/rails/blob/v5.0.1/actionview/CHANGELOG.md#rails-501rc1-december-01-2016

See also #508
@glebm
Copy link
Contributor

glebm commented Dec 26, 2016

This breaks backwards compatibility for collections that implement to_ary but not to_a.

@matthewd
Copy link
Member

@glebm Yeah, I think I'm okay with that. I'm not sure to_ary-without-to_a satisfies the expected contract of to_a/to_ary... and our use of to_ary wasn't documented.

Either one of those alone might be insufficient to justify a change on a stable branch, but combined, I don't think it's worth trying both methods.

Happy to revisit if this turns out to be a wider problem, though.

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

Successfully merging this pull request may close these issues.

None yet

8 participants