Module: X::Resources::BatchFinders

Includes:
Finders
Included in:
Media, Post, UserFinders, Space
Defined in:
x-resources/lib/x/resources/batch_finders.rb

Overview

Class methods that look resources up many at a time, extended into each resource class the API can look up that way

A resource the API looks up only one at a time, such as a list, a community, or a direct message event, extends Finders alone, and so answers neither find_all nor hydrate_all, rather than answer them only to raise.

Internal to x-resources: the methods it gives a resource class, such as X::Post.find_all, are public API, but the module is only how they are shared, and which classes extend or include it can change within 1.x.

Instance Method Summary collapse

Instance Method Details

#find(id, client:, **params) {|problem| ... } ⇒ Resource? Originally defined in module Finders

Look up a resource by identifier

Examples:

Look up a post by identifier

X::Post.find(1234567890, client: client)

Parameters:

  • id (String, Integer, Resource) —

    the identifier, or a resource of this class

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field or expansion parameter to leave some out builds resources that are not hydrated, so hydrate fetches the rest

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Resource, nil) —

    the resource or nil if it was not found, whether the API answers 200 with no data or a 404 that reports the resource as not found

Raises:

  • (ArgumentError) —

    if the identifier is not one, or is a resource of another class, before a request

  • (X::NotFound) —

    if the API answers any other 404, such as one from a client pointed at the wrong host or API version

#find!(id, client:, **params) ⇒ Resource Originally defined in module Finders

Look up a resource by identifier, which must exist

The error it raises names the identifier looked up, whether the identifier or a resource was given. Its cause is the X::NotFound of a lookup the API answered with a 404 that reports the resource as not found.

Examples:

Look up a post by identifier

X::Post.find!(1234567890, client: client)

Parameters:

  • id (String, Integer, Resource) —

    the identifier, or a resource of this class

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field or expansion parameter to leave some out builds resources that are not hydrated, so hydrate fetches the rest

Returns:

Raises:

  • (ArgumentError) —

    if the identifier is not one, or is a resource of another class, before a request

  • (MissingResource) —

    if the resource was not found, whether the API answers 200 with no data or a 404 that reports the resource as not found

  • (X::NotFound) —

    if the API answers any other 404, such as one from a client pointed at the wrong host or API version

#find_all(ids, client:, concurrency: DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<Resource>

Look up many resources by identifier, in parallel batches

The resources come back in the order of the identifiers they were asked for by, whatever order the batches were answered in, one for each identifier that was found, so an identifier asked for twice comes back twice, as hydrate_all keeps a resource it is given twice. Each identifier is asked for once, however often it is given.

Examples:

Look up many posts by identifier, reporting the ones that were not found

X::Post.find_all([1234567890, 1234567891], client: client) { |problem| warn problem.detail }

Look up many posts one batch at a time, to spend a rate limit more slowly

X::Post.find_all(ids, client: client, concurrency: 1)

Parameters:

  • ids (Array<String, Integer, Resource>) —

    the identifiers, or resources of this class

  • client (Object) —

    the client used to make the requests

  • concurrency (Integer) (defaults to: DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, four by default, which must be at least one; each is a request of up to 100 identifiers, so a lower number spends a rate limit more slowly

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field or expansion parameter to leave some out builds resources that are not hydrated, so hydrate fetches the rest

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as an identifier that was not found

Returns:

  • (Array<Resource>) —

    the resources that were found, one for each identifier that names one, frozen

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

  • (ArgumentError) —

    if an identifier is not one, or is a resource of another class, before a request



97
98
99
100
# File 'x-resources/lib/x/resources/batch_finders.rb', line 97

def find_all(ids, client:, concurrency: DEFAULT_CONCURRENCY, **params, &)
  ids = ids.map { |id| Utils.id_of(id, self) }
  in_order_of(lookup_in_batches(endpoint!, batch_key, ids, client:, concurrency:, **params, &), ids)
end

#hydrate_all(resources, client:, concurrency: DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<Resource>

Replace the resources that are not hydrated with the full resources

A resource that hydrate would look up is looked up, which is a stub and also a resource a response included without every field, so what comes back is hydrated throughout. A resource that was not found is dropped, as is nil, which a reference to no resource reads as, such as the author of a post whose response named none, and a hydrated resource is kept as it is, so resources that are all hydrated need no lookup. What was found is stored in each original, so hydrating one of them afterwards costs no request, unless params override a default field or expansion parameter: what such a lookup found is not the full resource, so it is returned without being stored, and hydrating an original fetches the full resource. A resource that holds what hydrate returns, because hydrate or an earlier hydrate_all stored it, is replaced with that, and looked up again by no request, since the API bills each resource a lookup returns.

Examples:

Look up every field of the authors of posts, the ones a search included among them

X::User.hydrate_all(posts.map(&:author), client: client)

Expand only the authors a search did not include, which the API bills for alone

X::User.hydrate_all(posts.filter_map(&:author).select(&:stub?), client: client)

Parameters:

  • resources (Array<Resource, nil>) —

    the resources, some of which may not be hydrated, and some nil

  • client (Object) —

    the client used to make the requests

  • concurrency (Integer) (defaults to: DEFAULT_CONCURRENCY) —

    the number of batch lookups made at once, four by default, which must be at least one

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field or expansion parameter to leave some out builds resources that are not hydrated, so hydrate fetches the rest

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Array<Resource>) —

    the resources, in order, with the ones that were not hydrated replaced, frozen

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

  • (ArgumentError) —

    if a resource is not of this class, which a lookup of its identifier would find another resource for, before a request



67
68
69
70
71
72
73
# File 'x-resources/lib/x/resources/batch_finders.rb', line 67

def hydrate_all(resources, client:, concurrency: DEFAULT_CONCURRENCY, **params, &)
  resources = resources.compact
  validate_class!(resources)
  partial = resources.reject { |resource| settled?(resource) }
  replace = replacer(find_all(partial, client:, concurrency:, **params, &), params)
  resources.filter_map { |resource| settled?(resource) ? resource.hydrate : replace.call(resource) }.freeze
end