instapaper_full 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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