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,174 @@
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.008812'
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:50 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
+ - ! '"e99f62e3f2bdb84c23f5e4072ad6d53a"'
72
+ Server:
73
+ - thin 1.4.1 codename Chromeo
74
+ X-Runtime:
75
+ - '0.038310'
76
+ X-Ua-Compatible:
77
+ - IE=Edge,chrome=1
78
+ Transfer-Encoding:
79
+ - chunked
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/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:51 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://cl.ly","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
+ - ! '"188c220a63dd6f84e99c636f9cde2b5c"'
114
+ Server:
115
+ - thin 1.4.1 codename Chromeo
116
+ Set-Cookie:
117
+ - _engine_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRkkiJTNhMjc2YTNiNWY3ZmVkN2U5Y2ZmNTU4YjNlY2E2NzNjBjsAVA%3D%3D--cd71d5b8f83a5628f1bd299d79bce081d406413b;
118
+ path=/; HttpOnly
119
+ X-Runtime:
120
+ - '0.047050'
121
+ X-Ua-Compatible:
122
+ - IE=Edge,chrome=1
123
+ Content-Length:
124
+ - '995'
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/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"}]}]}}'
130
+ http_version:
131
+ recorded_at: Tue, 11 Sep 2012 15:46:51 GMT
132
+ - request:
133
+ method: get
134
+ uri: https://api.getcloudapp.com/drops/21907682
135
+ body:
136
+ encoding: US-ASCII
137
+ string: ''
138
+ headers:
139
+ Authorization:
140
+ - Token token="abc123"
141
+ Accept:
142
+ - application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html,
143
+ text/plain
144
+ Accept-Encoding:
145
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
146
+ User-Agent:
147
+ - Ruby
148
+ response:
149
+ status:
150
+ code: 200
151
+ message: OK
152
+ headers:
153
+ Cache-Control:
154
+ - max-age=0, private, must-revalidate
155
+ Content-Type:
156
+ - application/vnd.collection+json; charset=utf-8
157
+ Etag:
158
+ - ! '"188c220a63dd6f84e99c636f9cde2b5c"'
159
+ Server:
160
+ - thin 1.4.1 codename Chromeo
161
+ X-Runtime:
162
+ - '0.017271'
163
+ X-Ua-Compatible:
164
+ - IE=Edge,chrome=1
165
+ Transfer-Encoding:
166
+ - chunked
167
+ Connection:
168
+ - keep-alive
169
+ body:
170
+ encoding: US-ASCII
171
+ 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"}]}]}}'
172
+ http_version:
173
+ recorded_at: Tue, 11 Sep 2012 15:46:51 GMT
174
+ recorded_with: VCR 2.0.1
@@ -0,0 +1,32 @@
1
+ require 'helper'
2
+
3
+ require 'cloudapp/service/authorized_representation'
4
+
5
+ describe CloudApp::AuthorizedRepresentation do
6
+ let(:representation) { stub :representation, __response__: response }
7
+ let(:response) { stub :response, status: status }
8
+ let(:status) { 200 }
9
+ let(:href) { stub }
10
+ subject { representation }
11
+ before do
12
+ representation.extend CloudApp::AuthorizedRepresentation
13
+ end
14
+
15
+ describe '#authorized?' do
16
+ it { should be_authorized }
17
+
18
+ context 'unauthorized status' do
19
+ let(:status) { 401 }
20
+ it { should_not be_authorized }
21
+ end
22
+ end
23
+
24
+ describe '#unauthorized?' do
25
+ it { should_not be_unauthorized }
26
+
27
+ context 'unauthorized status' do
28
+ let(:status) { 401 }
29
+ it { should be_unauthorized }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,45 @@
1
+ require 'helper'
2
+
3
+ require 'cloudapp/collection_json/item'
4
+
5
+ describe CloudApp::CollectionJson::Item do
6
+ let(:item_data) {{ 'data' => [] }}
7
+ subject { CloudApp::CollectionJson::Item.new item_data }
8
+
9
+ its(:href) { should be_nil }
10
+ its(:links) { should be_empty }
11
+ its(:data) { should be_empty }
12
+
13
+ context 'with an href' do
14
+ let(:href) { stub :href }
15
+ before do item_data['href'] = href end
16
+ its(:href) { should eq(href) }
17
+ end
18
+
19
+ context 'with links' do
20
+ let(:links) {[
21
+ { 'rel' => 'canonical', 'href' => 'link1' },
22
+ { 'rel' => 'alternate', 'href' => 'link2' }
23
+ ]}
24
+ before do item_data['links'] = links end
25
+
26
+ its(:links) { should have(2).items }
27
+
28
+ it 'presents links' do
29
+ subject.links.find {|link| link.rel == 'canonical' }.
30
+ href.should eq('link1')
31
+ subject.links.find {|link| link.rel == 'alternate' }.
32
+ href.should eq('link2')
33
+ end
34
+ end
35
+
36
+ context 'with data' do
37
+ let(:data) {[
38
+ { 'name' => 'attr1', 'value' => 'value1' },
39
+ { 'name' => 'attr2', 'value' => 'value2' }
40
+ ]}
41
+ before do item_data['data'] = data end
42
+
43
+ its(:data) { should eq('attr1' => 'value1', 'attr2' => 'value2') }
44
+ end
45
+ end
@@ -0,0 +1,118 @@
1
+ require 'helper'
2
+
3
+ require 'cloudapp/collection_json/representation'
4
+
5
+ describe CloudApp::CollectionJson::Representation do
6
+ let(:response) { stub :response, status: status }
7
+ let(:status) { 200 }
8
+ let(:representation) {{
9
+ 'collection' => {
10
+ 'href' => href,
11
+ 'items' => []
12
+ }
13
+ }}
14
+ let(:href) { stub }
15
+ subject { representation }
16
+ before do
17
+ representation.extend CloudApp::CollectionJson::Representation
18
+ representation.stub __response__: response
19
+ end
20
+
21
+ its(:href) { should eq(href) }
22
+
23
+ describe '#collection_links' do
24
+ its(:collection_links) { should be_empty }
25
+
26
+ context 'with collection links' do
27
+ let(:links) {[
28
+ { 'rel' => 'next', 'href' => '/next' },
29
+ { 'rel' => 'prev', 'href' => '/prev' }
30
+ ]}
31
+ before do representation['collection']['links'] = links end
32
+
33
+ its(:collection_links) { should have(2).items }
34
+
35
+ it 'presents links' do
36
+ subject.collection_links.find {|link| link.rel == 'next' }.
37
+ href.should eq('/next')
38
+ subject.collection_links.find {|link| link.rel == 'prev' }.
39
+ href.should eq('/prev')
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#items' do
45
+ it 'is empty' do
46
+ subject.items(stub(:item_source)).should be_empty
47
+ end
48
+
49
+ context 'with items' do
50
+ let(:items) {[ stub(:item1), stub(:item2) ]}
51
+ let(:item_data) {[ stub(:item_data1), stub(:item_data2) ]}
52
+ let(:item_source) { ->(item) {
53
+ if item == item_data[0]
54
+ items[0]
55
+ elsif item == item_data[1]
56
+ items[1]
57
+ end
58
+ }}
59
+ before do representation['collection']['items'] = item_data end
60
+
61
+ it 'should have items' do
62
+ subject.items(item_source).should eq(items)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#templates' do
68
+ it 'is nil' do
69
+ subject.template(stub(:template_source)).should be_nil
70
+ end
71
+
72
+ context 'with a template' do
73
+ let(:template) { stub :template }
74
+ let(:template_data) { stub :template_data }
75
+ let(:template_source) { ->(item) { template if item == template_data }}
76
+ before do representation['collection']['template'] = template_data end
77
+
78
+ it 'finds the template' do
79
+ subject.template(template_source).should eq(template)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#queries' do
85
+ it 'is nil' do
86
+ subject.queries(stub(:query_source)).should be_nil
87
+ end
88
+
89
+ context 'with queries' do
90
+ let(:queries) {[ stub(:query1), stub(:query2) ]}
91
+ let(:query_data) {[ stub(:query_data1), stub(:query_data2) ]}
92
+ let(:query_source) { ->(query) { queries.at(query_data.index(query)) }}
93
+ before do representation['collection']['queries'] = query_data end
94
+
95
+ it 'should have queries' do
96
+ subject.queries(query_source).should eq(queries)
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#query' do
102
+ it 'is nil' do
103
+ subject.query('rel', stub(:query_source)).should be_nil
104
+ end
105
+
106
+ context 'with queries' do
107
+ let(:queries) {[ stub(:query1, rel: 'one'),
108
+ stub(:query2, rel: 'two') ]}
109
+ let(:query_data) {[ stub(:query_data1), stub(:query_data2) ]}
110
+ let(:query_source) { ->(query) { queries.at(query_data.index(query)) }}
111
+ before do representation['collection']['queries'] = query_data end
112
+
113
+ it 'finds the query by rel' do
114
+ subject.query('one', query_source).should eq(queries.first)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,53 @@
1
+ require 'helper'
2
+ require 'cloudapp/collection_json/item'
3
+
4
+ require 'cloudapp/collection_json/template'
5
+
6
+ describe CloudApp::CollectionJson::Template do
7
+ let(:template_data) {{ 'data' => [] }}
8
+ subject { CloudApp::CollectionJson::Template.new template_data }
9
+
10
+ its(:rel) { should be_nil }
11
+ its(:enctype) { should be_nil }
12
+
13
+
14
+ context 'with a rel' do
15
+ let(:rel) { stub :rel }
16
+ before do template_data['rel'] = rel end
17
+
18
+ its(:rel) { should eq(rel) }
19
+ end
20
+
21
+ context 'with an encoding type' do
22
+ let(:enctype) { stub :enctype }
23
+ before do template_data['enctype'] = enctype end
24
+
25
+ its(:enctype) { should eq(enctype) }
26
+ end
27
+
28
+ describe '#fill' do
29
+ let(:email) { 'arthur@dent.com' }
30
+ let(:template_data) {{
31
+ 'data' => [
32
+ { 'name' => 'email', 'value' => '' },
33
+ { 'name' => 'age', 'value' => 29 }
34
+ ]
35
+ }}
36
+
37
+ it 'returns a filled template' do
38
+ expected = { 'email' => email, 'age' => 29 }
39
+ subject.fill('email' => email).should eq(expected)
40
+ end
41
+
42
+ it 'leaves data untouched' do
43
+ subject.fill('email' => email)
44
+ subject.data.should eq('email' => '', 'age' => 29)
45
+ end
46
+
47
+ it 'ignores attributes not in the template' do
48
+ expected = { 'email' => email, 'age' => 29 }
49
+ new_data = { 'email' => email, 'ignore' => 'me' }
50
+ subject.fill(new_data).should eq(expected)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,148 @@
1
+ require 'helper'
2
+ require 'ostruct'
3
+
4
+ require 'cloudapp/service/drop_collection'
5
+
6
+ describe CloudApp::DropCollection do
7
+ let(:representation) { stub :representation, unauthorized?: false,
8
+ items: items }
9
+ let(:service) { stub :service }
10
+ let(:items) {[ stub(:item1), stub(:item2) ]}
11
+ let(:drop_class) { stub(:drop_class, new: :drop) }
12
+ subject { CloudApp::DropCollection.new representation, service, drop_class }
13
+
14
+ describe 'collection' do
15
+ it 'is a collection of drops' do
16
+ subject.size.should eq(2)
17
+ end
18
+
19
+ it 'decodes each drop' do
20
+ items.each do |drop|
21
+ drop_class.should_receive(:new).once.
22
+ with(drop, kind_of(CloudApp::DropCollection))
23
+ end
24
+ subject
25
+ end
26
+ end
27
+
28
+ describe '#authorized?' do
29
+ let(:authorized) { stub :authorized }
30
+ before do representation.stub(authorized?: authorized) end
31
+
32
+ it 'delegates to representation' do
33
+ subject.authorized?.should eq(authorized)
34
+ end
35
+ end
36
+
37
+ describe '#unauthorized?' do
38
+ let(:unauthorized) { stub :unauthorized }
39
+ before do representation.stub(unauthorized?: unauthorized) end
40
+
41
+ it 'delegates to representation' do
42
+ subject.unauthorized?.should eq(unauthorized)
43
+ end
44
+ end
45
+
46
+ describe '#follow' do
47
+ let(:link_representation) {
48
+ stub :link_representation, unauthorized?: false,
49
+ items: []
50
+ }
51
+ subject {
52
+ CloudApp::DropCollection.
53
+ new(representation, service, drop_class).
54
+ follow('yes')
55
+ }
56
+
57
+ before do
58
+ link = stub :link
59
+ link.should_receive(:follow).once.and_return(link_representation)
60
+ representation.should_receive(:link).once.and_return(link)
61
+ end
62
+
63
+ it { subject.should be_a(CloudApp::DropCollection) }
64
+ its(:representation) { should eq(link_representation) }
65
+ its(:drop_class) { should eq(drop_class) }
66
+
67
+ it 'passes along service' do
68
+ service.should_receive(:trash_drop)
69
+ subject.trash stub.as_null_object
70
+ end
71
+ end
72
+
73
+ describe '#has_link?' do
74
+ context 'with link' do
75
+ before do
76
+ representation.should_receive(:link).
77
+ once.
78
+ with('yes').
79
+ and_return(stub(:link))
80
+ end
81
+
82
+ it 'returns false' do
83
+ subject.has_link?('yes').should eq(true)
84
+ end
85
+ end
86
+
87
+ context 'without link' do
88
+ before do
89
+ representation.should_receive(:link).
90
+ once.
91
+ with('no').
92
+ and_yield
93
+ end
94
+
95
+ it 'returns true' do
96
+ subject.has_link?('no').should eq(false)
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#privatize' do
102
+ it 'privatizes the given drop' do
103
+ href = stub :href
104
+ service.should_receive(:privatize_drop).once.with(href)
105
+ subject.privatize stub(:drop, href: href)
106
+ end
107
+ end
108
+
109
+ describe '#publicize' do
110
+ it 'publicizes the given drop' do
111
+ href = stub :href
112
+ service.should_receive(:publicize_drop).once.with(href)
113
+ subject.publicize stub(:drop, href: href)
114
+ end
115
+ end
116
+
117
+ describe '#trash' do
118
+ it 'trashes the given drop' do
119
+ href = stub :href
120
+ service.should_receive(:trash_drop).once.with(href)
121
+ subject.trash stub(:drop, href: href)
122
+ end
123
+ end
124
+
125
+ describe '#recover' do
126
+ it 'recovers the given drop' do
127
+ href = stub :href
128
+ service.should_receive(:recover_drop).once.with(href)
129
+ subject.recover stub(:drop, href: href)
130
+ end
131
+ end
132
+
133
+ describe '#delete' do
134
+ it 'deletes the given drop' do
135
+ href = stub :href
136
+ service.should_receive(:delete_drop).once.with(href)
137
+ subject.delete stub(:drop, href: href)
138
+ end
139
+ end
140
+
141
+ describe '#delete'
142
+
143
+ context 'unauthorized' do
144
+ before do representation.stub(unauthorized?: true) end
145
+
146
+ it { should be_empty }
147
+ end
148
+ end