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