Skip to content

Conversation

@Edouard-chin
Copy link
Member

@Edouard-chin Edouard-chin commented Nov 6, 2025

Add a way to temporarily opt-out to #all now lazily making requests:

  • Problem

    In 9372d5a we made a change that defer the http request submission until the collection is used.

    # Before
    
    Product.all #=> HTTP request is made
    
    # After
    
    products = Product.all #=> No HTTP request is made
    products.each { ... } #=> HTTP request is made here

    This create issues for codepaths that used to expect the Product.all to make the http request and this feels like a breaking change that should go through a deprecation cycle (too late now but let's have a way to opt-out to the new behaviour).

    Context

    We have a safety net in our application that does something like this:

    products = allow_explicit_http_connections do
      Product.all
    end
    
    products.each { ... }

    This is now broken because we make the request outside of the explictly allowed resiliency block.

    Solution

    Introduce a way to opt out to the new behaviour and trigger a deprecation warning.

cc/ @seanpdoyle

seanpdoyle added a commit to seanpdoyle/activeresource that referenced this pull request Nov 6, 2025
Closes rails#460

Introduce both the `config.active_resource.lazy_collections`
configuration, as well as the `Base.lazy_collections` class attribute.

Restore the original behavior of immediately retrieving collections by
defaulting the initial value to `false`, but deprecate the behavior and
encourage migrating to setting the value to `true`.
seanpdoyle added a commit to seanpdoyle/activeresource that referenced this pull request Nov 6, 2025
Closes rails#460

Introduce both the `config.active_resource.lazy_collections`
configuration, as well as the `Base.lazy_collections` class attribute.

Restore the original behavior of immediately retrieving collections by
defaulting the initial value to `false`, but deprecate the behavior and
encourage migrating to setting the value to `true`.
seanpdoyle added a commit to seanpdoyle/activeresource that referenced this pull request Nov 6, 2025
Closes rails#460

Introduce both the `config.active_resource.lazy_collections`
configuration, as well as the `Base.lazy_collections` class attribute.

Restore the original behavior of immediately retrieving collections by
defaulting the initial value to `false`, but deprecate the behavior and
encourage migrating to setting the value to `true`.
@seanpdoyle
Copy link
Contributor

seanpdoyle commented Nov 6, 2025

Should this commit mention the behavior and configuration in the README or somewhere else? I had drafted some changes as part of #461:


Collections are lazily loaded by default, deferring the HTTP request until its items are first accessed.:

# Build a lazy collection with initial query parameters
people = Person.where(first_name: "Tyler")

# Modify the lazy collection with additional query parameters
people = people.where(last_name: "Durden")

# Expects a response of
#
# [
#   {"id":1,"first_name":"Tyler","last_name":"Durden"},
# ]
#
# for GET http://api.people.com:3000/people.json?first_name=Tyler&last_name=Durden
people.first  # => <Person::xxx 'first_name' => 'Tyler' ...>

Set lazy_load_collections = false to eagerly load collections:

Person.lazy_load_collections = false

# Expects a response of
#
# [
#   {"id":1,"first_name":"Tyler","last_name":"Durden"},
# ]
#
# for GET http://api.people.com:3000/people.json?first_name=Tyler
people = Person.where(first_name: "Tyler")

# Access items from the eagerly-loaded collection
people.first  # => <Person::xxx 'first_name' => 'Tyler' ...>

Support for lazily loaded collections is deprecated, and will be removed in a future release.

@rafaelfranca
Copy link
Member

Another plan is to allow this to be configured, and deprecate setting the config to disable the lazy load. I'm ok with that too.

@Edouard-chin
Copy link
Member Author

Yeah I think deprecating the flag assignment may be less noisy (although we lose the ability to track down callers in production that may be not covered in tests). I'll make the change.

Also, FYI @seanpdoyle, I realized there is another change related to the collection_parser. I'm not sure whether this is so much of an issue, but it's a surprising change.

class BillingAPI < ActiveResource::Base
  self.collection_parser = CustomCollection
end

# Before
BillingAPI.all #=> Returns a `CustomCollection`

# After
# We have no way to return the collection_parser object itself. We can only call Enumerable methods on it.

seanpdoyle added a commit to seanpdoyle/activeresource that referenced this pull request Nov 7, 2025
Follow-up to rails#460

Expose the underlying `ActiveResource::Collection` instance for the
`WhereClause` class returned from `Base.all` and `Base.where`. To do so,
rename the previously private `resources` method to `collection`, and
move it to a public declaration.
@seanpdoyle
Copy link
Contributor

We have no way to return the collection_parser object itself. We can only call Enumerable methods on it.

I've opened #462 to resolve that issue. It's a bit of a band-aid, since ActiveResource::WhereClause is a :nodoc: class, but it provides the underlying instance more directly.

I think a better solution would be to change how ActiveResource::Collection instances are constructed, but that could involve more effort, since that class, attributes, and methods are documented parts of the public API.

- ### Problem

  In 9372d5a we made a change that
  defer the http request submission until the collection is used.

  ```ruby
  # Before

  Product.all #=> HTTP request is made

  # After

  products = Product.all #=> No HTTP request is made
  products.each { ... } #=> HTTP request is made here
  ```

  This create issues for codepaths that used to expect the
  `Product.all` to make the http request and this feels like
  a breaking change that should go through a deprecation cycle
  (too late now but let's have a way to opt-out to the new behaviour).

  ### Context

  We have a safety net in our application that does something like
  this:

  ```ruby
    products = allow_explicit_http_connections do
      Product.all
    end

    products.each { ... }
  ```

  This is now broken because we make the request outside of the
  explictly allowed resiliency block.

  ### Solution

  Introduce a way to opt out to the new behaviour and trigger a
  deprecation warning.
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

Successfully merging this pull request may close these issues.

3 participants