[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
SlideShare a Scribd company logo
REST with Sinatra
  Emanuele DelBono
      @emadb
Thanks to the sponsors
About me
I’m a software developer in
CodicePlastico.
I write Web applications in C# with
ASP.NET MVC. I’m a member of
<WEBdeBS/> a local web
community.
...and I’m a wannabe Ruby
developer ;-)
Agenda
‣ Philosophy.
 Sweet and easy.
‣ REST.
 Yet another boring introduction.
‣ Hello world.
 Show some magic.
‣ Features.
 Amazing!
‣ Inside.
 How it works?
‣ Q&A.
 ...and thanks.
Sinatra as a REST server
What is isn’t
‣ Sinatra is not
 -another MV* framework
 -a full framework like Rails
 -an ORM
 -a game project
Sinatra is just a DSL
   written in ruby
Hello world
Hello world
~$ gem install sinatra
~$ vi app.rb
Hello world
~$ gem install sinatra
~$ vi app.rb

require 'sinatra'
get '/' do
 'hello world!'
end
Hello world
~$ gem install sinatra
~$ vi app.rb

require 'sinatra'
get '/' do
 'hello world!'
end

~$ ruby app.rb
Sinatra for REST services
Is it really so easy?
‣ Yep! (special thanks to Ruby
  sweeties)
  - get is a just a method (more later)
  - the route is the parameter
  - the block is what should be
    executed
  - the block returns the result
  - the server is built in (based on Rack)
Let’s go REST


What? Yet another REST introduction?
Sinatra for REST services
5 REST principles
‣ Uniform Interface
    ‣ HATEAOS
‣   Stateless
‣   Cacheable
‣   Client-Server
‣   Layered System
Sinatra and REST
‣ Sinatra supports
  - All HTTP verbs
  - Caching
  - Content types
  - Routes
  - ....and all you need to build a rest server
Give a look at the
Verbs
‣ Support for all verbs
  - Get, Post, Put, Delete, Head, Options,
    Patch


‣ They are just methods
Routing
‣ No need of route files or route
  maps
‣ The verb method takes the route
  as parameter
Routing


get ( ‘/todos’ ) {...}
get ( ‘/todo/:id’ ) {...}
get ( ‘/*’ ) {...}
Routing Conditions
get '/foo', :agent => /Mozilla/(d.d)s
w?/ do
 "You're using Mozilla version
#{params[:agent][0]}"
end
get '/foo' do
 # Matches non-Mozilla browsers
end
Routing Custom
set(:prob) { |v| condition { rand <= v } }

get '/win_a_car', :prob => 0.1 do
 "You won!"
end

get '/win_a_car' {"Sorry, you lost."}
Xml or Json
‣ You can set the content_type to
  whatever you need



content_type :json
content_type :xml
Status codes
‣ Set the HTTP status code is


  status 200
  status 403
Http Headers
‣ You can add your own headers


headers "X-My-Value" => "this is my
header"
Redirect


redirect to ‘/todos’
Cache
‣ You can set your cache information
  using the headers method

headers "Cache-Control" => "public,
must-revalidate, max-age=3600",
"Expires" => Time.at(Time.now.to_i + (60
Cache
‣ Or better, you can use the expires


expires 3600, :public
Filters
before do
 #...
end
after do
 #...
Authentication


‣ Basic authentication support through
  Rack
‣ Key-based authentication
Basic Authentication

use Rack::Auth::Basic, "Private area" do
|usr, pwd|
 [usr, pwd] == ['admin', 'admin']
end
Token authentication
before do
 if env[‘HTTP_MY_KEY’] ==’rubyrocks‘
   error 401
 end
end
Demo
Stream
‣ Supports streamed content
‣ Streamed responses have no Content-
  Length header
‣ But have Transfer-Encoding: chunked
Inside
Inside
‣ Sinatra source code is on github:
    http://github.com/sinatra/sinatra
‣ Less than 2k lines of code
‣ Based on Rack
Application style


Classic application
         vs
Modular application
Classic App

  Single file
 Standalone
Modular Apps


         Multi file
Subclassing Sinatra::Base
  Distributed as library
The code
What happen when a request arrives?


get (‘/hello’) do { ‘hello world’ }
Where do get come from?
require "sinatra"
outer_self = self
get '/' do
 "outer self: #{outer_self}, inner self: #{self}"
end


 outer self: main, inner self: #<Sinatra::Application:

        closures in ruby are “scope gate”
Sinatra for REST services
$ irb
$ irb
ruby-1.9.2-p180 > require 'sinatra'
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
=> [:delegate, :target, :target=]
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
=> [:delegate, :target, :target=]
ruby-1.9.2-p180 > Sinatra::Delegator.target
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
=> [:delegate, :target, :target=]
ruby-1.9.2-p180 > Sinatra::Delegator.target
=> Sinatra::Application
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
=> [:delegate, :target, :target=]
ruby-1.9.2-p180 > Sinatra::Delegator.target
=> Sinatra::Application
ruby-1.9.2-p180 > Sinatra::Application.method(:get)
$ irb
ruby-1.9.2-p180 > require 'sinatra'
=> true
ruby-1.9.2-p180 > method(:get)
=> #<Method: Object(Sinatra::Delegator)#get>
ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
=> [:delegate, :target, :target=]
ruby-1.9.2-p180 > Sinatra::Delegator.target
=> Sinatra::Application
ruby-1.9.2-p180 > Sinatra::Application.method(:get)
=> #<Method:
Sinatra::Application(Sinatra::Base).get>
Where do get come from?
‣ get is defined twice:
  – Once in Sinatra::Delegator a mixin
    extending Object
  – Once in Sinatra::Application
‣ The Delegator implementation simply
  delegate to Application
‣ That’s why we have get/post/...
  methods on main
How the block is invoked?
‣ The route! method finds the correct
  route

def route_eval
 throw :halt, yield
end

‣ throw is used to go back to the invoker
In base.rb

‣ The invoker


def invoke
 res = catch(:halt) { yield }
 # .... other code...
end
It’s a kind of magic!
Sinatra has ended his set
    (crowd applauds)
Questions?
demos at:
http://github.com/emadb/
     webnetconf_demo
Please rate this session
Scan the code, go online, rate this session

More Related Content

Sinatra for REST services

  • 1. REST with Sinatra Emanuele DelBono @emadb
  • 2. Thanks to the sponsors
  • 3. About me I’m a software developer in CodicePlastico. I write Web applications in C# with ASP.NET MVC. I’m a member of <WEBdeBS/> a local web community. ...and I’m a wannabe Ruby developer ;-)
  • 4. Agenda ‣ Philosophy. Sweet and easy. ‣ REST. Yet another boring introduction. ‣ Hello world. Show some magic. ‣ Features. Amazing! ‣ Inside. How it works? ‣ Q&A. ...and thanks.
  • 5. Sinatra as a REST server
  • 6. What is isn’t ‣ Sinatra is not -another MV* framework -a full framework like Rails -an ORM -a game project
  • 7. Sinatra is just a DSL written in ruby
  • 9. Hello world ~$ gem install sinatra ~$ vi app.rb
  • 10. Hello world ~$ gem install sinatra ~$ vi app.rb require 'sinatra' get '/' do 'hello world!' end
  • 11. Hello world ~$ gem install sinatra ~$ vi app.rb require 'sinatra' get '/' do 'hello world!' end ~$ ruby app.rb
  • 13. Is it really so easy? ‣ Yep! (special thanks to Ruby sweeties) - get is a just a method (more later) - the route is the parameter - the block is what should be executed - the block returns the result - the server is built in (based on Rack)
  • 14. Let’s go REST What? Yet another REST introduction?
  • 16. 5 REST principles ‣ Uniform Interface ‣ HATEAOS ‣ Stateless ‣ Cacheable ‣ Client-Server ‣ Layered System
  • 17. Sinatra and REST ‣ Sinatra supports - All HTTP verbs - Caching - Content types - Routes - ....and all you need to build a rest server
  • 18. Give a look at the
  • 19. Verbs ‣ Support for all verbs - Get, Post, Put, Delete, Head, Options, Patch ‣ They are just methods
  • 20. Routing ‣ No need of route files or route maps ‣ The verb method takes the route as parameter
  • 21. Routing get ( ‘/todos’ ) {...} get ( ‘/todo/:id’ ) {...} get ( ‘/*’ ) {...}
  • 22. Routing Conditions get '/foo', :agent => /Mozilla/(d.d)s w?/ do "You're using Mozilla version #{params[:agent][0]}" end get '/foo' do # Matches non-Mozilla browsers end
  • 23. Routing Custom set(:prob) { |v| condition { rand <= v } } get '/win_a_car', :prob => 0.1 do "You won!" end get '/win_a_car' {"Sorry, you lost."}
  • 24. Xml or Json ‣ You can set the content_type to whatever you need content_type :json content_type :xml
  • 25. Status codes ‣ Set the HTTP status code is status 200 status 403
  • 26. Http Headers ‣ You can add your own headers headers "X-My-Value" => "this is my header"
  • 28. Cache ‣ You can set your cache information using the headers method headers "Cache-Control" => "public, must-revalidate, max-age=3600", "Expires" => Time.at(Time.now.to_i + (60
  • 29. Cache ‣ Or better, you can use the expires expires 3600, :public
  • 31. Authentication ‣ Basic authentication support through Rack ‣ Key-based authentication
  • 32. Basic Authentication use Rack::Auth::Basic, "Private area" do |usr, pwd| [usr, pwd] == ['admin', 'admin'] end
  • 33. Token authentication before do if env[‘HTTP_MY_KEY’] ==’rubyrocks‘ error 401 end end
  • 34. Demo
  • 35. Stream ‣ Supports streamed content ‣ Streamed responses have no Content- Length header ‣ But have Transfer-Encoding: chunked
  • 37. Inside ‣ Sinatra source code is on github: http://github.com/sinatra/sinatra ‣ Less than 2k lines of code ‣ Based on Rack
  • 38. Application style Classic application vs Modular application
  • 39. Classic App Single file Standalone
  • 40. Modular Apps Multi file Subclassing Sinatra::Base Distributed as library
  • 41. The code What happen when a request arrives? get (‘/hello’) do { ‘hello world’ }
  • 42. Where do get come from? require "sinatra" outer_self = self get '/' do "outer self: #{outer_self}, inner self: #{self}" end outer self: main, inner self: #<Sinatra::Application: closures in ruby are “scope gate”
  • 44. $ irb
  • 45. $ irb ruby-1.9.2-p180 > require 'sinatra'
  • 46. $ irb ruby-1.9.2-p180 > require 'sinatra' => true
  • 47. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get)
  • 48. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get>
  • 49. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false)
  • 50. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false) => [:delegate, :target, :target=]
  • 51. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false) => [:delegate, :target, :target=] ruby-1.9.2-p180 > Sinatra::Delegator.target
  • 52. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false) => [:delegate, :target, :target=] ruby-1.9.2-p180 > Sinatra::Delegator.target => Sinatra::Application
  • 53. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false) => [:delegate, :target, :target=] ruby-1.9.2-p180 > Sinatra::Delegator.target => Sinatra::Application ruby-1.9.2-p180 > Sinatra::Application.method(:get)
  • 54. $ irb ruby-1.9.2-p180 > require 'sinatra' => true ruby-1.9.2-p180 > method(:get) => #<Method: Object(Sinatra::Delegator)#get> ruby-1.9.2-p180 > Sinatra::Delegator.methods(false) => [:delegate, :target, :target=] ruby-1.9.2-p180 > Sinatra::Delegator.target => Sinatra::Application ruby-1.9.2-p180 > Sinatra::Application.method(:get) => #<Method: Sinatra::Application(Sinatra::Base).get>
  • 55. Where do get come from? ‣ get is defined twice: – Once in Sinatra::Delegator a mixin extending Object – Once in Sinatra::Application ‣ The Delegator implementation simply delegate to Application ‣ That’s why we have get/post/... methods on main
  • 56. How the block is invoked? ‣ The route! method finds the correct route def route_eval throw :halt, yield end ‣ throw is used to go back to the invoker
  • 57. In base.rb ‣ The invoker def invoke res = catch(:halt) { yield } # .... other code... end
  • 58. It’s a kind of magic!
  • 59. Sinatra has ended his set (crowd applauds)
  • 62. Please rate this session Scan the code, go online, rate this session

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n