Dynamic, framework-neutral metadata describing path/URI structures natively in Python and through JSON, YAML and XML representations.
Its purpose is to define and support a metadata format capable of being generated dynamically from web applications, describing the URI structure of the application in its entirety or of specific resources and their related subresources. In a very lightweight manner - focussed only on resource identification - it aims to cover a spectrum ranging from application description languages (cf WSDL and WADL) through to more dynamic, hyperlinked interaction (cf REST and HATEOAS).
See roadmap for the original Ruby-based described_routes and path-to at positiveincline.com/?p=213.
-
EXAMPLES
-
Converting to and from JSON and YAML
-
Plain text representation
-
XML
-
Basic URI and path generation
-
Dynamic partial template expansion
-
-
DATA STRUCTURE
-
Attributes
-
Navigation
-
-
REQUIREMENTS
-
AUTHOR AND CONTACT INFORMATION
The ResourceTemplate and ResourceTemplates classes have constructors that can take native Python dicts and lists (of dicts or of ResourceTemplate objects), so that converting to and from JSON and YAML is very straightforward. For example (with output edited for brevity and formatting), JSON conversions look like this:
>>> import json >>> user = ResourceTemplate(json.loads(user_json)) >>> print json.dumps(user.to_dict()) { "name": "user", "uri_template": "http://example.com/users/{user_id}{-prefix|.|format}", "params": ["user_id"], "optional_params": ["format"], "options": ["GET", "PUT", "DELETE"], "resource_templates": [ { "name": "edit_user", "rel": "edit", "uri_template": "http://example.com/users/{user_id}/edit{-prefix|.|format}", "params": ["user_id"], "optional_params": ["format"], "options": ["GET"] }, { "name": "user_articles", "rel": "articles", "uri_template": "http://example.com/users/{user_id}/articles{-prefix|.|format}", "params": ["user_id"], "optional_params": ["format"], "options": ["GET", "POST"] } ] }
This (or something very much like it) should work with your favourite JSON module; YAML similarly.
ResourceTemplate and ResourceTemplates objects print as follows (using a more complete example):
>>> print users users users GET, POST http://example.com/users{-prefix|.|format} new_user new_user GET http://example.com/users/new{-prefix|.|format} {user_id} user GET, PUT, DELETE http://example.com/users/{user_id}{-prefix|.|format} edit edit_user GET http://example.com/users/{user_id}/edit{-prefix|.|format} articles user_articles GET, POST http://example.com/users/{user_id}/articles{-prefix|.|format} new_user_article new_user_article GET http://example.com/users/{user_id}/articles/new{-prefix|.|format} recent recent_user_articles GET http://example.com/users/{user_id}/articles/recent{-prefix|.|format} {article_id} user_article GET, PUT, DELETE http://example.com/users/{user_id}/articles/{article_id}{-prefix|.|format} edit edit_user_article GET http://example.com/users/{user_id}/articles/{article_id}/edit{-prefix|.|format} profile user_profile GET, PUT, DELETE, POST http://example.com/users/{user_id}/profile{-prefix|.|format} edit edit_user_profile GET http://example.com/users/{user_id}/profile/edit{-prefix|.|format} new new_user_profile GET http://example.com/users/{user_id}/profile/new{-prefix|.|format}
- not yet implemented in Python
-
This follows the natural structure but with the following modifications:
-
A
ResourceTemplate
element for each resource template -
A
ResourceTemplates
element for each list of resources (top level or subresources) -
Params
andOptionalParams
elements forparams
andoptional_params
, each containingparam
elements -
A single
options
element contains the applicable HTTP methods as a comma-separated list
URIs and paths can be generated for specific resources, given a dict of actual parameters:
>>> actual_params = {"user_id": "dojo", "format": "json"} >>> users.uri_for({"user_id": "dojo", "format": "json"}) "http://example.com/users/dojo.json" >>> users.path_for({"user_id": "dojo", "format": "json"}) "/users/dojo.json"
Where the resource template has a
path_template
but nouri_template
, abase
parameter may be supplied to theuri_for()
method.ResourceTemplate objects can be parameterised as shown below. The effect of this is to define a “mini application” around a concrete resource of set of resources, supporting a more dynamic, hyperlinked style.
>>> actual_params = {"user_id": "dojo", "format": "json"} >>> print user.partial_expand(actual_params) user user GET, PUT, DELETE http://example.com/users/dojo.json edit edit_user GET http://example.com/users/dojo/edit.json articles user_articles GET, POST http://example.com/users/dojo/articles.json new_user_article new_user_article GET http://example.com/users/dojo/articles/new.json recent recent_user_articles GET http://example.com/users/dojo/articles/recent.json {article_id} user_article GET, PUT, DELETE http://example.com/users/dojo/articles/{article_id}.json edit edit_user_article GET http://example.com/users/dojo/articles/{article_id}/edit.json profile user_profile GET, PUT, DELETE, POST http://example.com/users/dojo/profile.json edit edit_user_profile GET http://example.com/users/dojo/profile/edit.json new new_user_profile GET http://example.com/users/dojo/profile/new.json
A ResourceTemplate object has the following attributes:
name
-
An application-wide identifier
rel
-
An indication of a child resource’s relationship to its parent
path_template
-
A template for the resource’s path, in the style of URI Template but as a relative path
uri_template
-
A template for the resource’s URI (generated only if the root URI is known at generation time)
params
-
A list of parameters required by path_template
optional_params
-
A list of optional parameters that may be incorporated by the path_template
options
-
A list of HTTP methods supported by the resource
resource_templates
-
A list of ResourceTemplate objects, implemented by the ResourceTemplates class
All attributes are optional; empty or blank attributes are omitted in external representations.
By convention, members of collections identified by key attributes don’t have a
rel
attribute. In the examples above, theuser
template has children namededit_user
anduser_articles
withrel
attributes of “edit” and “articles” respectively, but theuser_article
child ofuser_articles
has none, as it is identified relative to its parent by anarticle_id
parameter.These data structures (trees or list of trees) can be traversed by iterating through the ResourceTemplate members of ResourceTemplates objects, which might be at the top level or the
resource_templates
attribute of a ResourceTemplate object.Two methods support navigation by
name
or byrel
:1) <code>ResourceTemplates.all_by_name()<code>: this returns a memoized dict of all ResourceTemplate objects in or below the ResourceTemplates collection
2)
ResourceTemplate.find_by_rel(rel)
: this returns a list of all ResourceTemplate objects that are direct descendants of the target ResourceTemplate and have arel
attribute equal to the one the supplied (which may take the valueNone
).Joe Gregorio’s URI Template parser (to be found at code.google.com/p/uri-templates/source/browse/#svn/trunk).
Your favourite JSON or YAML printer/parser.
Mike Burrows (asplake), email mjb@asplake.co.uk, website positiveincline.com (see articles tagged described_routes)
-