Skip to content

About

The exposed-method contract every hub_kernel interface gem builds on: what a hub exposes, who may call it, and calling it in an account's scope.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

hub_kernel-interface

The exposed-method contract every hub_kernel interface gem builds on: what a hub exposes, who may call it, and calling it in an account's scope. An interface gem, such as a JSON API or an MCP server, depends on this gem alone rather than on all of hub_kernel.

Usage

A hub lists the methods outside callers may reach by name, the values each takes, and whether it writes:

module Supplies
  extend HubKernel::Exposes

  exposes :price_of, takes: %i[item], writes: false

  def self.price_of(item:) = "#{item} costs 3"
end

The host sets the permission check, asked with the person, hub:method and the account, and the account scope, wrapped around every call:

HubKernel::Authz.check = ->(person, action, account) { true }
HubKernel::Context.scope = ->(account, &call) { call.call }

An interface gem lists the methods a person may call on an account, and calls one by name:

Supplies.exposures_for(person: person, account: account)
Supplies.call_exposed("price_of", values: { item: "soap" }, person: person, account: account)

A call is refused with HubKernel::UnexposedMethodError for a name the hub does not expose, HubKernel::NotAllowed when the permission check refuses, and HubKernel::MissingArgumentError for a missing person, account or required value. A hub refuses with HubKernel::Refused and a reason a person can read. A call made while the permission check or the account scope is unset raises HubKernel::UnwiredPortError.

Every interface serves every hub that exposes a method, each at its module name in snake case, unless the host names the hubs to serve, once. A hub listed alone is served at its module name in snake case, and a hub listed as a one-pair hash is served only at the name given. Setting the list to nil serves every exposing hub again. An interface gem finds the hub served at a name, or nil when none is:

HubKernel::Interface.hubs = [ Supplies, { "money" => Billing::Ledger } ]

HubKernel::Interface.find("supplies") # => Supplies
HubKernel::Interface.find("money")    # => Billing::Ledger
HubKernel::Interface.served           # => { "supplies" => Supplies, "money" => Billing::Ledger }

The permission check is still asked about a hub served under a chosen name by its own name, such as ledger:record_spend.

The host names the methods that give the calling person and account once, here, for an interface gem to read:

HubKernel::Interface.person_method = :hub_person
HubKernel::Interface.account_method = :hub_account

An interface gem's boot check runs HubKernel::Interface.check!, which raises HubKernel::Interface::UnservableHubError naming every problem in the served list at once: an entry that exposes no methods, two entries that answer at the same name, and each problem in a served hub's exposed list.

An interface gem checks a call's values before calling, and describes a record the call named that does not exist, so every interface gives the same reasons:

HubKernel::Interface::CallReasons.refuse_unlisted_values(Supplies, "price_of", values: values, person: person, account: account)
HubKernel::Interface::CallReasons.missing_record(error) # => "No consumable has the id 7"

refuse_unlisted_values does nothing for a call that sends only listed values, and asks the permission check nothing. For a call that sends any other value, it asks the permission check once about the called method, raises HubKernel::NotAllowed when it refuses, and otherwise raises HubKernel::Refused naming the method and every value it is not listed with.

Supplies.allows?("price_of", person: person, account: account) asks the permission check about that one method. missing_record reads the error's model and id.

Supplies.exposure_problems names each exposed method the hub has no method for, or lists with values it does not take. HubKernel::Interface::ExposingHubs.list holds every hub that declares an exposed method.

Installation

gem "hub_kernel-interface"

hub_kernel releases before 0.19.0 define this contract themselves. Loading hub_kernel-interface beside one raises HubKernel::Interface::OlderHubKernelError, which names the hub_kernel release to upgrade to. An app with no hub_kernel, or with hub_kernel 0.19.0 or later, loads it as usual.

License

The gem is available as open source under the terms of the MIT License.

About

The exposed-method contract every hub_kernel interface gem builds on: what a hub exposes, who may call it, and calling it in an account's scope.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages