cloudapp-service 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.md +34 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +56 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +14 -0
  6. data/Rakefile +148 -0
  7. data/cloudapp-service.gemspec +115 -0
  8. data/lib/cloudapp/collection_json.rb +3 -0
  9. data/lib/cloudapp/collection_json/item.rb +20 -0
  10. data/lib/cloudapp/collection_json/representation.rb +74 -0
  11. data/lib/cloudapp/collection_json/template.rb +17 -0
  12. data/lib/cloudapp/collection_json/tint.rb +11 -0
  13. data/lib/cloudapp/service.rb +204 -0
  14. data/lib/cloudapp/service/authorized_representation.rb +17 -0
  15. data/lib/cloudapp/service/drop.rb +44 -0
  16. data/lib/cloudapp/service/drop_collection.rb +39 -0
  17. data/spec/cassettes/account_token.yml +88 -0
  18. data/spec/cassettes/create_bookmark.yml +132 -0
  19. data/spec/cassettes/create_bookmark_with_name.yml +133 -0
  20. data/spec/cassettes/create_bookmark_with_privacy.yml +133 -0
  21. data/spec/cassettes/delete_drop.yml +215 -0
  22. data/spec/cassettes/list_drops.yml +488 -0
  23. data/spec/cassettes/list_drops_with_bad_token.yml +45 -0
  24. data/spec/cassettes/list_drops_with_filter.yml +345 -0
  25. data/spec/cassettes/list_drops_with_limit.yml +129 -0
  26. data/spec/cassettes/privatize_drop.yml +222 -0
  27. data/spec/cassettes/publicize_drop.yml +222 -0
  28. data/spec/cassettes/purge_drops.yml +764 -0
  29. data/spec/cassettes/recover_drop.yml +309 -0
  30. data/spec/cassettes/rename_drop.yml +222 -0
  31. data/spec/cassettes/token_for_account.yml +88 -0
  32. data/spec/cassettes/token_for_account_with_bad_credentials.yml +86 -0
  33. data/spec/cassettes/trash_drop.yml +222 -0
  34. data/spec/cassettes/update_drop_bookmark_url.yml +222 -0
  35. data/spec/cassettes/update_file.yml +365 -0
  36. data/spec/cassettes/upload_file.yml +276 -0
  37. data/spec/cassettes/upload_file_with_name.yml +278 -0
  38. data/spec/cassettes/upload_file_with_privacy.yml +277 -0
  39. data/spec/cassettes/view_drop.yml +174 -0
  40. data/spec/cloudapp/authorized_representation_spec.rb +32 -0
  41. data/spec/cloudapp/collection_json/item_spec.rb +45 -0
  42. data/spec/cloudapp/collection_json/representation_spec.rb +118 -0
  43. data/spec/cloudapp/collection_json/template_spec.rb +53 -0
  44. data/spec/cloudapp/drop_collection_spec.rb +148 -0
  45. data/spec/cloudapp/drop_spec.rb +186 -0
  46. data/spec/cloudapp/service_spec.rb +321 -0
  47. data/spec/helper.rb +8 -0
  48. data/spec/integration_spec.rb +77 -0
  49. data/spec/support/files/favicon.ico +0 -0
  50. data/spec/support/stub_class_or_module.rb +22 -0
  51. data/spec/support/vcr.rb +24 -0
  52. metadata +194 -0
@@ -0,0 +1,17 @@
1
+ module CloudApp
2
+ module CollectionJson
3
+ class Template < Item
4
+ attr_reader :rel, :enctype
5
+ def initialize(template_data)
6
+ super
7
+ @rel = template_data.fetch('rel', nil)
8
+ @enctype = template_data.fetch('enctype', nil)
9
+ end
10
+
11
+ def fill(template_data)
12
+ template_data = template_data.select {|key| data.has_key?(key) }
13
+ data.merge template_data
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module CloudApp
2
+ module CollectionJson
3
+ Tint = Leadlight::Tint.new 'collection+json', status: [ :success, 401 ] do
4
+ match_content_type 'application/vnd.collection+json'
5
+ extend CollectionJson::Representation
6
+ collection_links.each do |link|
7
+ add_link link.href, link.rel
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,204 @@
1
+ require 'leadlight'
2
+ require 'typhoeus'
3
+ require 'cloudapp/collection_json'
4
+ require 'cloudapp/collection_json/tint'
5
+ require 'cloudapp/service/authorized_representation'
6
+ require 'cloudapp/service/drop'
7
+ require 'cloudapp/service/drop_collection'
8
+
9
+ module CloudApp
10
+ class Service
11
+ VERSION = '1.0.0.beta.1'
12
+
13
+ Leadlight.build_service(self) do
14
+ url 'https://api.getcloudapp.com'
15
+ tints << CollectionJson::Tint
16
+ tints << AuthorizedRepresentation::Tint
17
+
18
+ build_connection do |c|
19
+ c.adapter :typhoeus
20
+ end
21
+ end
22
+
23
+ def initialize
24
+ super
25
+ # logger.level = Logger::INFO
26
+ logger.level = Logger::WARN
27
+ end
28
+
29
+ def token=(token)
30
+ connection.token_auth token
31
+ end
32
+
33
+ def self.using_token(token)
34
+ new.tap do |service|
35
+ service.token = token
36
+ end
37
+ end
38
+
39
+ def self.token_for_account(email, password)
40
+ representation = new.account_token email, password
41
+ return if representation.unauthorized?
42
+
43
+ representation.items.first.data['token']
44
+ end
45
+
46
+ def account_token(email, password)
47
+ authenticate_response = root
48
+ data = authenticate_response.template.
49
+ fill('email' => email, 'password' => password)
50
+
51
+ post(authenticate_response.href, data) do |response|
52
+ return response
53
+ end
54
+ end
55
+
56
+ def drops(params = {})
57
+ params = params.each_with_object({}) {|(key, value), params|
58
+ params[key.to_s] = value
59
+ }
60
+
61
+ get('/') do |response|
62
+ return DropCollection.new(response, self) if response.unauthorized?
63
+
64
+ drops = response.link('drops').follow
65
+ return DropCollection.new(drops, self) if params.empty?
66
+
67
+ drops_query = drops.query 'drops-list'
68
+ href = Addressable::URI.parse drops_query.href
69
+ href.query_values = drops_query.fill params
70
+
71
+ get(href) do |response| return DropCollection.new(response, self) end
72
+ end
73
+ end
74
+
75
+ def drop(href)
76
+ DropCollection.new drops_at(href), self
77
+ end
78
+
79
+ def update(href, options = {})
80
+ collection = drops_at href
81
+ drop = collection.items.first
82
+ path = options.fetch :path, nil
83
+ attributes = drop.data.merge fetch_drop_attributes(options)
84
+ data = collection.template.fill attributes
85
+
86
+ put(drop.href, data) do |collection|
87
+ if not path
88
+ return DropCollection.new collection, self
89
+ else
90
+ return DropCollection.new upload_file(path, collection), self
91
+ end
92
+ end
93
+ end
94
+
95
+ def bookmark(url, options = {})
96
+ attributes = fetch_drop_attributes options.merge(url: url)
97
+ collection = drops_at :root
98
+ data = collection.template.fill(attributes)
99
+
100
+ post(collection.href, data) do |response|
101
+ return DropCollection.new response, self
102
+ end
103
+ end
104
+
105
+ def upload(path, options = {})
106
+ attributes = fetch_drop_attributes options.merge(path: path)
107
+ collection = drops_at :root
108
+ data = collection.template.fill(attributes)
109
+
110
+ post(collection.href, data) do |collection|
111
+ return DropCollection.new upload_file(path, collection), self
112
+ end
113
+ end
114
+
115
+ def privatize_drop(href)
116
+ update href, private: true
117
+ end
118
+
119
+ def publicize_drop(href)
120
+ update href, private: false
121
+ end
122
+
123
+ def trash_drop(href)
124
+ update href, trash: true
125
+ end
126
+
127
+ def recover_drop(href)
128
+ update href, trash: false
129
+ end
130
+
131
+ def delete_drop(href)
132
+ delete(href) do |response|
133
+ return response
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ def drops_at(href, params = {})
140
+ params = params.each_with_object({}) {|(key, value), params|
141
+ params[key.to_s] = value
142
+ }
143
+
144
+ if href == :root
145
+ get('/') do |response|
146
+ return response if response.__response__.status == 401
147
+ href = response.link('drops').href
148
+ end
149
+ end
150
+
151
+ get(href) do |response|
152
+ return response if response.__response__.status == 401
153
+ if not params.empty?
154
+ drops_query = response.query('drops-list')
155
+ href = Addressable::URI.parse drops_query.href
156
+ href.query_values = drops_query.fill params
157
+ else
158
+ return response
159
+ end
160
+ end
161
+
162
+ get(href) do |response|
163
+ return response if response.__response__.status == 401
164
+ return response
165
+ end
166
+ end
167
+
168
+ def fetch_drop_attributes(options)
169
+ path = options.delete :path
170
+ options[:file_size] = FileTest.size(path) if path
171
+ { url: 'bookmark_url',
172
+ file_size: 'file_size',
173
+ name: 'name',
174
+ private: 'private',
175
+ trash: 'trash'
176
+ }.each_with_object({}) {|(key, name), attributes|
177
+ attributes[name] = options.fetch(key) if options.has_key?(key)
178
+ }
179
+ end
180
+
181
+ def upload_file(path, collection)
182
+ uri = Addressable::URI.parse collection.href
183
+ file = File.open path
184
+ file_io = Faraday::UploadIO.new file, 'image/png'
185
+ fields = collection.template.fill('file' => file_io)
186
+
187
+ conn = Faraday.new(url: uri.site) {|builder|
188
+ if collection.template.enctype == Faraday::Request::Multipart.mime_type
189
+ builder.request :multipart
190
+ end
191
+
192
+ builder.response :logger, logger
193
+ builder.adapter :typhoeus
194
+ }
195
+
196
+ conn.post(uri.request_uri, fields).on_complete do |env|
197
+ location = Addressable::URI.parse env[:response_headers]['Location']
198
+ get(location) do |upload_response|
199
+ return upload_response
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,17 @@
1
+ require 'leadlight'
2
+
3
+ module CloudApp
4
+ module AuthorizedRepresentation
5
+ Tint = Leadlight::Tint.new 'authorized', status: [ :success, 401 ] do
6
+ extend AuthorizedRepresentation
7
+ end
8
+
9
+ def authorized?
10
+ not unauthorized?
11
+ end
12
+
13
+ def unauthorized?
14
+ __response__.status == 401
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ require 'date'
2
+ require 'ostruct'
3
+
4
+ module CloudApp
5
+ class Drop < OpenStruct
6
+ attr_accessor :href, :data
7
+
8
+ def initialize(collection_item, collection)
9
+ @href = collection_item.href
10
+ @links = collection_item.links
11
+ @data = collection_item.data
12
+ @collection = collection
13
+ super @data
14
+ end
15
+
16
+ def name() super || share_url end
17
+ def private?() private == true end
18
+ def public?() !private? end
19
+ def trashed?() data['trash'] end
20
+ def created() DateTime.parse(super) end
21
+
22
+ def share_url() link_for_relation('canonical') end
23
+ def thumbnail_url() link_for_relation('icon') end
24
+ def embed_url() link_for_relation('embed') end
25
+ def download_url() link_for_relation('download') end
26
+
27
+ def publicize() @collection.publicize(self) end
28
+ def privatize() @collection.privatize(self) end
29
+ def toggle_privacy
30
+ public? ? privatize : publicize
31
+ end
32
+
33
+ def trash() @collection.trash(self) end
34
+ def recover() @collection.recover(self) end
35
+ def delete() @collection.delete(self) end
36
+
37
+ protected
38
+
39
+ def link_for_relation(relation)
40
+ link = @links.find {|link| link.rel == relation }
41
+ link and link.href
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+
4
+ module CloudApp
5
+ class DropCollection < SimpleDelegator
6
+ extend Forwardable
7
+ def_delegators :representation, :authorized?, :unauthorized?
8
+
9
+ attr_reader :representation, :drop_class
10
+
11
+ def initialize(representation, service, drop_class = Drop)
12
+ @representation = representation
13
+ @service = service
14
+ @drop_class = drop_class
15
+ super drops
16
+ end
17
+
18
+ def follow(relation)
19
+ DropCollection.new representation.link(relation).follow, @service, drop_class
20
+ end
21
+
22
+ def has_link?(relation)
23
+ !representation.link(relation) { nil }.nil?
24
+ end
25
+
26
+ def privatize(drop) @service.privatize_drop drop.href end
27
+ def publicize(drop) @service.publicize_drop drop.href end
28
+ def trash(drop) @service.trash_drop drop.href end
29
+ def recover(drop) @service.recover_drop drop.href end
30
+ def delete(drop) @service.delete_drop drop.href end
31
+
32
+ protected
33
+
34
+ def drops
35
+ return [] if unauthorized?
36
+ @drops ||= representation.items.map {|drop| drop_class.new(drop, self) }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,88 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.getcloudapp.com/
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
12
+ text/plain
13
+ Accept-Encoding:
14
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
15
+ User-Agent:
16
+ - Ruby
17
+ response:
18
+ status:
19
+ code: 401
20
+ message: Unauthorized
21
+ headers:
22
+ Cache-Control:
23
+ - no-cache
24
+ Content-Type:
25
+ - application/vnd.collection+json; charset=utf-8
26
+ Server:
27
+ - thin 1.4.1 codename Chromeo
28
+ Www-Authenticate:
29
+ - Token realm="CloudApp"
30
+ X-Runtime:
31
+ - '0.024863'
32
+ X-Ua-Compatible:
33
+ - IE=Edge,chrome=1
34
+ Transfer-Encoding:
35
+ - chunked
36
+ Connection:
37
+ - keep-alive
38
+ body:
39
+ encoding: US-ASCII
40
+ string: ! '{"collection":{"version":"1.0","href":"https://api.getcloudapp.com/account/token","links":[{"rel":"root","href":"https://api.getcloudapp.com/"}],"template":{"data":[{"name":"email","value":null},{"name":"password","value":null}]}}}'
41
+ http_version:
42
+ recorded_at: Tue, 11 Sep 2012 15:46:43 GMT
43
+ - request:
44
+ method: post
45
+ uri: https://api.getcloudapp.com/account/token
46
+ body:
47
+ encoding: UTF-8
48
+ string: ! '{"email":"arthur@dent.com","password":"towel"}'
49
+ headers:
50
+ Content-Type:
51
+ - application/json
52
+ Accept:
53
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
54
+ text/plain
55
+ User-Agent:
56
+ - Ruby
57
+ response:
58
+ status:
59
+ code: 201
60
+ message: Created
61
+ headers:
62
+ Cache-Control:
63
+ - max-age=0, private, must-revalidate
64
+ Content-Type:
65
+ - application/vnd.collection+json; charset=utf-8
66
+ Etag:
67
+ - ! '"4438a3f4f703a01c251b7ecc1feab772"'
68
+ Server:
69
+ - thin 1.4.1 codename Chromeo
70
+ Set-Cookie:
71
+ - _engine_session=BAh7CEkiFXVzZXJfY3JlZGVudGlhbHMGOgZFRkkiAYBhOGUwNDBkMDY3YWJkMTEyZGM1NTJhOTlkMjYyNmU1NzU0Y2FmZjc3MDEzZTc0ZDU5NTFjMTI2M2QxYTEyMGQxNThjZjI3ZDgzNmVmN2QyMmEwZjQzN2RlMjg3NGE3OWQwMWM4NWViOWFmYTVmMzQ1ZGFjZjQyZDAxNjlkMTY4MwY7AFRJIhh1c2VyX2NyZWRlbnRpYWxzX2lkBjsARmkDp4EBSSIPc2Vzc2lvbl9pZAY7AEZJIiVkMWNjNjJlNTcyNzE0YjYwODFhMjIwMGUyZTRiMzk4MgY7AFQ%3D--ea8789606501f7f43eef6667e1af82ebb52322eb;
72
+ path=/; HttpOnly
73
+ - user_credentials=a8e040d067abd112dc552a99d2626e5754caff77013e74d5951c1263d1a120d158cf27d836ef7d22a0f437de2874a79d01c85eb9afa5f345dacf42d0169d1683%3A%3A98727;
74
+ path=/; expires=Tue, 11-Dec-2012 15:46:44 GMT
75
+ X-Runtime:
76
+ - '0.007753'
77
+ X-Ua-Compatible:
78
+ - IE=Edge,chrome=1
79
+ Transfer-Encoding:
80
+ - chunked
81
+ Connection:
82
+ - keep-alive
83
+ body:
84
+ encoding: US-ASCII
85
+ string: ! '{"collection":{"version":"1.0","href":"https://api.getcloudapp.com/account/token","links":[{"rel":"root","href":"https://api.getcloudapp.com/"}],"items":[{"href":"https://api.getcloudapp.com/account/token","data":[{"name":"token","value":"abc123"}]}]}}'
86
+ http_version:
87
+ recorded_at: Tue, 11 Sep 2012 15:46:43 GMT
88
+ recorded_with: VCR 2.0.1
@@ -0,0 +1,132 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.getcloudapp.com/
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - Token token="abc123"
12
+ Accept:
13
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
14
+ text/plain
15
+ Accept-Encoding:
16
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
17
+ User-Agent:
18
+ - Ruby
19
+ response:
20
+ status:
21
+ code: 200
22
+ message: OK
23
+ headers:
24
+ Cache-Control:
25
+ - max-age=0, private, must-revalidate
26
+ Content-Type:
27
+ - application/vnd.collection+json; charset=utf-8
28
+ Etag:
29
+ - ! '"44e8e003e0f1b5e38f7ca916ff4497d7"'
30
+ Server:
31
+ - thin 1.4.1 codename Chromeo
32
+ X-Runtime:
33
+ - '0.004864'
34
+ X-Ua-Compatible:
35
+ - IE=Edge,chrome=1
36
+ Content-Length:
37
+ - '139'
38
+ Connection:
39
+ - keep-alive
40
+ body:
41
+ encoding: US-ASCII
42
+ string: ! '{"collection":{"version":"1.0","href":"https://api.getcloudapp.com/","links":[{"rel":"drops","href":"https://api.getcloudapp.com/drops"}]}}'
43
+ http_version:
44
+ recorded_at: Tue, 11 Sep 2012 15:46:52 GMT
45
+ - request:
46
+ method: get
47
+ uri: https://api.getcloudapp.com/drops
48
+ body:
49
+ encoding: US-ASCII
50
+ string: ''
51
+ headers:
52
+ Authorization:
53
+ - Token token="abc123"
54
+ Accept:
55
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
56
+ text/plain
57
+ Accept-Encoding:
58
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
59
+ User-Agent:
60
+ - Ruby
61
+ response:
62
+ status:
63
+ code: 200
64
+ message: OK
65
+ headers:
66
+ Cache-Control:
67
+ - max-age=0, private, must-revalidate
68
+ Content-Type:
69
+ - application/vnd.collection+json; charset=utf-8
70
+ Etag:
71
+ - ! '"71c7981a2f34f856e0c966b4bd9d410d"'
72
+ Server:
73
+ - thin 1.4.1 codename Chromeo
74
+ X-Runtime:
75
+ - '0.018958'
76
+ X-Ua-Compatible:
77
+ - IE=Edge,chrome=1
78
+ Content-Length:
79
+ - '2114'
80
+ Connection:
81
+ - keep-alive
82
+ body:
83
+ encoding: US-ASCII
84
+ string: ! '{"collection":{"version":"1.0","href":"https://api.getcloudapp.com/drops","links":[{"rel":"drops-stream","href":"https://api.getcloudapp.com/drops/stream"}],"queries":[{"href":"https://api.getcloudapp.com/drops","rel":"drops-list","data":[{"name":"filter","value":"active"},{"name":"limit","value":20}]}],"template":{"data":[{"name":"name","value":null},{"name":"private","value":true},{"name":"trash","value":false},{"name":"bookmark_url","value":null},{"name":"file_size","value":null}]},"items":[{"href":"https://api.getcloudapp.com/drops/21907682","links":[{"rel":"collection","href":"https://api.getcloudapp.com/drops"},{"rel":"canonical","href":"http://cl.ly/2x3S331L1a3o"},{"rel":"icon","href":"https://d9yac136u048z.cloudfront.net/images/thumbnails/bookmark.png"}],"data":[{"name":"id","value":21907682},{"name":"name","value":"http://cl.ly"},{"name":"private","value":true},{"name":"trash","value":false},{"name":"views","value":0},{"name":"created","value":"2012-09-11T15:46:52Z"}]},{"href":"https://api.getcloudapp.com/drops/21907679","links":[{"rel":"collection","href":"https://api.getcloudapp.com/drops"},{"rel":"canonical","href":"http://cl.ly/1G282W04401a"},{"rel":"icon","href":"https://d9yac136u048z.cloudfront.net/images/thumbnails/bookmark.png"}],"data":[{"name":"id","value":21907679},{"name":"name","value":"http://cl.ly"},{"name":"private","value":true},{"name":"trash","value":false},{"name":"views","value":0},{"name":"created","value":"2012-09-11T15:46:47Z"}]},{"href":"https://api.getcloudapp.com/drops/21907678","links":[{"rel":"collection","href":"https://api.getcloudapp.com/drops"},{"rel":"canonical","href":"http://cl.ly/image/1u232n412l14"},{"rel":"icon","href":"https://thumbs.getcloudapp.com/1u232n412l14"},{"rel":"embed","href":"http://cl.ly/image/1u232n412l14/favicon.ico"},{"rel":"download","href":"http://cl.ly/image/1u232n412l14/download/favicon.ico"}],"data":[{"name":"id","value":21907678},{"name":"name","value":"favicon.ico"},{"name":"private","value":true},{"name":"trash","value":false},{"name":"views","value":0},{"name":"created","value":"2012-09-11T15:46:45Z"}]}]}}'
85
+ http_version:
86
+ recorded_at: Tue, 11 Sep 2012 15:46:52 GMT
87
+ - request:
88
+ method: post
89
+ uri: https://api.getcloudapp.com/drops
90
+ body:
91
+ encoding: UTF-8
92
+ string: ! '{"name":null,"private":true,"trash":false,"bookmark_url":"http://getcloudapp.com","file_size":null}'
93
+ headers:
94
+ Authorization:
95
+ - Token token="abc123"
96
+ Content-Type:
97
+ - application/json
98
+ Accept:
99
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
100
+ text/plain
101
+ User-Agent:
102
+ - Ruby
103
+ response:
104
+ status:
105
+ code: 201
106
+ message: Created
107
+ headers:
108
+ Cache-Control:
109
+ - max-age=0, private, must-revalidate
110
+ Content-Type:
111
+ - application/vnd.collection+json; charset=utf-8
112
+ Etag:
113
+ - ! '"3486b7b686f30b992ebca8d89b09cf01"'
114
+ Server:
115
+ - thin 1.4.1 codename Chromeo
116
+ Set-Cookie:
117
+ - _engine_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRkkiJWY5N2M3NmVkMjBlZTIzYmM0MjVjYTJiMDkyNjhmMzViBjsAVA%3D%3D--1940b1355947a4954f093626e9753a36f6550327;
118
+ path=/; HttpOnly
119
+ X-Runtime:
120
+ - '0.083921'
121
+ X-Ua-Compatible:
122
+ - IE=Edge,chrome=1
123
+ Transfer-Encoding:
124
+ - chunked
125
+ Connection:
126
+ - keep-alive
127
+ body:
128
+ encoding: US-ASCII
129
+ string: ! '{"collection":{"version":"1.0","href":"https://api.getcloudapp.com/drops","links":[{"rel":"drops-stream","href":"https://api.getcloudapp.com/drops/stream"}],"queries":[{"href":"https://api.getcloudapp.com/drops","rel":"drops-list","data":[{"name":"filter","value":"active"},{"name":"limit","value":20}]}],"template":{"data":[{"name":"name","value":null},{"name":"private","value":true},{"name":"trash","value":false},{"name":"bookmark_url","value":null},{"name":"file_size","value":null}]},"items":[{"href":"https://api.getcloudapp.com/drops/21907683","links":[{"rel":"collection","href":"https://api.getcloudapp.com/drops"},{"rel":"canonical","href":"http://cl.ly/0e0E2w0w0T0U"},{"rel":"icon","href":"https://d9yac136u048z.cloudfront.net/images/thumbnails/bookmark.png"}],"data":[{"name":"id","value":21907683},{"name":"name","value":"http://getcloudapp.com"},{"name":"private","value":true},{"name":"trash","value":false},{"name":"views","value":0},{"name":"created","value":"2012-09-11T15:46:54Z"}]}]}}'
130
+ http_version:
131
+ recorded_at: Tue, 11 Sep 2012 15:46:52 GMT
132
+ recorded_with: VCR 2.0.1