SlideShare a Scribd company logo
API
JSON AND THE ARGONAUTS



                     WYNNNETHERLAND
whoami
+
JSON and the APInauts
A lot of API wrappers!

I write API wrappers
& more!
Why create API wrappers?
After all, we have
curl
curl http://api.twitter.com/1/users/show.json?screen_name=pengwynn
Net::HTTP
url = "http://api.twitter.com/1/users/show.json?screen_name=pengwynn"
Net::HTTP.get(URI.parse(url))
Because we're Rubyists, and we want
Idiomatic access
{"chart":{
    "issueDate":2006-03-04,
    "description":"Chart",
    "chartItems":{
      "firstPosition":1,
      "totalReturned":15,
      "totalRecords":25663,
      "chartItem":[{
        "songName":"Lonely Runs Both Ways",
        "artistName":"Alison Krauss + Union Station",
        "peek":1,
        "catalogNo":"610525",
        "rank":1,
        "exrank":1,
        "weeksOn":65,
        "albumId":655684,
      ...
 }}
{"chart":{
    "issueDate":2006-03-04,
    "description":"Chart",
    "chartItems":{
      "firstPosition":1,
      "totalReturned":15,
      "totalRecords":25663,
      "chartItem":[{
        "songName":"Lonely Runs Both Ways",
        "artistName":"Alison Krauss + Union Station",
        "peek":1,
        "catalogNo":"610525",
        "rank":1,
        "exrank":1,
        "weeksOn":65,
        "albumId":655684,
      ...
 }}
Rubyified keys
{"chart":{
    "issueDate":2006-03-04,
    "description":"Chart",
    "chartItems":{
      "firstPosition":1,
      "totalReturned":15,
      "totalRecords":25663,
      "chartItem":[{
        "songName":"Lonely Runs Both Ways",
        "artistName":"Alison Krauss + Union Station",
        "peek":1,
        "catalogNo":"610525",
        "rank":1,
        "exrank":1,
        "weeksOn":65,
        "albumId":655684,
      ...
 }}
{"chart":{
    "issue_date":2006-03-04,
    "description":"Chart",
    "chart_items":{
      "first_position":1,
      "total_returned":15,
      "total_records":25663,
      "chart_item":[{
        "song_name":"Lonely Runs Both Ways",
        "artist_name":"Alison Krauss + Union Station",
        "peek":1,
        "catalog_no":"610525",
        "rank":1,
        "exrank":1,
        "weeks_on":65,
        "album_id":655684,
      ...
 }}
... and method names
# Retrieve the details about a user by email
#
# +email+ (Required)
# The email of the user to look within. To run getInfoByEmail on
multiple addresses, simply pass a comma-separated list of valid email
addresses.
#

def self.info_by_email(email)
  email = email.join(',') if email.is_a?(Array)
  Mash.new(self.get('/',
! ! :query => {
! ! ! :method => 'user.getInfoByEmail',
! ! ! :email => email
       }.merge(Upcoming.default_options))).rsp.user
end
# Retrieve the details about a user by email
#
# +email+ (Required)
# The email of the user to look within. To run getInfoByEmail on
multiple addresses, simply pass a comma-separated list of valid email
addresses.
#
                                       More Ruby like th    an
def self.info_by_email(email)
  email = email.join(',') if email.is_a?(Array)
  Mash.new(self.get('/',
! ! :query => {
! ! ! :method => 'user.getInfoByEmail',
! ! ! :email => email
       }.merge(Upcoming.default_options))).rsp.user
end
SYNTACTIC SUGAR
Twitter::Search.new.from('jnunemaker').to('pengwynn').hashed('ruby').fetch()
Method chaining


Twitter::Search.new.from('jnunemaker').to('pengwynn').hashed('ruby').fetch()
stores = client.stores({:area => ['76227', 50]}).products({:salePrice => {'$gt' => 3000}}).fetch.stores
Method chaining


stores = client.stores({:area => ['76227', 50]}).products({:salePrice => {'$gt' => 3000}}).fetch.stores
client.statuses.update.json! :status => 'this status is from grackle'
Method chaining        Bang for POST



client.statuses.update.json! :status => 'this status is from grackle'
APPROACHES
Simple Wrapping
SOME TWITTER EXAMPLES
Twitter Auth from @mbleigh
                                           Wrapping
user.twitter.post(
  '/statuses/update.json',
  'status' => 'Tweet, tweet!'
)
                                  Wrapping... with style
Grackle from @hayesdavis
client.statuses.update.json! :status => 'Tweet, tweet!'


                                        Abstraction
Twitter from @jnunemaker
client.update('Tweet, tweet!')
Why simply wrap?
Insulate against change
Leverage API documentation
Why abstract?
...or writing APIs to wrap APIs
Simplify a complex API
Provide a business domain
TOOLS
Transports
Net::HTTP
Patron
http://toland.github.com/patron/
Typhoeus
http://github.com/pauldix/typhoeus
em-http-request
http://github.com/igrigorik/em-http-request
Parsers
Crack
http://github.com/jnunemaker/crack

      Puts the party in HTTParty
JSON
yajl-ruby
http://github.com/brianmario/yajl-ruby
multi_json
http://github.com/intridea/multi_json
Higher-level libraries
HTTParty
http://github.com/jnunemaker/httparty


  When you HTTParty, you must party hard!
HTTParty
- Ruby module
  - GET, POST, PUT, DELETE
  - basic_auth, base_uri, default_params, etc.
- Net::HTTP for transport
- Crack parses JSON and XML
HTTParty
class Delicious
  include HTTParty
  base_uri 'https://api.del.icio.us/v1'
  
  def initialize(u, p)
    @auth = {:username => u, :password => p}
  end

  ...

  def recent(options={})
    options.merge!({:basic_auth => @auth})
    self.class.get('/posts/recent', options)
  end

...
monster_mash
http://github.com/dbalatero/monster_mash

   HTTParty for Typhoeus, a monster. Get it?
RestClient
http://github.com/adamwiggins/rest-client
RestClient
- Simple DSL
- ActiveResource support
- Built-in shell

RestClient.post(
! 'http://example.com/resource',
   :param1 => 'one',
   :nested => { :param2 => 'two' }
)
Weary
http://github.com/mwunsch/weary
Weary
- Simple DSL
- Specify required, optional params
- Async support
Weary
declare "foo" do |r|
  r.url = "path/to/foo"
  r.via = :post
  r.requires = [:id, :bar]
  r.with = [:blah]                 becomes
  r.authenticates = false
  r.follows = false
  r.headers = {'Accept' => 'text/html'}
end

client.foo :id => 123, :bar => 'baz'
RackClient
http://github.com/halorgium/rack-client
RackClient
- Rack API
- Middleware!

client = Rack::Client.new('http://
whoismyrepresentative.com')
Faraday
http://github.com/technoweenie/faraday
Faraday
- Rack-like
- Middleware!
- Adapters
Faraday
url = 'http://api.twitter.com/1'
conn = Faraday::Connection.new(:url => url ) do |builder|
  builder.adapter Faraday.default_adapter
  builder.use Faraday::Response::MultiJson
  builder.use Faraday::Response::Mashify
end

resp = conn.get do |req|
  req.url '/users/show.json', :screen_name => 'pengwynn'
end

u = resp.body
u.name
# => "Wynn Netherland"
Faraday Middleware
http://github.com/pengwynn/faraday-middleware
Faraday Middleware
- Hashie
- Multi JSON
- OAuth, OAuth2 as needed
My current stack
- Faraday
- Faraday Middleware
   - Hashie
   - Multi JSON
- OAuth, OAuth2 as needed
Hashie
- Mash
- Dash
- Trash
- Clash
JSON and the APInauts
HTTPScoop
If you have an iOS app, you have an API ;-)




         Charles Proxy
Testing
Fakeweb
http://github.com/chrisk/fakeweb
VCR
http://github.com/myronmarston/vcr
Artifice
    http://github.com/wycats/artifice

Artifice.activate_with(rack_endpoint) do
  # make some requests using Net::HTTP
end
                        a @wycats joint
ShamRack
http://github.com/mdub/sham_rack
ShamRack
ShamRack.at("sinatra.xyz").sinatra do
  get "/hello/:subject" do
    "Hello, #{params[:subject]}"
  end
end

open("http://sinatra.xyz/hello/
stranger").read

#=> "Hello, stranger"
ShamRack            Rack 'em up!
ShamRack.at("rackup.xyz").rackup do
  use Some::Middleware
  use Some::Other::Middleware
  run MyApp.new
end
Authentication
Basic
OAuth
http://oauth.rubyforge.org/
OAuth2
http://github.com/intridea/oauth2
QUESTIONS?
 @pengwynn

More Related Content

JSON and the APInauts