Top Banner
JSON AND THE ARGONAUTS API WYNNNETHERLAND
84

JSON and the APInauts

Jan 28, 2015

Download

Technology

Wynn Netherland

Guide to writing great API wrappers in Ruby.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: JSON and the APInauts

JSON AND THE ARGONAUTSAPI

WYNNNETHERLAND

Page 2: JSON and the APInauts

whoami

Page 4: JSON and the APInauts
Page 5: JSON and the APInauts

I write API wrappersA lot of API wrappers!

Page 6: JSON and the APInauts

& more!

Page 7: JSON and the APInauts

Why create API wrappers?

Page 8: JSON and the APInauts

After all, we have

Page 9: JSON and the APInauts

curl

Page 10: JSON and the APInauts

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

Page 11: JSON and the APInauts

Net::HTTP

Page 12: JSON and the APInauts

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

Page 13: JSON and the APInauts

Because we're Rubyists, and we want

Page 14: JSON and the APInauts

Idiomatic access

Page 15: JSON and the APInauts

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

Page 16: JSON and the APInauts

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

Page 17: JSON and the APInauts

Rubyified keys

Page 18: JSON and the APInauts

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

Page 19: JSON and the APInauts

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

Page 20: JSON and the APInauts

... and method names

Page 21: JSON and the APInauts

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

Page 22: JSON and the APInauts

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

More Ruby like than

Page 23: JSON and the APInauts

SYNTACTIC SUGAR

Page 24: JSON and the APInauts

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

Page 25: JSON and the APInauts

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

Method chaining

Page 26: JSON and the APInauts

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

Page 27: JSON and the APInauts

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

Method chaining

Page 28: JSON and the APInauts

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

Page 29: JSON and the APInauts

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

Method chaining Bang for POST

Page 30: JSON and the APInauts

APPROACHES

Page 31: JSON and the APInauts

Simple Wrapping

Page 32: JSON and the APInauts

SOME TWITTER EXAMPLESTwitter Auth from @mbleighuser.twitter.post('/statuses/update.json', 'status' => 'Tweet, tweet!'

)

Grackle from @hayesdavisclient.statuses.update.json! :status => 'Tweet, tweet!'

Twitter from @jnunemaker client.update('Tweet, tweet!')

Wrapping

Wrapping... with style

Abstraction

Page 33: JSON and the APInauts

Why simply wrap?

Page 34: JSON and the APInauts

Insulate against change

Page 35: JSON and the APInauts

Leverage API documentation

Page 36: JSON and the APInauts

Why abstract?

Page 38: JSON and the APInauts

Simplify a complex API

Page 39: JSON and the APInauts

Provide a business domain

Page 40: JSON and the APInauts

TOOLS

Page 41: JSON and the APInauts

Transports

Page 42: JSON and the APInauts

Net::HTTP

Page 46: JSON and the APInauts

Parsers

Page 48: JSON and the APInauts

JSON

Page 50: JSON and the APInauts

multi_jsonhttp://github.com/intridea/multi_json

Page 51: JSON and the APInauts

Higher-level libraries

Page 53: JSON and the APInauts

HTTParty- Ruby module - GET, POST, PUT, DELETE - basic_auth, base_uri, default_params, etc.

- Net::HTTP for transport- Crack parses JSON and XML

Page 54: JSON and the APInauts

HTTPartyclass 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

...

Page 57: JSON and the APInauts

RestClient- Simple DSL- ActiveResource support- Built-in shell

RestClient.post(! 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' })

Page 59: JSON and the APInauts

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

Page 60: JSON and the APInauts

Wearydeclare "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

Page 62: JSON and the APInauts

RackClient- Rack API- Middleware!

client = Rack::Client.new('http://whoismyrepresentative.com')

Page 64: JSON and the APInauts

Faraday- Rack-like- Middleware!- Adapters

Page 65: JSON and the APInauts

Faradayurl = '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::Mashifyend

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

u = resp.bodyu.name# => "Wynn Netherland"

Page 67: JSON and the APInauts

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

Page 68: JSON and the APInauts

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

Page 69: JSON and the APInauts

Hashie- Mash- Dash- Trash- Clash

Page 71: JSON and the APInauts

HTTPScoop

Page 72: JSON and the APInauts

Charles Proxy

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

Page 73: JSON and the APInauts

Testing

Page 75: JSON and the APInauts

VCRhttp://github.com/myronmarston/vcr

Page 76: JSON and the APInauts

Artifice

Artifice.activate_with(rack_endpoint) do # make some requests using Net::HTTPend

a @wycats joint

http://github.com/wycats/artifice

Page 78: JSON and the APInauts

ShamRackShamRack.at("sinatra.xyz").sinatra do get "/hello/:subject" do "Hello, #{params[:subject]}" endend

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

#=> "Hello, stranger"

Page 79: JSON and the APInauts

ShamRackShamRack.at("rackup.xyz").rackup do use Some::Middleware use Some::Other::Middleware run MyApp.newend

Rack 'em up!

Page 80: JSON and the APInauts

Authentication

Page 81: JSON and the APInauts

Basic

Page 82: JSON and the APInauts

OAuthhttp://oauth.rubyforge.org/