API Design
Upcoming SlideShare
Loading in...5
×
 

API Design

on

  • 2,311 views

This is a tour of API's good, poor, an unusual given to OK.rb in June.

This is a tour of API's good, poor, an unusual given to OK.rb in June.

Statistics

Views

Total Views
2,311
Views on SlideShare
2,287
Embed Views
24

Actions

Likes
5
Downloads
31
Comments
0

1 Embed 24

http://www.slideshare.net 24

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

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
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

API Design API Design Presentation Transcript

  • API DESIGN The James scale of likes and dislikes
  • WHAT DO WE LIKE?
  • WHAT DO WE LIKE? • We work with a lot of API’s
  • WHAT DO WE LIKE? • We work with a lot of API’s • We generally have strong opinions about what “feels” right to us
  • WHAT DO WE LIKE? • We work with a lot of API’s • We generally have strong opinions about what “feels” right to us • How do we decide?
  • I HAVE NO CLUE So we’re just going to talk about what I like
  • I HAVE NO CLUE So we’re just going to talk about what I like
  • API’S JAMES LIKES
  • RUBY’S STANDARD LIBRARY Where many treasures can be found
  • RUBY’S STANDARD LIBRARY Where many treasures can be found
  • FILEUTILS Unix style file manipulations
  • require "fileutils" FileUtils.cd dir, options FileUtils.cd dir, options do # ... end FileUtils.mkdir dir, options FileUtils.mkdir_p dir, options FileUtils.ln src, dest, options FileUtils.ln_s src, dest, options FILEUTILS FileUtils.cp src, dest, options FileUtils.cp_r src, dest, options FileUtils.mv src, dest, options FileUtils.rm files, options Unix style file manipulations FileUtils.rm_r files, FileUtils.rm_rf files, options options FileUtils.chmod mode, files, options FileUtils.chmod_R mode, files, options FileUtils.touch files, options # # or # # FileUtils::Verbose... # FileUtils::NoWrite... # FileUtils::DryRun... #
  • GOOD DOESN’T MEAN… Complex, powerful, all encompassing, etc.
  • GOOD DOESN’T MEAN… Complex, powerful, all encompassing, etc.
  • OPENURI The Web as just another IO
  • #!/usr/bin/env ruby -wKU require "open-uri" OPENURI open(__FILE__) do |f| p f.gets # >> "#!/usr/bin/env ruby -wKUn" end The Web as just another IO open("http://blog.grayproductions.net") do |u| p u.gets # >> "<!DOCTYPE HTML PUBLIC …" p u.content_type # >> "text/html" end
  • FAMILIAR IS GOOD But don’t be afraid to add little extras
  • FAMILIAR IS GOOD But don’t be afraid to add little extras
  • PATHNAME Object oriented paths
  • #!/usr/bin/env ruby -wKU require "pathname" # using plain Ruby if File.exist? some_dir data = File.read( File.join( File.dirname(some_dir), PATHNAME "another_dir", File.basename(some_file) ) ) # ... Object oriented paths end # or using Pathname some_dir = Pathname.new("some_dir") some_file = Pathname.new("some_file") if some_dir.exist? data = ( some_dir.dirname + "another_dir" + some_file.basename ).read # ... end
  • FRESH IDEAS Sometimes you just need to try something different
  • FRESH IDEAS Sometimes you just need to try something different
  • PSTORE A hash in a file
  • #!/usr/bin/env ruby -wKU require "pstore" PSTORE store = PStore.new("data_file.pstore") store.transaction do # begin transaction store[:single_object] = "My data..." store[:obj_heirarchy] = { "Kev Jackson" => A hash in a file [ "rational.rb", "pstore.rb" ], "James Gray" => [ "erb.rb", "pstore.rb" ] } end # commit changes to file
  • A POWERFUL COMBINATION There’s no easier and safer way to manage a little data
  • A POWERFUL COMBINATION There’s no easier and safer way to manage a little data
  • API’S JAMES DOESN’T LIKE
  • S3 LIBRARIES It seems like all S3 wrappers for Ruby are missing some details
  • S3 LIBRARIES It seems like all S3 wrappers for Ruby are missing some details
  • AWS Streaming double-interface
  • #!/usr/bin/env ruby -wKU require "rubygems" AWS require "aws" s3 = Aws::S3.new( "KEY_ID", Streaming double-interface ) "SECRET_KEY" bucket = s3.bucket("graysoftinc", :create) open(__FILE__) do |f| bucket.put(File.basename(__FILE__), f) end
  • #!/usr/bin/env ruby -wKU require "rubygems" require "aws" AWS s3 = Aws::S3Interface.new( "KEY_ID", "SECRET_KEY" ) Streaming double-interface ary_of_hshs = s3.list_bucket("graysoftinc") if hsh = ary_of_hshs.first open("downloaded.rb", "w") do |f| s3.get("graysoftinc", hsh[:key]) do |chunk| f << chunk end end end
  • OUT OF PLACE Why support half of a feature in an API?
  • OUT OF PLACE Why support half of a feature in an API?
  • DON’T USE WITH THREADS! The many-threads-sharing-one-connection code is a textbook perfect example of how not to do threading!
  • DON’T USE WITH THREADS! The many-threads-sharing-one-connection code is a textbook perfect example of how not to do threading!
  • FOG Warnings galore
  • #!/usr/bin/env ruby -wKU require "rubygems" FOG require "fog" s3 = Fog::AWS::S3.new( Warnings galore :aws_access_key_id => "SECRET", :aws_secret_access_key => "SECRET" ) p s3.directories.all
  • !!! DEPRECATION WARNING !!! Hey Champ!  I see you're using Ruby 1.8.6!  While I applaud you for sticking to your guns and using The One True Ruby, I have to let you know that we're going FOG to stop supporting 1.8.6.  I know, it's sad.  But, we just don't have time to support every version of Ruby out there.  Whether we like it or not, time moves forward and so does our software. On August 1, 2010, we will no longer support Ruby 1.8.6.  If nokogiri happens to Warnings galore work on 1.8.6 after that date, then great!  We will hownever, no longer test, use, or endorse 1.8.6 as a supported platform. Thanks,   Team Nokogiri
  • (eval):1: warning: method redefined; discarding old reject (eval):1: warning: method redefined; discarding old select /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/collection.rb:40: warning: method redefined; discarding old clear /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/collection.rb:55: warning: method redefined; discarding old inspect /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws.rb:95: warning: redefine image_id /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/terremark/parser.rb:6: warning: method redefined; discarding old parse /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/s3.rb:7: warning: instance variable @required not initialized FOG /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/models/s3/file.rb:20: warning: method redefined; discarding old body /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/models/s3/file.rb:53: warning: method redefined; discarding old owner= /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/models/s3/files.rb:80: warning: method redefined; discarding old directory= Warnings galore /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/parsers/s3/ get_bucket_logging.rb:8: warning: method redefined; discarding old reset /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog/aws/parsers/s3/ get_bucket_logging.rb:13: warning: method redefined; discarding old end_element /usr/local/lib/ruby/gems/1.8/gems/fog-0.1.8/lib/fog.rb:77: warning: instance variable @mocking not initialized /usr/local/lib/ruby/gems/1.8/gems/excon-0.0.26/lib/excon/connection.rb:90: warning: using default DH parameters.   <Fog::AWS::S3::Directories     [       <Fog::AWS::S3::Directory         key="graysoftinc",         creation_date=Wed Jun 09 03:10:07 UTC 2010       >     ]   >
  • PERL WHIPS RUBY IN ONE AREA Perl programmers use warnings!
  • PERL WHIPS RUBY IN ONE AREA Perl programmers use warnings!
  • FREE DEBUGGING Who hates free bug finding?
  • #!/usr/bin/env ruby -wKU FREE class Name def initialize(first, last) @first = first DEBUGGING @last = last end Who hates free bug finding? def full "#{@firt} #{@last}".strip end end name = Name.new("James", "Gray") puts name.full
  • #!/usr/bin/env ruby -KU class Name def initialize(first, last) @first = first FREE @last = last end DEBUGGING def first @firt end Who hates free bug finding? end name = Name.new("James", "Gray") Thread.new(name.first) do |first| sleep 3 puts first.capitalize end sleep
  • LESS HOOPS TO JUGGLE In programming, that’s always a win
  • LESS HOOPS TO JUGGLE In programming, that’s always a win
  • RANDOM COMPARISONS
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • RANDOM COMPARISONS AWS Fog S3 AWS::S3 Not just no, Present, Docs Good Good but hell no! but weird Works Not for me Yes Yes Yes Streaming With pain Yes No Yes Logs and Features Logs Basics only Fancy logs versions Thread-safe Don’t do it! Yes No No Warnings Yes Uh, yeah! Yes Yes, a lot Command- Extras No Shell No line tool
  • DO WE HAVE A WINNER? None of them feel ribbon worthy to me
  • DO WE HAVE A WINNER? None of them feel ribbon worthy to me
  • DESIGN CHOICES
  • LET’S TALK TWITTER Ruby’s Twitter libraries take pretty different approaches
  • LET’S TALK TWITTER Ruby’s Twitter libraries take pretty different approaches
  • THE TWITTER GEM A typical object oriented API
  • #!/usr/bin/env ruby -wKU require "rubygems" THE require "twitter" oauth = Twitter::OAuth.new( "consumer token", "consumer secret" ) TWITTER GEM oauth.authorize_from_access( "access token", "access secret" ) client = Twitter::Base.new(oauth) client.friends_timeline.each do |tweet| A typical object oriented API p tweet end client.user_timeline.each do |tweet| p tweet end client.replies.each do |tweet| p tweet end client.update("Heeeyyyyoooo from Twitter Gem!")
  • WHAT WE LIKE TO RIDE Most API wrappers work like this
  • WHAT WE LIKE TO RIDE Most API wrappers work like this
  • THE GRACKLE GEM Grackle is a dynamic wrapper over Twitter’s raw API
  • #!/usr/bin/env ruby -wKU require "rubygems" require "grackle" client = Grackle::Client.new( :auth => { :type => :oauth, :consumer_key => "KEY", :consumer_secret => "SECRET", :token => "TOKEN", :token_secret => "TOKEN_SECRET" }) # http://twitter.com/users/show.json?screen_name=some_user client.users.show? :screen_name => "some_user" # POST to http://twitter.com/statuses/update.json client.statuses.update! :status => "this status is from grackle" client[:rest].users.show? :id => "hayesdavis" client[:v1].users.show? :id => "hayesdavis" THE GRACKLE GEM Grackle is a dynamic wrapper over Twitter’s raw API
  • TEACH GRACKLE NEW TRICKS All Twitter API updates are instantly supported
  • TEACH GRACKLE NEW TRICKS All Twitter API updates are instantly supported
  • WHAT ABOUT MONGODB? How do we bridge the gap to this new kind of storage?
  • WHAT ABOUT MONGODB? How do we bridge the gap to this new kind of storage?
  • MONGO- MAPPER DataMapper for MongoDB
  • #!/usr/bin/env ruby -wKU require "rubygems" require "mongo_mapper" MongoMapper.database = "testing" MONGO- class User include MongoMapper::Document key :first_name, String, MAPPER :required => true key :last_name, String, :required => true key :token, String, DataMapper for MongoDB :default => lambda { "some random string" } key :age, Integer key :skills, Array key :friend_ids, Array, :typecast => "ObjectId" timestamps! end User.collection.remove # empties collection john = User.create( :first_name => "John", :last_name => "Nunemaker", :age => 28, :skills => ["ruby", "mongo", "javascript"], )
  • ALMOST THE NORM This is the kind of data access ActiveRecord has trained us to expect
  • ALMOST THE NORM This is the kind of data access ActiveRecord has trained us to expect
  • CANDY A very different approach
  • #!/usr/bin/env ruby -wKU require "rubygems" require "candy" class Person include Candy::Piece end me = Person.new me.last_name = "Eley" # New record created and saved to Mongo me.id # => ObjectID(4bb606f9609c8417cf00004b) me[:height] = 67 # Or me.height = 67 me.favorites = { composer: "Yoko Kanno", seafood: "Maryland blue crabs", scotch: ["Glenmorangie Port Wood Finish", "Balvenie Single Barrel"]} me.spouse = Person.piece(first_name: "Anna", eyes: :blue) me.spouse.eyes # => :blue me.favorites.scotch[1] # => "Balvenie Single Barrel" Person.last_name("Smith") # Returns the first Smith Person.age(21) # Returns the first legal drinker (in the U.S.) Person(12345) # Returns the person with an _id of 12345 CANDY A very different approach
  • #!/usr/bin/env ruby -wKU require "rubygems" require "candy" class People include Candy::Collection collects :person # Declares the Mongo collection is 'Person' end # (and so is the Candy::Piece class) People.last_name('Smith') # Returns an enumeration of all Smiths People.age(19).sort(:birthdate, :down).limit(10) # We can chain options People(limit: 47, occupation: :ronin) # Or People.all(params) or People.new(params) People.each(|p| p.shout = 'Norm!') # Where everybody knows your name... CANDY A very different approach
  • ALL ABOUT THE MAGIC There’s no save() method!
  • ALL ABOUT THE MAGIC There’s no save() method!
  • THIS ENDS THE TOUR
  • THIS ENDS THE TOUR • It’s hard to know what’s good or bad in an API
  • THIS ENDS THE TOUR • It’s hard to know what’s good or bad in an API • They definitely seem to have a “feel” to them though
  • THIS ENDS THE TOUR • It’s hard to know what’s good or bad in an API • They definitely seem to have a “feel” to them though • Aim for the natural feel, if you can find it
  • ALL IMAGES COURTESY OF BLACK AND WTF http://blackandwtf.tumblr.com/
  • DISCUSSION TOPIC Can we see anything in here that applies to PortableHole?
  • DISCUSSION TOPIC Can we see anything in here that applies to PortableHole?