restfully
An attempt at dynamically providing “clever” wrappers on top of RESTful APIs that follow the principle of Hyperlinks As The Engine Of Application State (HATEOAS). It does not require to use specific (and often complex) server-side libraries, but a few constraints and conventions must be followed:
-
Return sensible HTTP status codes;
-
Make use of GET, POST, PUT, DELETE HTTP methods;
-
Return a Location HTTP header in 201 or 202 responses;
-
Return a
links
property in all responses to a GET request, that contains a list of link objects:{ "property": "value", "links": [ {"rel": "self", "href": "uri/to/resource", "type": "application/vnd.whatever+json;level=1,application/json"}, {"rel": "parent", "href": "uri/to/parent/resource", "type": "application/json"} {"rel": "collection", "href": "uri/to/collection", "title": "my_collection", "type": "application/json"}, {"rel": "member", "href": "uri/to/member", "title": "member_title", "type": "application/json"} ] }
-
Adding a
parent
link automatically creates a#parent
method on the current resource. -
Adding a
collection
link automatically creates a#my_collection
method that will fetch the Collection when called. -
Adding a
member
link automatically creates a#member_title
method that will fetch the Resource when called.
-
-
Advertise allowed HTTP methods in the response to GET requests by returning a
Allow
HTTP header containing a comma-separated list of the HTTP methods that can be used on the resource. This will allow the automatic generation of methods to interact with the resource. e.g.: advertising aPOST
method (Allow: GET, POST
) will result in the creation of asubmit
method on the resource.
Installation
$ gem install restfully --source http://gemcutter.org
Usage
Command line
$ export RUBYOPT="-rubygems"
$ restfully base_uri [-u username] [-p password]
e.g., for the Grid5000 API:
$ restfully https://api.grid5000.fr/sid/grid5000 -u username -p password
If the connection was successful, you should get a prompt. You may enter
irb(main):001:0> pp root
to get back a pretty-printed output of the root resource:
#<Restfully::Resource:0x91f08c
@uri=#<URI::HTTP:0x123e30c URL:http://api.local/sid/grid5000>
LINKS
@environments=#<Restfully::Collection:0x917666>,
@sites=#<Restfully::Collection:0x9170d0>,
@version=#<Restfully::Resource:0x91852a>,
@versions=#<Restfully::Collection:0x917e68>
PROPERTIES
"uid"=>"grid5000",
"type"=>"grid",
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8">
You can see the LINKS and PROPERTIES headers that respectively indicate what links you can follow from there (by calling root.link_name
) and what properties are available (by calling root[property_name]
).
Let’s say you want to access the collection of sites
, you would enter:
irb(main):002:0> pp root.sites
and get back:
#<Restfully::Collection:0x9170d0
@uri=#<URI::HTTP:0x122e128 URL:http://api.local/sid/grid5000/sites>
LINKS
@version=#<Restfully::Resource:0x8f553e>,
@versions=#<Restfully::Collection:0x8f52be>
PROPERTIES
"total"=>9,
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
"offset"=>0
ITEMS (0..9)/9
#<Restfully::Resource:0x9058bc uid="bordeaux">,
#<Restfully::Resource:0x903d0a uid="grenoble">,
#<Restfully::Resource:0x901cc6 uid="lille">,
#<Restfully::Resource:0x8fff0c uid="lyon">,
#<Restfully::Resource:0x8fe288 uid="nancy">,
#<Restfully::Resource:0x8fc4a6 uid="orsay">,
#<Restfully::Resource:0x8fa782 uid="rennes">,
#<Restfully::Resource:0x8f8bb2 uid="sophia">,
#<Restfully::Resource:0x8f6c9a uid="toulouse">>
A Restfully::Collection is a special kind of Resource, which includes the Enumerable module, which means you can call all of its methods on the Restfully::Collection object. For example:
irb(main):003:0> pp root.sites.find{|s| s['uid'] == 'rennes'}
#<Restfully::Resource:0x8fa782
@uri=#<URI::HTTP:0x11f4e64 URL:http://api.local/sid/grid5000/sites/rennes>
LINKS
@environments=#<Restfully::Collection:0x8f9ab2>,
@parent=#<Restfully::Resource:0x8f981e>,
@deployments=#<Restfully::Collection:0x8f935a>,
@clusters=#<Restfully::Collection:0x8f9d46>,
@version=#<Restfully::Resource:0x8fa354>,
@versions=#<Restfully::Collection:0x8fa0b6>,
@status=#<Restfully::Collection:0x8f95ee>
PROPERTIES
"name"=>"Rennes",
"latitude"=>48.1,
"location"=>"Rennes, France",
"security_contact"=>"[email protected]",
"uid"=>"rennes",
"type"=>"site",
"user_support_contact"=>"[email protected]",
"version"=>"4fe96b25d2cbfee16abe5a4fb999c82dbafc2ee8",
"description"=>"",
"longitude"=>-1.6667,
"compilation_server"=>false,
"email_contact"=>"[email protected]",
"web"=>"http://www.irisa.fr",
"sys_admin_contact"=>"[email protected]">
or:
irb(main):006:0> root.sites.map{|s| s['uid']}.grep(/re/)
=> ["grenoble", "rennes"]
A shortcut is available to find a specific entry in a collection, by entering the searched uid as a Symbol:
irb(main):007:0> root.sites[:rennes]
# will find the item whose uid is "rennes"
For ease of use and better security, you may prefer to use a configuration file to avoid re-entering the options every time you use the client:
$ echo '
base_uri: https://api.grid5000.fr/sid/grid5000
username: MYLOGIN
password: MYPASSWORD
' > ~/.restfully/api.grid5000.fr.yml && chmod 600 ~/.restfully/api.grid5000.fr.yml
And then:
$ restfully -c ~/.restfully/api.grid5000.fr.yml
As a library
See the examples
directory for examples.
Discovering the API capabilities
A Restfully::Resource (and by extension its child Restfully::Collection) has the following methods available for introspection:
-
links
will return a hash whose keys are the name of the methods that can be called to navigate between resources; -
http_methods
will return an array containing the list of the HTTP methods that are allowed on the resource;
Note on Patches/Pull Requests
-
Fork the project.
-
Make your feature addition or bug fix.
-
Add tests for it. This is important so I don’t break it in a future version unintentionally.
-
Commit, do not mess with rakefile, version, or history (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull).
-
Send me a pull request. Bonus points for topic branches.
Testing
-
rake spec, or
-
run autotest in the project directory.
Copyright
Copyright © 2009 Cyril Rohr, INRIA Rennes - Bretagne Atlantique. See LICENSE for details.