cloudapp 1.1.0 → 2.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 (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