vibe.d beta banner
get vibe.d
0.10.1

Asynchronous I/O that doesn’t get in your way, written in D

Struct Collection

Models REST collection interfaces using natural D syntax.

struct Collection(I)
  
if (is(I == interface));

Use this type as the return value of a REST interface getter method/property to model a collection of objects. opIndex is used to make the individual entries accessible using the [index] syntax. Nested collections are supported.

The interface I needs to define a struct named CollectionIndices. The members of this struct denote the types and names of the indexes that lead to a particular resource. If a collection is nested within another collection, the order of these members must match the nesting order (outermost first).

The parameter list of all of I's methods must begin with all but the last entry in CollectionIndices. Methods that also match the last entry will be considered methods of a collection item (collection[index].method()), wheres all other methods will be considered methods of the collection itself (collection.method()).

The name of the index parameters affects the default path of a method's route. Normal parameter names will be subject to the same rules as usual routes (see registerRestInterface) and will be mapped to query or form parameters at the protocol level. Names starting with an underscore will instead be mapped to path placeholders. For example, void getName(int __item_id) will be mapped to a GET request to the path ":item_id/name".

Constructors

NameDescription
this (api, pids) Constructs a new collection instance that is tied to a particular parent collection entry.

Fields

NameTypeDescription
m_interface I
m_parentIDs Collection.ParentIDs

Methods

NameDescription
opIndex (id) Accesses a single collection entry.

Inner structs

NameDescription
Item

Aliases

NameDescription
AllIDNames
AllIDs
Interface
ItemID
ParentIDNames
ParentIDs

Example

Model two nested collections using path based indexes

//
// API definition
//
interface SubItemAPI {
	// Define the index path that leads to a sub item
	struct CollectionIndices {
		// The ID of the base item. This must match the definition in
		// ItemAPI.CollectionIndices
		string _item;
		// The index if the sub item
		int _index;
	}

	// GET /items/:item/subItems/length
	@property int length(string _item);

	// GET /items/:item/subItems/:index/squared_position
	int getSquaredPosition(string _item, int _index);
}

interface ItemAPI {
	// Define the index that identifies an item
	struct CollectionIndices {
		string _item;
	}

	// base path /items/:item/subItems
	Collection!SubItemAPI subItems(string _item);

	// GET /items/:item/name
	@property string name(string _item);
}

interface API {
	// a collection of items at the base path /items/
	Collection!ItemAPI items();
}

//
// Local API implementation
//
class SubItemAPIImpl : SubItemAPI {
	@property int length(string _item) { return 10; }

	int getSquaredPosition(string _item, int _index) { return _index ^^ 2; }
}

class ItemAPIImpl : ItemAPI {
	private SubItemAPIImpl m_subItems;

	this() { m_subItems = new SubItemAPIImpl; }

	Collection!SubItemAPI subItems(string _item) { return Collection!SubItemAPI(m_subItems, _item); }

	string name(string _item) { return _item; }
}

class APIImpl : API {
	private ItemAPIImpl m_items;

	this() { m_items = new ItemAPIImpl; }

	Collection!ItemAPI items() { return Collection!ItemAPI(m_items); }
}

//
// Resulting API usage
//
API api = new APIImpl; // A RestInterfaceClient!API would work just as well

// GET /items/foo/name
assert(api.items["foo"].name == "foo");
// GET /items/foo/sub_items/length
assert(api.items["foo"].subItems.length == 10);
// GET /items/foo/sub_items/2/squared_position
assert(api.items["foo"].subItems[2].getSquaredPosition() == 4);

Example

Model two nested collections using normal query parameters as indexes

//
// API definition
//
interface SubItemAPI {
	// Define the index path that leads to a sub item
	struct CollectionIndices {
		// The ID of the base item. This must match the definition in
		// ItemAPI.CollectionIndices
		string item;
		// The index if the sub item
		int index;
	}

	// GET /items/subItems/length?item=...
	@property int length(string item);

	// GET /items/subItems/squared_position?item=...&index=...
	int getSquaredPosition(string item, int index);
}

interface ItemAPI {
	// Define the index that identifies an item
	struct CollectionIndices {
		string item;
	}

	// base path /items/subItems?item=...
	Collection!SubItemAPI subItems(string item);

	// GET /items/name?item=...
	@property string name(string item);
}

interface API {
	// a collection of items at the base path /items/
	Collection!ItemAPI items();
}

//
// Local API implementation
//
class SubItemAPIImpl : SubItemAPI {
	@property int length(string item) { return 10; }

	int getSquaredPosition(string item, int index) { return index ^^ 2; }
}

class ItemAPIImpl : ItemAPI {
	private SubItemAPIImpl m_subItems;

	this() { m_subItems = new SubItemAPIImpl; }

	Collection!SubItemAPI subItems(string item) { return Collection!SubItemAPI(m_subItems, item); }

	string name(string item) { return item; }
}

class APIImpl : API {
	private ItemAPIImpl m_items;

	this() { m_items = new ItemAPIImpl; }

	Collection!ItemAPI items() { return Collection!ItemAPI(m_items); }
}

//
// Resulting API usage
//
API api = new APIImpl; // A RestInterfaceClient!API would work just as well

// GET /items/name?item=foo
assert(api.items["foo"].name == "foo");
// GET /items/subitems/length?item=foo
assert(api.items["foo"].subItems.length == 10);
// GET /items/subitems/squared_position?item=foo&index=2
assert(api.items["foo"].subItems[2].getSquaredPosition() == 4);
Authors

Sönke Ludwig, Михаил Страшун, Mathias 'Geod24' Lang

Copyright

© 2012-2018 Sönke Ludwig

License

Subject to the terms of the MIT license, as written in the included LICENSE.txt file.