The OpenD Programming Language

serveRestObject

Serves a REST object, similar to a Ruby on Rails resource.

You put data members in your class. cgi.d will automatically make something out of those.

It will call your constructor with the ID from the URL. This may be null. It will then populate the data members from the request. It will then call a method, if present, telling what happened. You don't need to write these! It finally returns a reply.

Your methods are passed a list of fields it actually set.

The URL mapping - despite my general skepticism of the wisdom - matches up with what most REST APIs I have used seem to follow. (I REALLY want to put trailing slashes on it though. Works better with relative linking. But meh.)

GET /items -> index. all values not set. GET /items/id -> get. only ID will be set, other params ignored. POST /items -> create. values set as given PUT /items/id -> replace. values set as given or POST /items/id with cgi.post["_method"] (thus urlencoded or multipart content-type) set to "PUT" to work around browser/html limitation a GET with cgi.get["_method"] (in the url) set to "PUT" will render a form. PATCH /items/id -> update. values set as given, list of changed fields passed or POST /items/id with cgi.post["_method"] == "PATCH" DELETE /items/id -> destroy. only ID guaranteed to be set or POST /items/id with cgi.post["_method"] == "DELETE"

Following the stupid convention, there will never be a trailing slash here, and if it is there, it will redirect you away from it.

API clients should set the Accept HTTP header to application/json or the cgi.get["_format"] = "json" var.

I will also let you change the default, if you must.

// One add-on is validation. You can issue a HTTP GET to a resource with _method = VALIDATE to check potential changes.

You can define sub-resources on your object inside the object. These sub-resources are also REST objects that follow the same thing. They may be individual resources or collections themselves.

Your class is expected to have at least the following methods:

FIXME: i kinda wanna add a routes object to the initialize call

create Create returns the new address on success, some code on failure. show index update remove

You will want to be able to customize the HTTP, HTML, and JSON returns but generally shouldn't have to - the defaults should usually work. The returned JSON will include a field "href" on all returned objects along with "id". Or omething like that.

Usage of this function will add a dependency on arsd.dom and arsd.jsvar.

NOT IMPLEMENTED

More...
version(with_breaking_cgi_features)
serveRestObject
(
T
)
(
string urlPrefix
)

Detailed Description

Really, a collection is a resource with a bunch of subresources.

GET /items index because it is GET on the top resource

GET /items/foo item but different than items?

class Items {

}

... but meh, a collection can be automated. not worth making it a separate thing, let's look at a real example. Users has many items and a virtual one, /users/current.

the individual users have properties and two sub-resources: session, which is just one, and comments, a collection.

class User : RestObject!() { // no parent int id; string name;

// the default implementations of the urlId ones is to call load(that_id) then call the arg-less one. // but you can override them to do it differently.

// any member which is of type RestObject can be linked automatically via href btw.

void show() {} void show(string urlId) {} // automated! GET of this specific thing void create() {} // POST on a parent collection - this is called from a collection class after the members are updated void replace(string urlId) {} // this is the PUT; really, it just updates all fields. void update(string urlId, string[] fieldList) {} // PATCH, it updates some fields. void remove(string urlId) {} // DELETE

void load(string urlId) {} // the default implementation of show() populates the id, then

this() {}

mixin Subresource!Session; mixin Subresource!Comment; }

class Session : RestObject!() { // the parent object may not be fully constructed/loaded this(User parent) {}

}

class Comment : CollectionOf!Comment { this(User parent) {} }

class Users : CollectionOf!User { // but you don't strictly need ANYTHING on a collection; it will just... collect. Implement the subobjects. void index() {} // GET on this specific thing; just like show really, just different name for the different semantics. User create() {} // You MAY implement this, but the default is to create a new object, populate it from args, and then call create() on the child }

Meta