instapaper_full 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Ruby wrapper for the [Instapaper Full API](http://www.instapaper.com/api/full)
4
4
 
5
- Draft version.
6
-
7
5
  Note that you need to [request OAuth Application tokens manually](http://www.instapaper.com/main/request_oauth_consumer_token) and that most methods only work for Instapaper subscribers.
8
6
 
9
7
  # Installation
@@ -15,4 +13,13 @@ Note that you need to [request OAuth Application tokens manually](http://www.ins
15
13
  ip = InstapaperFull::API.new :consumer_key => "my key", :consumer_secret => "my secret"
16
14
  ip.authenticate "someone@example.com", "password"
17
15
  puts ip.options.user_id
18
- puts ip.bookmarks_list(:limit => 1)[0]['url']
16
+ ip.bookmarks_list(:limit => 1) do |b|
17
+ if b['type'] == 'bookmark'
18
+ puts b['url']
19
+ end
20
+ end
21
+
22
+ # Authors
23
+
24
+ * [Matt Biddulph](http://github.com/mattb)
25
+ * [Tom Taylor / Newspaper Club](http://github.com/tomtaylor)
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
1
  require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/*_test.rb'
8
+ end
9
+
10
+ task :default => 'test'
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = "instapaper_full"
7
7
  s.version = InstapaperFull::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Matt Biddulph"]
10
- s.email = ["mb@hackdiary.com"]
9
+ s.authors = ["Matt Biddulph", "Tom Taylor"]
10
+ s.email = ["mb@hackdiary.com", "tom@tomtaylor.co.uk"]
11
11
  s.homepage = "https://github.com/mattb/instapaper_full"
12
12
  s.summary = %q{Wrapper for the Instapaper Full Developer API}
13
13
  s.description = %q{See http://www.instapaper.com/api/full}
@@ -26,4 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency("yajl-ruby", "~> 1.1.0")
27
27
 
28
28
  s.add_development_dependency("rake")
29
+ s.add_development_dependency("test-unit", "~> 2.4.2")
30
+ s.add_development_dependency("webmock", "~> 1.7.8")
29
31
  end
data/lib/errors.rb ADDED
@@ -0,0 +1,13 @@
1
+ module InstapaperFull
2
+ class API
3
+ class Error < RuntimeError
4
+
5
+ attr_reader :code, :message
6
+
7
+ def initialize(code, message)
8
+ @code, @message = code, message
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,4 @@
1
+ require 'errors'
1
2
  require 'json'
2
3
  require 'faraday/request/oauth'
3
4
  require 'faraday/response/parse_json'
@@ -5,37 +6,31 @@ require 'faraday/response/parse_json'
5
6
  module InstapaperFull
6
7
  class API
7
8
  attr_accessor :options
8
- def initialize(options={})
9
+ def initialize(options = {})
9
10
  @options = options
10
11
  end
11
12
 
12
13
  def connection(options = {})
13
- skip_json = options.delete(:skip_json)
14
-
15
14
  options.merge!({
16
15
  :proxy => @options[:proxy],
17
16
  :ssl => {:verify => false},
18
17
  :url => "https://www.instapaper.com/api/1/"
19
18
  })
20
19
 
21
- oauth_options = {
20
+ oauth_params = {
22
21
  :consumer_key => @options[:consumer_key],
23
22
  :consumer_secret => @options[:consumer_secret]
24
23
  }
25
24
 
26
25
  if authenticated?
27
- oauth_options[:token] = @options[:oauth_token]
28
- oauth_options[:token_secret] = @options[:oauth_token_secret]
26
+ oauth_params[:token] = @options[:oauth_token]
27
+ oauth_params[:token_secret] = @options[:oauth_token_secret]
29
28
  end
30
29
 
31
30
  Faraday.new(options) do |builder|
32
- builder.use Faraday::Request::OAuth, oauth_options
31
+ builder.use Faraday::Request::OAuth, oauth_params
33
32
  builder.use Faraday::Request::UrlEncoded
34
- builder.use Faraday::Response::Logger
35
33
  builder.adapter Faraday.default_adapter
36
- if authenticated? && !skip_json
37
- builder.use Faraday::Response::ParseJson
38
- end
39
34
  end
40
35
  end
41
36
 
@@ -43,10 +38,10 @@ module InstapaperFull
43
38
  @options.has_key? :oauth_token and @options.has_key? :oauth_token_secret
44
39
  end
45
40
 
46
- def authenticate(username,password)
41
+ def authenticate(username, password)
47
42
  @options.delete(:oauth_token)
48
43
  @options.delete(:oauth_token_secret)
49
- result = connection.post 'oauth/access_token' do |r|
44
+ result = connection.post 'oauth/access_token' do |r|
50
45
  r.body = { :x_auth_username => username, :x_auth_password => password, :x_auth_mode => "client_auth" }
51
46
  end
52
47
  if result.status == 200
@@ -64,73 +59,81 @@ module InstapaperFull
64
59
  end
65
60
  end
66
61
 
67
- def call(method, body = {})
68
- skip_json = body.delete(:skip_json)
69
- result = connection({:skip_json => skip_json}).post(method) do |r|
70
- r.body = body unless body.empty?
62
+ def call(method, params = {}, connection_options = {})
63
+ result = connection(connection_options).post(method) do |r|
64
+ r.body = params unless params.empty?
65
+ end
66
+
67
+ if result.headers['content-type'] == 'application/json'
68
+ JSON.parse(result.body).tap do |d|
69
+ if error = d.find { |e| e['type'] == 'error' }
70
+ raise InstapaperFull::API::Error.new(error['error_code'], error['message'])
71
+ end
72
+ end
73
+ else
74
+ raise InstapaperFull::API::Error.new(-1, result.body) if result.status != 200
75
+ result.body
71
76
  end
72
- return result.body
73
77
  end
74
78
 
75
79
  def verify_credentials
76
80
  call('account/verify_credentials')[0]
77
81
  end
78
82
 
79
- def bookmarks_list(options = {})
80
- call('bookmarks/list', options)[2..-1] # slice off the 'meta' and 'user' from the front of the array
83
+ def bookmarks_list(params = {})
84
+ call('bookmarks/list', params)
81
85
  end
82
86
 
83
- def bookmarks_update_read_progress(options = {})
84
- call('bookmarks/update_read_progress', options)
87
+ def bookmarks_update_read_progress(params = {})
88
+ call('bookmarks/update_read_progress', params)
85
89
  end
86
90
 
87
- def bookmarks_add(options = {})
88
- call('bookmarks/add',options)
91
+ def bookmarks_add(params = {})
92
+ call('bookmarks/add', params)
89
93
  end
90
94
 
91
- def bookmarks_delete(options = {})
92
- call('bookmarks/delete', options)
95
+ def bookmarks_delete(params = {})
96
+ call('bookmarks/delete', params)
93
97
  end
94
98
 
95
- def bookmarks_star(options = {})
96
- call('bookmarks/star', options)
99
+ def bookmarks_star(params = {})
100
+ call('bookmarks/star', params)
97
101
  end
98
102
 
99
- def bookmarks_unstar(options = {})
100
- call('bookmarks/unstar', options)
103
+ def bookmarks_unstar(params = {})
104
+ call('bookmarks/unstar', params)
101
105
  end
102
106
 
103
- def bookmarks_archive(options = {})
104
- call('bookmarks/archive', options)
107
+ def bookmarks_archive(params = {})
108
+ call('bookmarks/archive', params)
105
109
  end
106
110
 
107
- def bookmarks_unarchive(options = {})
108
- call('bookmarks/unarchive', options)
111
+ def bookmarks_unarchive(params = {})
112
+ call('bookmarks/unarchive', params)
109
113
  end
110
114
 
111
- def bookmarks_move(options = {})
112
- call('bookmarks/move', options)
115
+ def bookmarks_move(params = {})
116
+ call('bookmarks/move', params)
113
117
  end
114
118
 
115
- def bookmarks_get_text(options = {})
116
- options[:skip_json] = true
117
- call('bookmarks/get_text', options)
119
+ def bookmarks_get_text(params = {})
120
+ call('bookmarks/get_text', params)
118
121
  end
119
122
 
120
123
  def folders_list
121
124
  call('folders/list')
122
125
  end
123
126
 
124
- def folders_add(options = {})
125
- call('folders/add', options)
127
+ def folders_add(params = {})
128
+ call('folders/add', params)
126
129
  end
127
130
 
128
- def folders_delete(options = {})
129
- call('folders/delete', options)
131
+ def folders_delete(params = {})
132
+ call('folders/delete', params)
130
133
  end
131
134
 
132
- def folders_set_order(options = {})
133
- call('folders/set_order', options)
135
+ def folders_set_order(params = {})
136
+ call('folders/set_order', params)
134
137
  end
135
138
  end
136
139
  end
@@ -1,3 +1,3 @@
1
1
  module InstapaperFull
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,9 @@
1
+ module AssetHelpers
2
+
3
+ def http_response(name)
4
+ name += ".txt"
5
+ path = File.join(File.dirname(__FILE__), 'http_responses', name)
6
+ File.read(path)
7
+ end
8
+
9
+ end
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 401 Unauthorized
2
+ Date: Thu, 01 Dec 2011 16:32:47 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Content-Length: 26
11
+ Connection: close
12
+ Content-Type: text/html; charset=UTF-8
13
+
14
+ Invalid xAuth credentials.
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Thu, 01 Dec 2011 16:31:04 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Content-Length: 132
11
+ Connection: close
12
+ Content-Type: application/x-www-form-urlencoded
13
+
14
+ oauth_token=thisisatoken&oauth_token_secret=thisisasecret
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 400 Bad Request
2
+ Date: Thu, 01 Dec 2011 16:43:12 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Content-Length: 70
11
+ Connection: close
12
+ Content-Type: application/json
13
+
14
+ [{"type":"error","error_code":1240,"message":"Invalid URL specified"}]
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 400 Bad Request
2
+ Date: Thu, 01 Dec 2011 16:36:08 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Content-Length: 79
11
+ Connection: close
12
+ Content-Type: application/json
13
+
14
+ [{"type":"error","error_code":1241,"message":"Invalid or missing bookmark_id"}]
@@ -0,0 +1,436 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Thu, 01 Dec 2011 16:35:34 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Connection: close
11
+ Transfer-Encoding: chunked
12
+ Content-Type: text/html; charset=utf-8
13
+
14
+
15
+ <html>
16
+ <head>
17
+ <title>Wieden+Kennedy &raquo; Why We&rsquo;re Not Hiring Creative Technologists</title>
18
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; user-scalable=no; minimum-scale=1.0; maximum-scale=1.0;" />
19
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
20
+ <meta name="robots" content="noindex"/>
21
+ <link rel="icon" href="/images/favicon.png"/>
22
+ <!-- IP:TITLE
23
+ Wieden+Kennedy » Why We’re Not Hiring Creative Technologists
24
+ /IP:TITLE -->
25
+ <!-- IP:IMAGES
26
+
27
+ /IP:IMAGES -->
28
+ <style type="text/css">
29
+ body {
30
+ font-family: Georgia;
31
+ font-size: 16px;
32
+ margin: 0px auto 0px auto;
33
+ width: 500px word-wrap: break-word;
34
+ }
35
+
36
+ h1 { font-size: 1.3em; }
37
+ h2 { font-size: 1.15em; }
38
+ h3, h4, h5, h6, h7 { font-size: 1.0em; }
39
+
40
+ img { border: 0; display: block; margin: 0.5em 0; }
41
+ pre, code { overflow: scroll; }
42
+ #story {
43
+ clear: both; padding: 0 10px; overflow: hidden; margin-bottom: 40px;
44
+ }
45
+
46
+ .bar {
47
+ color: #555;
48
+ font-family: 'Helvetica';
49
+ font-size: 11pt;
50
+ margin: 0 -20px;
51
+ padding: 10px 0;
52
+ }
53
+ .top { border-bottom: 2px solid #000; }
54
+
55
+ .top a {
56
+ display: block;
57
+ float: right;
58
+ text-decoration: none;
59
+ font-size: 11px;
60
+ background-color: #eee;
61
+ -webkit-border-radius: 8px;
62
+ -moz-border-radius: 8px;
63
+ padding: 2px 15px;
64
+ }
65
+
66
+ #story div {
67
+ margin: 1em 0;
68
+ }
69
+
70
+ .bottom {
71
+ border-top: 2px solid #000;
72
+ color: #555;
73
+ }
74
+
75
+ .bar a { color: #444; }
76
+
77
+ blockquote {
78
+ border-top: 1px solid #bbb;
79
+ border-bottom: 1px solid #bbb;
80
+ margin: 1.5em 0;
81
+ padding: 0.5em 0;
82
+ }
83
+ blockquote.short { font-style: italic; }
84
+
85
+ pre {
86
+ white-space: pre-wrap;
87
+ }
88
+
89
+ ul.bodytext, ol.bodytext {
90
+ list-style: none;
91
+ margin-left: 0;
92
+ padding-left: 0em;
93
+ }
94
+
95
+ </style>
96
+ </head>
97
+ <body onload="loadFont();">
98
+ <div class="bar top">
99
+ <a href="http://blog.wk.com/2011/10/21/why-we-are-not-hiring-creative-technologists/">View original</a>
100
+ <div class="sm">blog.wk.com</div>
101
+ </div>
102
+
103
+ <div id="editing_controls" style="float: right; padding-top: 2px;">
104
+ </div>
105
+
106
+ <div id="story">
107
+ <div>
108
+ <h1><a href="http://blog.wk.com/2011/10/21/why-we-are-not-hiring-creative-technologists/" rel="bookmark" title="Permanent Link to Why We&#x2019;re Not Hiring Creative Technologists">
109
+ Why We’re Not Hiring Creative Technologists</a></h1>
110
+ <p><a href="http://blog.wk.com/2011/10/20/why-we%E2%80%99re-not-hiring-creative-technologists">
111
+ <img title="102011programming" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011programming.jpg" alt="" /></a></p>
112
+ <p>In the digital, interactive and social media-focused agency
113
+ world, it’s easy to talk a big game, but for disciplines that
114
+ require true, deep knowledge of the subject for success,
115
+ there’s a fine line between “understanding” and
116
+ “expertise”. Our Creative Technology Director Igor
117
+ Clark explains why ideas aren’t enough, below the jump.</p>
118
+ <p><br />
119
+ By Igor Clark, Creative Technology Director</p>
120
+ <p>I’ve pretty much had it with the term “Creative
121
+ Technology”. I’m a “Creative Technology
122
+ Director” myself, and even I’m over it: already it
123
+ seems clichéd at best, and at worst, bordering on the
124
+ meaningless. Here’s why.</p>
125
+ <p>Not so long ago, the rise and rise of “digital”
126
+ meant agencies having to come up with increasing amounts of
127
+ interactive work. They didn’t know how to do it, so their
128
+ developers got screwed, and the work suffered. Horribly.</p>
129
+ <p>Few outside the tech teams grasped what was involved in building
130
+ the software needed for digital campaigns. Crazy deadlines,
131
+ unrealistic expectations, ill-considered and even ill-advised
132
+ requirements led to ever-more “inventive” technical
133
+ solutions. Then, when the last-minute hack they had to cobble
134
+ together failed to stand up to the traffic they never promised it
135
+ would, developers were cursed and vilified.</p>
136
+ <p>But this wasn’t the really bad part. Software folk who
137
+ found their way into agency-land either loved it, and stayed
138
+ – or they didn’t, and left. For the ones who stuck
139
+ around, and who felt the pain most acutely, the really bad part
140
+ wasn’t the pressure or the deadlines: it was that their work
141
+ wasn’t understood, so it wasn’t properly
142
+ recognized.</p>
143
+ <p>Their work wasn’t purely science or technology; though
144
+ grounded in both, it was far from the simple application of
145
+ formulae or solving of equations. Developers knew that you
146
+ couldn’t take a creative brief as a set of instructions and
147
+ just “translate” it into software. You have to
148
+ interpret it, and that takes an extra spark. A creative spark. They
149
+ saw this was a fundamental part of the overall interactive creative
150
+ process, and yet a parallel, creative process of its own. The
151
+ naming perpetuated the misunderstanding, and so it had to
152
+ change.</p>
153
+ <p>At the same time, people across agencies were recognizing that
154
+ their existing creative model just wasn’t working out for
155
+ “interactive”. Crews outside the fortress walls were
156
+ doing innovative and engaging work, not only through using new and
157
+ different technologies to do it (openFrameworks, Processing, robots
158
+ and Arduino, computer vision &amp; Kinect, projection mapping, the
159
+ list goes on), but also by trying out different approaches and
160
+ processes. Namely: the technology was the creative.</p>
161
+ <p><img title="102011kinect" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011kinect.jpg" alt="" /></p>
162
+ <p><em><small>Photo by <a href="http://www.flickr.com/photos/maveric2003/5810664761/sizes/z/in/photostream/">
163
+ maveric2003</a>, licensed under the <a href="http://creativecommons.org/licenses/by/2.0/">Creative
164
+ Commons</a></small></em></p>
165
+ <p>In this way, “creative technology” was born: partly
166
+ to assuage the accumulating angst of downtrodden developers; partly
167
+ to enable those developers willing to step up to the creative plate
168
+ also to step outside the conventional development toolkit; and
169
+ partly – perhaps most importantly – to spread awareness
170
+ across the board that where interactive work is concerned, creating
171
+ involves making; making interactive stuff involves technology; and
172
+ people can be creative in a range of disciplines, not only blue-sky
173
+ ideation.</p>
174
+ <p>At its inception, this was A Good Thing, and it happened for
175
+ Good Reasons. So what happened? Why do I now find myself wondering
176
+ whether Creative Technology, as a label and a discipline, is
177
+ effectively bankrupt?</p>
178
+ <p>As in any new, burgeoning and (to many) incomprehensible field,
179
+ most people have neither the background nor the time to get under
180
+ the surface and really understand what it’s all about. So
181
+ they need people to help do that.</p>
182
+ <p>Unfortunately, in any field requiring background and time to get
183
+ beneath the surface, the ninja dust is easy to throw in the faces
184
+ of the uninitiated, disguising the underlying truth – which
185
+ is, all too frequently, only a surface-level familiarity with the
186
+ necessary materials.</p>
187
+ <p>University and training courses spring up, servicing the new
188
+ market of people wanting to get educated in the new field. Courses
189
+ need funding; funding requires admissions; admissions policies get
190
+ broadened; broad admissions policies welcome novices and amateurs.
191
+ Ergo, disciplines become ill-disciplined, specialization becomes
192
+ flabby and watered-down to the point of meaninglessness.</p>
193
+ <p>Outcome: “creative technologists” who think that
194
+ their daily use of social media, “passion for digital”
195
+ and pile of half-baked ideas about QR codes, mobile integration and
196
+ Facebook apps constitute an entitlement to have those ideas brought
197
+ to life by the still-downtrodden developers, still languishing in
198
+ the dungeons of overworked production companies and in-house
199
+ development teams.</p>
200
+ <p><img title="102011code2" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011code2.jpg" alt="" /></p>
201
+ <p><em><small>Photo by <a href="http://twitter.com/igorclark/">Igor
202
+ Clark</a></small></em></p>
203
+ <p>As a result, “Creative Technology” has become
204
+ watered down to the point where people fresh out of “creative
205
+ tech” courses need only sprinkle some of that digital
206
+ ninja-dust on their resumés, and those without the requisite
207
+ background, know-how and experience to sort the wheat from the
208
+ chaff are none the wiser.</p>
209
+ <p>The talent drifts off. The ninjas plan and conceive the work,
210
+ and analyse its success, using metrics no-one else understands. Bad
211
+ work proliferates, becomes accepted and normalized within the
212
+ industry; the really good people get further alienated, more dust
213
+ is thrown to disguise others taking their places, and round and
214
+ round it goes, until no-one knows who’s a ninja and
215
+ who’s not. Except for the best people, who’ve left the
216
+ agency scene in the dust – and the audience, of course, who
217
+ are left unmoved. Or, worse, switched off.</p>
218
+ <p>Is this pattern inevitable? Can we, as agency-land
219
+ technologists, do anything about it?</p>
220
+ <p>Clearly many non-technical factors are involved, but there is
221
+ one simple and concrete thing we can do: stop hiring
222
+ “creative technologists”. Hire coders. Reject
223
+ compromise on this front, and resist pressure to give in to it.
224
+ Only hire people to work at the crossover of creative and
225
+ technology if they have strong, practical, current coding
226
+ skills.</p>
227
+ <p>Don’t fall for the illusion that a candidate is creatively
228
+ strong enough to compensate for the weak code. Spending a year or
229
+ two on a training course gaining a passing acquaintance with a
230
+ couple of trending technologies isn’t good enough. They need
231
+ to live and breathe this stuff, and to use the appropriate
232
+ languages and tools fluently and transparently, without stopping to
233
+ think about it. So if a person puts “creative
234
+ technologist” on their resumé, but doesn’t know
235
+ how to code, can’t show you things they’ve made, and
236
+ can’t prove they made them by explaining why they wrote the
237
+ code the way they did, don’t hire them. Simple as that.</p>
238
+ <p>Think this sounds elitist? Well, it is – and there’s
239
+ a reason.</p>
240
+ <p><img title="102011pages" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011pages.jpg" alt="" /></p>
241
+ <p><em><small>Photo by <a href="http://twitter.com/igorclark/">Igor
242
+ Clark</a></small></em></p>
243
+ <p>Agencies don’t hire writers just because they know the
244
+ rules of grammar. We hire them because they’re eloquent,
245
+ lucid, imaginative wordsmiths. We hire them because of their
246
+ practised ability to lovingly craft words into things that work.
247
+ Things that make people feel.</p>
248
+ <p>There are people who engineer excellent software. There are
249
+ people who come up with amazing ideas. The interactive space by
250
+ definition requires the fusion of the two, and technology at the
251
+ heart of creation. At the point of intersection, you’re going
252
+ to need people who understand both, and who have one foot on either
253
+ side. As Forrester’s Mike Gualtieri recently wrote in
254
+ <a href="http://blogs.forrester.com/mike_gualtieri/11-10-12-agile_software_is_a_cop_out_heres_whats_next">
255
+ a fresh piece about how to create great software</a>, that means
256
+ “renaissance developers who have passion, creativity,
257
+ discipline, domain knowledge, and user empathy”.</p>
258
+ <p>This is difficult territory for creative agencies. Maybe you
259
+ don’t know how to hire these people yet. Maybe it
260
+ doesn’t fit with your structures. Tough, isn’t it? But
261
+ you’re going to have to deal with it, and fix it.</p>
262
+ <p>Ultimately, to do that you need to provide an environment
263
+ that’s as appealing and satisfying for extraordinary,
264
+ creative software people as the one you already provide is for
265
+ traditional creative folks. But it also needs to be as appealing to
266
+ this new breed as their potential alternate settings at Google,
267
+ Facebook, Tech Startup X. Fortunately, you have the potential to
268
+ make it even more so for genuine creative coders – because
269
+ they’re not looking for pure engineering any more than you
270
+ are.</p>
271
+ <p>While you don’t need to become an engineering company, you
272
+ face some of their challenges. You need to understand, accept and
273
+ embrace some of the nuts and bolts of software development, and
274
+ take on board the work dedicated shops are doing on its processes.
275
+ You need such a strong streak of code running through the
276
+ atmosphere that coders want to come to you, and everyone else gets
277
+ code spilling over them.</p>
278
+ <p>But “digital” is a hybrid realm, and you need to
279
+ provide a balance. Fortunately, you’re in a perfect position
280
+ to counterpoint the engineering-first environment that others have
281
+ so successfully developed, leading so successfully to technically
282
+ excellent, efficient, and often creatively uninspiring work. You
283
+ have the creative angle covered (right?), so to get to the hybrid
284
+ middle-ground, you have to allow developers the flexibility, the
285
+ leeway and the time to engineer solid work – and you have to
286
+ welcome the hybrid creative coders into the heart of what you do,
287
+ to make a hybrid place where they feel at home, and where they can
288
+ help ensure that what gets sold makes sense, and that it can be
289
+ made without actually killing a team of engineers in the
290
+ attempt.</p>
291
+ <p><img title="102011comment" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011comment.jpg" alt="" /></p>
292
+ <p><em><small>Photo by <a href="http://twitter.com/igorclark/">Igor
293
+ Clark</a></small></em></p>
294
+ <p>Don’t get me wrong, this is hard, and it’ll take
295
+ time. It’s not just procedural, but cultural, so a big part
296
+ of doing it comes down to who you hire and how you let them do
297
+ their thing. But that’s exactly the point. That’s why
298
+ it’s most important, way before you get all that fixed, and
299
+ as the first major step on that road: just don’t hire
300
+ “creative technologists” who aren’t strong
301
+ coders.</p>
302
+ <p>Bottom line, these people need to make stuff, fast. They need to
303
+ prove or disprove concepts, in ways that non-technologists
304
+ understand, fast. So they need to know how to code efficiently,
305
+ economically and effectively. They need to understand the
306
+ appropriate technology stack from top to bottom, know which tools
307
+ are right for the job – and most of all, they must be
308
+ prepared to crack their knuckles, roll up their sleeves and get
309
+ their fingers into the code. Up to the elbows.</p>
310
+ <p>You’re probably thinking, “OK, those people are few
311
+ and far between; we still need people to bridge the gap between
312
+ them and the rest of the agency”. You’re not wrong;
313
+ you’ve put your finger straight on the really interesting
314
+ corollary, and the exact reason why creative agencies could be the
315
+ most inspiring environment for creative coders, which is simply
316
+ this: we have to be.</p>
317
+ <p>With integrated interactive work ever more critical, creative
318
+ agencies need to change drastically, in ways that suit perfectly
319
+ those people we most need to attract. We need to adapt and evolve
320
+ to survive. The serious talent is doing it for itself; going to
321
+ small shops, shooting solo. To reach the very best people, we need
322
+ to change in ways that make them want to come to us; to allow and
323
+ even help them to change us, and to help us shape our
324
+ evolution.</p>
325
+ <p><img title="102011code1" src="http://blog.wk.com/wordpress/wp-content/uploads/2011/10/102011code1.jpg" alt="" /></p>
326
+ <p><em><small>Photo by <a href="http://twitter.com/igorclark/">Igor
327
+ Clark</a></small></em></p>
328
+ <p>This is more than “building a digital team”, or
329
+ “covering digital bases”. The agency as a whole has to
330
+ step up to the tectonic plate and realize that not only are
331
+ digital, social, interactive, gaming all here to stay, but they
332
+ already permeate the entire landscape of what consumers are doing.
333
+ We need to change our processes, structures and approach to how we
334
+ create in order to accommodate this stuff, and open our arms to the
335
+ people who make it happen.</p>
336
+ <p>Start off by refusing to believe people who tell you that hiring
337
+ them to do the understanding for you means you can carry on as you
338
+ were. Instead, hire the right people in the right places, and make
339
+ the changes necessary to let them do what they do. Creative people
340
+ who can code up a storm, and, critically, experienced people who
341
+ can properly assess the code they’re shown. These are the
342
+ people who will help us flourish – if we can help them to do
343
+ the same.</p>
344
+ <p>At W+K we’re always looking to meet good technology
345
+ people, and we want to read your code. Developers, engineers,
346
+ creative coders. If that’s you, if you’ve got the
347
+ endurance to make it this far, and if you’re interested in
348
+ applying your creativity through code with us in Portland, then why
349
+ not <a href="http://www.wk.com/jobs/portland/technology">get in
350
+ touch</a>?</p>
351
+ <p>Find Igor on Twitter at <a href="http://twitter.com/igorclark">@IgorClark</a>.</p>
352
+ <p>Posted on 10.21.11</p>
353
+ <div>
354
+ <p>Category: <a href="http://blog.wk.com/category/guest-post/" title="View all posts in Guest Post" rel="category tag">Guest
355
+ Post</a>, <a href="http://blog.wk.com/category/interactive/" title="View all posts in Interactive" rel="category tag">Interactive</a></p>
356
+ </div>
357
+ <p><a href="http://blog.wk.com/2011/10/17/wk-12-7-class-graduates-celebrates/" rel="prev">«&#160;Older Post</a></p>
358
+ <div>
359
+ <h2>Features</h2>
360
+ <div>
361
+ <ul class="bodytext"><li><a href="http://blog.wk.com/2011/09/20/dan-wieden-honored-with-catalyst-award-at-2011-adcolor-awards/">
362
+ <img src="http://blog.wk.com/wordpress/wp-content/uploads/2011/09/092011danadcolor-310x139.jpg" alt="092011danadcolor" title="092011danadcolor" /></a>
363
+ <h3><a href="http://blog.wk.com/2011/09/20/dan-wieden-honored-with-catalyst-award-at-2011-adcolor-awards/">
364
+ Dan Wieden Honored with Catalyst Award at 2011 Adcolor
365
+ Awards</a></h3>
366
+ <p>This week, our own Dan Wieden was honored with the Catalyst
367
+ award at the 2011 Adcolor Awards ceremony in Los Angeles. Dan has
368
+ always been committed to making the voice and people of advertising
369
+ and specifically W+K more diverse. Our diversity and inclusion
370
+ manager Porsha Monroe has shared some thoughts with us.</p>
371
+ <a href="http://blog.wk.com/2011/09/20/dan-wieden-honored-with-catalyst-award-at-2011-adcolor-awards/">Read
372
+ More…</a></li>
373
+ <li><a href="http://blog.wk.com/2011/09/07/wk-community-mayor-sam-adams-celebrate-first-thursday-portland-incubator-experiment-pie-ribbon-cutting/">
374
+ <img src="http://blog.wk.com/wordpress/wp-content/uploads/2011/09/090711piemain-310x139.jpg" alt="090711piemain" title="090711piemain" /></a>
375
+ <h3><a href="http://blog.wk.com/2011/09/07/wk-community-mayor-sam-adams-celebrate-first-thursday-portland-incubator-experiment-pie-ribbon-cutting/">
376
+ W+K Community &amp; Mayor Sam Adams Celebrate First Thursday
377
+ Portland Incubator Experiment (PIE) Ribbon-Cutting</a></h3>
378
+ <p>Members of Wieden+Kennedy, the Portland tech community, Mayor
379
+ Sam Adams and our friends and associates gathered together for last
380
+ week’s <a href="http://www.firstthursdayportland.com/">First
381
+ Thursday</a> to celebrate the official ribbon cutting ceremony for
382
+ the <a href="http://piepdx.com">Portland Incubator Experiment</a>
383
+ (more commonly known as PIE), a collaborative center that partners
384
+ leading brands with technology innovators to cultivate community,
385
+ entrepreneurship and creative thinking.</p>
386
+ <a href="http://blog.wk.com/2011/09/07/wk-community-mayor-sam-adams-celebrate-first-thursday-portland-incubator-experiment-pie-ribbon-cutting/">Read
387
+ More…</a></li>
388
+ <li><a href="http://blog.wk.com/2011/08/29/musician-and-comic-book-artist-daniel-johnston-concludes-space-ducks-art-show-with-concert-performance-in-wk-foyer/">
389
+ <img src="http://blog.wk.com/wordpress/wp-content/uploads/2011/08/082911djmain-310x139.jpg" alt="082911djmain" title="082911djmain" /></a>
390
+ <h3><a href="http://blog.wk.com/2011/08/29/musician-and-comic-book-artist-daniel-johnston-concludes-space-ducks-art-show-with-concert-performance-in-wk-foyer/">
391
+ Musician &amp; Comic Artist Daniel Johnston Concludes <em>Space
392
+ Ducks</em> Art Show with Concert Performance in W+K Foyer</a></h3>
393
+ <p>On Thursday, the month-long show of Daniel Johnston’s
394
+ Space Ducks: An Infinite Comic Book of Musical Greatness concluded
395
+ with a performance by the man himself. The event coincided with the
396
+ relaunch of his site, http://hihowareyou.com, produced in
397
+ partnership with W+K and WKE.</p>
398
+ <a href="http://blog.wk.com/2011/08/29/musician-and-comic-book-artist-daniel-johnston-concludes-space-ducks-art-show-with-concert-performance-in-wk-foyer/">Read
399
+ More…</a></li>
400
+ </ul></div>
401
+ </div>
402
+ <div>
403
+ <h2>Goodness</h2>
404
+ <div>
405
+ <ul class="bodytext"><li><a href="http://wkstudio.bigcartel.com/product/deep-down-inside-we-all-love-math-t-shirt">
406
+ <img src="http://blog.wk.com/wordpress/wp-content/files_mf/math_shirt.jpg" alt="image" /></a>
407
+ <h3><a href="http://wkstudio.bigcartel.com/product/deep-down-inside-we-all-love-math-t-shirt">
408
+ We All Love Math T-shirt</a></h3>
409
+ <p>If this shirt + its thesis succeed, we will all revel in our
410
+ essential mathness.</p>
411
+ </li>
412
+ <li><a href="http://wkstudio.bigcartel.com/product/graphics-design-t-shirt"><img src="http://blog.wk.com/wordpress/wp-content/files_mf/graphicshirt.jpg" alt="image" /></a>
413
+ <h3><a href="http://wkstudio.bigcartel.com/product/graphics-design-t-shirt">Graphics
414
+ Design™ T-shirt</a></h3>
415
+ <p>The soul-wrenching existential crisis many commercial artists
416
+ struggle with…</p>
417
+ </li>
418
+ <li><a href="http://wkstudio.bigcartel.com/product/teen-baby-onesie"><img src="http://blog.wk.com/wordpress/wp-content/files_mf/babyshirt.jpg" alt="image" /></a>
419
+ <h3><a href="http://wkstudio.bigcartel.com/product/teen-baby-onesie">Modern
420
+ Baby Onesie</a></h3>
421
+ <p>You are observing a genuine onesie that… Oh, man. That
422
+ baby’s such a dick.</p>
423
+ </li>
424
+ </ul><p><a href="http://wkstudio.bigcartel.com/">See more at the
425
+ Goodness Store.</a></p>
426
+ </div>
427
+ </div>
428
+
429
+
430
+
431
+ </div></div>
432
+
433
+ <div class="bar bottom">
434
+ </div>
435
+ </body>
436
+ </html>
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Thu, 01 Dec 2011 16:33:47 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Connection: close
11
+ Transfer-Encoding: chunked
12
+ Content-Type: application/json
13
+
14
+ [{"type":"meta"},{"type":"user","user_id":140230,"username":"tom@tomtaylor.co.uk","subscription_is_active":"1"},{"type":"bookmark","bookmark_id":222945036,"url":"http:\/\/joemoransblog.blogspot.com\/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:\/\/potlatch.typepad.com\/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:\/\/dashes.com\/anil\/2011\/11\/how-the-99-and-the-tea-party-can-occupy-whitehousegov.html","title":"How the 99% and the Tea Party can Occupy WhiteHouse.gov","description":"","time":1320919525,"starred":"0","private_source":"","hash":"4PGtE6DR","progress":"0","progress_timestamp":1321099791},{"type":"bookmark","bookmark_id":221679923,"url":"http:\/\/blog.pinboard.in\/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:\/\/potlatch.typepad.com\/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:\/\/www.vanityfair.com\/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:\/\/nymag.com\/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:\/\/www.vanityfair.com\/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:\/\/www.theparisreview.org\/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:\/\/www.bbc.co.uk\/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:\/\/www.wired.com\/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:\/\/www.monbiot.com\/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:\/\/economicsintelligence.com\/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:\/\/theeuropean-magazine.com\/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:\/\/www.antipope.org\/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:\/\/blog.wk.com\/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:\/\/earlyretirementextreme.com\/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:\/\/earlyretirementextreme.com\/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:\/\/www.economist.com\/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:\/\/online.wsj.com\/article\/SB10001424052970203914304576627252381486880.html?mod=WSJ_hps_editorsPicks_1","title":"A Future for Pay Phones? - WSJ.com","description":"","time":1319006861,"starred":"0","private_source":"","hash":"BY3la9rz","progress":"0","progress_timestamp":1319007125},{"type":"bookmark","bookmark_id":214644989,"url":"http:\/\/www.monbiot.com\/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:\/\/caravanmagazine.in\/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:\/\/code.flickr.com\/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:\/\/www.guardian.co.uk\/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:\/\/www.engineyard.com\/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}]
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Thu, 01 Dec 2011 16:31:05 GMT
3
+ Server: Apache
4
+ P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
5
+ X-Robots-Tag: noindex
6
+ Cache-Control: no-cache
7
+ Pragma: no-cache
8
+ X-Powered-By: a lot of coffee and Phish
9
+ Vary: Accept-Encoding
10
+ Content-Length: 96
11
+ Connection: close
12
+ Content-Type: application/json
13
+
14
+ [{"type":"user","user_id":140230,"username":"tom@tomtaylor.co.uk","subscription_is_active":"1"}]
@@ -0,0 +1,115 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class InstapaperAPITest < Test::Unit::TestCase
4
+
5
+ include AssetHelpers
6
+
7
+ def stub_successful_authentication
8
+ stub_request(:post, "https://www.instapaper.com/api/1/oauth/access_token").to_return(
9
+ http_response('access_token_success')
10
+ )
11
+ end
12
+
13
+ def stub_failed_authentication
14
+ stub_request(:post, "https://www.instapaper.com/api/1/oauth/access_token").to_return(
15
+ http_response('access_token_failure')
16
+ )
17
+ end
18
+
19
+ def stub_successful_verify_credentials
20
+ stub_request(:post, "https://www.instapaper.com/api/1/account/verify_credentials").to_return(
21
+ http_response('verify_credentials_success')
22
+ )
23
+ end
24
+
25
+ def stub_successful_bookmarks_list
26
+ stub_request(:post, "https://www.instapaper.com/api/1/bookmarks/list").to_return(
27
+ http_response('bookmarks_list_success')
28
+ )
29
+ end
30
+
31
+ def stub_failed_bookmarks_add
32
+ stub_request(:post, "https://www.instapaper.com/api/1/bookmarks/add").to_return(
33
+ http_response('bookmarks_add_failure')
34
+ )
35
+ end
36
+
37
+ def stub_successful_bookmarks_get_text
38
+ stub_request(:post, "https://www.instapaper.com/api/1/bookmarks/get_text").
39
+ with(:body => {"bookmark_id"=>"1"}).
40
+ to_return(http_response('bookmarks_get_text_success'))
41
+ end
42
+
43
+ def stub_failed_bookmarks_get_text
44
+ stub_request(:post, "https://www.instapaper.com/api/1/bookmarks/get_text").
45
+ with(:body => {"bookmark_id"=>"2"}).
46
+ to_return(http_response('bookmarks_get_text_failure'))
47
+ end
48
+
49
+ def authenticated_client
50
+ InstapaperFull::API.new(:consumer_key => "key",
51
+ :consumer_secret => "secret",
52
+ :oauth_token => "token",
53
+ :oauth_token_secret => "tokensecret")
54
+ end
55
+
56
+ def test_successful_authentication
57
+ stub_successful_authentication
58
+ stub_successful_verify_credentials
59
+
60
+ ip = InstapaperFull::API.new(:consumer_key => "test", :consumer_secret => "")
61
+ assert_equal true, ip.authenticate("tom@testing.com", "test")
62
+ assert_equal "thisisatoken", ip.options[:oauth_token]
63
+ assert_equal "thisisasecret", ip.options[:oauth_token_secret]
64
+ assert_equal 140230, ip.options[:user_id]
65
+ assert_equal "tom@tomtaylor.co.uk", ip.options[:username]
66
+ end
67
+
68
+ def test_failed_authentication
69
+ stub_failed_authentication
70
+
71
+ ip = InstapaperFull::API.new(:consumer_key => "test", :consumer_secret => "")
72
+ assert_equal false, ip.authenticate("tom@testing.com", "test")
73
+ end
74
+
75
+ def test_successful_bookmarks_list
76
+ stub_successful_bookmarks_list
77
+ list = authenticated_client.bookmarks_list
78
+ assert_equal 27, list.length # 25 + 1 user element + 1 meta element
79
+ end
80
+
81
+ def test_failed_bookmarks_add
82
+ stub_failed_bookmarks_add
83
+ assert_raise(InstapaperFull::API::Error) { authenticated_client.bookmarks_add }
84
+
85
+ begin
86
+ authenticated_client.bookmarks_add
87
+ rescue InstapaperFull::API::Error => e
88
+ assert_equal 1240, e.code
89
+ assert_equal "Invalid URL specified", e.message
90
+ end
91
+ end
92
+
93
+ def test_successful_bookmarks_get_text
94
+ stub_successful_bookmarks_get_text
95
+
96
+ html = authenticated_client.bookmarks_get_text(:bookmark_id => 1)
97
+ assert html.kind_of?(String)
98
+ assert_equal 22788, html.length
99
+ end
100
+
101
+ def test_failed_bookmarks_get_text
102
+ stub_failed_bookmarks_get_text
103
+
104
+ assert_raise(InstapaperFull::API::Error) do
105
+ authenticated_client.bookmarks_get_text(:bookmark_id => 2)
106
+ end
107
+
108
+ begin
109
+ authenticated_client.bookmarks_get_text(:bookmark_id => 2)
110
+ rescue InstapaperFull::API::Error => e
111
+ assert_equal 1241, e.code
112
+ assert_equal "Invalid or missing bookmark_id", e.message
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,12 @@
1
+ require 'test/unit'
2
+
3
+ unless $LOAD_PATH.include? 'lib'
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join($LOAD_PATH.first, '..', 'lib'))
6
+ end
7
+
8
+ require 'instapaper_full'
9
+ require 'asset_helpers'
10
+ require 'webmock/test_unit'
11
+
12
+ WebMock.disable_net_connect!
metadata CHANGED
@@ -1,19 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instapaper_full
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Matt Biddulph
9
+ - Tom Taylor
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2011-11-30 00:00:00.000000000Z
13
+ date: 2011-12-02 00:00:00.000000000Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: faraday
16
- requirement: &70351849700360 !ruby/object:Gem::Requirement
17
+ requirement: &70362390471600 !ruby/object:Gem::Requirement
17
18
  none: false
18
19
  requirements:
19
20
  - - ~>
@@ -21,10 +22,10 @@ dependencies:
21
22
  version: 0.7.5
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *70351849700360
25
+ version_requirements: *70362390471600
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: faraday_middleware
27
- requirement: &70351849712720 !ruby/object:Gem::Requirement
28
+ requirement: &70362390470240 !ruby/object:Gem::Requirement
28
29
  none: false
29
30
  requirements:
30
31
  - - ~>
@@ -32,10 +33,10 @@ dependencies:
32
33
  version: 0.7.0
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *70351849712720
36
+ version_requirements: *70362390470240
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: simple_oauth
38
- requirement: &70351853834840 !ruby/object:Gem::Requirement
39
+ requirement: &70362390468680 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ~>
@@ -43,10 +44,10 @@ dependencies:
43
44
  version: '0.1'
44
45
  type: :runtime
45
46
  prerelease: false
46
- version_requirements: *70351853834840
47
+ version_requirements: *70362390468680
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: multi_json
49
- requirement: &70351853958460 !ruby/object:Gem::Requirement
50
+ requirement: &70362390468040 !ruby/object:Gem::Requirement
50
51
  none: false
51
52
  requirements:
52
53
  - - ~>
@@ -54,10 +55,10 @@ dependencies:
54
55
  version: 1.0.4
55
56
  type: :runtime
56
57
  prerelease: false
57
- version_requirements: *70351853958460
58
+ version_requirements: *70362390468040
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: yajl-ruby
60
- requirement: &70351862197880 !ruby/object:Gem::Requirement
61
+ requirement: &70362390467080 !ruby/object:Gem::Requirement
61
62
  none: false
62
63
  requirements:
63
64
  - - ~>
@@ -65,10 +66,10 @@ dependencies:
65
66
  version: 1.1.0
66
67
  type: :runtime
67
68
  prerelease: false
68
- version_requirements: *70351862197880
69
+ version_requirements: *70362390467080
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: rake
71
- requirement: &70351862292620 !ruby/object:Gem::Requirement
72
+ requirement: &70362390466520 !ruby/object:Gem::Requirement
72
73
  none: false
73
74
  requirements:
74
75
  - - ! '>='
@@ -76,10 +77,33 @@ dependencies:
76
77
  version: '0'
77
78
  type: :development
78
79
  prerelease: false
79
- version_requirements: *70351862292620
80
+ version_requirements: *70362390466520
81
+ - !ruby/object:Gem::Dependency
82
+ name: test-unit
83
+ requirement: &70362390465540 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: 2.4.2
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *70362390465540
92
+ - !ruby/object:Gem::Dependency
93
+ name: webmock
94
+ requirement: &70362390463180 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: 1.7.8
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: *70362390463180
80
103
  description: See http://www.instapaper.com/api/full
81
104
  email:
82
105
  - mb@hackdiary.com
106
+ - tom@tomtaylor.co.uk
83
107
  executables: []
84
108
  extensions: []
85
109
  extra_rdoc_files: []
@@ -90,8 +114,19 @@ files:
90
114
  - README.md
91
115
  - Rakefile
92
116
  - instapaper_full.gemspec
117
+ - lib/errors.rb
93
118
  - lib/instapaper_full.rb
94
119
  - lib/instapaper_full/version.rb
120
+ - test/asset_helpers.rb
121
+ - test/http_responses/access_token_failure.txt
122
+ - test/http_responses/access_token_success.txt
123
+ - test/http_responses/bookmarks_add_failure.txt
124
+ - test/http_responses/bookmarks_get_text_failure.txt
125
+ - test/http_responses/bookmarks_get_text_success.txt
126
+ - test/http_responses/bookmarks_list_success.txt
127
+ - test/http_responses/verify_credentials_success.txt
128
+ - test/instapaper_api_test.rb
129
+ - test/test_helper.rb
95
130
  homepage: https://github.com/mattb/instapaper_full
96
131
  licenses: []
97
132
  post_install_message:
@@ -104,17 +139,32 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
139
  - - ! '>='
105
140
  - !ruby/object:Gem::Version
106
141
  version: '0'
142
+ segments:
143
+ - 0
144
+ hash: 3203290908794437357
107
145
  required_rubygems_version: !ruby/object:Gem::Requirement
108
146
  none: false
109
147
  requirements:
110
148
  - - ! '>='
111
149
  - !ruby/object:Gem::Version
112
150
  version: '0'
151
+ segments:
152
+ - 0
153
+ hash: 3203290908794437357
113
154
  requirements: []
114
155
  rubyforge_project: instapaper_full
115
- rubygems_version: 1.8.6
156
+ rubygems_version: 1.8.10
116
157
  signing_key:
117
158
  specification_version: 3
118
159
  summary: Wrapper for the Instapaper Full Developer API
119
- test_files: []
120
- has_rdoc:
160
+ test_files:
161
+ - test/asset_helpers.rb
162
+ - test/http_responses/access_token_failure.txt
163
+ - test/http_responses/access_token_success.txt
164
+ - test/http_responses/bookmarks_add_failure.txt
165
+ - test/http_responses/bookmarks_get_text_failure.txt
166
+ - test/http_responses/bookmarks_get_text_success.txt
167
+ - test/http_responses/bookmarks_list_success.txt
168
+ - test/http_responses/verify_credentials_success.txt
169
+ - test/instapaper_api_test.rb
170
+ - test/test_helper.rb