Class: X::Resource
- Inherits:
-
Object
- Object
- X::Resource
- Extended by:
- AbstractClass, Attributes
- Includes:
- Identity, Marshalling, PublishedCount, Serialization
- Defined in:
- x-objects/lib/x/objects/resource.rb
Overview
Base class for immutable API resources with identity, references, and hydration
A reader of an object the API nests in a resource, or of a list of them, such as the entities, urls, public_metrics, edit_controls, attachments, and withheld of a post, the variants of media, the options of a poll, or the subscription and affiliation of a user, returns it as the API sends it: a frozen Hash keyed by String, or an Array of them. Each returns that throughout 1.x, and raises InvalidAttribute for a response that holds anything else in its place. A reader that returns an object, as the matching_rules of a post and the topics of a space do, is only ever added under a new name, never in place of one of these.
Direct Known Subclasses
BookmarkFolder, Community, DirectMessage, List, Media, Place, Poll, Post, Space, Topic, User
Instance Attribute Summary collapse
-
#attrs ⇒ Hash{String => Object}
readonly
The frozen attributes returned by the API.
-
#client ⇒ Object?
readonly
The client used to fetch this resource and its references.
Class Method Summary collapse
-
.default_params ⇒ Hash{String => Array<String>}
The default query parameters requesting every field and expansion.
-
.from_id(id, client: nil) ⇒ Resource
Build a resource from an identifier, or from a resource, without a request.
-
.from_response(body, client:, hydrated: false) ⇒ Resource, ...
Build the resource or resources a response holds.
Instance Method Summary collapse
-
#hydrate ⇒ Resource?
Fetch the full resource, memoizing the result.
-
#hydrated? ⇒ Boolean
Check whether the resource holds every field the object layer requests.
-
#id ⇒ Integer, String
The identifier.
-
#initialize(attrs, client: nil, hydrated: false) ⇒ Resource
constructor
Initialize a new immutable resource.
-
#inspect ⇒ String
Summarize the resource for the console.
-
#problems ⇒ Array<Problem>
The problems the API reported about this resource in the response it came from.
-
#refresh ⇒ Resource?
Fetch the full resource again, replacing the memoized result.
-
#stub? ⇒ Boolean
Check whether the resource holds nothing but its identifier.
Constructor Details
#initialize(attrs, client: nil, hydrated: false) ⇒ Resource
Initialize a new immutable resource
323 |
# File 'x-objects/lib/x/objects/resource.rb', line 323 def initialize(attrs, client: nil, hydrated: false) = setup(Utils.attributes!(attrs), client:, hydrated:) |
Instance Attribute Details
#attrs ⇒ Hash{String => Object} (readonly)
The frozen attributes returned by the API
41 42 43 |
# File 'x-objects/lib/x/objects/resource.rb', line 41 def attrs @attrs end |
#client ⇒ Object? (readonly)
The client used to fetch this resource and its references
48 49 50 |
# File 'x-objects/lib/x/objects/resource.rb', line 48 def client @client end |
Class Method Details
.default_params ⇒ Hash{String => Array<String>}
The default query parameters requesting every field and expansion
They are built from the FIELDS and EXPANSIONS of the classes they name, which a minor release may add to; see #hydrated?.
156 |
# File 'x-objects/lib/x/objects/resource.rb', line 156 def default_params = {} |
.from_id(id, client: nil) ⇒ Resource
Build a resource from an identifier, or from a resource, without a request
110 |
# File 'x-objects/lib/x/objects/resource.rb', line 110 def from_id(id, client: nil) = from_id_in_batch(id, client:) |
.from_response(body, client:, hydrated: false) ⇒ Resource, ...
Build the resource or resources a response holds
A response whose data is an object builds one resource, and one whose data is an array builds a Page of one for each element, which holds the meta of the response, such as its next_token, and the problems it reported. A list the API finds empty holds no data, only a meta, which may still name the token of a page after it, so a response with a meta and no data builds an empty Page. A lookup of several resources by their identifiers, such as users?ids=, that finds none of them holds no data and no meta, only a problem for each, which names the ids, media_keys, or usernames parameter, so it builds an empty Page of those problems, as a lookup that finds some of them builds a Page of those it found. A client calls this when a resource class is the object_class of a request. A later version of x-core may pass it keywords of its own, which are ignored, as X::Client asks of what it calls from_response on.
A response holds only the fields its request asked for, so what this builds is not hydrated unless told otherwise, and hydrate fetches the full resource.
221 222 223 224 225 |
# File 'x-objects/lib/x/objects/resource.rb', line 221 def from_response(body, client:, hydrated: false, **) return collection_from_response(body, client:, hydrated:) if Page.__send__(:list?, body.to_h) resource_from_response(body, client:, hydrated:) end |
Instance Method Details
#hydrate ⇒ Resource?
Fetch the full resource, memoizing the result
Each resource that is not hydrated costs a request of its own, so hydrate many resources, such as the authors of the posts of a page, with the hydrate_all of their class, which looks them up a hundred at a time, rather than call hydrate on each.
393 394 395 |
# File 'x-objects/lib/x/objects/resource.rb', line 393 def hydrate @memo.fetch { hydrated? ? self : fetch } end |
#hydrated? ⇒ Boolean
Check whether the resource holds every field the object layer requests
A resource is hydrated when it was the subject of a response to a request that asked for every default field and expansion, whether or not it asked for more. A stub, a reference a response included, and a resource looked up with parameters that leave out some of those defaults are not, so hydrate fetches the full resource.
The defaults are the FIELDS and EXPANSIONS of each class, which a minor release may add to as the API adds fields and expansions, so that a lookup with the defaults asks for them too. A resource looked up with a list of its own, even one that named every field of the release it was written for, then leaves out what was added, so it is no longer hydrated, and hydrate costs a lookup of the full resource that the same code did not pay before. To ask for more than the defaults, add to what default_params gives rather than list every value.
351 352 353 |
# File 'x-objects/lib/x/objects/resource.rb', line 351 def hydrated? @hydrated end |
#id ⇒ Integer, String
The identifier
331 332 333 |
# File 'x-objects/lib/x/objects/resource.rb', line 331 def id Attributes::CONVERTERS.fetch(self.class.__send__(:id_type)).call(attrs.fetch(self.class.__send__(:id_key))) end |
#inspect ⇒ String
Summarize the resource for the console
418 |
# File 'x-objects/lib/x/objects/resource.rb', line 418 def inspect = "#<#{self.class} #{attrs.map { |key, value| "#{key}=#{value.inspect}" }.join(" ")}>" |
#problems ⇒ Array<Problem>
The problems the API reported about this resource in the response it came from
A problem is about the resource when the identifier it names, as its resource_id or its value, is the identifier of the resource or of one the resource refers to directly, such as the author of a post, or the pinned post of a user, so each post of a page reports that its own author no longer exists, and none reports it of another. A problem that names no identifier could be about any resource of the response, so every one of them reports it. The page of a cursor reports every problem of its response, as a finder yields them.
367 |
# File 'x-objects/lib/x/objects/resource.rb', line 367 def problems = includes.problems_about([id, *self.class.__send__(:referenced_ids, attrs)]) |
#refresh ⇒ Resource?
Fetch the full resource again, replacing the memoized result
A stub that hydrates together with the others of its page looks itself up on its own, rather than read what the lookup of the page found.
408 409 410 |
# File 'x-objects/lib/x/objects/resource.rb', line 408 def refresh @memo.store(look_up) end |
#stub? ⇒ Boolean
Check whether the resource holds nothing but its identifier
A reference the response did not expand is a stub, and so is a resource built with from_id.
377 |
# File 'x-objects/lib/x/objects/resource.rb', line 377 def stub? = attrs.keys.eql?([self.class.__send__(:id_key)]) |