RESTful Sinatra

102 views
Skip to first unread message

Maximus

unread,
Jan 14, 2009, 7:54:45 PM1/14/09
to sinatrarb
Is it possible without modifying the source overly much? I was
thinking Rack middleware would be a cool way to implement this, but
that middleware executes too late. Ideally there would be "Sinatra
middleware" that can be executed somewhere in...here?: (http://
github.com/rtomayko/sinatra/blob/
7996b1131edaf7c153c6cf5bfff73fffd5749ed1/lib/sinatra/base.rb#L30).
There are additional tweaks that could be made to make this more
friendly, like passing in args to the HTTP verb methods (i.e. get/
post) through to "Sinatra middleware". That is, I could say get '/my/
resource/:foo', :restful => true do etc... and have my middleware see
the :restful => true value.

It seems like microframeworks are well-suited to this resource-
oriented view of things.

Thoughts?

Maximus

unread,
Jan 14, 2009, 7:59:17 PM1/14/09
to sinatrarb
Soo...terminology fail. I do realize that Sinatra fully has the
capacity to be RESTful (as RESTful as we make it) -- I mean more like
implicit calling of :to_json on *.json urls and :to_xml on *.xml urls,
etc.

Maximus

unread,
Jan 14, 2009, 8:41:32 PM1/14/09
to sinatrarb
Actually, I sort of answered my own question. In the following
example, if you call rest(my_value) in the last line of your route,
you'll get what I was going for:

MAPPING = {'application/json' => :to_json}
def rest(value)
request.env['HTTP_ACCEPT'].split(/[,;]/).each do |ct|
if MAPPING.key? ct and value.respond_to? MAPPING[ct]
content_type ct, :charset => 'utf-8'
return value.send(MAPPING[ct])
end
end
value.to_s
end

(defined in helpers block). It works in 0.3.3, haven't tested against
head.

-Max

Pat Nakajima

unread,
Jan 14, 2009, 10:28:54 PM1/14/09
to sinatrarb
Take a look at my Sinatra's Hat project: http://github.com/nakajima/sinatras-hat/tree/sinatra-0.9.0

I'm currently re-writing it for the newer version of Sinatra, but it
seems to be quite similar to what you're looking for.

Pat Nakajima

unread,
Jan 17, 2009, 4:48:28 PM1/17/09
to sinatrarb
I just merged my rewrite branch into master. I also added some
documentation to the README. It works with the latest Sinatra gem
(0.8.10). I'd love to know if it's the kind of thing that would fit
your needs here.

candlerb

unread,
Jan 20, 2009, 5:24:57 AM1/20/09
to sinatrarb
A suggestion: I looked through the README and the book, and I couldn't
find an example of how to access the body of a POST or PUT without
treating it as HTML FORM params. I had to dig through the Rack API
docs, and I think that 'request.body.read' is what's needed.

Anyway, here's a sample REST application, using JSON for the fun of
it, which you're welcome to include if you wish (or fix if it's
broken :-)

--- myapp.rb ---
require 'rubygems'
require 'sinatra'
require 'json'
require 'widget'

get '/widgets.json' do
Widget.all.to_json
end

get '/widgets/:id.json' do
Widget.find(Integer(params[:id])).to_json
end

post '/widgets.json' do
item = JSON.parse(request.body.read)
raise "Bad object" unless item.is_a? Widget
Widget.add(item).to_s # see note below
end

--- widget.rb ---
# Rubbish model, not thread-safe!
class Widget
attr_accessor :id, :name, :price
def initialize(id, name, price)
@id, @name, @price = id, name, price
end
def to_json(*a)
{
'json_class' => self.class.name,
'data' => [ @id, @name, @price ],
}.to_json(*a)
end
def self.json_create(o)
new *o['data']
end
def self.all
@all ||= []
end
def self.add(item)
@seq ||= 0
@seq += 1
item.id = @seq
all << item
return @seq
end
def self.create(*args)
add(new(*args))
end
def self.find(id)
all.find { |item| item.id == id }
end
end
Widget.create(nil, "flurble", 12.3)
Widget.create(nil, "boing", 4.56)

Note: I'm not exactly sure what a REST API should return in response
to a successful POST. Here I've just returned the new Widget's ID. But
notice that I had to do .to_s to make this work. If I return an
integer from the Sinatra post method, I just get an empty body back
(Content-Length: 0)

candlerb

unread,
Jan 20, 2009, 9:29:27 AM1/20/09
to sinatrarb
After a bit of discussion on ruby-talk, I think the following is
better: using raw JSON objects rather than the suggestion in the ruby
JSON library of "json_class" (string) and "data" (array) attributes.

Sinatra (or Rack) could help here by interpreting JSON and sticking it
in 'params' automatically, but it's no big deal to do it by hand.

--- myapp.rb ---
require 'rubygems'
require 'sinatra'
require 'json'
require 'widget'

get '/widgets.json' do
Widget.all.to_json
end

get '/widgets/:id.json' do
Widget.find(Integer(params[:id])).to_json
end

post '/widgets.json' do
jparams = JSON.parse(request.body.read)
Widget.create(jparams).to_s
end

--- widget.rb ---
# Rubbish model, not thread-safe!
class Widget
attr_accessor :id, :name, :price
def initialize(params)
@id = params["id"]
@name = params["name"]
@price = params["price"]
end
def to_json(*a)
instance_variables.inject({}) {
|h,v| h[v.to_s.sub(/^@/,'')] = instance_variable_get(v); h
}.to_json(*a)
end
def self.json_create(params)
new params
end
def self.all
@all ||= []
end
def self.add(item)
@seq ||= 0
@seq += 1
item.id = @seq
all << item
return @seq
end
def self.create(params)
add(new(params))
end
def self.find(id)
all.find { |item| item.id == id }
end
end
Widget.create("name" => "flurble", "price" => 12.3)
Widget.create("name" => "boing", "price" => 4.56)

Nicolás Sanguinetti

unread,
Jan 20, 2009, 9:49:59 AM1/20/09
to sina...@googlegroups.com
On Tue, Jan 20, 2009 at 12:29 PM, candlerb <b.ca...@pobox.com> wrote:
>
> After a bit of discussion on ruby-talk, I think the following is
> better: using raw JSON objects rather than the suggestion in the ruby
> JSON library of "json_class" (string) and "data" (array) attributes.
>
> Sinatra (or Rack) could help here by interpreting JSON and sticking it
> in 'params' automatically, but it's no big deal to do it by hand.

That has Middleware written all over =)
-foca

Jonathan Stott

unread,
Jan 20, 2009, 9:55:24 AM1/20/09
to sina...@googlegroups.com
On Tue, 20 Jan 2009 12:49:59 -0200
"Nicolás Sanguinetti" <god...@gmail.com> wrote:

>
> On Tue, Jan 20, 2009 at 12:29 PM, candlerb <b.ca...@pobox.com> wrote:
> >
> > After a bit of discussion on ruby-talk, I think the following is
> > better: using raw JSON objects rather than the suggestion in the ruby
> > JSON library of "json_class" (string) and "data" (array) attributes.
> >
> > Sinatra (or Rack) could help here by interpreting JSON and sticking it
> > in 'params' automatically, but it's no big deal to do it by hand.
>
> That has Middleware written all over =)
> -foca

It really does:

Rack::PostBodyContentTypeParser

http://github.com/rack/rack-contrib/tree/master

Regards,
Jon

Reply all
Reply to author
Forward
0 new messages