Class: X::PostUsage

Inherits:
Object
  • Object
show all
Includes:
Serialization, ValueEquality, ValueMarshalling, Resources::Serialization, Resources::ValueEquality, Resources::ValueMarshalling
Defined in:
x-resources/lib/x/resources/post_usage.rb

Overview

How many posts the app's project has read, as the usage endpoint reports it

The API caps the posts a project reads each month, and bills each post read, so the usage shows both what a project has spent and how much of its cap remains.

Constant Summary collapse

FIELDS =

Every field of the usage

%w[cap_reset_day daily_client_app_usage daily_project_usage project_cap project_id project_usage].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs) ⇒ PostUsage

Initialize the usage from the attributes the API reported

Examples:

Build a usage

X::PostUsage.new({"project_usage" => "1234"})

Parameters:

  • attrs (Hash{String, Symbol => Object}) —

    the attributes

Raises:

  • (ArgumentError) —

    if the attributes are not a Hash



91
92
93
94
# File 'x-resources/lib/x/resources/post_usage.rb', line 91

def initialize(attrs)
  @attrs = Utils.deep_freeze(Utils.attributes!(attrs))
  freeze
end

Instance Attribute Details

#attrs ⇒ Hash{String => Object} (readonly)

The raw attributes of the usage

Examples:

Get the raw attributes

usage.attrs # => {"project_usage" => "1234", "project_cap" => "3000000", ...}

Returns:

  • (Hash{String => Object}) —

    the attributes



42
43
44
# File 'x-resources/lib/x/resources/post_usage.rb', line 42

def attrs
  @attrs
end

Class Method Details

.current(client:, **params) {|problem| ... } ⇒ PostUsage?

Look up the current post usage of the project the client's app belongs to

A project has one usage, so it is looked up by no identifier, as X::User.current looks up the one user a client signs in as. The usage endpoint takes app-only authentication, so a client that signs its requests with OAuth 1.0a looks the usage up with a copy that authenticates as the app. A client signed in with OAuth 2.0 as a user that holds no credentials of the app requests as the user, which the endpoint refuses with X::Forbidden.

A response that holds no usage returns nil, as X::User.current does for a users/me that holds no user, and passes the problems it reported to the block, if there is one.

Examples:

Look up the usage of the last 30 days

X::PostUsage.current(client: client, days: 30)&.project_usage

Log why the API returned no usage

X::PostUsage.current(client: client) { |problem| warn problem.detail }

Parameters:

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (PostUsage, nil) —

    the usage, or nil if the response holds none



63
64
65
66
67
# File 'x-resources/lib/x/resources/post_usage.rb', line 63

def self.current(client:, **params)
  body = Utils.app_client(client).get(Utils.path(ENDPOINT, {"usage.fields" => FIELDS}.merge(params)), **Utils::JSON_CLASSES)
  Problem.all_from(body).each { |problem| yield problem } if block_given?
  Hash.try_convert(body.to_h["data"])&.then { |data| new(data) }
end

.current!(client:, **params) ⇒ PostUsage

Look up the current post usage of the project, which must be returned

Examples:

Look up the usage of the last 30 days

X::PostUsage.current!(client: client, days: 30).project_usage

Parameters:

  • client (Object) —

    the client used to make the request

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Returns:

Raises:



78
79
80
81
# File 'x-resources/lib/x/resources/post_usage.rb', line 78

def self.current!(client:, **params)
  problems = [] #: Array[Problem]
  current(client:, **params) { |problem| problems << problem } || raise(MissingResource.new("#{ENDPOINT} returned no usage", problems:))
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql? Originally defined in module Resources::ValueEquality

Check whether another object holds the same attributes

Examples:

Check whether the trends of the world changed since they were last read, a count included

X::Trend.at(1, client: client) == trends

Parameters:

  • other (Object) —

    the other object

Returns:

  • (Boolean) —

    true if the other is of the same class and holds the same attributes

#as_json ⇒ Hash{String => Object} Originally defined in module Resources::Serialization

The attributes, as a JSON encoder and ActiveSupport read them

Examples:

Serialize a resource

user.as_json # => {"id" => "7505382", "username" => "sferik"}

Serialize a problem

problem.as_json # => {"title" => "Not Found Error"}

Returns:

  • (Hash{String => Object}) —

    the attributes

#cap_reset_day ⇒ Integer?

The day of the month the billing cycle, and so the usage, starts over

It is an Integer whether the response holds it as a number or as a String, as it holds the other counts.

Examples:

Get the reset day

usage.cap_reset_day # => 16

Returns:

  • (Integer, nil) —

    the day of the month

Raises:



129
# File 'x-resources/lib/x/resources/post_usage.rb', line 129

def cap_reset_day = integer("cap_reset_day", attrs["cap_reset_day"])

#daily ⇒ Hash{Time => Integer}

The number of posts the project read each day

Examples:

Get the posts read yesterday

usage.daily.values.last(2).first

Returns:

  • (Hash{Time => Integer}) —

    the number of posts, keyed by the start of each day

Raises:

  • (InvalidAttribute) —

    if the response holds a day without a date in ISO 8601, or a number that is not one, or holds the days as something other than a list of objects



139
# File 'x-resources/lib/x/resources/post_usage.rb', line 139

def daily = days("daily", Shape.dig("#{self.class}#daily", attrs, %w[daily_project_usage usage]))

#daily_by_app ⇒ Hash{Integer, nil => Hash{Time => Integer}}

The number of posts each of the project's apps read each day

Examples:

Total the posts each app read

usage.daily_by_app.transform_values { |days| days.values.sum }

Returns:

  • (Hash{Integer, nil => Hash{Time => Integer}}) —

    the daily usage, keyed by the identifier of each app

Raises:

  • (InvalidAttribute) —

    if the response holds an app identifier that is not a number, a day without a date in ISO 8601, or a number that is not one, or holds the apps or their days as something other than a list of objects



149
150
151
152
153
154
155
# File 'x-resources/lib/x/resources/post_usage.rb', line 149

def daily_by_app
  by_app = {} #: Hash[Integer?, Hash[Time, Integer]]
  Shape.objects("#{self.class}#daily_by_app", attrs["daily_client_app_usage"]).each do |app|
    by_app[integer("daily_by_app", app["client_app_id"])] = days("daily_by_app", app["usage"])
  end
  by_app.freeze
end

#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}

Deconstruct the usage into what its readers read, so it matches a hash pattern

Only the readers a pattern names are read, so one that names the counts of the project matches a usage whose days cannot be read.

Examples:

Warn when the project has read nine tenths of its cap

warn "near the cap" if usage in {project_usage: Integer => used, project_cap: Integer => cap} and used * 10 >= cap * 9

Parameters:

  • keys (Array<Symbol>, nil) —

    the keys the pattern asks for, or nil for every reader

Returns:

  • (Hash{Symbol => Object}) —

    what the readers read

Raises:

  • (InvalidAttribute) —

    if the pattern asks for what the response holds as something that cannot be read



168
# File 'x-resources/lib/x/resources/post_usage.rb', line 168

def deconstruct_keys(keys) = Utils.deconstruct(self, keys, %i[project_id project_usage project_cap cap_reset_day daily daily_by_app])

#encode_with(coder) ⇒ void Originally defined in module Resources::ValueMarshalling

This method returns an undefined value.

Write the state Marshal writes as YAML

YAML would write the instance variables of the value, and read them back into one that is not frozen, so it says how it is written: each part of the state Marshal writes, under its name.

Examples:

Write a value as YAML

YAML.dump(trend)

Parameters:

  • coder (Psych::Coder) —

    the coder YAML writes the value with

#hash ⇒ Integer Originally defined in module Resources::ValueEquality

The hash of the object, which equal objects share

Examples:

Collect the distinct rules the posts of a stream matched

posts.flat_map(&:matching_rules).uniq

Returns:

  • (Integer) —

    the hash

#init_with(coder) ⇒ void Originally defined in module Resources::ValueMarshalling

This method returns an undefined value.

Restore a value YAML read, frozen, as Marshal restores one

Examples:

Read a value written as YAML

YAML.unsafe_load(YAML.dump(trend)).name

Parameters:

  • coder (Psych::Coder) —

    the coder YAML read the value with

Raises:

#marshal_dump ⇒ Array(Integer, Hash{String => Object}) Originally defined in module Resources::ValueMarshalling

The state Marshal writes

What is written is plain data, led by the number of its format, so that a value written by one release of 1.x is read by a later one: its attributes, as the API sent them.

Examples:

Cache the trends of a place

Rails.cache.write("trends", X::Trend.at(1, client: client))

Returns:

  • (Array(Integer, Hash{String => Object})) —

    the number of the format, then the attributes

#marshal_load(state) ⇒ void Originally defined in module Resources::ValueMarshalling

This method returns an undefined value.

Restore a value Marshal read, frozen as the value that was written was

Examples:

Read cached trends

Marshal.load(Marshal.dump(trend)).name

Parameters:

  • state (Array) —

    the state Marshal wrote

Raises:

#project_cap ⇒ Integer?

The number of posts the project may read in a billing cycle

Examples:

Get the cap

usage.project_cap # => 3000000

Returns:

  • (Integer, nil) —

    the cap



118
# File 'x-resources/lib/x/resources/post_usage.rb', line 118

def project_cap = integer("project_cap", attrs["project_cap"])

#project_id ⇒ Integer?

The identifier of the project

Examples:

Get the project identifier

usage.project_id # => 1234567890

Returns:

  • (Integer, nil) —

    the identifier



102
# File 'x-resources/lib/x/resources/post_usage.rb', line 102

def project_id = integer("project_id", attrs["project_id"])

#project_usage ⇒ Integer?

The number of posts the project has read in the current billing cycle

Examples:

Get the posts read this cycle

usage.project_usage # => 1234

Returns:

  • (Integer, nil) —

    the number of posts



110
# File 'x-resources/lib/x/resources/post_usage.rb', line 110

def project_usage = integer("project_usage", attrs["project_usage"])

#to_h {|key, value| ... } ⇒ Hash Originally defined in module 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.

Examples:

Convert a resource to a hash

user.to_h # => {"id" => "7505382", "username" => "sferik"}

Key the attributes by Symbol

user.to_h { |key, value| [key.to_sym, value] } # => {id: "7505382", username: "sferik"}

Delete the rule a post matched

streaming_client.delete_rules(post.matching_rules.first.to_h)

Yield Parameters:

  • key (String) —

    the name of each attribute

  • value (Object) —

    its value

Yield Returns:

  • (Array(Object, Object)) —

    the key and value of the attribute in the Hash

Returns:

  • (Hash) —

    the attributes, frozen, or the pairs the block returns

#to_json(state = nil) ⇒ String Originally defined in module Resources::Serialization

The attributes as JSON

Examples:

Serialize a resource

user.to_json # => "{\"id\":\"7505382\",\"username\":\"sferik\"}"

Cache a resource as JSON

Rails.cache.write("user", user.to_json)

Parameters:

  • state (JSON::State, nil) (defaults to: nil) —

    the state a JSON encoder passes, which the attributes are given

Returns:

  • (String) —

    the attributes as a JSON object