cloudapp 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/man/cloudapp.1.html CHANGED
@@ -86,7 +86,8 @@
86
86
  <p><code>cloudapp</code> [<code>--format</code>=<var>format</var>] <var>command</var> [<var>args</var>]<br />
87
87
  <code>cloudapp list</code> [<code>--count</code>=<var>count</var>]<br />
88
88
  <code>cloudapp bookmark</code> [<code>--private</code>] [<code>--public</code>] <var>url</var> [<var>url</var>...]<br />
89
- <code>cloudapp upload</code> [<code>--private</code>] [<code>--public</code>] <var>file</var> [<var>file</var>...]</p>
89
+ <code>cloudapp upload</code> [<code>--private</code>] [<code>--public</code>] <var>file</var> [<var>file</var>...]<br />
90
+ <code>cloudapp download</code> [<code>--output</code>=<var>file</var>] <var>url</var></p>
90
91
 
91
92
  <h2 id="DESCRIPTION">DESCRIPTION</h2>
92
93
 
@@ -105,8 +106,9 @@ output to a terminal and <strong>csv</strong> when output is piped to another pr
105
106
  <h2 id="COMMANDS">COMMANDS</h2>
106
107
 
107
108
  <dl>
108
- <dt><code>list</code> [<code>--count</code>=<var>count</var>]</dt><dd><p>List the most recent <var>count</var> drops. The default is to list <strong>20</strong> drops.</p></dd>
109
109
  <dt><code>bookmark</code> [<code>--private</code>] [<code>--public</code>] [<var>url</var>...]</dt><dd><p>Create a new bookmark to each <var>url</var> and print each link.</p></dd>
110
+ <dt><code>download</code> [<code>--output</code>=<var>file</var>] <var>url</var></dt><dd><p>Download the file at <var>url</var> to the current directory or to <var>file</var> if present.</p></dd>
111
+ <dt><code>list</code> [<code>--count</code>=<var>count</var>]</dt><dd><p>List the most recent <var>count</var> drops. The default is to list <strong>20</strong> drops.</p></dd>
110
112
  <dt><code>upload</code> [<code>--private</code>] [<code>--public</code>] <var>file</var> [<var>file</var>...]</dt><dd><p>Upload each <var>file</var> and print each link.</p></dd>
111
113
  </dl>
112
114
 
@@ -114,9 +116,11 @@ output to a terminal and <strong>csv</strong> when output is piped to another pr
114
116
  <h2 id="COMMAND-OPTIONS">COMMAND OPTIONS</h2>
115
117
 
116
118
  <dl>
119
+ <dt><code>-n</code> <var>count</var>, <code>--count</code>=<var>count</var></dt><dd><p>Display <var>count</var> drops. (Default: <strong>20</strong>)</p></dd>
120
+ <dt><code>-o</code> <var>file</var>, <code>--output</code>=<var>file</var></dt><dd><p>Download file as <var>file</var>. (Default: current directory using the remote file's
121
+ name)</p></dd>
117
122
  <dt><code>--private</code></dt><dd><p>Use a private (long) link when creating a new drop.</p></dd>
118
123
  <dt><code>--public</code></dt><dd><p>Use a public (short) link when creating a new drop.</p></dd>
119
- <dt><code>-n</code> <var>count</var>, <code>--count</code>=<var>count</var></dt><dd><p>Display <var>count</var> drops. (Default: <strong>20</strong>)</p></dd>
120
124
  </dl>
121
125
 
122
126
 
@@ -161,13 +165,13 @@ Cover.jpeg,http://cl.ly/1x20...,1
161
165
  Chapter 1.md,http://cl.ly/3U3d...,7
162
166
  </code></pre>
163
167
 
164
- <p>Share a new bookmark to "http://douglasadams.com":</p>
168
+ <p>Share a new bookmark to <strong>http://douglasadams.com</strong>:</p>
165
169
 
166
170
  <pre><code>$ cloudapp bookmark http://douglasadams.com
167
171
  Bookmarking http://douglasadams.com... http://cl.ly/1y0j403g3D0c0X1G0R3m
168
172
  </code></pre>
169
173
 
170
- <p>Share the file "screenshot.png":</p>
174
+ <p>Share the file <strong>screenshot.png</strong>:</p>
171
175
 
172
176
  <pre><code>$ cloudapp upload screenshot.png
173
177
  Uploading screenshot.png... http://cl.ly/040u2o3X1w0z1z3n2T04
@@ -185,6 +189,18 @@ $ cloudapp upload screenshot.png | pbcopy
185
189
  http://cl.ly/1y0j403g3D0c0X1G0R3m
186
190
  </code></pre>
187
191
 
192
+ <p>Download a drop to the current direcctory:</p>
193
+
194
+ <pre><code>$ cloudapp download http://cl.ly/040u2o3X1w0z1z3n2T04
195
+ Downloading screenshot.png... done
196
+ </code></pre>
197
+
198
+ <p>Download a drop to another path:</p>
199
+
200
+ <pre><code>$ cloudapp download --output=/Users/Larry/image.png http://cl.ly/040u2o3X1w0z1z3n2T04
201
+ Downloading screenshot.png to /Users/Larry... done
202
+ </code></pre>
203
+
188
204
  <h2 id="LICENSE">LICENSE</h2>
189
205
 
190
206
  <p><code>cloudapp</code> is distributed under the <a href="https://github.com/cloudapp/cloudapp/blob/master/MIT-LICENSE">MIT license</a>.</p>
data/man/cloudapp.1.ronn CHANGED
@@ -6,7 +6,8 @@ cloudapp(1) -- All the pleasures of CloudApp now at your terminal
6
6
  `cloudapp` [`--format`=<format>] <command> [<args>]<br>
7
7
  `cloudapp list` [`--count`=<count>]<br>
8
8
  `cloudapp bookmark` [`--private`] [`--public`] <url> [<url>...]<br>
9
- `cloudapp upload` [`--private`] [`--public`] <file> [<file>...]
9
+ `cloudapp upload` [`--private`] [`--public`] <file> [<file>...]<br>
10
+ `cloudapp download` [`--output`=<file>] <url>
10
11
 
11
12
  ## DESCRIPTION
12
13
 
@@ -22,26 +23,33 @@ terminal and other times it's the only tool available.
22
23
 
23
24
  ## COMMANDS
24
25
 
25
- * `list` [`--count`=<count>]:
26
- List the most recent <count> drops. The default is to list **20** drops.
27
-
28
26
  * `bookmark` [`--private`] [`--public`] [<url>...]:
29
27
  Create a new bookmark to each <url> and print each link.
30
28
 
29
+ * `download` [`--output`=<file>] <url>:
30
+ Download the file at <url> to the current directory or to <file> if present.
31
+
32
+ * `list` [`--count`=<count>]:
33
+ List the most recent <count> drops. The default is to list **20** drops.
34
+
31
35
  * `upload` [`--private`] [`--public`] <file> [<file>...]:
32
36
  Upload each <file> and print each link.
33
37
 
34
38
  ## COMMAND OPTIONS
35
39
 
40
+ * `-n` <count>, `--count`=<count>:
41
+ Display <count> drops. (Default: **20**)
42
+
43
+ * `-o` <file>, `--output`=<file>:
44
+ Download file as <file>. (Default: current directory using the remote file's
45
+ name)
46
+
36
47
  * `--private`:
37
48
  Use a private (long) link when creating a new drop.
38
49
 
39
50
  * `--public`:
40
51
  Use a public (short) link when creating a new drop.
41
52
 
42
- * `-n` <count>, `--count`=<count>:
43
- Display <count> drops. (Default: **20**)
44
-
45
53
  ## REQUIREMENTS
46
54
 
47
55
  `cloudapp` requires Ruby 1.9.2 or greater.
@@ -80,12 +88,12 @@ List newest 5 drops in CSV format:
80
88
  Cover.jpeg,http://cl.ly/1x20...,1
81
89
  Chapter 1.md,http://cl.ly/3U3d...,7
82
90
 
83
- Share a new bookmark to "http://douglasadams.com":
91
+ Share a new bookmark to **http://douglasadams.com**:
84
92
 
85
93
  $ cloudapp bookmark http://douglasadams.com
86
94
  Bookmarking http://douglasadams.com... http://cl.ly/1y0j403g3D0c0X1G0R3m
87
95
 
88
- Share the file "screenshot.png":
96
+ Share the file **screenshot.png**:
89
97
 
90
98
  $ cloudapp upload screenshot.png
91
99
  Uploading screenshot.png... http://cl.ly/040u2o3X1w0z1z3n2T04
@@ -100,6 +108,16 @@ Share a new bookmark and output only the URL:
100
108
  $ cloudapp --format=csv bookmark http://douglasadams.com
101
109
  http://cl.ly/1y0j403g3D0c0X1G0R3m
102
110
 
111
+ Download a drop to the current direcctory:
112
+
113
+ $ cloudapp download http://cl.ly/040u2o3X1w0z1z3n2T04
114
+ Downloading screenshot.png... done
115
+
116
+ Download a drop to another path:
117
+
118
+ $ cloudapp download --output=/Users/Larry/image.png http://cl.ly/040u2o3X1w0z1z3n2T04
119
+ Downloading screenshot.png to /Users/Larry... done
120
+
103
121
 
104
122
  ## LICENSE
105
123
 
@@ -0,0 +1,116 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://cl.ly/C23W/drop_presenter.rb
6
+ body: ''
7
+ headers:
8
+ Accept:
9
+ - application/json
10
+ response:
11
+ status:
12
+ code: 302
13
+ message: Moved Temporarily
14
+ headers:
15
+ Server:
16
+ - nginx
17
+ Date:
18
+ - Fri, 17 Feb 2012 01:43:07 GMT
19
+ Content-Type:
20
+ - text/html;charset=utf-8
21
+ Connection:
22
+ - keep-alive
23
+ Vary:
24
+ - Accept
25
+ Cache-Control:
26
+ - public, max-age=900
27
+ Location:
28
+ - http://api.cld.me/C23W/drop_presenter.rb
29
+ X-Content-Digest:
30
+ - da39a3ee5e6b4b0d3255bfef95601890afd80709
31
+ X-Rack-Cache:
32
+ - stale, invalid, store
33
+ Content-Length:
34
+ - '0'
35
+ X-Varnish:
36
+ - '1852427632'
37
+ Age:
38
+ - '0'
39
+ Via:
40
+ - 1.1 varnish
41
+ body: ''
42
+ http_version:
43
+ recorded_at: Fri, 17 Feb 2012 01:43:07 GMT
44
+ - request:
45
+ method: get
46
+ uri: http://api.cld.me/C23W/drop_presenter.rb
47
+ body: ''
48
+ headers:
49
+ Accept:
50
+ - application/json
51
+ response:
52
+ status:
53
+ code: 302
54
+ message: Moved Temporarily
55
+ headers:
56
+ Cache-Control:
57
+ - no-cache
58
+ Content-Type:
59
+ - text/html; charset=utf-8
60
+ Location:
61
+ - http://f.cl.ly/items/152S3l2X1y3P2M262D3V/drop_presenter.rb
62
+ Server:
63
+ - thin 1.3.1 codename Triple Espresso
64
+ X-Runtime:
65
+ - '0.025914'
66
+ X-Ua-Compatible:
67
+ - IE=Edge,chrome=1
68
+ Transfer-Encoding:
69
+ - chunked
70
+ Connection:
71
+ - keep-alive
72
+ body: <html><body>You are being <a href="http://f.cl.ly/items/152S3l2X1y3P2M262D3V/drop_presenter.rb">redirected</a>.</body></html>
73
+ http_version:
74
+ recorded_at: Fri, 17 Feb 2012 01:44:07 GMT
75
+ - request:
76
+ method: get
77
+ uri: http://f.cl.ly/items/152S3l2X1y3P2M262D3V/drop_presenter.rb
78
+ body: ''
79
+ headers:
80
+ Accept:
81
+ - application/json
82
+ response:
83
+ status:
84
+ code: 200
85
+ message: OK
86
+ headers:
87
+ X-Amz-Id-2:
88
+ - iyFXxRKOKM5CYt1f7jI9bN8btGeacX2VeXxCIMNL2/AQNzPEH7pAIUjQ9A4Q/TYC
89
+ X-Amz-Request-Id:
90
+ - 9092E1275FD0C3F5
91
+ Date:
92
+ - Fri, 17 Feb 2012 01:44:09 GMT
93
+ Last-Modified:
94
+ - Tue, 22 Nov 2011 19:40:51 GMT
95
+ Etag:
96
+ - ! '"f24a305d5a90516f0b0877b93158ee0a"'
97
+ Accept-Ranges:
98
+ - bytes
99
+ Content-Type:
100
+ - application/ruby
101
+ Content-Length:
102
+ - '610'
103
+ Server:
104
+ - AmazonS3
105
+ body: ! "require 'delegate'\nrequire 'yajl'\n\nclass DropPresenter < SimpleDelegator\n
106
+ \ def initialize(drop, template)\n @template = template\n\n super drop\n
107
+ \ end\n\n def render_html\n if bookmark?\n @template.redirect_to_api\n
108
+ \ else\n @template.erb template_name, locals: { drop: self, body_id:
109
+ body_id }\n end\n end\n\n def render_json\n Yajl::Encoder.encode data\n
110
+ \ end\n\nprivate\n\n def template_name\n if image?\n :image\n elsif
111
+ text?\n :text\n else\n :other\n end\n end\n\n def body_id\n
112
+ \ if image?\n 'image'\n elsif text?\n 'text'\n else\n 'other'\n
113
+ \ end\n end\n\nend\n"
114
+ http_version:
115
+ recorded_at: Fri, 17 Feb 2012 01:44:08 GMT
116
+ recorded_with: VCR 2.0.0.rc1
@@ -0,0 +1,81 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://cl.ly/C23W
6
+ body: ''
7
+ headers:
8
+ Accept:
9
+ - application/json
10
+ response:
11
+ status:
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ Server:
16
+ - nginx
17
+ Date:
18
+ - Thu, 16 Feb 2012 20:19:27 GMT
19
+ Content-Type:
20
+ - application/json;charset=utf-8
21
+ Connection:
22
+ - keep-alive
23
+ Vary:
24
+ - Accept
25
+ X-Rack-Cache:
26
+ - miss
27
+ Content-Length:
28
+ - '627'
29
+ X-Varnish:
30
+ - '587912818'
31
+ Age:
32
+ - '0'
33
+ Via:
34
+ - 1.1 varnish
35
+ body: ! '{"created_at":"2011-11-22T19:40:50Z","deleted_at":null,"id":12142483,"item_type":"text","name":"drop_presenter.rb","private":false,"redirect_url":null,"remote_url":"http://f.cl.ly/items/152S3l2X1y3P2M262D3V/drop_presenter.rb","source":"Cloud/1.5.3b1
36
+ CFNetwork/520.2.5 Darwin/11.2.0 (x86_64) (MacBookPro5%2C5)","updated_at":"2012-02-16T20:09:52Z","view_counter":90,"href":"http://my.cl.ly/items/12142483","url":"http://cl.ly/C23W","content_url":"http://cl.ly/C23W/drop_presenter.rb","icon":"http://assets.my.cld.me/images/item-types/text.png","subscribed":true,"download_url":"http://api.cld.me/C23W/download/drop_presenter.rb"}'
37
+ http_version:
38
+ recorded_at: Thu, 16 Feb 2012 20:19:27 GMT
39
+ - request:
40
+ method: get
41
+ uri: http://cl.ly/nonexistent
42
+ body: ''
43
+ headers:
44
+ Accept:
45
+ - application/json
46
+ response:
47
+ status:
48
+ code: 404
49
+ message: Not Found
50
+ headers:
51
+ Server:
52
+ - nginx
53
+ Date:
54
+ - Thu, 16 Feb 2012 20:26:01 GMT
55
+ Content-Type:
56
+ - text/html;charset=utf-8
57
+ Connection:
58
+ - keep-alive
59
+ Vary:
60
+ - Accept
61
+ X-Rack-Cache:
62
+ - miss
63
+ Content-Length:
64
+ - '621'
65
+ X-Varnish:
66
+ - '2047120769'
67
+ Age:
68
+ - '0'
69
+ Via:
70
+ - 1.1 varnish
71
+ body: ! "<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n\n <title>CloudApp
72
+ &mdash; Not Found</title>\n\n <link href='/favicon.ico' rel='shortcut icon'
73
+ />\n <link href=\"/stylesheets/error.css\" rel=\"stylesheet\" type=\"text/css\"
74
+ />\n\n <!--[if lt IE 9]>\n <script src=\"//html5shim.googlecode.com/svn/trunk/html5.js\"></script>\n
75
+ \ <![endif]-->\n </head>\n\n <body>\n <div class=\"wrapper\">\n <h1>Cloud<em>App</em></h1>\n\n
76
+ \ <h2>Sorry, no drops live here.</h2>\n\n <p>\n Perhaps the
77
+ link you clicked was broken or the owner of this drop has\n deleted it.\n
78
+ \ </p>\n </div>\n </body>\n</html>\n"
79
+ http_version:
80
+ recorded_at: Thu, 16 Feb 2012 20:26:01 GMT
81
+ recorded_with: VCR 2.0.0.rc1
@@ -255,7 +255,7 @@ http_interactions:
255
255
  recorded_at: Wed, 15 Feb 2012 14:39:02 GMT
256
256
  - request:
257
257
  method: get
258
- uri: https://my.cl.ly/items/s3?bucket=f.cl.ly&etag=%22567d2d49d2d30e2db956d680a4d03f25%22&key=items/2B0E373Z1J2U1u071E28/favicon.ico&item%5Bprivate%5D=false
258
+ uri: https://my.cl.ly/items/s3?bucket=f.cl.ly&etag=%22567d2d49d2d30e2db956d680a4d03f25%22&item%5Bprivate%5D=false&key=items/2B0E373Z1J2U1u071E28/favicon.ico
259
259
  body: ''
260
260
  headers:
261
261
  Authorization:
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+ require 'tempfile'
3
+
4
+ require 'cloudapp/config'
5
+
6
+ describe CloudApp::Config do
7
+ let(:config) { {} }
8
+ let(:content) { YAML.dump(config) }
9
+ let(:path) { config_file.path }
10
+ let(:config_file) {
11
+ Tempfile.new('cloudapprc', '.').tap do |file|
12
+ file << content
13
+ file.close
14
+ end
15
+ }
16
+
17
+ describe '#new' do
18
+ subject { CloudApp::Config.new path }
19
+
20
+ describe 'an existing config file' do
21
+ let(:config) {{ token: 'token' }}
22
+
23
+ it 'reads the content' do
24
+ subject.token.should eq(config[:token])
25
+ end
26
+
27
+ it 'updates a key' do
28
+ new_token = 'new token'
29
+ subject.token = new_token
30
+ expected = YAML.dump token: new_token
31
+
32
+ content = File.open(path) {|file| file.read }
33
+ content.should eq(expected)
34
+ end
35
+
36
+ it 'clears a key' do
37
+ subject.token = nil
38
+ expected = YAML.dump({})
39
+
40
+ content = File.open(path) {|file| file.read }
41
+ content.should eq(expected)
42
+ end
43
+ end
44
+
45
+ describe 'an empty config file' do
46
+ it 'returns nil' do
47
+ subject.token.should be_nil
48
+ end
49
+ end
50
+
51
+ describe 'a nonexistent config file' do
52
+ let(:path) { 'nonexistent' }
53
+
54
+ it 'returns nil' do
55
+ subject.token.should be_nil
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,21 @@
1
+ require 'helper'
2
+ require 'support/vcr_rspec'
3
+
4
+ require 'cloudapp/drop_content'
5
+
6
+ describe CloudApp::DropContent do
7
+ describe '.download' do
8
+ let(:content_url) { 'http://cl.ly/C23W/drop_presenter.rb' }
9
+ let(:drop) { stub :drop, content_url: content_url }
10
+ subject {
11
+ VCR.use_cassette('DropContent/download') {
12
+ CloudApp::DropContent.download drop
13
+ }
14
+ }
15
+
16
+ it 'downloads content' do
17
+ expected = "require 'delegate'"
18
+ subject[0...expected.size].should == expected
19
+ end
20
+ end
21
+ end
@@ -11,10 +11,10 @@ describe CloudApp::DropPresenter do
11
11
  let(:io) { StringIO.new }
12
12
 
13
13
  describe 'a single line' do
14
- subject do
14
+ subject {
15
15
  CloudApp::DropPresenter.print(options, &action)
16
16
  io.tap(&:rewind).readlines
17
- end
17
+ }
18
18
 
19
19
  describe 'with no format' do
20
20
  let(:options) {{ on: io, waiting: waiting }}
@@ -47,16 +47,16 @@ describe CloudApp::DropPresenter do
47
47
 
48
48
  describe 'a table of data' do
49
49
  let(:columns) {{ name: 'Name', url: 'Link' }}
50
- let(:result) do
50
+ let(:result) {
51
51
  [ stub(:result1, name: 'Stub 1', url: 'http://stub1.com'),
52
52
  stub(:result2, name: 'Stub 2', url: 'http://stub2.com') ]
53
- end
53
+ }
54
54
  let(:options) {{ on: io, format: format, columns: columns }}
55
55
 
56
- subject do
56
+ subject {
57
57
  CloudApp::DropPresenter.print(options, &action)
58
58
  io.tap(&:rewind).readlines
59
- end
59
+ }
60
60
 
61
61
  describe 'pretty format' do
62
62
  let(:format) { :pretty }
@@ -91,5 +91,30 @@ describe CloudApp::DropPresenter do
91
91
  end
92
92
  end
93
93
  end
94
+
95
+ describe 'providing multiple formats' do
96
+ let(:result) {{ pretty: 'pretty', csv: 'csv' }}
97
+ subject {
98
+ CloudApp::DropPresenter.print(options, &action)
99
+ io.tap(&:rewind).readlines
100
+ }
101
+
102
+ describe 'pretty format' do
103
+ let(:options) {{ on: io, format: :pretty }}
104
+
105
+ it 'pretty prints' do
106
+ subject.first.should eq("pretty\n")
107
+ end
108
+ end
109
+
110
+ describe 'csv format' do
111
+ let(:options) {{ on: io, format: :csv }}
112
+
113
+ it 'prints csv' do
114
+ subject.first.should eq("csv\n")
115
+ end
116
+ end
117
+
118
+ end
94
119
  end
95
120
  end
@@ -1,10 +1,10 @@
1
1
  require 'helper'
2
+ require 'support/fakefs_rspec'
2
3
  require 'support/vcr_rspec'
3
4
 
4
5
  require 'cloudapp/drop_service'
5
6
 
6
7
  describe CloudApp::DropService do
7
-
8
8
  let(:token) { '8762f6679f8d001016b2' }
9
9
 
10
10
  describe '.using_token' do
@@ -29,6 +29,160 @@ describe CloudApp::DropService do
29
29
  end
30
30
  end
31
31
 
32
+ describe '#drop' do
33
+ let(:service) { CloudApp::DropService.new }
34
+
35
+ describe 'retrieving a drop' do
36
+ let(:url) { 'http://cl.ly/C23W' }
37
+ subject {
38
+ VCR.use_cassette('DropService/drop',
39
+ match_requests_on: [:method, :uri, :body, :headers]) {
40
+ service.drop url
41
+ }
42
+ }
43
+
44
+ it 'returns the drop' do
45
+ subject.should be_a(CloudApp::Drop)
46
+ end
47
+
48
+ it 'parses the response' do
49
+ subject.id.should == 12142483
50
+ end
51
+ end
52
+
53
+ describe 'retrieving a nonexistent drop' do
54
+ let(:url) { 'http://cl.ly/nonexistent' }
55
+ subject {
56
+ VCR.use_cassette('DropService/drop',
57
+ match_requests_on: [:method, :uri, :body, :headers]) {
58
+ service.drop url
59
+ }
60
+ }
61
+
62
+ it 'returns nil' do
63
+ subject.should be_nil
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#download_drop', :fakefs do
69
+ let(:service) { CloudApp::DropService.new }
70
+ let(:url) { 'http://cl.ly/C23W' }
71
+ let(:options) { {} }
72
+ let(:content) { 'content' }
73
+ let(:content_url) { 'http://cl.ly/C23W/drop_presenter.rb' }
74
+ let(:drop) { stub :drop, content: content,
75
+ content_url: content_url,
76
+ has_content?: true }
77
+
78
+ before do
79
+ CloudApp::DropContent.stub download: content
80
+ service.stub drop: drop
81
+ end
82
+
83
+ it 'fetches the drop' do
84
+ CloudApp::DropContent.should_receive(:download).with(drop)
85
+ service.download_drop url, options
86
+ end
87
+
88
+ it 'returns the content' do
89
+ service.download_drop(url, options).should eq(content)
90
+ end
91
+
92
+ it 'saves the file to the current directory using the remote filename' do
93
+ service.download_drop url, options
94
+ downloaded = File.open('drop_presenter.rb') {|f| f.read }
95
+
96
+ downloaded.should eq(content)
97
+ end
98
+
99
+ describe 'to a path' do
100
+ let(:options) {{ to: '/tmp/file.txt' }}
101
+
102
+ it 'saves the file to the given path' do
103
+ service.download_drop url, options
104
+ File.exist?(options[:to]).should be_true
105
+ end
106
+ end
107
+
108
+ describe 'to an existing file' do
109
+ let(:options) {{ to: '/tmp/file.txt' }}
110
+ before do
111
+ Dir.mkdir '/tmp'
112
+ File.open(options[:to], 'w', 0600) {|file| file << 'existing' }
113
+ end
114
+
115
+ it 'overwrites the file' do
116
+ service.download_drop url, options
117
+ downloaded = File.open(options[:to]) {|f| f.read }
118
+
119
+ downloaded.should eq(content)
120
+ end
121
+ end
122
+
123
+ describe 'an unexpanded path' do
124
+ let(:options) {{ to: '~/file.txt' }}
125
+ before do
126
+ FileUtils.mkdir_p File.expand_path('~')
127
+ end
128
+
129
+ it 'saves the file to the expanded directory' do
130
+ service.download_drop url, options
131
+ saved_path = File.expand_path(options[:to])
132
+
133
+ File.exist?(saved_path).should be_true
134
+ end
135
+ end
136
+
137
+ describe 'to an existing directory' do
138
+ let(:options) {{ to: '/tmp' }}
139
+ before do
140
+ Dir.mkdir '/tmp'
141
+ end
142
+
143
+ it 'raises an exception' do
144
+ -> { service.download_drop url, options }.
145
+ should raise_exception(Errno::EISDIR)
146
+ end
147
+ end
148
+
149
+ describe 'a bookmark' do
150
+ let(:drop) { stub :drop, has_content?: false }
151
+
152
+ it 'raises an exception' do
153
+ -> { service.download_drop url, options }.
154
+ should raise_exception(CloudApp::DropService::NO_CONTENT)
155
+ end
156
+
157
+ it "doesn't save a file" do
158
+ FakeFS::FileSystem.files.should be_empty
159
+ end
160
+ end
161
+
162
+ describe 'a file with URL-encoded name' do
163
+ let(:content_url) { 'http://cl.ly/C23W/screen%20shot.png' }
164
+
165
+ it 'decodes the filename' do
166
+ service.download_drop url, options
167
+
168
+ File.exist?('screen shot.png').should be_true
169
+ end
170
+ end
171
+
172
+ describe 'a nonexistent drop' do
173
+ let(:drop) { nil }
174
+
175
+ it 'raises an exception' do
176
+ -> { service.download_drop url, options }.
177
+ should raise_exception(CloudApp::DropService::NO_CONTENT)
178
+ end
179
+
180
+ it "doesn't save a file" do
181
+ FakeFS::FileSystem.files.should be_empty
182
+ end
183
+ end
184
+ end
185
+
32
186
  describe '#drops' do
33
187
  let(:service) { CloudApp::DropService.using_token token }
34
188