JSON and the APInauts
Upcoming SlideShare
Loading in...5
×
 

JSON and the APInauts

on

  • 9,524 views

Guide to writing great API wrappers in Ruby.

Guide to writing great API wrappers in Ruby.

Statistics

Views

Total Views
9,524
Views on SlideShare
9,521
Embed Views
3

Actions

Likes
23
Downloads
92
Comments
0

1 Embed 3

http://twitter.com 3

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

JSON and the APInauts JSON and the APInauts Presentation Transcript

  • API JSON AND THE ARGONAUTS WYNNNETHERLAND
  • whoami
  • +
  • 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
  • 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