Slide 1

Slide 1 text

JSON AND THE ARGONAUTS API WYNNNETHERLAND

Slide 2

Slide 2 text

whoami

Slide 3

Slide 3 text

+

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

I write API wrappers A lot of API wrappers!

Slide 6

Slide 6 text

& more!

Slide 7

Slide 7 text

Why create API wrappers?

Slide 8

Slide 8 text

After all, we have

Slide 9

Slide 9 text

curl

Slide 10

Slide 10 text

curl http://api.twitter.com/1/users/show.json?screen_name=pengwynn

Slide 11

Slide 11 text

Net::HTTP

Slide 12

Slide 12 text

url = "http://api.twitter.com/1/users/show.json?screen_name=pengwynn" Net::HTTP.get(URI.parse(url))

Slide 13

Slide 13 text

Because we're Rubyists, and we want

Slide 14

Slide 14 text

Idiomatic access

Slide 15

Slide 15 text

{"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, ... }}

Slide 16

Slide 16 text

{"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, ... }}

Slide 17

Slide 17 text

Rubyified keys

Slide 18

Slide 18 text

{"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, ... }}

Slide 19

Slide 19 text

{"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, ... }}

Slide 20

Slide 20 text

... and method names

Slide 21

Slide 21 text

# 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

Slide 22

Slide 22 text

# 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 More Ruby like than

Slide 23

Slide 23 text

SYNTACTIC SUGAR

Slide 24

Slide 24 text

Twitter::Search.new.from('jnunemaker').to('pengwynn').hashed('ruby').fetch()

Slide 25

Slide 25 text

Twitter::Search.new.from('jnunemaker').to('pengwynn').hashed('ruby').fetch() Method chaining

Slide 26

Slide 26 text

stores = client.stores({:area => ['76227', 50]}).products({:salePrice => {'$gt' => 3000}}).fetch.stores

Slide 27

Slide 27 text

stores = client.stores({:area => ['76227', 50]}).products({:salePrice => {'$gt' => 3000}}).fetch.stores Method chaining

Slide 28

Slide 28 text

client.statuses.update.json! :status => 'this status is from grackle'

Slide 29

Slide 29 text

client.statuses.update.json! :status => 'this status is from grackle' Method chaining Bang for POST

Slide 30

Slide 30 text

APPROACHES

Slide 31

Slide 31 text

Simple Wrapping

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Why simply wrap?

Slide 34

Slide 34 text

Insulate against change

Slide 35

Slide 35 text

Leverage API documentation

Slide 36

Slide 36 text

Why abstract?

Slide 37

Slide 37 text

...or writing APIs to wrap APIs

Slide 38

Slide 38 text

Simplify a complex API

Slide 39

Slide 39 text

Provide a business domain

Slide 40

Slide 40 text

TOOLS

Slide 41

Slide 41 text

Transports

Slide 42

Slide 42 text

Net::HTTP

Slide 43

Slide 43 text

Patron http://toland.github.com/patron/

Slide 44

Slide 44 text

Typhoeus http://github.com/pauldix/typhoeus

Slide 45

Slide 45 text

em-http-request http://github.com/igrigorik/em-http-request

Slide 46

Slide 46 text

Parsers

Slide 47

Slide 47 text

Crack Puts the party in HTTParty http://github.com/jnunemaker/crack

Slide 48

Slide 48 text

JSON

Slide 49

Slide 49 text

yajl-ruby http://github.com/brianmario/yajl-ruby

Slide 50

Slide 50 text

multi_json http://github.com/intridea/multi_json

Slide 51

Slide 51 text

Higher-level libraries

Slide 52

Slide 52 text

HTTParty When you HTTParty, you must party hard! http://github.com/jnunemaker/httparty

Slide 53

Slide 53 text

HTTParty - Ruby module - GET, POST, PUT, DELETE - basic_auth, base_uri, default_params, etc. - Net::HTTP for transport - Crack parses JSON and XML

Slide 54

Slide 54 text

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 ...

Slide 55

Slide 55 text

monster_mash http://github.com/dbalatero/monster_mash HTTParty for Typhoeus, a monster. Get it?

Slide 56

Slide 56 text

RestClient http://github.com/adamwiggins/rest-client

Slide 57

Slide 57 text

RestClient - Simple DSL - ActiveResource support - Built-in shell RestClient.post( ! 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' } )

Slide 58

Slide 58 text

Weary http://github.com/mwunsch/weary

Slide 59

Slide 59 text

Weary - Simple DSL - Specify required, optional params - Async support

Slide 60

Slide 60 text

Weary declare "foo" do |r| r.url = "path/to/foo" r.via = :post r.requires = [:id, :bar] r.with = [:blah] r.authenticates = false r.follows = false r.headers = {'Accept' => 'text/html'} end client.foo :id => 123, :bar => 'baz' becomes

Slide 61

Slide 61 text

RackClient http://github.com/halorgium/rack-client

Slide 62

Slide 62 text

RackClient - Rack API - Middleware! client = Rack::Client.new('http:// whoismyrepresentative.com')

Slide 63

Slide 63 text

Faraday http://github.com/technoweenie/faraday

Slide 64

Slide 64 text

Faraday - Rack-like - Middleware! - Adapters

Slide 65

Slide 65 text

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"

Slide 66

Slide 66 text

Faraday Middleware http://github.com/pengwynn/faraday-middleware

Slide 67

Slide 67 text

Faraday Middleware - Hashie - Multi JSON - OAuth, OAuth2 as needed

Slide 68

Slide 68 text

My current stack - Faraday - Faraday Middleware - Hashie - Multi JSON - OAuth, OAuth2 as needed

Slide 69

Slide 69 text

Hashie - Mash - Dash - Trash - Clash

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

HTTPScoop

Slide 72

Slide 72 text

Charles Proxy If you have an iOS app, you have an API ;-)

Slide 73

Slide 73 text

Testing

Slide 74

Slide 74 text

Fakeweb http://github.com/chrisk/fakeweb

Slide 75

Slide 75 text

VCR http://github.com/myronmarston/vcr

Slide 76

Slide 76 text

Artifice Artifice.activate_with(rack_endpoint) do # make some requests using Net::HTTP end a @wycats joint http://github.com/wycats/artifice

Slide 77

Slide 77 text

ShamRack http://github.com/mdub/sham_rack

Slide 78

Slide 78 text

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"

Slide 79

Slide 79 text

ShamRack ShamRack.at("rackup.xyz").rackup do use Some::Middleware use Some::Other::Middleware run MyApp.new end Rack 'em up!

Slide 80

Slide 80 text

Authentication

Slide 81

Slide 81 text

Basic

Slide 82

Slide 82 text

OAuth http://oauth.rubyforge.org/

Slide 83

Slide 83 text

OAuth2 http://github.com/intridea/oauth2

Slide 84

Slide 84 text

QUESTIONS? @pengwynn