Class: X::Resource
- Inherits:
-
Object
- Object
- X::Resource
- Extended by:
- AbstractClass, Attributes
- Includes:
- Identity, Marshalling, PublishedCount, Serialization, X::Resources::Identity, X::Resources::Marshalling, X::Resources::Serialization
- Defined in:
- x-resources/lib/x/resources/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
-
#==(other) ⇒ Boolean
(also: #eql?)
included
from X::Resources::Identity
Compare resources by class and identifier.
-
#as_json ⇒ Hash{String => Object}
included
from X::Resources::Serialization
The attributes, as a JSON encoder and ActiveSupport read them.
-
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}
included
from X::Resources::Identity
Deconstruct the resource into its attributes, so it matches a hash pattern.
-
#encode_with(coder) ⇒ void
included
from X::Resources::Marshalling
Write the state Marshal writes as YAML, which leaves out the client.
-
#hash ⇒ Integer
included
from X::Resources::Identity
Hash resources by class and identifier.
-
#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.
-
#init_with(coder) ⇒ void
included
from X::Resources::Marshalling
Restore a resource YAML read, as Marshal restores one.
-
#initialize(attrs, client: nil, hydrated: false) ⇒ Resource
constructor
Initialize a new immutable resource.
-
#inspect ⇒ String
Summarize the resource for the console.
-
#marshal_dump ⇒ Array
included
from X::Resources::Marshalling
The state Marshal writes, which leaves out the client.
-
#marshal_load(state) ⇒ void
included
from X::Resources::Marshalling
Restore a resource Marshal read, which has no client and so makes no request.
-
#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.
-
#to_h {|key, value| ... } ⇒ Hash
included
from X::Resources::Serialization
The attributes, or with a block the Hash of the pairs it returns for them.
-
#to_json(state = nil) ⇒ String
included
from X::Resources::Serialization
The attributes as JSON.
Constructor Details
#initialize(attrs, client: nil, hydrated: false) ⇒ Resource
Initialize a new immutable resource
330 |
# File 'x-resources/lib/x/resources/resource.rb', line 330 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
48 49 50 |
# File 'x-resources/lib/x/resources/resource.rb', line 48 def attrs @attrs end |
#client ⇒ Object? (readonly)
The client used to fetch this resource and its references
55 56 57 |
# File 'x-resources/lib/x/resources/resource.rb', line 55 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?.
163 |
# File 'x-resources/lib/x/resources/resource.rb', line 163 def default_params = {} |
.from_id(id, client: nil) ⇒ Resource
Build a resource from an identifier, or from a resource, without a request
117 |
# File 'x-resources/lib/x/resources/resource.rb', line 117 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.
228 229 230 231 232 |
# File 'x-resources/lib/x/resources/resource.rb', line 228 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
#==(other) ⇒ Boolean Also known as: eql? Originally defined in module X::Resources::Identity
Compare resources by class and identifier
#as_json ⇒ Hash{String => Object} Originally defined in module X::Resources::Serialization
The attributes, as a JSON encoder and ActiveSupport read them
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object} Originally defined in module X::Resources::Identity
Deconstruct the resource into its attributes, so it matches a hash pattern
Every attribute the resource declares is read as its own method reads it, so a pattern sees the identifier as a number, a timestamp as a Time, and a metric by the name it is read by. A pattern can ask for an attribute by another name it is read by, such as retweet_count for repost_count, and a pattern that asks for every attribute, with a double splat, gets each once, by the name the resource declares.
#encode_with(coder) ⇒ void Originally defined in module X::Resources::Marshalling
This method returns an undefined value.
Write the state Marshal writes as YAML, which leaves out the client
YAML reads no marshal_dump, and would write every instance variable, the client and its credentials among them, so a resource says how it is written: each part of the state Marshal writes, under its name.
#hash ⇒ Integer Originally defined in module X::Resources::Identity
Hash resources by class and identifier
#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.
402 403 404 |
# File 'x-resources/lib/x/resources/resource.rb', line 402 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, or a reference a response included that expands nothing, such as media, a poll, a place, or a topic, when the request asked for every default field of it. A stub, any other 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.
360 361 362 |
# File 'x-resources/lib/x/resources/resource.rb', line 360 def hydrated? @hydrated end |
#id ⇒ Integer, String
The identifier
338 339 340 |
# File 'x-resources/lib/x/resources/resource.rb', line 338 def id Attributes::CONVERTERS.fetch(self.class.__send__(:id_type)).call(attrs.fetch(self.class.__send__(:id_key))) end |
#init_with(coder) ⇒ void Originally defined in module X::Resources::Marshalling
This method returns an undefined value.
Restore a resource YAML read, as Marshal restores one
It has no client, and so makes no request.
#inspect ⇒ String
Summarize the resource for the console
427 |
# File 'x-resources/lib/x/resources/resource.rb', line 427 def inspect = "#<#{self.class} #{attrs.map { |key, value| "#{key}=#{value.inspect}" }.join(" ")}>" |
#marshal_dump ⇒ Array Originally defined in module X::Resources::Marshalling
The state Marshal writes, which leaves out the client
The client is left out, since it holds credentials and a connection. What is written is plain data, led by the number of its format, so that a resource written by one release of 1.x is read by a later one: its attributes, whether it is hydrated, and, of the response it came from, the included objects it refers to, and those they refer to in turn, the problems about any of them, and the query, so that the references it resolves, and the problems it and they report, are what they were, while the rest of the response is left out, however many other resources it held.
#marshal_load(state) ⇒ void Originally defined in module X::Resources::Marshalling
This method returns an undefined value.
Restore a resource Marshal read, which has no client and so makes no request
What Marshal reads is a client-less resource that answers its readers, resolves the references its response included, and reports its problems, as the resource that was written did, and raises from a hydrate, refresh, or collection that would make a request. It is hydrated if it was, and the query of its request asks for every field this release requests, so that one written before a minor release added to the fields is not.
#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.
376 |
# File 'x-resources/lib/x/resources/resource.rb', line 376 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.
417 418 419 |
# File 'x-resources/lib/x/resources/resource.rb', line 417 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.
386 |
# File 'x-resources/lib/x/resources/resource.rb', line 386 def stub? = attrs.keys.eql?([self.class.__send__(:id_key)]) |
#to_h {|key, value| ... } ⇒ Hash Originally defined in module X::Resources::Serialization
The attributes, or with a block the Hash of the pairs it returns for them
With a block it is the to_h of the attributes, as a Hash builds it, rather than the attributes, the block ignored.
#to_json(state = nil) ⇒ String Originally defined in module X::Resources::Serialization
The attributes as JSON