public
Description:
Homepage:
Clone URL: git://github.com/caelum/restfulie.git
name age message
file .gitignore Mon Nov 23 10:57:20 -0800 2009 Added .tmtags (an artifact of the Textmate CTag... [bguthrie]
file README.textile Thu Nov 26 10:03:23 -0800 2009 Merge branch 'bgut' into work [guilhermesilveira]
file Rakefile Tue Nov 24 18:34:33 -0800 2009 docs [guilhermesilveira]
file init.rb Wed Oct 28 23:04:28 -0700 2009 restfulie is included into AR in the lib. Case ... [dcrec1]
directory lib/ Thu Nov 26 09:41:52 -0800 2009 hash methods [guilhermesilveira]
directory spec/ Thu Nov 26 10:03:23 -0800 2009 Merge branch 'bgut' into work [guilhermesilveira]
README.textile

Quit pretending

CRUD through HTTP is a good step forward to using resources and becoming RESTful, another step further into it is to make use of hypermedia based services and this gem allows you to do it really fast.

You can read the article on using the web for real which gives an introduction to hypermedia/resources/services.

Why would I use restfulie?

1. Easy —> writing hypermedia aware resource based clients
2. Easy —> hypermedia aware resource based services
3. Small → it’s not a bloated solution with a huge list of APIs
4. HATEOAS —> clients you are unaware of will not bother if you change your URIs
5. HATEOAS —> services that you consume will not affect your software whenever they change part of their flow or URIs

Could you compare it with Spring or JAX-RS based APIs?

Restfulie is the first API which tries to somehow implement Jim Webber and Ian Robinson opinion on how RESTFul systems use hypermedia
as the way to lead your client’s path through a business process.

Therefore Restfulie is unique both in its feature set when compared to both Spring and JAX-RS based implementations, and its implementation: looking for simple code and favoring conventions over manual configurations.

Short examples

Restfulie: client-side

Example on accessing a resource and its services through the restfulie API:

order = Order.from_web resource_uri

puts "Order price is #{order.price}"

order.pay payment                        # sends a post request to pay this order

order.cancel                             # sends a delete request

Restfulie: server-side

This is a simple example how to make your state changes available to your resource consumers:

class Order < ActiveRecord::Base

  acts_as_restfulie

  def following_transitions
    transitions = []
    transitions << [:show, {}]
    transitions << [:destroy, {}] if can_cancel?
    transitions << [:pay, {:id => id}] if can_pay?
    transitions
  end
end

You might want to create a migration with a string field named status for your resource:

scripts/generate migration add_status_to_order	

Content:

class AddStatusToOrder < ActiveRecord::Migration
  def self.up
    add_column :orders, :status, :string
	Order.all.each do |order|
		order.status = "unpaid"
		order.save
	end
  end

  def self.down
    remove_column :orders, :status
  end
end

Or simply define a status reader and writer on your own.

Restfulie server-side: state machine

For those willing to implement a more complex or advanced state machine, you can use the dsl-like api:

class Order < ActiveRecord::Base
  acts_as_restfulie

  state :unpaid, :allow => [:latest, :pay, :cancel]
  state :cancelled, :allow => :latest

  transition :latest, {:action => :show}
  transition :cancel, {:action => :destroy}, :cancelled
  transition :pay, {}, :preparing
end

Installing

Just add in your environment.rb the following line:

config.gem "restfulie", :source => "http://gemcutter.org"

And then execute:

rake gems:install

or, if you prefer to install it as a plugin:

script/plugin install git://github.com/caelum/restfulie.git

Typical hypermedia aware example

Trying to follow the definition of a RESTful application supporting resources with hypermedia content, a resource would be:

<order>
	<product>basic rails course</product>
	<product>RESTful training</product>
	<atom:link rel="refresh" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
	<atom:link rel="update" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
	<atom:link rel="pay" href="http://www.caelum.com.br/orders/1/pay" xmlns:atom="http://www.w3.org/2005/Atom"/>
	<atom:link rel="destroy" href="http://www.caelum.com.br/orders/1" xmlns:atom="http://www.w3.org/2005/Atom"/>
</order>

Client Usage

Create your class and invoke the uses_restfulie method:

class Order < ActiveRecord::Base
	uses_restfulie
end

One should first acquire the representation from the server through your common GET request and process it through the usual from_* methods:

xml = Net::HTTP.get(URI.parse(‘http://www.caelum.com.br/orders/1’))
order = Order.from_xml(xml)

or use the restfulie from_web:
order = Order.from_web ‘http://www.caelum.com.br/orders/1’

And now you can invoke all those actions in order to change your resource’s state:

order.refresh
order.update
order.destroy
order.pay

Note that:

  • refresh is get
  • update is put (and you have to put everything back)
  • destroy is delete
  • pay (unknown methods) is post

Resource format support

Restfulie currently supports full xml+atom, partial xml+rel and will soon expand its support to json+links.

Help

If you are looking for or want to help, let us know at the mailing list:

http://groups.google.com/group/restfulie

Client-side configuration: how to customize your request

HTTP verbs

By default, restfulie uses the following table:

  • destroy, cancel and delete send a DELETE request
  • update sends a PUT request
  • refresh, reload, show, latest sends a GET request
  • other methods sends a POST request

If you want to use a custom http verb in order to send your request, you can do it by setting the optional string ‘method’:

order.update(:method=>"post")

Request parameters

If you want to send extra parameters, you can do it through the data parameter:

order.pay(:data => {:payment => my_payment})

The parameters will be serialized either to xml or json according to which format was used to deserialize the order at first place.

Executing another GET request

If your method executes another GET request, it will automatically deserialize its result as:

order = Order.from_web order_uri
payment = order.check_payment_info

If you want to parse the response yourself, instead of receiving just the final deserialized object, you can do it by passing a body to your method

order = Order.from_web order_uri
successful = order.check_payment_info do |response|
  return response.code==200
end

Server-side configuration

There are two different approaches that can be combined to create a full hypermedia aware resource based service, including awareness of its states and transitions.

Simple usage: following transitions

The most easy way to use restfulie is to write the following_transitions method.
There are three easy steps to make it work:

1. Create your model (i.e. Order) with an status field


script/generate scaffold Order status:string location:string
rake db:create
rake db:migrate

Note that with this usage the status field is optional (from 0.3.0 onwards).

2. Add the acts_as_restfulie invocation and following_transitions method returning an array of possible transitions:

acts_as_restfulie

def following_transitions
  transitions = []
  transitions << [:show, {}]
  transitions
end

3. Update your show method within the OrdersController to show the hypermedia content:

 def show
   @order = Order.find(params[:id])

   respond_to do |format|
     format.html # show.html.erb
     format.xml  { render :xml => @order.to_xml(:controller=>self) }
   end
 end

You are ready to go, create a new order and save it into the database:

	order = Order.new
	order.location = "take away"
	order.status = "unpaid"
	order.save
	puts "Order #{order.id} saved"

Start up the server:

	script/server

And now access your server at http://localhost:3000/orders/1.xml

<?xml version="1.0" encoding="UTF-8"?>
<order>
  <created-at>2009-11-23T00:15:15Z</created-at>
  <id>1</id>
  <location>take away</location>
  <status>unpaid</status>
  <updated-at>2009-11-23T00:15:15Z</updated-at>
  <atom:link rel="show" xmlns:atom="http://www.w3.org/2005/Atom" href="http://localhost:3000/orders/3"/>
</order>

Customizing the rel name

You can also override the action used, but still keep the rel

def following_transitions
  transitions = []
  transitions << [:cancel, { :action => :destroy }]
  transitions
end

Which will generate an hyperlink as

<atom:link rel="cancel" rel="http://yourserver/orders/15" />

Example

A full example showing all capabilities of this method follows:

def following_transitions
  transitions = []
  transitions << [:show, {}]
  transitions << [:destroy, {}] if can_cancel?
  transitions << [:pay, {:id => id}] if can_pay?
  transitions << [:show, {:controller => :payments, :payment_id => payment.id }] if paid?
  transitions
end

Advanced usage: Defining the state machine and its transitions

The second way of defining your available transitions is to explicitely define the states and transitions.

By using this approach, one has to define a new column named status in a database migration file.

The first step involves defining all your states, each one with its own name and possible transitions, as:

	state :state_name, :allow => [ :first_transition_name, :second_transition_name]

The following example shows all possible states for an order:

class Order < ActiveRecord::Base

	acts_as_restfulie
	
	state :unpaid, :allow => [:latest, :pay, :cancel]
	state :cancelled, :allow => :latest
	state :received, :allow => [:latest, :check_payment_info]
	state :preparing, :allow => [:latest, :check_payment_info]
	state :ready, :allow => [:latest, :receive, :check_payment_info]
end

Now its time to define which controller and action each transition invokes, in a much similar way to
the transition definitions in the following_transitions method:

class Order < ActiveRecord::Base
end

Once a transition has been given a name, its name can be used in the following_transitions method also.
The next example does not configure the transition because it was already defined, only adding it to the
list of available transition whenever the can_pay? method returns true:

class Order < ActiveRecord::Base

	acts_as_restfulie
	
	transition :pay, {:action => pay_this_order, :controller => :payments}, :preparing

	def following_transitions
	  transitions = []
	  transitions << :pay if can_pay?
	  transitions
	end
end

Note that whenever one defines a transition, there is a third – optional – argument, this is the
transition’s target’s state. Whenever the method order.pay method is invoked in the server, it will
automatically change the order’s status to preparing.

You can download the server side example to see the complete code.

The last usage of the transition definition involves passing a block which receives the element in which
the transition URI’s is required. The block should return all the necessary information for retrieving the URI, now having access to your element’s instance variables:

class Order < ActiveRecord::Base
	transition :check_payment_info do |order|
	   {:controller => :payments, :action => :show, :order_id => order.id, :payment_id => order.payments[0].id, :rel => "check_payment_info"}
	end
end

Accessing all possible transitions

One can access all possible transitions for an object by invoking its available_transitions method:

	transitions = order.available_transitions

Checking the possibility of following transitions

By following the advanced usage, one receives also all can_ method. i.e.:

	order.status = :unpaid
	puts(order.can_pay?)              # will print true
	order.status = :paid
	puts(order.can_pay?)              # will print false

You can use the can_xxx methods in your controllers to check if your current resource’s state can be changed:

def pay
  @order = Order.find(params[:id])
  raise "impossible to pay due to this order status #{order.status}" if !@order.can_pay?

  # payment code
end

Using xml+rel links instead of atom links

Atom is everywhere and can be consumed by a number of existing tools but if your system wants to supply its
services through commons rel+link xml as

	<order>
		<product>basic rails course</product>
		<product>RESTful training</product>
		<refresh>http://www.caelum.com.br/orders/1</refresh>
		<update>http://www.caelum.com.br/orders/1</update>
		<pay>http://www.caelum.com.br/orders/1/pay</pay>
		<destroy>http://www.caelum.com.br/orders/1</destroy>
	</order>

You can do it by passing the use_name_based_link argument:

    order.to_xml(:controller => my_controller, :use_name_based_link => true)

Team

Restfulie was created and is maintained within Caelum by

Projetct Founder

Active Commiters

Contributors

  • Diego Carrion
  • Leandro Silva
  • Gavin-John Noonan

Try it online

We have a live example of a server implementation using a resource+hypermedia course ordering system available.

Follow the steps below to try out the system:

  • Access the server system
  • Create a couple of trainings
  • Create an order
  • Access the order listing and retrieve its xml link

And now you can try the restfulie client api through a simple and generic resource+hypermedia client application:

  • Access the client system
  • Enter your order uri
  • Check your order information which was retrieved and all available actions

Now you can either:

  • latest – refresh your order information order.latest
  • cancel – cancel your order (dead end!) order.destroy
  • pay – pay for your order, and don’t forget to send your (fake) credit card information order.pay(payment)
  • check_payment_info – after paying you can check the payment information stored at the server order.check_payment_info

In order to pay do not forget to send the parameter payment with a value as

 <payment>
   <amount>15</amount>
   <cardholder_name>Guilherme Silveira</cardholder_name>
   <card_number>123456789012</card_number>
   <expiry_month>12</expiry_month>
   <expiry_year>12</expiry_year>
 </payment>

Sources

You can see an application’s source code here, both client and server side were implemented using restfulie:

Client
Server

More tutorials

There is a portuguese tutorial on the server-side support, more on restfulie – portuguese and a blog post on the entire ecosystem in english

What’s new

next release

  • API change: you need to invoke “acts_as_restfulie” to your models
  • implemented support to can_*** methods
  • post data through http POST body
  • no need for the status field if you use the following_transition approach on the server side
  • bug fixed when status was nil
  • lots of internal refactoring
  • lots of new documentation

0.2

  • support to state machine configuration
  • resources must contain a status field

0.1

  • first release

Coming soon

  • support lastModified
  • support eTag
  • remove client dependency from ActiveRecord
  • remove server side dependency from ActiveRecord (its ok to use anything else)
  • transitions << [:show] should work
  • Generate and link to rubydoc
  • change parameter order for transition: last one is HASH, first is name, second (if existing) is target state
  • rel prepend suffix as http://iansrobinson.com/resources/link-relations/preceding
  • automatically generate uri for this rel with its transition description
  • release 0.4
  • controller filtering and methods
  • english tutorial and screencast
  • allows pure String/byte array client side post and server side retrieval
  • integration tests on orderserver/client api
  • controller method should check if its an restfulie resource
  • full support to extended json
  • pure href definition of link
  • post entry point support
  • Set the correct media type instead of application/xml
  • rails 3 easier support (no controller argument!)
  • allow servers to define transitions by accessing other systems
  • allow servers to define a state method instead of internal variable
  • when receiving a 201 + content, it should believe the content
  • when receiving a 201 without any content, it should allow to redirect or not
  • client side should allow withTimeStamp, withETag, withAuth
  • is there is an etag, use it by default (maybe NOT use it by default)… modified since and so on (header tags)
  • server side maybe allow hypermedia controls or not
  • server and client side 303 support on alternate uri (or content-location)
  • Use If-Unmodified-Since on a timestamp to
    *  Or use If-Match and an ETag
    *  You’ll get a 412 Prec if you lost the race

License

/*

  • Copyright © 2009 Caelum – www.caelum.com.br/opensource
  • All rights reserved.
    *
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
    *
  • http://www.apache.org/licenses/LICENSE-2.0
    *
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */