rest-core
A modular Ruby REST client collection/infrastructure

avatar Lin Jen-Shin (godfat)


avatar Lin Jen-Shin (godfat)


avatar Lin Jen-Shin (godfat)


avatar Lin Jen-Shin (godfat)


  • Programmer at Cardinal Blue
  • Programming Language
  • Functional Programming (Haskell)

avatar Lin Jen-Shin (godfat)


  • Programmer at Cardinal Blue
  • Programming Language
  • Functional Programming (Haskell)
  • Ruby

  • Quick Example

  • Quick Example
  • Rack

  • Quick Example
  • Rack
  • Difference from Rack

  • Quick Example
  • Rack
  • Difference from Rack
  • Architecture

web-services

tools

soapbox

REST
rest

Solution: rest-core

Inspired by faraday and Rack

01 Github = RestCore::Builder.client do
02 end
01 Github = RestCore::Builder.client do
02 
03 
04 
05 end
06 
07 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03 
04 
05 end
06 
07 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04 
05 end
06 
07 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   run RestClient
05 end
06 
07 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   run RestClient
05 end
06 
07 Github.new.get('godfat')
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   run RestClient
05 end
06 
07 Github.new.get('godfat')
08 
09 {"type"=>"User","company"=>"cardinalblue",
10  "blog"=>"http://godfat.org","hireable"=>false,
11  "url"=>"https://api.github.com/users/godfat",
12  "followers"=>40,"html_url"=>"https://github.com/godfat",
13  "bio"=>nil,"created_at"=>"2008-05-15T18:33:24Z",
14  "avatar_url"=>"https://...","following"=>33,
15  "name"=>"Lin Jen-Shin (godfat)","location"=>"Taiwan",
16  "email"=>"godfat (XD) godfat.org","public_repos"=>62,
17  "id"=>10416,"login"=>"godfat","public_gists"=>59}
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   
05   run RestClient
06 end
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07
08 client = Github.new
09 
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07
08 client = Github.new
09 client.get('godfat') # slow
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07
08 client = Github.new
09 client.get('godfat') # slow
10 client.get('godfat') # cache hit
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07
08 client = Github.new
09 client.get('godfat') # slow
10 client.get('godfat') # cache hit
11 client.get('godfat') # cache hit
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use Cache       , {}, 3600
05   run RestClient
06 end
07
08 client = Github.new
09 client.get('godfat') # slow
10 client.get('godfat') # cache hit
11 client.get('godfat') # cache hit
12 client.get('godfat') # cache hit
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 client = Github.new
10 
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 client = Github.new
10 client.get('godfat') # slow
11 
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 client = Github.new
10 client.get('godfat') # slow
11 # RestCore: spent 1.498819 Requested https://api.github.com/users/godfat
12 
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 client = Github.new
10 client.get('godfat') # slow
11 # RestCore: spent 1.498819 Requested https://api.github.com/users/godfat
12 client.get('godfat') # cache hit
13 
14 
15 
16 
17 
01 Github = RestCore::Builder.client do
02   use DefaultSite , 'https://api.github.com/users/'
03   use JsonDecode  , true
04   use CommonLogger, method(:puts)
05   use Cache       , {}, 3600
06   run RestClient
07 end
08 
09 client = Github.new
10 client.get('godfat') # slow
11 # RestCore: spent 1.498819 Requested https://api.github.com/users/godfat
12 client.get('godfat') # cache hit
13 # RestCore: spent 2.0e-05 CacheHit https://api.github.com/users/godfat
14 # RestCore: spent 6.9e-05 Requested https://api.github.com/users/godfat
15 
16 
17 

Web Application as servers

client-server

server-client

Web Application as clients

The History

CGI (Common Gateway Interface)

FastCGI

SCGI (Simple CGI)

Mongrel

Rack (from WSGI)






rack


01 
02   (middleware (middleware (middleware app)))
03 
04 
05 
06 

01 -- --> --> --> --> --> --> --> --> --> -->
02   (middleware (middleware (middleware app)))
03 -- <-- <-- <-- <-- <-- <-- <-- <-- <-- <--
04 
05 
06 

01 -- --> --> --> --> --> --> --> --> --> -->
02   (middleware (middleware (middleware app)))
03 -- <-- <-- <-- <-- <-- <-- <-- <-- <-- <--
04 
05 app        :: env -> response
06 

01 -- --> --> --> --> --> --> --> --> --> -->
02   (middleware (middleware (middleware app)))
03 -- <-- <-- <-- <-- <-- <-- <-- <-- <-- <--
04 
05 app        :: env -> response
06 middleware :: app -> app

01 
02   (middleware (middleware (middleware app)))
03 --                                    __^ app
04 
05 app        :: env -> response
06 middleware :: app -> app

01 
02   (middleware (middleware (middleware app)))
03 --                                    __^ app
04 --                         _____________^ app
05 app        :: env -> response
06 middleware :: app -> app

01 
02   (middleware (middleware (middleware app)))
03 --                                    __^ app
04 --                         _____________^ app
05 --             _________________________^ app
06 middleware :: app -> app

01 
02   (middleware (middleware (middleware app)))
03 --                                    __^ app
04 --                         _____________^ app
05 --             _________________________^ app
06 -- _____________________________________^ app

Composable and Reusable

Why not do the same for
Web Application as clients?

client-server

server-client

rack

middleware-large

But actually clients are more complex...

multiple-clients

more-clients

multiple-clients

multiple-clients

01 github = Github.new
02 
03 
04 
05 
06 
07 
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 
04 
05 
06 
07 
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 
05 
06 
07 
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 restgraph = RestGraph.new # Facebook Graph API
05 
06 
07 
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 restgraph = RestGraph.new # Facebook Graph API
05 
06 github.get('godfat')
07 
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 restgraph = RestGraph.new # Facebook Graph API
05 
06 github.get('godfat')
07 twitter.get('user_timeline.json', :id => 'godfat')
08 
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 restgraph = RestGraph.new # Facebook Graph API
05 
06 github.get('godfat')
07 twitter.get('user_timeline.json', :id => 'godfat')
08 linkedin.get('v1/people/~') # need authorize first
09 
01 github = Github.new
02 twitter = Twitter.new
03 linkedin = Linkedin.new
04 restgraph = RestGraph.new # Facebook Graph API
05 
06 github.get('godfat')
07 twitter.get('user_timeline.json', :id => 'godfat')
08 linkedin.get('v1/people/~') # need authorize first
09 restgraph.get('spellbook')

multiple-clients

more-clients

01 common = RestGraph.new                # Facebook Graph API
02 
03 
04 
05 
06 
07 
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 
03 
04 
05 
06 
07 
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 common.get('spellbook')
03 
04 
05 
06 
07 
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 common.get('spellbook')
03 
04 upload = RestGraph.new(:timeout => 10)
05 
06 
07 
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 common.get('spellbook')
03 
04 upload = RestGraph.new(:timeout => 10)
05 upload.post('4/photos', :source => File.open('...'))
06 
07 
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 common.get('spellbook')
03 
04 upload = RestGraph.new(:timeout => 10)
05 upload.post('4/photos', :source => File.open('...'))
06 
07 common.get('spellbook', {}, :timeout => 10)
08 
01 common = RestGraph.new(:timeout => 2) # Facebook Graph API
02 common.get('spellbook')
03 
04 upload = RestGraph.new(:timeout => 10)
05 upload.post('4/photos', :source => File.open('...'))
06 
07 common.get('spellbook', {}, :timeout => 10)
08 common.get('spellbook') # still timeout 2

more-clients

stack

middlewares

middleware

middleware-large

middleware-app

app

app

01                        app

app-mid

01            (middleware app)

app-mid-mid

01 middleware (middleware app))

app-mid-mid-mid

01 middleware (middleware app)))

app-mid-mid-mid-arrow

01 middleware (middleware app)))

more-clients

Tools used to build this slide

  • Landslide to make this slide
  • Pygments to do syntax highlighting
  • Adobe Illustrator to draw diagrams
  • TextMate to edit markdown
  • Nokogiri to fix generated HTML
  • Rib to interactively find out how to fix HTML
  • Firefox to view HTML