Directive Handler
handler
defines a block of run-time code that should be executed in
place of a default handler when processing a HTTP request.
The default handlers are the core of Toast. They implement the action triggered by API calls, which work on ActiveRecord objects and their associations. There are 13 handlers defined, which are documented here.
All of them can be overridden by the handler
directive as
necessary.
handler
may appear under following directives:
The Handler Matrix
Follow the links in the matrix to see how the handler is implemented. Empty cells don’t have a handler, because not all combinations make sense.
GET | PATCH | POST | DELETE | LINK | UNLINK | |
---|---|---|---|---|---|---|
single | -> | |||||
collection | -> | -> | ||||
singular association | -> | -> | -> | |||
plural association | -> | -> | -> | -> | ||
canonical | -> | -> | -> |
Handlers
GET Single
Fetch a single record using a models class method.
Default Handler:
handler do |uri_params|
{MODEL}.{NAME}
end
, where
{MODEL}
is the exposed model-class and{NAME}
is the name of a class method returning an instance of the model.uri_params
[Hash] all parameters of the request URI
Example:
Model:
class Person < ApplicationRecord
def self.richest
order("net_value DESC").first
end
end
Toast Configuration:
expose Person {
readables :name, :net_value
single :richest {
via_get {
allow do |*args|
true
end
## implicit Handler:
# handler do |uri_params|
# Person.richest
# end
}
}
}
Request:
GET /people/richest
-> { :name => "Uncle Scrooge",
:net_value => 100000000000000000000,
:self => "https://example.fernwerk.net/people/394" }
End Example
GET Collection
Fetch an Array of records using a models class method (scope)
Default Handler
handler do |uri_params|
{MODEL}.{NAME}
end
, where
{MODEL}
is the exposed model-class and{NAME}
is the name of the collectionuri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
def self.first_letter_a
where("name LIKE 'a%'")
end
end
Toast Configuration:
expose Person {
readables :name
collection :first_letter_a {
via_get {
allow do |*args|
true
end
## implicit Handler:
# handler do |uri_params|
# Person.first_letter_a
# end
}
}
}
Request:
GET /people/first_letter_a
-> [{ :name => "Abraham", :self => "https://example.fernwerk.net/people/1937" },
{ :name => "Alfons", :self => "https://example.fernwerk.net/people/19378" },
... ]
End Example
GET Singular Association
Fetch a single resource/model-instance from a model association
Default Handler
handler do |source, uri_params|
source.{NAME}
end
, where
source
[ActiveRecord::Base] is a model-instance that corresponds to the request URIuri_params
[Hash] all parameters of the request URI{NAME}
is the name of the association with type:has_one
or:belongs_to
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
## implicit Handler:
# handler do |source, uri_params|
# source.group
# end
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
}
}
Request:
GET /people/1937/group
-> { :name => "Clients",
:self => "https://example.fernwerk.net/groups/23",
:members => "https://example.fernwerk.net/groups/23/members"}
End Example
GET Plural Association
Fetch an array of resources/model-instances from a model associatiion.
Default Handler
handler do |source, uri_params|
source.{NAME}
end
, where
source
[ActiveRecord::Base] is a model-instance that corresponds to the request URIuri_params
[Hash] all parameters of the request URI{NAME}
is the name of the association with type:has_many
or:has_and_belongs_to
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
## Implicit handler
# handler do |source, uri_params|
# source.members
# end
}
}
}
Request:
GET /groups/23/members
-> [
{ :name => "Alfons",
:self => "https://example.fernwerk.net/people/2947",
:group => "https://example.fernwerk.net/people/2947/group" },
{ :name => "Bert",
:self => "https://example.fernwerk.net/people/293",
:group => "https://example.fernwerk.net/people/293/group" }
]
End Example
GET Canonical URI
Fetch a resource/model-instance by it’s canonical URI, which is a unique ID for it.
Default Handler
handler do |model_instance, uri_params|
model_instance
end
, where
model_instance
is a ActiveRecord::Base object or descendanturi_params
[Hash] all parameters of the request URI
Example
Model:
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Group) {
readables :name
association(:members) {
via_get {
allow do |*args|
true
end
## implicit handler
# handler do |model_instance, uri_params|
# model_instance
# end
}
}
Request:
GET /groups/23
-> { :name => "Clients",
:self => "https://example.fernwerk.net/groups/23",
:members => "https://example.fernwerk.net/groups/23/members"}
End Example
PATCH Canonical URI
Update a resource/model-instance by it’s canonical URI.
Default Handler
handler do |model_instance, payload, uri_params|
model_instance.update payload
end
, where
model_instance
is a ActiveRecord::Base object or descendantpayload
[Hash], the decoded request body (from JSON), all non-writable attributes were cleared before.uri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
has_and_belongs_to_many :groups
def group_names
groups.map(&:name).join(' ')
end
end
Toast Configuration:
expose(Person) {
writables :first_namue, :last_name
readables :group_names
via_get {
allow do |*args|
true
end
}
via_patch {
allow do |*args|
true
end
## implicit handler
# handler do |model_instance, payload, uri_params|
# model_instance.update payload
# end
}
}
Request:
GET /people/5419
-> { :firs_name => "John",
:last_name => "Foe",
:self => "https://example.fernwerk.net/people/5419",
:group_names => "Friends ToastCommitters"}
PATCH /people/5419, {"first_name":"John", "last_name":"Doe"}
-> { :firs_name => "John",
:last_name => "Doe",
:self => "https://example.fernwerk.net/people/5419",
:group_names => "Friends ToastCommitters"}
End Example
POST (to) Collection
Create a new resource/model-instance of the collections type by the collections URI.
Note, that POST is only configurable for the all
collection
(URIs: /{MODEL}
or /{MODEL}/all
) and not for other collections/scopes of
the model class.
Default Handler
handler do |payload, uri_params|
{MODEL}.create payload
end
, where
{MODEL}
is a the model-classpayload
is a Hash, the decoded request body (JSON), all non-writable attributes were cleared before.uri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
def self.first_letter_a
where("name LIKE 'a%'")
end
end
Toast Configuration:
expose(Person) {
writables :first_name, :last_name
collection(:all) {
via_post { # allowed in collection(:all) only
allow do |*args|
true
end
## implicit handler
# handler do |payload, uri_params|
# Person.create payload
# end
}
}
}
Request:
POST /people, {"first_name": "Henry", "last_name": "Conrad"}
-> { :firs_name => "Henry",
:last_name => "Conrad",
:self => "https://example.fernwerk.net/people/875",
:group_names => ""}
End Example
POST (to) Plural Association
Create a new resource/model-instance via a association. The record is created and linked to the association to which the request is sent.
Default Handler
handler do |source, payload, uri_params|
source.{NAME}.create payload
end
, where
payload
[Hash] is the data for the new model-instanceuri_params
[Hash] all parameters of the request URI{NAME}
[String] is the name of an asssociation of type:has_many
or:has_and_belongs_to_many
.
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
via_post {
allow do |*args|
true
end
## Implicit handler
# handler do |source, payload, uri_params|
# source.members.create payload
# end
}
}
Request:
GET /groups/23/members
-> [
{ "name": "Alfons",
"self": "https://example.fernwerk.net/people/2947",
"group": "https://example.fernwerk.net/people/2947/group" },
{ "name": "Bert",
"self": "https://example.fernwerk.net/people/293",
"group": "https://example.fernwerk.net/people/293/group" }
]
POST /groups/23/members, {"name":"Conrad"}
-> { "name" : "Conrad",
"self" : "https://example.fernwerk.net/people/20348"
"group" : https://example.fernwerk.net/people/20348/group" }
GET /groups/23/members
-> [
{ "name": "Alfons",
"self": "https://example.fernwerk.net/people/2947",
"group": "https://example.fernwerk.net/people/2947/group" },
{ "name": "Bert",
"self": "https://example.fernwerk.net/people/293",
"group": "https://example.fernwerk.net/people/293/group" },
{ "name" : "Conrad",
"self" : "https://example.fernwerk.net/people/20348"
"group" : https://example.fernwerk.net/people/20348/group" }
]
End Example
DELETE Canonical URI
Delete a resource/model-instance by it’s canonical URI.
Default Handler
handler do |model_instance, uri_params|
model_instance.destroy
end
, where
model_instance
is a ActiveRecord::Base object or descendanturi_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
end
Toast Configuration:
expose(Person) {
writables :first_namue, :last_name
via_delete {
allow do |*args|
true
end
## implicit handler
# handler do |model_instance, uri_params|
# model_instance.destroy
# end
}
}
Request:
DELETE /people/5419
-> "200 OK"
End Example
LINK Singular Association
Associate resources/model-instances via a singular model associations or update such associations.
Default Handler
handler do |source, target, uri_params|
source.{NAME} = target
source.save
end
, where
{NAME}
is the name of the association (type: has_one, belongs_to)source
is base model-instance owning the associationtarget
is model-instance which corresponds to URI found in the link headeruri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
via_link {
allow do |*args|
true
end
## implicit handler
# handler do |source, target, uri_params|
# source.group = target
# source.save
# end
}
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
}
}
Request:
GET /people/1937/group
-> "404 Not Found"
LINK /people/1937/group, Link: <https://example.fernwerk.net/groups/23>,rel='related'
-> 200 OK
GET /people/1937/group
-> { "name": "Clients",
"self": "https://example.fernwerk.net/groups/23",
"members": "https://example.fernwerk.net/groups/23/members"}
End Example
LINK Plural Association
Associate resources/model-instances via plural model associations or update such associations.
Default Handler
handler do |source, target, uri_params|
source.{NAME} << target
end
, where
{NAME}
is the name of the association (type: has_many, has_and_belongs_to_many)source
is base model-instance owning the associationtarget
is model-instance which corresponds to URI found in the link headeruri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
via_link {
allow do |*args|
true
end
## implicit handler
# handler do |source, target, uri_params|
# source.members << target
# end
}
}
}
Request:
GET /group/781/members
-> [{"name": "Carlo", "self" : "https://example.fernwerk.net/people/14"}]
LINK /group/781/members, Link: <https://example.fernwerk.net/people/20>,rel='related'
-> 200 OK
GET /group/781/members
-> [{"name": "Carlo", "self": "https://example.fernwerk.net/people/14"},
{"name": "Anna", "self": "https://example.fernwerk.net/people/20"}]
End Example
UNLINK Singular Association
Remove links between resources/model-instances via singular model associations.
Note that, the target to be unlinked must be passed with the Link header. If it’s not the currently linked one the request has no effect.
Default Handler
handler do |source, target, uri_params|
if source.{NAME} == target
source.{NAME} = nil
source.save
end
end
, where
{NAME}
is the name of the association (type: has_one, belongs_to)source
is base model-instance owning the associationtarget
is model-instance (to be unlinked) which corresponds to URI found in the link headeruri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"
end
Toast Configuration:
expose(Person){
readables :name
association :group {
via_get {
allow do |*args|
true
end
via_unlink {
allow do |*args|
true
end
## implicit handler
# handler do |source, target, uri_params|
# if source.group == target
# source.group = nil
# source.save
# end
# end
}
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
}
}
Request:
GET /people/1937/group
-> { "name": "Clients",
"self": "https://example.fernwerk.net/groups/23",
"members": "https://example.fernwerk.net/groups/23/members"}
UNLINK /people/1937/group, Link: <https://example.fernwerk.net/groups/23>,rel='related'
-> 200 OK
GET /people/1937/group
-> "404 Not Found"
End Example
UNLINK Plural Association
Remove links between resources/model-instances via plural model associations.
Default Handler
handler do |source, target, uri_params|
source.{NAME}.delete(target)
end
, where
{NAME}
is the name of the association (type: has_many, has_and_belongs_to_many)source
is base model-instance owning the associationtarget
is model-instance which corresponds to URI found in the link headeruri_params
[Hash] all parameters of the request URI
Example
Model:
class Person < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
has_many :members, class_name: "Person"3
end
Toast Configuration:
expose(Person) {
readables :name
association :group {
via_get {
allow do |*args|
true
end
}
}
}
expose(Group) {
readables :name
association :members {
via_get {
allow do |*args|
true
end
}
via_unlink {
allow do |*args|
true
end
## implicit handler
# handler do |source, target, uri_params|
# source.members.delete(target)
# end
}
}
}
Request:
GET /group/781/members
-> [{"name": "Carlo", "self": "https://example.fernwerk.net/people/14"},
{"name": "Anna", "self": "https://example.fernwerk.net/people/20"}]
UNLINK /group/781/members, Link: <https://example.fernwerk.net/people/14>,rel='related'
-> 200 OK
GET /group/781/members
-> [{"name": "Anna", "self" : "https://example.fernwerk.net/people/20"}]
End Example