cloudapp-service 1.0.0.beta.1

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.
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