Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Calls now raise InstapaperFull::API::Error on failed API calls, with …

…the code and message in the object. Added tests.
  • Loading branch information...
commit 1cd32ecc977c5c70acf9665bd685c3f5d4a12c7f 1 parent ee46984
@tomtaylor tomtaylor authored
@@ -15,4 +15,8 @@ Note that you need to [request OAuth Application tokens manually](http://www.ins
ip = :consumer_key => "my key", :consumer_secret => "my secret"
ip.authenticate "", "password"
puts ip.options.user_id
- puts ip.bookmarks_list(:limit => 1)[0]['url']
+ ip.bookmarks_list(:limit => 1) do |b|
+ if b['type'] == 'bookmark'
+ puts b['url']
+ end
+ end
8 Rakefile
@@ -1,2 +1,10 @@
require 'bundler'
+require 'rake/testtask'
+ do |test|
+ test.libs << 'lib' << 'test'
+ test.pattern = 'test/*_test.rb'
+task :default => 'test'
2  instapaper_full.gemspec
@@ -26,4 +26,6 @@ do |s|
s.add_dependency("yajl-ruby", "~> 1.1.0")
+ s.add_development_dependency("test-unit", "~> 2.4.2")
+ s.add_development_dependency("webmock", "~> 1.7.8")
13 lib/errors.rb
@@ -0,0 +1,13 @@
+module InstapaperFull
+ class API
+ class Error < RuntimeError
+ attr_reader :code, :message
+ def initialize(code, message)
+ @code, @message = code, message
+ end
+ end
+ end
93 lib/instapaper_full.rb
@@ -1,41 +1,36 @@
+require 'errors'
require 'json'
require 'faraday/request/oauth'
require 'faraday/response/parse_json'
module InstapaperFull
class API
- attr_accessor :options
- def initialize(options={})
+ attr_accessor :params
+ def initialize(options = {})
@options = options
def connection(options = {})
- skip_json = options.delete(:skip_json)
:proxy => @options[:proxy],
:ssl => {:verify => false},
:url => ""
- oauth_options = {
+ oauth_params = {
:consumer_key => @options[:consumer_key],
:consumer_secret => @options[:consumer_secret]
if authenticated?
- oauth_options[:token] = @options[:oauth_token]
- oauth_options[:token_secret] = @options[:oauth_token_secret]
+ oauth_params[:token] = @options[:oauth_token]
+ oauth_params[:token_secret] = @options[:oauth_token_secret]
end do |builder|
- builder.use Faraday::Request::OAuth, oauth_options
+ builder.use Faraday::Request::OAuth, oauth_params
builder.use Faraday::Request::UrlEncoded
- builder.use Faraday::Response::Logger
builder.adapter Faraday.default_adapter
- if authenticated? && !skip_json
- builder.use Faraday::Response::ParseJson
- end
@@ -43,7 +38,7 @@ def authenticated?
@options.has_key? :oauth_token and @options.has_key? :oauth_token_secret
- def authenticate(username,password)
+ def authenticate(username, password)
result = 'oauth/access_token' do |r|
@@ -64,73 +59,81 @@ def authenticate(username,password)
- def call(method, body = {})
- skip_json = body.delete(:skip_json)
- result = connection({:skip_json => skip_json}).post(method) do |r|
- r.body = body unless body.empty?
+ def call(method, params = {}, connection_options = {})
+ result = connection(connection_options).post(method) do |r|
+ r.body = params unless params.empty?
+ end
+ if result.headers['content-type'] == 'application/json'
+ JSON.parse(result.body).tap do |d|
+ if error = d.find { |e| e['type'] == 'error' }
+ raise['error_code'], error['message'])
+ end
+ end
+ else
+ raise, result.body) if result.status != 200
+ result.body
- return result.body
def verify_credentials
- def bookmarks_list(options = {})
- call('bookmarks/list', options)
+ def bookmarks_list(params = {})
+ call('bookmarks/list', params)
- def bookmarks_update_read_progress(options = {})
- call('bookmarks/update_read_progress', options)
+ def bookmarks_update_read_progress(params = {})
+ call('bookmarks/update_read_progress', params)
- def bookmarks_add(options = {})
- call('bookmarks/add',options)
+ def bookmarks_add(params = {})
+ call('bookmarks/add', params)
- def bookmarks_delete(options = {})
- call('bookmarks/delete', options)
+ def bookmarks_delete(params = {})
+ call('bookmarks/delete', params)
- def bookmarks_star(options = {})
- call('bookmarks/star', options)
+ def bookmarks_star(params = {})
+ call('bookmarks/star', params)
- def bookmarks_unstar(options = {})
- call('bookmarks/unstar', options)
+ def bookmarks_unstar(params = {})
+ call('bookmarks/unstar', params)
- def bookmarks_archive(options = {})
- call('bookmarks/archive', options)
+ def bookmarks_archive(params = {})
+ call('bookmarks/archive', params)
- def bookmarks_unarchive(options = {})
- call('bookmarks/unarchive', options)
+ def bookmarks_unarchive(params = {})
+ call('bookmarks/unarchive', params)
- def bookmarks_move(options = {})
- call('bookmarks/move', options)
+ def bookmarks_move(params = {})
+ call('bookmarks/move', params)
- def bookmarks_get_text(options = {})
- options[:skip_json] = true
- call('bookmarks/get_text', options)
+ def bookmarks_get_text(params = {})
+ call('bookmarks/get_text', params)
def folders_list
- def folders_add(options = {})
- call('folders/add', options)
+ def folders_add(params = {})
+ call('folders/add', params)
- def folders_delete(options = {})
- call('folders/delete', options)
+ def folders_delete(params = {})
+ call('folders/delete', params)
- def folders_set_order(options = {})
- call('folders/set_order', options)
+ def folders_set_order(params = {})
+ call('folders/set_order', params)
9 test/asset_helpers.rb
@@ -0,0 +1,9 @@
+module AssetHelpers
+ def http_response(name)
+ name += ".txt"
+ path = File.join(File.dirname(__FILE__), 'http_responses', name)
+ end
14 test/http_responses/access_token_failure.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 401 Unauthorized
+Date: Thu, 01 Dec 2011 16:32:47 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Content-Length: 26
+Connection: close
+Content-Type: text/html; charset=UTF-8
+Invalid xAuth credentials.
14 test/http_responses/access_token_success.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Date: Thu, 01 Dec 2011 16:31:04 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Content-Length: 132
+Connection: close
+Content-Type: application/x-www-form-urlencoded
14 test/http_responses/bookmarks_add_failure.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 400 Bad Request
+Date: Thu, 01 Dec 2011 16:43:12 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Content-Length: 70
+Connection: close
+Content-Type: application/json
+[{"type":"error","error_code":1240,"message":"Invalid URL specified"}]
14 test/http_responses/bookmarks_get_text_failure.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 400 Bad Request
+Date: Thu, 01 Dec 2011 16:36:08 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Content-Length: 79
+Connection: close
+Content-Type: application/json
+[{"type":"error","error_code":1241,"message":"Invalid or missing bookmark_id"}]
436 test/http_responses/bookmarks_get_text_success.txt
@@ -0,0 +1,436 @@
+HTTP/1.1 200 OK
+Date: Thu, 01 Dec 2011 16:35:34 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Connection: close
+Transfer-Encoding: chunked
+Content-Type: text/html; charset=utf-8
+ <head>
+ <title>Wieden+Kennedy &raquo; Why We&rsquo;re Not Hiring Creative Technologists</title>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; user-scalable=no; minimum-scale=1.0; maximum-scale=1.0;" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="robots" content="noindex"/>
+ <link rel="icon" href="/images/favicon.png"/>
+<!-- IP:TITLE
+Wieden+Kennedy » Why We’re Not Hiring Creative Technologists
+/IP:TITLE -->
+ <style type="text/css">
+ body {
+ font-family: Georgia;
+ font-size: 16px;
+ margin: 0px auto 0px auto;
+ width: 500px word-wrap: break-word;
+ }
+ h1 { font-size: 1.3em; }
+ h2 { font-size: 1.15em; }
+ h3, h4, h5, h6, h7 { font-size: 1.0em; }
+ img { border: 0; display: block; margin: 0.5em 0; }
+ pre, code { overflow: scroll; }
+ #story {
+ clear: both; padding: 0 10px; overflow: hidden; margin-bottom: 40px;
+ }
+ .bar {
+ color: #555;
+ font-family: 'Helvetica';
+ font-size: 11pt;
+ margin: 0 -20px;
+ padding: 10px 0;
+ }
+ .top { border-bottom: 2px solid #000; }
+ .top a {
+ display: block;
+ float: right;
+ text-decoration: none;
+ font-size: 11px;
+ background-color: #eee;
+ -webkit-border-radius: 8px;
+ -moz-border-radius: 8px;
+ padding: 2px 15px;
+ }
+ #story div {
+ margin: 1em 0;
+ }
+ .bottom {
+ border-top: 2px solid #000;
+ color: #555;
+ }
+ .bar a { color: #444; }
+ blockquote {
+ border-top: 1px solid #bbb;
+ border-bottom: 1px solid #bbb;
+ margin: 1.5em 0;
+ padding: 0.5em 0;
+ }
+ blockquote.short { font-style: italic; }
+ pre {
+ white-space: pre-wrap;
+ }
+ ul.bodytext, ol.bodytext {
+ list-style: none;
+ margin-left: 0;
+ padding-left: 0em;
+ }
+ </style>
+ </head>
+ <body onload="loadFont();">
+ <div class="bar top">
+ <a href="">View original</a>
+ <div class="sm"></div>
+ </div>
+ <div id="editing_controls" style="float: right; padding-top: 2px;">
+ </div>
+ <div id="story">
+<h1><a href="" rel="bookmark" title="Permanent Link to Why We&#x2019;re Not Hiring Creative Technologists">
+Why We’re Not Hiring Creative Technologists</a></h1>
+<p><a href="">
+<img title="102011programming" src="" alt="" /></a></p>
+<p>In the digital, interactive and social media-focused agency
+world, it’s easy to talk a big game, but for disciplines that
+require true, deep knowledge of the subject for success,
+there’s a fine line between “understanding” and
+“expertise”. Our Creative Technology Director Igor
+Clark explains why ideas aren’t enough, below the jump.</p>
+<p><br />
+By Igor Clark, Creative Technology Director</p>
+<p>I’ve pretty much had it with the term “Creative
+Technology”. I’m a “Creative Technology
+Director” myself, and even I’m over it: already it
+seems clichéd at best, and at worst, bordering on the
+meaningless. Here’s why.</p>
+<p>Not so long ago, the rise and rise of “digital”
+meant agencies having to come up with increasing amounts of
+interactive work. They didn’t know how to do it, so their
+developers got screwed, and the work suffered. Horribly.</p>
+<p>Few outside the tech teams grasped what was involved in building
+the software needed for digital campaigns. Crazy deadlines,
+unrealistic expectations, ill-considered and even ill-advised
+requirements led to ever-more “inventive” technical
+solutions. Then, when the last-minute hack they had to cobble
+together failed to stand up to the traffic they never promised it
+would, developers were cursed and vilified.</p>
+<p>But this wasn’t the really bad part. Software folk who
+found their way into agency-land either loved it, and stayed
+– or they didn’t, and left. For the ones who stuck
+around, and who felt the pain most acutely, the really bad part
+wasn’t the pressure or the deadlines: it was that their work
+wasn’t understood, so it wasn’t properly
+<p>Their work wasn’t purely science or technology; though
+grounded in both, it was far from the simple application of
+formulae or solving of equations. Developers knew that you
+couldn’t take a creative brief as a set of instructions and
+just “translate” it into software. You have to
+interpret it, and that takes an extra spark. A creative spark. They
+saw this was a fundamental part of the overall interactive creative
+process, and yet a parallel, creative process of its own. The
+naming perpetuated the misunderstanding, and so it had to
+<p>At the same time, people across agencies were recognizing that
+their existing creative model just wasn’t working out for
+“interactive”. Crews outside the fortress walls were
+doing innovative and engaging work, not only through using new and
+different technologies to do it (openFrameworks, Processing, robots
+and Arduino, computer vision &amp; Kinect, projection mapping, the
+list goes on), but also by trying out different approaches and
+processes. Namely: the technology was the creative.</p>
+<p><img title="102011kinect" src="" alt="" /></p>
+<p><em><small>Photo by <a href="">
+maveric2003</a>, licensed under the <a href="">Creative
+<p>In this way, “creative technology” was born: partly
+to assuage the accumulating angst of downtrodden developers; partly
+to enable those developers willing to step up to the creative plate
+also to step outside the conventional development toolkit; and
+partly – perhaps most importantly – to spread awareness
+across the board that where interactive work is concerned, creating
+involves making; making interactive stuff involves technology; and
+people can be creative in a range of disciplines, not only blue-sky
+<p>At its inception, this was A Good Thing, and it happened for
+Good Reasons. So what happened? Why do I now find myself wondering
+whether Creative Technology, as a label and a discipline, is
+effectively bankrupt?</p>
+<p>As in any new, burgeoning and (to many) incomprehensible field,
+most people have neither the background nor the time to get under
+the surface and really understand what it’s all about. So
+they need people to help do that.</p>
+<p>Unfortunately, in any field requiring background and time to get
+beneath the surface, the ninja dust is easy to throw in the faces
+of the uninitiated, disguising the underlying truth – which
+is, all too frequently, only a surface-level familiarity with the
+necessary materials.</p>
+<p>University and training courses spring up, servicing the new
+market of people wanting to get educated in the new field. Courses
+need funding; funding requires admissions; admissions policies get
+broadened; broad admissions policies welcome novices and amateurs.
+Ergo, disciplines become ill-disciplined, specialization becomes
+flabby and watered-down to the point of meaninglessness.</p>
+<p>Outcome: “creative technologists” who think that
+their daily use of social media, “passion for digital”
+and pile of half-baked ideas about QR codes, mobile integration and
+Facebook apps constitute an entitlement to have those ideas brought
+to life by the still-downtrodden developers, still languishing in
+the dungeons of overworked production companies and in-house
+development teams.</p>
+<p><img title="102011code2" src="" alt="" /></p>
+<p><em><small>Photo by <a href="">Igor
+<p>As a result, “Creative Technology” has become
+watered down to the point where people fresh out of “creative
+tech” courses need only sprinkle some of that digital
+ninja-dust on their resumés, and those without the requisite
+background, know-how and experience to sort the wheat from the
+chaff are none the wiser.</p>
+<p>The talent drifts off. The ninjas plan and conceive the work,
+and analyse its success, using metrics no-one else understands. Bad
+work proliferates, becomes accepted and normalized within the
+industry; the really good people get further alienated, more dust
+is thrown to disguise others taking their places, and round and
+round it goes, until no-one knows who’s a ninja and
+who’s not. Except for the best people, who’ve left the
+agency scene in the dust – and the audience, of course, who
+are left unmoved. Or, worse, switched off.</p>
+<p>Is this pattern inevitable? Can we, as agency-land
+technologists, do anything about it?</p>
+<p>Clearly many non-technical factors are involved, but there is
+one simple and concrete thing we can do: stop hiring
+“creative technologists”. Hire coders. Reject
+compromise on this front, and resist pressure to give in to it.
+Only hire people to work at the crossover of creative and
+technology if they have strong, practical, current coding
+<p>Don’t fall for the illusion that a candidate is creatively
+strong enough to compensate for the weak code. Spending a year or
+two on a training course gaining a passing acquaintance with a
+couple of trending technologies isn’t good enough. They need
+to live and breathe this stuff, and to use the appropriate
+languages and tools fluently and transparently, without stopping to
+think about it. So if a person puts “creative
+technologist” on their resumé, but doesn’t know
+how to code, can’t show you things they’ve made, and
+can’t prove they made them by explaining why they wrote the
+code the way they did, don’t hire them. Simple as that.</p>
+<p>Think this sounds elitist? Well, it is – and there’s
+a reason.</p>
+<p><img title="102011pages" src="" alt="" /></p>
+<p><em><small>Photo by <a href="">Igor
+<p>Agencies don’t hire writers just because they know the
+rules of grammar. We hire them because they’re eloquent,
+lucid, imaginative wordsmiths. We hire them because of their
+practised ability to lovingly craft words into things that work.
+Things that make people feel.</p>
+<p>There are people who engineer excellent software. There are
+people who come up with amazing ideas. The interactive space by
+definition requires the fusion of the two, and technology at the
+heart of creation. At the point of intersection, you’re going
+to need people who understand both, and who have one foot on either
+side. As Forrester’s Mike Gualtieri recently wrote in
+<a href="">
+a fresh piece about how to create great software</a>, that means
+“renaissance developers who have passion, creativity,
+discipline, domain knowledge, and user empathy”.</p>
+<p>This is difficult territory for creative agencies. Maybe you
+don’t know how to hire these people yet. Maybe it
+doesn’t fit with your structures. Tough, isn’t it? But
+you’re going to have to deal with it, and fix it.</p>
+<p>Ultimately, to do that you need to provide an environment
+that’s as appealing and satisfying for extraordinary,
+creative software people as the one you already provide is for
+traditional creative folks. But it also needs to be as appealing to
+this new breed as their potential alternate settings at Google,
+Facebook, Tech Startup X. Fortunately, you have the potential to
+make it even more so for genuine creative coders – because
+they’re not looking for pure engineering any more than you
+<p>While you don’t need to become an engineering company, you
+face some of their challenges. You need to understand, accept and
+embrace some of the nuts and bolts of software development, and
+take on board the work dedicated shops are doing on its processes.
+You need such a strong streak of code running through the
+atmosphere that coders want to come to you, and everyone else gets
+code spilling over them.</p>
+<p>But “digital” is a hybrid realm, and you need to
+provide a balance. Fortunately, you’re in a perfect position
+to counterpoint the engineering-first environment that others have
+so successfully developed, leading so successfully to technically
+excellent, efficient, and often creatively uninspiring work. You
+have the creative angle covered (right?), so to get to the hybrid
+middle-ground, you have to allow developers the flexibility, the
+leeway and the time to engineer solid work – and you have to
+welcome the hybrid creative coders into the heart of what you do,
+to make a hybrid place where they feel at home, and where they can
+help ensure that what gets sold makes sense, and that it can be
+made without actually killing a team of engineers in the
+<p><img title="102011comment" src="" alt="" /></p>
+<p><em><small>Photo by <a href="">Igor
+<p>Don’t get me wrong, this is hard, and it’ll take
+time. It’s not just procedural, but cultural, so a big part
+of doing it comes down to who you hire and how you let them do
+their thing. But that’s exactly the point. That’s why
+it’s most important, way before you get all that fixed, and
+as the first major step on that road: just don’t hire
+“creative technologists” who aren’t strong
+<p>Bottom line, these people need to make stuff, fast. They need to
+prove or disprove concepts, in ways that non-technologists
+understand, fast. So they need to know how to code efficiently,
+economically and effectively. They need to understand the
+appropriate technology stack from top to bottom, know which tools
+are right for the job – and most of all, they must be
+prepared to crack their knuckles, roll up their sleeves and get
+their fingers into the code. Up to the elbows.</p>
+<p>You’re probably thinking, “OK, those people are few
+and far between; we still need people to bridge the gap between
+them and the rest of the agency”. You’re not wrong;
+you’ve put your finger straight on the really interesting
+corollary, and the exact reason why creative agencies could be the
+most inspiring environment for creative coders, which is simply
+this: we have to be.</p>
+<p>With integrated interactive work ever more critical, creative
+agencies need to change drastically, in ways that suit perfectly
+those people we most need to attract. We need to adapt and evolve
+to survive. The serious talent is doing it for itself; going to
+small shops, shooting solo. To reach the very best people, we need
+to change in ways that make them want to come to us; to allow and
+even help them to change us, and to help us shape our
+<p><img title="102011code1" src="" alt="" /></p>
+<p><em><small>Photo by <a href="">Igor
+<p>This is more than “building a digital team”, or
+“covering digital bases”. The agency as a whole has to
+step up to the tectonic plate and realize that not only are
+digital, social, interactive, gaming all here to stay, but they
+already permeate the entire landscape of what consumers are doing.
+We need to change our processes, structures and approach to how we
+create in order to accommodate this stuff, and open our arms to the
+people who make it happen.</p>
+<p>Start off by refusing to believe people who tell you that hiring
+them to do the understanding for you means you can carry on as you
+were. Instead, hire the right people in the right places, and make
+the changes necessary to let them do what they do. Creative people
+who can code up a storm, and, critically, experienced people who
+can properly assess the code they’re shown. These are the
+people who will help us flourish – if we can help them to do
+the same.</p>
+<p>At W+K we’re always looking to meet good technology
+people, and we want to read your code. Developers, engineers,
+creative coders. If that’s you, if you’ve got the
+endurance to make it this far, and if you’re interested in
+applying your creativity through code with us in Portland, then why
+not <a href="">get in
+<p>Find Igor on Twitter at <a href="">@IgorClark</a>.</p>
+<p>Posted on 10.21.11</p>
+<p>Category: <a href="" title="View all posts in Guest Post" rel="category tag">Guest
+Post</a>, <a href="" title="View all posts in Interactive" rel="category tag">Interactive</a></p>
+<p><a href="" rel="prev">«&#160;Older Post</a></p>
+<ul class="bodytext"><li><a href="">
+<img src="" alt="092011danadcolor" title="092011danadcolor" /></a>
+<h3><a href="">
+Dan Wieden Honored with Catalyst Award at 2011 Adcolor
+<p>This week, our own Dan Wieden was honored with the Catalyst
+award at the 2011 Adcolor Awards ceremony in Los Angeles. Dan has
+always been committed to making the voice and people of advertising
+and specifically W+K more diverse. Our diversity and inclusion
+manager Porsha Monroe has shared some thoughts with us.</p>
+<a href="">Read
+<li><a href="">
+<img src="" alt="090711piemain" title="090711piemain" /></a>
+<h3><a href="">
+W+K Community &amp; Mayor Sam Adams Celebrate First Thursday
+Portland Incubator Experiment (PIE) Ribbon-Cutting</a></h3>
+<p>Members of Wieden+Kennedy, the Portland tech community, Mayor
+Sam Adams and our friends and associates gathered together for last
+week’s <a href="">First
+Thursday</a> to celebrate the official ribbon cutting ceremony for
+the <a href="">Portland Incubator Experiment</a>
+(more commonly known as PIE), a collaborative center that partners
+leading brands with technology innovators to cultivate community,
+entrepreneurship and creative thinking.</p>
+<a href="">Read
+<li><a href="">
+<img src="" alt="082911djmain" title="082911djmain" /></a>
+<h3><a href="">
+Musician &amp; Comic Artist Daniel Johnston Concludes <em>Space
+Ducks</em> Art Show with Concert Performance in W+K Foyer</a></h3>
+<p>On Thursday, the month-long show of Daniel Johnston’s
+Space Ducks: An Infinite Comic Book of Musical Greatness concluded
+with a performance by the man himself. The event coincided with the
+relaunch of his site,, produced in
+partnership with W+K and WKE.</p>
+<a href="">Read
+<ul class="bodytext"><li><a href="">
+<img src="" alt="image" /></a>
+<h3><a href="">
+We All Love Math T-shirt</a></h3>
+<p>If this shirt + its thesis succeed, we will all revel in our
+essential mathness.</p>
+<li><a href=""><img src="" alt="image" /></a>
+<h3><a href="">Graphics
+Design™ T-shirt</a></h3>
+<p>The soul-wrenching existential crisis many commercial artists
+struggle with…</p>
+<li><a href=""><img src="" alt="image" /></a>
+<h3><a href="">Modern
+Baby Onesie</a></h3>
+<p>You are observing a genuine onesie that… Oh, man. That
+baby’s such a dick.</p>
+</ul><p><a href="">See more at the
+Goodness Store.</a></p>
+ <div class="bar bottom">
+ </div>
+ </body>
14 test/http_responses/bookmarks_list_success.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Date: Thu, 01 Dec 2011 16:33:47 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Connection: close
+Transfer-Encoding: chunked
+Content-Type: application/json
+[{"type":"meta"},{"type":"user","user_id":140230,"username":"","subscription_is_active":"1"},{"type":"bookmark","bookmark_id":222945036,"url":"http:\/\/\/2011\/11\/quiet-pleas.html","title":"Quiet pleas","description":"","time":1321217383,"starred":"0","private_source":"","hash":"cSyver1h","progress":"0","progress_timestamp":1321222208},{"type":"bookmark","bookmark_id":222697290,"url":"http:\/\/\/weblog\/2011\/11\/poppies-as-national-cultural-audit.html","title":"poppies as national cultural audit","description":"","time":1321119220,"starred":"0","private_source":"","hash":"n7Ic8ADA","progress":"0","progress_timestamp":1321212733},{"type":"bookmark","bookmark_id":222036793,"url":"http:\/\/\/anil\/2011\/11\/how-the-99-and-the-tea-party-can-occupy-whitehousegov.html","title":"How the 99% and the Tea Party can Occupy","description":"","time":1320919525,"starred":"0","private_source":"","hash":"4PGtE6DR","progress":"0","progress_timestamp":1321099791},{"type":"bookmark","bookmark_id":221679923,"url":"http:\/\/\/2011\/11\/the_social_graph_is_neither\/","title":"The Social Graph is Neither","description":"","time":1320825317,"starred":"0","private_source":"","hash":"JlTb3HiL","progress":"0","progress_timestamp":1321099791},{"type":"bookmark","bookmark_id":221194670,"url":"http:\/\/\/weblog\/2011\/11\/the-post-speculative-olympics.html","title":"the post-speculative Olympics","description":"","time":1320700650,"starred":"0","private_source":"","hash":"JY4hOv1h","progress":"0.655525","progress_timestamp":1322300394},{"type":"bookmark","bookmark_id":221193800,"url":"http:\/\/\/hollywood\/features\/2011\/12\/david-fincher-201112","title":"V.F. Portrait: David Fincher | Hollywood | Vanity Fair","description":"","time":1320700460,"starred":"0","private_source":"","hash":"TQWRqpPd","progress":"0.694745","progress_timestamp":1321431332},{"type":"bookmark","bookmark_id":220538037,"url":"http:\/\/\/print\/?\/news\/media\/elisabeth-murdoch-2011-11\/","title":"Elisabeth of the Murdochs","description":"","time":1320483738,"starred":"0","private_source":"","hash":"7EnvQLNi","progress":"0","progress_timestamp":1320613297},{"type":"bookmark","bookmark_id":220537972,"url":"http:\/\/\/business\/features\/2011\/12\/murdoch-kids-201112.print","title":"The Rules of Succession | Business | Vanity Fair","description":"","time":1320483700,"starred":"0","private_source":"","hash":"LR7xzrgT","progress":"0","progress_timestamp":1320613300},{"type":"bookmark","bookmark_id":219761486,"url":"http:\/\/\/interviews\/6089\/the-art-of-fiction-no-211-william-gibson","title":"Paris Review - The Art of Fiction No. 211, William Gibson","description":"","time":1320262786,"starred":"0","private_source":"","hash":"2zPjDydh","progress":"0.721902","progress_timestamp":1320597912},{"type":"bookmark","bookmark_id":218916491,"url":"http:\/\/\/blogs\/adamcurtis\/2011\/10\/dream_on.html","title":"DREAM ON","description":"","time":1320044972,"starred":"0","private_source":"","hash":"g6KlbVIC","progress":"0","progress_timestamp":1320309552},{"type":"bookmark","bookmark_id":202971816,"url":"http:\/\/\/wired\/archive\/4.12\/ffglass_pr.html","title":"4.12: Mother Earth Mother Board","description":"","time":1319928054,"starred":"0","private_source":"","hash":"ZguEiZ00","progress":"0.0743572","progress_timestamp":1321227679},{"type":"bookmark","bookmark_id":217988594,"url":"http:\/\/\/2011\/10\/27\/its-the-rich-wot-gets-the-pleasure\/","title":"It\u2019s the Rich Wot Gets the Pleasure","description":"","time":1319742534,"starred":"0","private_source":"","hash":"KjYD4hen","progress":"0","progress_timestamp":1319961988},{"type":"bookmark","bookmark_id":217124108,"url":"http:\/\/\/2011\/03\/11\/the-economics-of-bike-lanes-%E2%80%93-how-can-john-cassidy-get-it-so-wrong\/","title":"The Economics of Bike Lanes \u2013 How can John Cassidy get it so wrong? | Economics Intelligence","description":"","time":1319535770,"starred":"0","private_source":"","hash":"hPpLAMzZ","progress":"0","progress_timestamp":1319961991},{"type":"bookmark","bookmark_id":216402077,"url":"http:\/\/\/352-dyson-george\/353-evolution-and-innovation","title":"George Dyson | Evolution and Innovation - Information Is Cheap, Meaning Is Expensive | The European Magazine","description":"","time":1319326029,"starred":"0","private_source":"","hash":"jfzPj89D","progress":"0","progress_timestamp":1319534414},{"type":"bookmark","bookmark_id":216295911,"url":"http:\/\/\/charlie\/blog-static\/2011\/10\/a-cultural-experiment.html","title":"A cultural thought experiment","description":"","time":1319294676,"starred":"0","private_source":"","hash":"S9RqWztN","progress":"0","progress_timestamp":1319534424},{"type":"bookmark","bookmark_id":216141787,"url":"http:\/\/\/2011\/10\/21\/why-we-are-not-hiring-creative-technologists\/","title":"Wieden+Kennedy \u00bb Why We\u2019re Not Hiring Creative Technologists","description":"","time":1319235364,"starred":"0","private_source":"","hash":"mWKHh9eT","progress":"0","progress_timestamp":1319534425},{"type":"bookmark","bookmark_id":215927373,"url":"http:\/\/\/how-i-live-on-7000-per-year.html","title":"\u00bb How I live on $7,000 per year Early Retirement Extreme: \u2014 The choice nobody ever told you about","description":"","time":1319181501,"starred":"0","private_source":"","hash":"DuQSUQ5U","progress":"0","progress_timestamp":1319534436},{"type":"bookmark","bookmark_id":215927355,"url":"http:\/\/\/peak-oil-next-kondratiev-cycle-turningsand-ere.html","title":"\u00bb Peak oil, the next Kondratiev cycle, generational turnings, and ERE Early Retirement Extreme: \u2014 The choice nobody ever told you about","description":"","time":1319181495,"starred":"0","private_source":"","hash":"V4tGRMj7","progress":"0","progress_timestamp":1319534438},{"type":"bookmark","bookmark_id":215927269,"url":"http:\/\/\/node\/21533400","title":"Capitalism and its critics: Rage against the machine | The Economist","description":"","time":1319181457,"starred":"0","private_source":"","hash":"tk3W7aXn","progress":"0","progress_timestamp":1319534439},{"type":"bookmark","bookmark_id":215201984,"url":"http:\/\/\/article\/SB10001424052970203914304576627252381486880.html?mod=WSJ_hps_editorsPicks_1","title":"A Future for Pay Phones? -","description":"","time":1319006861,"starred":"0","private_source":"","hash":"BY3la9rz","progress":"0","progress_timestamp":1319007125},{"type":"bookmark","bookmark_id":214644989,"url":"http:\/\/\/2011\/10\/17\/show-me-the-money\/","title":"George Monbiot \u2013 Show Me The Money","description":"","time":1318881511,"starred":"0","private_source":"","hash":"aYYsaIpT","progress":"0","progress_timestamp":1319007128},{"type":"bookmark","bookmark_id":213000484,"url":"http:\/\/\/Story.aspx?StoryId=1095","title":"Mind the Gap","description":"","time":1318400657,"starred":"0","private_source":"","hash":"AGxX4SrJ","progress":"0","progress_timestamp":1318521885},{"type":"bookmark","bookmark_id":213000080,"url":"http:\/\/\/blog\/2011\/10\/11\/talk-real-time-updates-on-the-cheap-for-fun-and-profit\/","title":"Talk: Real-time Updates on the Cheap for Fun and Profit","description":"","time":1318400552,"starred":"0","private_source":"","hash":"lXv7Dujf","progress":"0","progress_timestamp":1318521895},{"type":"bookmark","bookmark_id":212091573,"url":"http:\/\/\/world\/2011\/oct\/08\/amanda-knox-facial-expressions","title":"Amanda Knox: What's in a face? | World news | The Guardian","description":"","time":1318148366,"starred":"0","private_source":"","hash":"LJ0N8IeG","progress":"0","progress_timestamp":1318521896},{"type":"bookmark","bookmark_id":211853230,"url":"http:\/\/\/podcast\/s01e43-chris-nelson","title":"Ruby Cloud | Ruby Support | Engine Yard","description":"","time":1318058840,"starred":"0","private_source":"","hash":"Js779Psg","progress":"0","progress_timestamp":1318521902}]
14 test/http_responses/verify_credentials_success.txt
@@ -0,0 +1,14 @@
+HTTP/1.1 200 OK
+Date: Thu, 01 Dec 2011 16:31:05 GMT
+Server: Apache
+X-Robots-Tag: noindex
+Cache-Control: no-cache
+Pragma: no-cache
+X-Powered-By: a lot of coffee and Phish
+Vary: Accept-Encoding
+Content-Length: 96
+Connection: close
+Content-Type: application/json
77 test/instapaper_api_test.rb
@@ -0,0 +1,77 @@
+require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
+class InstapaperAPITest < Test::Unit::TestCase
+ include AssetHelpers
+ def stub_successful_authentication
+ stub_request(:post, "").to_return(
+ http_response('access_token_success')
+ )
+ end
+ def stub_failed_authentication
+ stub_request(:post, "").to_return(
+ http_response('access_token_failure')
+ )
+ end
+ def stub_successful_verify_credentials
+ stub_request(:post, "").to_return(
+ http_response('verify_credentials_success')
+ )
+ end
+ def stub_successful_bookmarks_list
+ stub_request(:post, "").to_return(
+ http_response('bookmarks_list_success')
+ )
+ end
+ def stub_failed_bookmarks_add
+ stub_request(:post, "").to_return(
+ http_response('bookmarks_add_failure')
+ )
+ end
+ def authenticated_client
+ => "key",
+ :consumer_secret => "secret",
+ :oauth_token => "token",
+ :oauth_token_secret => "tokensecret")
+ end
+ def test_successful_authentication
+ stub_successful_authentication
+ stub_successful_verify_credentials
+ ip = => "test", :consumer_secret => "")
+ assert_equal true, ip.authenticate("", "test")
+ end
+ def test_failed_authentication
+ stub_failed_authentication
+ ip = => "test", :consumer_secret => "")
+ assert_equal false, ip.authenticate("", "test")
+ end
+ def test_successful_bookmarks_list
+ stub_successful_bookmarks_list
+ list = authenticated_client.bookmarks_list
+ assert_equal 27, list.length # 25 + 1 user element + 1 meta element
+ end
+ def test_failed_bookmarks_add
+ stub_failed_bookmarks_add
+ assert_raise(InstapaperFull::API::Error) { authenticated_client.bookmarks_add }
+ begin
+ authenticated_client.bookmarks_add
+ rescue InstapaperFull::API::Error => e
+ assert_equal 1240, e.code
+ assert_equal "Invalid URL specified", e.message
+ end
+ end
12 test/test_helper.rb
@@ -0,0 +1,12 @@
+require 'test/unit'
+unless $LOAD_PATH.include? 'lib'
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
+ $LOAD_PATH.unshift(File.join($LOAD_PATH.first, '..', 'lib'))
+require 'instapaper_full'
+require 'asset_helpers'
+require 'webmock/test_unit'
Please sign in to comment.
Something went wrong with that request. Please try again.