cloudapp 1.1.0 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG.md +3 -0
  2. data/Gemfile.lock +18 -34
  3. data/README.md +2 -1
  4. data/Rakefile +5 -1
  5. data/cloudapp.gemspec +50 -41
  6. data/lib/cloudapp/account.rb +111 -0
  7. data/lib/cloudapp/authorized_representation.rb +17 -0
  8. data/lib/cloudapp/collection_json/item.rb +20 -0
  9. data/lib/cloudapp/collection_json/representation.rb +74 -0
  10. data/lib/cloudapp/collection_json/template.rb +17 -0
  11. data/lib/cloudapp/collection_json/tint.rb +11 -0
  12. data/lib/cloudapp/collection_json.rb +3 -0
  13. data/lib/cloudapp/drop.rb +22 -6
  14. data/lib/cloudapp/drop_collection.rb +22 -0
  15. data/lib/cloudapp/service.rb +172 -0
  16. data/lib/cloudapp/token.rb +27 -0
  17. data/lib/cloudapp.rb +5 -1
  18. data/man/cloudapp.1 +202 -45
  19. data/man/cloudapp.1.html +114 -42
  20. data/man/cloudapp.1.ronn +125 -45
  21. data/spec/cassettes/create_bookmark.yml +255 -0
  22. data/spec/cassettes/create_bookmark_with_name.yml +266 -0
  23. data/spec/cassettes/create_bookmark_with_privacy.yml +277 -0
  24. data/spec/cassettes/delete_drop.yml +383 -0
  25. data/spec/cassettes/list_drops.yml +139 -0
  26. data/spec/cassettes/list_drops_with_bad_token.yml +57 -0
  27. data/spec/cassettes/list_drops_with_filter.yml +209 -0
  28. data/spec/cassettes/list_drops_with_href.yml +287 -0
  29. data/spec/cassettes/list_drops_with_href_and_filter.yml +287 -0
  30. data/spec/cassettes/list_drops_with_href_and_limit.yml +287 -0
  31. data/spec/cassettes/list_drops_with_limit.yml +211 -0
  32. data/spec/cassettes/list_drops_with_nil_href.yml +139 -0
  33. data/spec/cassettes/purge_drops.yml +1103 -0
  34. data/spec/cassettes/recover_drop.yml +628 -0
  35. data/spec/cassettes/rename_drop.yml +367 -0
  36. data/spec/cassettes/setup_drops.yml +968 -0
  37. data/spec/cassettes/token_for_account.yml +127 -0
  38. data/spec/cassettes/token_for_account_with_bad_credentials.yml +115 -0
  39. data/spec/cassettes/trash_drop.yml +478 -0
  40. data/spec/cassettes/update_drop_bookmark_url.yml +379 -0
  41. data/spec/cassettes/update_file.yml +573 -0
  42. data/spec/cassettes/upload_file.yml +470 -0
  43. data/spec/cassettes/upload_file_with_name.yml +484 -0
  44. data/spec/cassettes/upload_file_with_privacy.yml +495 -0
  45. data/spec/cassettes/view_drop.yml +73 -0
  46. data/spec/cloudapp/account_spec.rb +166 -0
  47. data/spec/cloudapp/authorized_representation_spec.rb +32 -0
  48. data/spec/cloudapp/collection_json/item_spec.rb +45 -0
  49. data/spec/cloudapp/collection_json/representation_spec.rb +118 -0
  50. data/spec/cloudapp/collection_json/template_spec.rb +53 -0
  51. data/spec/cloudapp/drop_collection_spec.rb +63 -0
  52. data/spec/cloudapp/drop_spec.rb +81 -51
  53. data/spec/cloudapp/service_spec.rb +357 -0
  54. data/spec/cloudapp/token_spec.rb +33 -0
  55. data/spec/integration_spec.rb +100 -0
  56. data/spec/support/stub_class_or_module.rb +6 -3
  57. data/spec/support/vcr.rb +21 -5
  58. metadata +84 -106
  59. data/bin/cloudapp +0 -184
  60. data/lib/cloudapp/config.rb +0 -39
  61. data/lib/cloudapp/drop_content.rb +0 -34
  62. data/lib/cloudapp/drop_presenter.rb +0 -80
  63. data/lib/cloudapp/drop_service.rb +0 -255
  64. data/lib/cloudapp/identity.rb +0 -14
  65. data/spec/cassettes/DropContent/download.yml +0 -116
  66. data/spec/cassettes/DropService/create_bookmark.yml +0 -105
  67. data/spec/cassettes/DropService/create_bookmark_with_bad_credentials.yml +0 -39
  68. data/spec/cassettes/DropService/create_bookmark_with_name.yml +0 -105
  69. data/spec/cassettes/DropService/create_private_bookmark.yml +0 -105
  70. data/spec/cassettes/DropService/create_public_bookmark.yml +0 -105
  71. data/spec/cassettes/DropService/drop.yml +0 -81
  72. data/spec/cassettes/DropService/list_drops.yml +0 -135
  73. data/spec/cassettes/DropService/list_drops_with_bad_credentials.yml +0 -39
  74. data/spec/cassettes/DropService/list_drops_with_limit.yml +0 -117
  75. data/spec/cassettes/DropService/list_trash.yml +0 -111
  76. data/spec/cassettes/DropService/list_trash_with_bad_credentials.yml +0 -39
  77. data/spec/cassettes/DropService/retrieve_token.yml +0 -42
  78. data/spec/cassettes/DropService/retrieve_token_with_bad_credentials.yml +0 -43
  79. data/spec/cassettes/DropService/upload_file.yml +0 -291
  80. data/spec/cassettes/DropService/upload_public_file.yml +0 -293
  81. data/spec/cloudapp/config_spec.rb +0 -59
  82. data/spec/cloudapp/drop_content_spec.rb +0 -21
  83. data/spec/cloudapp/drop_presenter_spec.rb +0 -120
  84. data/spec/cloudapp/drop_service_spec.rb +0 -400
  85. data/spec/cloudapp/identity_spec.rb +0 -27
  86. data/spec/support/fakefs_rspec.rb +0 -5
  87. data/spec/support/vcr_rspec.rb +0 -9
@@ -0,0 +1,357 @@
1
+ require 'helper'
2
+ require 'support/vcr'
3
+
4
+ require 'cloudapp/service'
5
+
6
+ describe CloudApp::Service do
7
+ let(:service) { CloudApp::Service.using_token token }
8
+ let(:token) { 'abc123' }
9
+ let(:favicon) {
10
+ Pathname('../../support/files/favicon.ico').expand_path(__FILE__)
11
+ }
12
+
13
+ after(:all) do
14
+ VCR.use_cassette('purge_drops') do
15
+ service.drops(filter: 'all').items.each do |drop|
16
+ service.delete_drop(drop.href)
17
+ end
18
+ end
19
+ end
20
+
21
+ describe '#drops' do
22
+ subject { VCR.use_cassette('list_drops') { service.drops }}
23
+ before do
24
+ VCR.use_cassette('setup_drops') do
25
+ service.upload favicon
26
+ service.bookmark 'http://cl.ly', private: false
27
+ drop = service.bookmark 'http://getcloudapp.com', name: 'CloudApp'
28
+ service.trash_drop drop.href
29
+ end
30
+ end
31
+
32
+ it { should be_a(CloudApp::CollectionJson::Representation) }
33
+
34
+ it 'has 2 drops' do
35
+ subject.should have(2).items
36
+ end
37
+
38
+ context 'with filter' do
39
+ subject {
40
+ VCR.use_cassette('list_drops_with_filter') {
41
+ service.drops filter: 'trash'
42
+ }
43
+ }
44
+
45
+ it 'returns the filtered drops' do
46
+ subject.should have(1).items
47
+ end
48
+ end
49
+
50
+ context 'with limit' do
51
+ subject {
52
+ VCR.use_cassette('list_drops_with_limit') {
53
+ service.drops limit: 1
54
+ }
55
+ }
56
+
57
+ it 'limits drops' do
58
+ subject.should have(1).items
59
+ end
60
+ end
61
+
62
+ context 'with href' do
63
+ subject {
64
+ VCR.use_cassette('list_drops_with_href') {
65
+ @href = service.drops(limit: 1).link('next').href
66
+ service.drops href: @href
67
+ }
68
+ }
69
+
70
+ it 'returns the resource at the given href' do
71
+ subject.link('self').href.should eq(@href)
72
+ end
73
+ end
74
+
75
+ context 'with an empty href' do
76
+ subject {
77
+ VCR.use_cassette('list_drops_with_nil_href') {
78
+ service.drops(href: nil)
79
+ }
80
+ }
81
+
82
+ it 'has 2 drops' do
83
+ subject.should have(2).items
84
+ end
85
+ end
86
+
87
+ context 'with href and filter' do
88
+ subject {
89
+ VCR.use_cassette('list_drops_with_href_and_filter') {
90
+ @href = service.drops(limit: 1).link('next').href
91
+ service.drops href: @href, filter: 'trash'
92
+ }
93
+ }
94
+
95
+ it 'ignores filter' do
96
+ subject.should have(1).items
97
+ end
98
+ end
99
+
100
+ context 'with href and limit' do
101
+ subject {
102
+ VCR.use_cassette('list_drops_with_href_and_limit') {
103
+ @href = service.drops(limit: 1).link('next').href
104
+ service.drops href: @href, limit: 42
105
+ }
106
+ }
107
+
108
+ it 'ignores limit' do
109
+ subject.should have(1).items
110
+ end
111
+ end
112
+
113
+ context 'with a bad token' do
114
+ let(:token) { 'wrong' }
115
+ subject {
116
+ VCR.use_cassette('list_drops_with_bad_token') {
117
+ service.drops
118
+ }
119
+ }
120
+
121
+ it { should be_unauthorized }
122
+ end
123
+ end
124
+
125
+ describe '#drop_at' do
126
+ subject {
127
+ VCR.use_cassette('list_drops') {
128
+ @href = service.drops.items.first.href
129
+ }
130
+ VCR.use_cassette('view_drop') { service.drop_at(@href) }
131
+ }
132
+
133
+ it { should be_a(CloudApp::CollectionJson::Representation) }
134
+
135
+ it 'returns a drop' do
136
+ subject.should have(1).item
137
+ end
138
+ end
139
+
140
+ describe '#update' do
141
+ let(:url) { 'http://getcloudapp.com' }
142
+ let(:name) { 'New Drop Name' }
143
+ let(:private) { false }
144
+ subject {
145
+ VCR.use_cassette('rename_drop') {
146
+ drop = service.bookmark(url).items.first
147
+ service.update drop.href, name: name, private: private
148
+ }
149
+ }
150
+
151
+ it { should be_a(CloudApp::CollectionJson::Representation) }
152
+
153
+ it 'returns the updated drop' do
154
+ subject.should have(1).item
155
+ end
156
+
157
+ it 'updates the name' do
158
+ subject.items.first.data['name'].should eq(name)
159
+ end
160
+
161
+ it 'updates the privacy' do
162
+ subject.items.first.data['private'].should eq(private)
163
+ end
164
+
165
+ context 'updating bookmark link' do
166
+ subject {
167
+ VCR.use_cassette('update_drop_bookmark_url') {
168
+ drop = service.bookmark(url).items.first
169
+ service.update drop.href, url: 'http://example.org'
170
+ }
171
+ }
172
+
173
+ it { should be_a(CloudApp::CollectionJson::Representation) }
174
+
175
+ it 'returns the new drop' do
176
+ subject.should have(1).item
177
+ end
178
+ end
179
+
180
+ context 'updating file' do
181
+ let(:options) {{ path: favicon }}
182
+ subject {
183
+ VCR.use_cassette('update_file') {
184
+ drop = service.bookmark(url).items.first
185
+ service.update drop.href, options
186
+ }
187
+ }
188
+
189
+ it { should be_a(CloudApp::CollectionJson::Representation) }
190
+
191
+ it 'returns the new drop' do
192
+ subject.should have(1).item
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '#bookmark' do
198
+ let(:url) { 'http://getcloudapp.com' }
199
+ subject {
200
+ VCR.use_cassette('create_bookmark') { service.bookmark(url) }
201
+ }
202
+
203
+ it { should be_a(CloudApp::CollectionJson::Representation) }
204
+
205
+ it 'returns the new drop' do
206
+ subject.should have(1).item
207
+ end
208
+
209
+ it 'bookmarks the url'
210
+
211
+ context 'with a name' do
212
+ let(:name) { 'New Bookmark' }
213
+ subject {
214
+ VCR.use_cassette('create_bookmark_with_name') {
215
+ service.bookmark url, name: name
216
+ }
217
+ }
218
+
219
+ it 'has the given name' do
220
+ subject.items.first.data['name'].should eq(name)
221
+ end
222
+ end
223
+
224
+ context 'with a privacy' do
225
+ subject {
226
+ VCR.use_cassette('create_bookmark_with_privacy') {
227
+ service.bookmark url, private: false
228
+ }
229
+ }
230
+
231
+ it 'is public' do
232
+ subject.items.first.data['private'].should eq(false)
233
+ end
234
+ end
235
+ end
236
+
237
+ describe '#upload' do
238
+ subject {
239
+ VCR.use_cassette('upload_file') { service.upload(favicon) }
240
+ }
241
+
242
+ it { should be_a(CloudApp::CollectionJson::Representation) }
243
+
244
+ it 'returns the new drop' do
245
+ subject.should have(1).item
246
+ end
247
+
248
+ context 'with a name' do
249
+ let(:name) { 'New File' }
250
+ subject {
251
+ VCR.use_cassette('upload_file_with_name') {
252
+ service.upload favicon, name: name
253
+ }
254
+ }
255
+
256
+ it 'has the given name' do
257
+ subject.items.first.data['name'].should eq(name)
258
+ end
259
+ end
260
+
261
+ context 'with a privacy' do
262
+ subject {
263
+ VCR.use_cassette('upload_file_with_privacy') {
264
+ service.upload favicon, private: false
265
+ }
266
+ }
267
+
268
+ it 'is public' do
269
+ subject.items.first.data['private'].should eq(false)
270
+ end
271
+ end
272
+
273
+ describe 'too large file'
274
+ end
275
+
276
+ describe '#token_for_account' do
277
+ let(:service) { CloudApp::Service.new }
278
+ let(:email) { 'arthur@dent.com' }
279
+ let(:password) { 'towel' }
280
+ subject {
281
+ VCR.use_cassette('token_for_account') {
282
+ CloudApp::Service.new.token_for_account email, password
283
+ }
284
+ }
285
+
286
+ it { should be_a(CloudApp::CollectionJson::Representation) }
287
+
288
+ it 'returns a single item' do
289
+ subject.should have(1).item
290
+ end
291
+
292
+ it 'returns the token' do
293
+ subject.items.first.data.should eq('token' => token)
294
+ end
295
+
296
+ context 'with bad credentials' do
297
+ let(:password) { 'wrong' }
298
+ subject {
299
+ VCR.use_cassette('token_for_account_with_bad_credentials') {
300
+ CloudApp::Service.new.token_for_account email, password
301
+ }
302
+ }
303
+
304
+ it { should be_unauthorized }
305
+ end
306
+ end
307
+
308
+ describe '#trash_drop' do
309
+ subject {
310
+ VCR.use_cassette('trash_drop') {
311
+ drop = service.bookmark('http://getcloudapp.com').items.first
312
+ service.trash_drop drop.href
313
+ }
314
+ }
315
+
316
+ it { should be_a(CloudApp::CollectionJson::Representation) }
317
+
318
+ it 'returns the trashed drop' do
319
+ subject.should have(1).item
320
+ end
321
+
322
+ it 'trashes the drop' do
323
+ subject.items.first.data['trash'].should eq(true)
324
+ end
325
+ end
326
+
327
+ describe '#recover_drop' do
328
+ subject {
329
+ VCR.use_cassette('recover_drop') {
330
+ drop = service.bookmark('http://getcloudapp.com').items.first
331
+ service.trash_drop drop.href
332
+ service.recover_drop drop.href
333
+ }
334
+ }
335
+
336
+ it { should be_a(CloudApp::CollectionJson::Representation) }
337
+
338
+ it 'returns the recovered drop' do
339
+ subject.should have(1).item
340
+ end
341
+
342
+ it 'recovers the drop' do
343
+ subject.items.first.data['trash'].should eq(false)
344
+ end
345
+ end
346
+
347
+ describe '#delete_drop' do
348
+ subject {
349
+ VCR.use_cassette('delete_drop') {
350
+ drop = service.bookmark('http://getcloudapp.com').items.first
351
+ service.delete_drop drop.href
352
+ }
353
+ }
354
+
355
+ it { should be_authorized }
356
+ end
357
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ require 'cloudapp/token'
4
+
5
+ describe CloudApp::Token do
6
+ let(:service_source) { -> { service }}
7
+ before do CloudApp::Token.service_source = service_source end
8
+ after do CloudApp::Token.service_source = nil end
9
+
10
+ describe '.for_account' do
11
+ let(:email) { 'arthur@dent.com' }
12
+ let(:password) { 'towel' }
13
+ let(:service) { stub :service, token_for_account: representation }
14
+ let(:representation) { stub :representation, unauthorized?: unauthorized,
15
+ items: [ item ]}
16
+ let(:unauthorized) { false }
17
+ let(:item) { stub :item, data: { 'token' => token }}
18
+ let(:token) { 'token' }
19
+ subject { CloudApp::Token.for_account(email, password) }
20
+
21
+ it { should eq(token) }
22
+
23
+ it 'queries the drop service' do
24
+ service.should_receive(:token_for_account).with(email, password)
25
+ subject
26
+ end
27
+
28
+ context 'with bad credentials' do
29
+ let(:unauthorized) { true }
30
+ it { should be_nil }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,100 @@
1
+ require 'helper'
2
+ require 'support/vcr'
3
+
4
+ require 'cloudapp'
5
+
6
+ describe CloudApp, :integration do
7
+ let(:path) { Pathname('../support/files/favicon.ico').expand_path(__FILE__) }
8
+
9
+ def all_drops(options = {})
10
+ options = { filter: 'all' }.merge(options)
11
+ @account.drops(options)
12
+ end
13
+
14
+ def active_drops
15
+ @account.drops(filter: 'active')
16
+ end
17
+
18
+ def trashed_drops
19
+ @account.drops(filter: 'trash')
20
+ end
21
+
22
+ def create_bookmark(url, options = {})
23
+ @account.bookmark(url, options).first
24
+ end
25
+
26
+ def create_upload(path, options = {})
27
+ @account.upload(path, options).first
28
+ end
29
+
30
+ def drop_details(drop)
31
+ @account.drop_at(drop.href).first
32
+ end
33
+
34
+ def recover_drop(drop)
35
+ @account.recover_drop(drop.href).first
36
+ end
37
+
38
+ def trash_drop(drop)
39
+ @account.trash_drop(drop.href).first
40
+ end
41
+
42
+ def delete_drop(drop)
43
+ @account.delete_drop(drop.href)
44
+ end
45
+
46
+
47
+ it 'integrates with the API' do
48
+ token = CloudApp::Token.for_account 'arthur@dent.com', 'towel'
49
+ token.should_not be_nil
50
+
51
+ @account = CloudApp::Account.using_token token
52
+ drops = all_drops
53
+ unless all_drops.empty?
54
+ drops.each do |drop| delete_drop(drop) end
55
+ end
56
+
57
+ all_drops.should be_empty
58
+
59
+ bookmark = create_bookmark 'http://getcloudapp.com', name: 'CloudApp'
60
+ file = create_upload path
61
+ public_drop = create_bookmark 'http://getcloudapp.com', private: false
62
+
63
+ all_drops.should have(3).items
64
+
65
+ bookmark_details = drop_details bookmark
66
+ bookmark_details.name .should eq('CloudApp')
67
+ bookmark_details.private.should eq(true)
68
+
69
+ drop_details(file).name.should eq('favicon.ico')
70
+ drop_details(public_drop).private.should eq(false)
71
+
72
+ trash_drop bookmark
73
+ trash_drop file
74
+ all_drops .should have(3).items
75
+ active_drops .should have(1).items
76
+ trashed_drops.should have(2).items
77
+
78
+ recover_drop bookmark
79
+ all_drops .should have(3).items
80
+ active_drops .should have(2).items
81
+ trashed_drops.should have(1).items
82
+
83
+ limited = all_drops(limit: 1)
84
+ limited.should have(1).item
85
+
86
+ next_page = all_drops(href: limited.link('next').href)
87
+ next_page.should have(1).item
88
+
89
+ # TODO: Uncomment when pagination with filter is fixed.
90
+ next_page = all_drops(href: next_page.link('next').href)
91
+ next_page.should have(1).item
92
+ next_page.link('next') { nil }.should be_nil
93
+
94
+ delete_drop bookmark
95
+ delete_drop file
96
+ delete_drop public_drop
97
+
98
+ all_drops.should have(0).items
99
+ end
100
+ end
@@ -1,3 +1,5 @@
1
+ module Stubbed end
2
+
1
3
  def stub_module(full_name, &block)
2
4
  stub_class_or_module full_name, Module, &block
3
5
  end
@@ -7,13 +9,14 @@ def stub_class(full_name, &block)
7
9
  end
8
10
 
9
11
  def stub_class_or_module(full_name, kind, &block)
10
- full_name.to_s.split(/::/).inject(Object) do |context, name|
12
+ stub = full_name.to_s.split(/::/).inject(Object) do |context, name|
11
13
  begin
12
14
  context.const_get(name)
13
15
  rescue NameError
14
- context.const_set(name, kind.new)
16
+ stubbed = kind.new do include(Stubbed) end
17
+ context.const_set(name, stubbed)
15
18
  end
16
19
  end
17
20
 
18
- Object.const_get(full_name).class_eval &block if block
21
+ stub.class_eval &block if block
19
22
  end
data/spec/support/vcr.rb CHANGED
@@ -1,8 +1,24 @@
1
1
  require 'vcr'
2
2
 
3
- VCR.configure do |c|
4
- c.cassette_library_dir = 'spec/cassettes'
5
- c.default_cassette_options = { record: :none,
6
- match_requests_on: [ :method, :uri, :body ]}
7
- c.hook_into :webmock
3
+ VCR.configure do |vcr|
4
+ vcr.cassette_library_dir = 'spec/cassettes'
5
+ vcr.default_cassette_options = { record: :none,
6
+ match_requests_on: [ :method, :uri, :body ]}
7
+ vcr.hook_into :webmock
8
+ vcr.configure_rspec_metadata!
9
+ end
10
+
11
+ RSpec.configure do |rspec|
12
+ rspec.treat_symbols_as_metadata_keys_with_true_values = true
13
+ rspec.before :each, integration: true do
14
+ VCR.configure do |vcr|
15
+ vcr.allow_http_connections_when_no_cassette = true
16
+ end
17
+ end
18
+
19
+ rspec.after :each, integration: true do
20
+ VCR.configure do |vcr|
21
+ vcr.allow_http_connections_when_no_cassette = false
22
+ end
23
+ end
8
24
  end