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/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Change Log
2
+
3
+ Below is a complete listing of changes for each revision of `cloudapp`.
4
+
5
+ ## Unreleased
6
+
7
+ - General housecleaning in `bin/cloudapp`
8
+ - Fix typo ([#22][] by [Mark Mulder][bittersweet])
9
+ - Extract Config class
10
+
11
+ [#22]: https://github.com/cloudapp/cloudapp/pull/22
12
+ [bittersweet]: https://github.com/bittersweet
13
+
14
+ ## 1.0.2
15
+
16
+ - Bump leadlight and relax highline dependencies.
17
+
18
+ ## 1.0.1
19
+
20
+ - Tightened leadlight and highline dependencies.
21
+
22
+ ## 1.0.0
23
+
24
+ - Stable release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudapp (1.0.2)
4
+ cloudapp (1.1.0)
5
5
  addressable
6
6
  faraday (~> 0.8.0.rc2)
7
7
  gli
@@ -12,10 +12,11 @@ PATH
12
12
  GEM
13
13
  remote: http://rubygems.org/
14
14
  specs:
15
- addressable (2.2.6)
15
+ addressable (2.2.7)
16
16
  crack (0.3.1)
17
17
  diff-lcs (1.1.3)
18
18
  fail-fast (1.0.0)
19
+ fakefs (0.4.0)
19
20
  faraday (0.8.0.rc2)
20
21
  multipart-post (~> 1.1)
21
22
  fattr (2.2.1)
@@ -63,6 +64,7 @@ PLATFORMS
63
64
 
64
65
  DEPENDENCIES
65
66
  cloudapp!
67
+ fakefs
66
68
  rake
67
69
  ronn
68
70
  rspec
data/README.md CHANGED
@@ -5,6 +5,10 @@ interface to CloudApp as an added bonus.
5
5
 
6
6
  [cloudapp api]: http://developer.getcloudapp.com
7
7
 
8
+ ## Requirements
9
+
10
+ `cloudapp` requires Ruby 1.9.2 or greater.
11
+
8
12
 
9
13
  ## Usage
10
14
 
@@ -16,21 +20,19 @@ _Usage from Ruby is still a work in progress._
16
20
  Experience all the pleasures of sharing with CloudApp now in your terminal. The
17
21
  goal of `cloudapp` is to be simple and Unix-friendly.
18
22
 
19
- ### Requirements
20
-
21
- `cloudapp` requires Ruby 1.9.2 or greater.
22
-
23
23
  ### Quick Start
24
24
 
25
25
  gem install cloudapp
26
26
  cloudapp list
27
27
  cloudapp bookmark http://douglasadams.com
28
- cloduapp upload ~/Desktop/screenshot.png
28
+ cloudapp upload ~/Desktop/screenshot.png
29
29
 
30
- ### Usage
30
+ ### Examples
31
31
 
32
32
  - Bookmark a link: `cloudapp bookmark http://getcloudapp.com`
33
+ - Bookmark several links: `cloudapp bookmark http://douglasadams.com http://zombo.com`
33
34
  - Share a file: `cloudapp upload screenshot.png`
35
+ - Share several files: `cloudapp upload *.png`
34
36
  - List newest drops: `cloudapp list [--count=5]`
35
37
  - Copy a new drop's link (OS X): `cloudapp bookmark http://douglasadams.com | pbcopy`
36
38
  - Output drops in CSV: `cloudapp --format=csv list`
@@ -41,22 +43,19 @@ More examples can be found on [the man page][man-page].
41
43
 
42
44
  ### Wish List
43
45
 
44
- Some baseic features that should be added:
45
-
46
- - Share several files: `cloudapp upload *.png`
47
- - Create several bookmarks: `cloudapp bookmark http://douglasadams.com http://zombo.com`
48
-
49
- A little more flare would be swell.
46
+ `cloudapp` could be awesome with a little more flare.
50
47
 
51
48
  - Download a drop: `cloudapp download http://cl.ly/abc123`
52
49
  - Output specific columns: `cloudapp list --columns=name,views,link`
53
- - Handle input from STDIN: `pbpaste | cloudapp bookmark -`
50
+ - Handle bookmarks from STDIN: `pbpaste | cloudapp bookmark`
51
+ - Handle files from STDIN: `find *.png | cloudapp upload`
54
52
  - Archive and share several files: `cloudapp upload --archive *.png`
55
53
  - Encrypt and share a file: `cloudapp upload --encrypt launch_codes.txt`
56
- - Download and decrypt and encrypted drop: `cloudapp download http://cl.ly/abc123 def456`
54
+ - Download and decrypt and encrypted drop: `cloudapp download --key=def456 http://cl.ly/abc123`
57
55
 
58
- While we're dreaming, what could you do if it kept a local copy of all your
59
- drops? Bonus points for a light weight daemon that kept everything in sync.
56
+ While we're dreaming, what could you do if `cloudapp` had a database of all your
57
+ drops? Bonus points for a light weight daemon that kept everything in sync at
58
+ all times.
60
59
 
61
60
  - Find all your screen shots: `cloudapp list /^screen ?shot.*\.png$/`
62
61
  - Trash all your stale drops: `cloudapp delete --last-viewed="> 1 month ago"`
data/bin/cloudapp CHANGED
@@ -5,72 +5,64 @@ require 'rubygems'
5
5
  require 'gli'
6
6
  require 'highline'
7
7
  require 'pathname'
8
- require 'yaml'
9
8
 
10
9
  require 'cloudapp'
10
+ require 'cloudapp/config'
11
11
  require 'cloudapp/drop_presenter'
12
12
  require 'cloudapp/drop_service'
13
13
 
14
14
 
15
- def service
16
- CloudApp::DropService.using_token token
17
- end
18
-
19
- def token_for_account(email, password)
20
- CloudApp::DropService.retrieve_token email, password
21
- end
15
+ extend Forwardable
16
+ def_delegator 'HighLine.new', :color
22
17
 
23
- def config_file
24
- File.expand_path '~/.cloudapprc'
18
+ def config
19
+ CloudApp::Config.new
25
20
  end
26
21
 
27
- def token
28
- return unless File.exist?(config_file)
29
- config = File.open(config_file) {|file| YAML::load(file) }
30
- config[:token]
31
- end
32
-
33
- def save_token(token)
34
- File.open(config_file, 'w', 0600) do |file|
35
- YAML.dump({ token: token }, file)
36
- end
37
- end
38
-
39
- def clear_token
40
- return unless File.exist?(config_file)
41
- File.delete(config_file)
42
- end
43
-
44
- def color(*args)
45
- HighLine.new.color *args
22
+ def service
23
+ CloudApp::DropService.using_token config.token
46
24
  end
47
25
 
48
- def check_for_credentials
49
- return unless token.nil?
26
+ def require_credentials
27
+ return unless config.token.nil?
50
28
 
51
29
  $stdout.puts 'Sign into your CloudApp account.'
52
30
 
53
- email = HighLine.new.ask('Email: ')
54
- password = HighLine.new.ask('Password: ') {|q| q.echo = false }
55
- token = CloudApp::DropService.retrieve_token email, password
56
-
57
- save_token token
31
+ email = HighLine.new.ask('Email: ')
32
+ password = HighLine.new.ask('Password: ') {|q| q.echo = false }
33
+ config.token = CloudApp::DropService.retrieve_token email, password
58
34
  end
59
35
 
60
- def format_from(global_options)
61
- if global_options[:format].nil?
36
+ def format_from_options(options)
37
+ if options[:format].nil?
62
38
  $stdout.tty? ? :pretty : :csv
63
39
  else
64
- global_options[:format].to_sym
40
+ options[:format].to_sym
65
41
  end
66
42
  end
67
43
 
68
- def privacy_from(options)
44
+ def privacy_from_options(options)
69
45
  if options[:private] || options[:public]
70
46
  options[:private] || !options[:public]
71
47
  end
72
48
  end
73
49
 
50
+ def create_drop_action_on_command(c)
51
+ c.desc 'Use a private (long) link'
52
+ c.switch :private
53
+
54
+ c.desc 'Use a public (short) link'
55
+ c.switch :public
56
+
57
+ c.action do |global_options, options, args|
58
+ require_credentials
59
+ format = format_from_options global_options
60
+ private = privacy_from_options options
61
+
62
+ yield format, private, args
63
+ end
64
+ end
65
+
74
66
 
75
67
  include GLI
76
68
 
@@ -84,18 +76,7 @@ flag [:f, :format]
84
76
  desc 'Create a new bookmark to each url and print each link'
85
77
  arg_name 'url [url...]'
86
78
  command :bookmark do |c|
87
-
88
- c.desc 'Use a private (long) link'
89
- c.switch :private
90
-
91
- c.desc 'Use a public (short) link'
92
- c.switch :public
93
-
94
- c.action do |global_options, options, urls|
95
- check_for_credentials
96
- format = format_from global_options
97
- private = privacy_from options
98
-
79
+ create_drop_action_on_command(c) do |format, private, urls|
99
80
  urls.each do |url|
100
81
  url = URI.parse url
101
82
  waiting = "Bookmarking #{ url }... "
@@ -116,18 +97,7 @@ end
116
97
  desc 'Upload each file and print each link'
117
98
  arg_name 'file [file...]'
118
99
  command :upload do |c|
119
-
120
- c.desc 'Use a private (long) link'
121
- c.switch :private
122
-
123
- c.desc 'Use a public (short) link'
124
- c.switch :public
125
-
126
- c.action do |global_options, options, files|
127
- check_for_credentials
128
- format = format_from global_options
129
- private = privacy_from options
130
-
100
+ create_drop_action_on_command(c) do |format, private, files|
131
101
  files.each do |file|
132
102
  file = Pathname.new file
133
103
  waiting = "Uploading #{ file }... "
@@ -154,11 +124,11 @@ command [:list, :ls] do |c|
154
124
  c.flag :n, :count
155
125
 
156
126
  c.action do |global_options, options, args|
157
- check_for_credentials
127
+ require_credentials
158
128
 
159
129
  count = options[:count].to_i
160
130
  columns = { display_name: 'Name', url: 'Link', view_counter: 'Views' }
161
- format = format_from global_options
131
+ format = format_from_options global_options
162
132
 
163
133
  CloudApp::DropPresenter.print(on: $stdout,
164
134
  columns: columns,
@@ -168,10 +138,43 @@ command [:list, :ls] do |c|
168
138
  end
169
139
  end
170
140
 
141
+
142
+ desc 'Download the drop at <url>'
143
+ arg_name 'url'
144
+ command :download do |c|
145
+
146
+ c.desc 'Download the file to <file>'
147
+ c.arg_name 'file'
148
+ c.flag :o, :output
149
+
150
+ c.action do |global_options, options, urls|
151
+ format = format_from_options global_options
152
+ download_options = {}
153
+ download_options[:to] = options[:output] if options[:output]
154
+
155
+ urls.each do |url|
156
+ url = URI.parse url
157
+ waiting = "Downloading #{ url }... "
158
+
159
+ CloudApp::DropPresenter.print(on: $stdout,
160
+ waiting: waiting,
161
+ format: format) do
162
+ content = service.download_drop url, download_options
163
+ { pretty: 'done', csv: content }
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+
171
170
  on_error do |e|
172
- if CloudApp::DropService::UNAUTHORIZED === e
171
+ case e
172
+ when CloudApp::DropService::UNAUTHORIZED
173
173
  $stderr.puts color('Email and password given are incorrect.', :bold)
174
- clear_token
174
+ config.token = nil
175
+ false
176
+ when CloudApp::DropService::NO_CONTENT
177
+ $stderr.puts 'drop was deleted or is a bookmark'
175
178
  false
176
179
  else
177
180
  true
data/cloudapp.gemspec CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'cloudapp'
16
- s.version = '1.0.2'
17
- s.date = '2012-02-15'
16
+ s.version = '1.1.0'
17
+ s.date = '2012-02-18'
18
18
  s.rubyforge_project = 'cloudapp'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -52,6 +52,7 @@ Gem::Specification.new do |s|
52
52
 
53
53
  ## List your development dependencies here. Development dependencies are
54
54
  ## those that are only needed during development
55
+ s.add_development_dependency 'fakefs'
55
56
  s.add_development_dependency 'rake'
56
57
  s.add_development_dependency 'ronn'
57
58
  s.add_development_dependency 'rspec'
@@ -63,6 +64,7 @@ Gem::Specification.new do |s|
63
64
  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
64
65
  # = MANIFEST =
65
66
  s.files = %w[
67
+ CHANGELOG.md
66
68
  Gemfile
67
69
  Gemfile.lock
68
70
  MIT-LICENSE
@@ -71,18 +73,22 @@ Gem::Specification.new do |s|
71
73
  bin/cloudapp
72
74
  cloudapp.gemspec
73
75
  lib/cloudapp.rb
76
+ lib/cloudapp/config.rb
74
77
  lib/cloudapp/drop.rb
78
+ lib/cloudapp/drop_content.rb
75
79
  lib/cloudapp/drop_presenter.rb
76
80
  lib/cloudapp/drop_service.rb
77
81
  lib/cloudapp/identity.rb
78
82
  man/cloudapp.1
79
83
  man/cloudapp.1.html
80
84
  man/cloudapp.1.ronn
85
+ spec/cassettes/DropContent/download.yml
81
86
  spec/cassettes/DropService/create_bookmark.yml
82
87
  spec/cassettes/DropService/create_bookmark_with_bad_credentials.yml
83
88
  spec/cassettes/DropService/create_bookmark_with_name.yml
84
89
  spec/cassettes/DropService/create_private_bookmark.yml
85
90
  spec/cassettes/DropService/create_public_bookmark.yml
91
+ spec/cassettes/DropService/drop.yml
86
92
  spec/cassettes/DropService/list_drops.yml
87
93
  spec/cassettes/DropService/list_drops_with_bad_credentials.yml
88
94
  spec/cassettes/DropService/list_drops_with_limit.yml
@@ -92,11 +98,14 @@ Gem::Specification.new do |s|
92
98
  spec/cassettes/DropService/retrieve_token_with_bad_credentials.yml
93
99
  spec/cassettes/DropService/upload_file.yml
94
100
  spec/cassettes/DropService/upload_public_file.yml
101
+ spec/cloudapp/config_spec.rb
102
+ spec/cloudapp/drop_content_spec.rb
95
103
  spec/cloudapp/drop_presenter_spec.rb
96
104
  spec/cloudapp/drop_service_spec.rb
97
105
  spec/cloudapp/drop_spec.rb
98
106
  spec/cloudapp/identity_spec.rb
99
107
  spec/helper.rb
108
+ spec/support/fakefs_rspec.rb
100
109
  spec/support/files/favicon.ico
101
110
  spec/support/stub_class_or_module.rb
102
111
  spec/support/vcr.rb
@@ -0,0 +1,39 @@
1
+ require 'pathname'
2
+ require 'singleton'
3
+ require 'yaml'
4
+
5
+ module CloudApp
6
+ class Config
7
+ def initialize(path = File.expand_path('~/.cloudapprc'))
8
+ @path = path
9
+ @config = read_config
10
+ end
11
+
12
+ def token
13
+ @config[:token]
14
+ end
15
+
16
+ def token=(token)
17
+ if token
18
+ @config[:token] = token
19
+ else
20
+ @config.delete :token
21
+ end
22
+
23
+ write_config
24
+ end
25
+
26
+ private
27
+
28
+ def read_config
29
+ return {} unless File.exist?(@path)
30
+ File.open(@path) {|file| YAML.load(file) }
31
+ end
32
+
33
+ def write_config
34
+ File.open(@path, 'w', 0600) {|file|
35
+ YAML.dump @config, file
36
+ }
37
+ end
38
+ end
39
+ end
data/lib/cloudapp/drop.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'ostruct'
2
+
2
3
  module CloudApp
3
4
  class Drop < OpenStruct
4
5
  def private?() private == true end
@@ -7,5 +8,9 @@ module CloudApp
7
8
  def display_name
8
9
  name || redirect_url || url
9
10
  end
11
+
12
+ def has_content?
13
+ item_type != 'bookmark'
14
+ end
10
15
  end
11
16
  end
@@ -0,0 +1,34 @@
1
+ require 'faraday'
2
+ require 'logger'
3
+ require 'typhoeus'
4
+
5
+ module CloudApp
6
+ class DropContent
7
+ def initialize(drop)
8
+ @content_url = drop.content_url
9
+ end
10
+
11
+ def self.download(drop)
12
+ new(drop).content
13
+ end
14
+
15
+ def content(url = @content_url)
16
+ connection.get(url) do |req|
17
+ req.headers['Accept'] = 'application/json'
18
+ end.on_complete do |env|
19
+ location = env[:response]['location']
20
+ if location
21
+ return content(location)
22
+ else
23
+ return env[:body]
24
+ end
25
+ end
26
+ end
27
+
28
+ def connection
29
+ Faraday.new do |builder|
30
+ builder.adapter :typhoeus
31
+ end
32
+ end
33
+ end
34
+ end
@@ -70,6 +70,11 @@ module CloudApp
70
70
 
71
71
  def response
72
72
  @response ||= @action.call
73
+
74
+ case @response
75
+ when Hash then @response.fetch(@format, @response)
76
+ else @response
77
+ end
73
78
  end
74
79
  end
75
80
  end
@@ -1,6 +1,9 @@
1
- require 'leadlight'
2
1
  require 'addressable/uri'
2
+ require 'fileutils'
3
+ require 'leadlight'
4
+ require 'multi_json'
3
5
  require 'cloudapp/drop'
6
+ require 'cloudapp/drop_content'
4
7
 
5
8
  # Leadlight service for mucking about with drops in the CloudApp API.
6
9
  #
@@ -43,6 +46,7 @@ module CloudApp
43
46
 
44
47
  # Rased when given credentials are incorrect.
45
48
  class UNAUTHORIZED < StandardError; end
49
+ class NO_CONTENT < StandardError; end
46
50
 
47
51
  Leadlight.build_connection_common do |c|
48
52
  c.request :multipart
@@ -152,6 +156,36 @@ module CloudApp
152
156
  raise UNAUTHORIZED
153
157
  end
154
158
 
159
+ def drop(url)
160
+ conn = Faraday.new(url: url) do |builder|
161
+ builder.response :logger, logger
162
+ builder.adapter :typhoeus
163
+ end
164
+
165
+ conn.get do |req|
166
+ req.headers['Accept'] = 'application/json'
167
+ end.on_complete do |env|
168
+ if env.fetch(:response).status == 200
169
+ drop = Drop.new(MultiJson.decode(env[:body]))
170
+ end
171
+
172
+ return drop
173
+ end
174
+ end
175
+
176
+ def download_drop(url, options = {})
177
+ drop = drop url
178
+ raise NO_CONTENT if drop.nil? or !drop.has_content?
179
+
180
+ filename = Addressable::URI.unencode(File.basename(drop.content_url))
181
+ path = Pathname.new(options.fetch(:to, filename)).expand_path
182
+ FileUtils.mkdir_p path.parent
183
+
184
+ DropContent.download(drop).tap do |content|
185
+ File.open(path, 'w', 0600) {|file| file << content }
186
+ end
187
+ end
188
+
155
189
  def drops(count = 20)
156
190
  root.paginated_drops(per_page: count)['items'].map do |drop_data|
157
191
  Drop.new drop_data
data/lib/cloudapp.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module CloudApp
2
- VERSION = '1.0.2'
2
+ VERSION = '1.1.0'
3
3
  end
data/man/cloudapp.1 CHANGED
@@ -18,6 +18,9 @@
18
18
  .br
19
19
  \fBcloudapp upload\fR [\fB\-\-private\fR] [\fB\-\-public\fR] \fIfile\fR [\fIfile\fR\.\.\.]
20
20
  .
21
+ .br
22
+ \fBcloudapp download\fR [\fB\-\-output\fR=\fIfile\fR] \fIurl\fR
23
+ .
21
24
  .SH "DESCRIPTION"
22
25
  \fBcloudapp\fR is a simple, Unix\-friendly tool for CloudApp meant to augment the Mac and Web apps\. Sometimes it\'s more convenient to share without leaving the terminal and other times it\'s the only tool available\.
23
26
  .
@@ -30,20 +33,32 @@ Output using either \fBpretty\fR or \fBcsv\fR format\. (Default: \fBpretty\fR wh
30
33
  .SH "COMMANDS"
31
34
  .
32
35
  .TP
33
- \fBlist\fR [\fB\-\-count\fR=\fIcount\fR]
34
- List the most recent \fIcount\fR drops\. The default is to list \fB20\fR drops\.
35
- .
36
- .TP
37
36
  \fBbookmark\fR [\fB\-\-private\fR] [\fB\-\-public\fR] [\fIurl\fR\.\.\.]
38
37
  Create a new bookmark to each \fIurl\fR and print each link\.
39
38
  .
40
39
  .TP
40
+ \fBdownload\fR [\fB\-\-output\fR=\fIfile\fR] \fIurl\fR
41
+ Download the file at \fIurl\fR to the current directory or to \fIfile\fR if present\.
42
+ .
43
+ .TP
44
+ \fBlist\fR [\fB\-\-count\fR=\fIcount\fR]
45
+ List the most recent \fIcount\fR drops\. The default is to list \fB20\fR drops\.
46
+ .
47
+ .TP
41
48
  \fBupload\fR [\fB\-\-private\fR] [\fB\-\-public\fR] \fIfile\fR [\fIfile\fR\.\.\.]
42
49
  Upload each \fIfile\fR and print each link\.
43
50
  .
44
51
  .SH "COMMAND OPTIONS"
45
52
  .
46
53
  .TP
54
+ \fB\-n\fR \fIcount\fR, \fB\-\-count\fR=\fIcount\fR
55
+ Display \fIcount\fR drops\. (Default: \fB20\fR)
56
+ .
57
+ .TP
58
+ \fB\-o\fR \fIfile\fR, \fB\-\-output\fR=\fIfile\fR
59
+ Download file as \fIfile\fR\. (Default: current directory using the remote file\'s name)
60
+ .
61
+ .TP
47
62
  \fB\-\-private\fR
48
63
  Use a private (long) link when creating a new drop\.
49
64
  .
@@ -51,10 +66,6 @@ Use a private (long) link when creating a new drop\.
51
66
  \fB\-\-public\fR
52
67
  Use a public (short) link when creating a new drop\.
53
68
  .
54
- .TP
55
- \fB\-n\fR \fIcount\fR, \fB\-\-count\fR=\fIcount\fR
56
- Display \fIcount\fR drops\. (Default: \fB20\fR)
57
- .
58
69
  .SH "REQUIREMENTS"
59
70
  \fBcloudapp\fR requires Ruby 1\.9\.2 or greater\.
60
71
  .
@@ -118,7 +129,7 @@ Chapter 1\.md,http://cl\.ly/3U3d\.\.\.,7
118
129
  .IP "" 0
119
130
  .
120
131
  .P
121
- Share a new bookmark to "http://douglasadams\.com":
132
+ Share a new bookmark to \fBhttp://douglasadams\.com\fR:
122
133
  .
123
134
  .IP "" 4
124
135
  .
@@ -132,7 +143,7 @@ Bookmarking http://douglasadams\.com\.\.\. http://cl\.ly/1y0j403g3D0c0X1G0R3m
132
143
  .IP "" 0
133
144
  .
134
145
  .P
135
- Share the file "screenshot\.png":
146
+ Share the file \fBscreenshot\.png\fR:
136
147
  .
137
148
  .IP "" 4
138
149
  .
@@ -173,5 +184,33 @@ http://cl\.ly/1y0j403g3D0c0X1G0R3m
173
184
  .
174
185
  .IP "" 0
175
186
  .
187
+ .P
188
+ Download a drop to the current direcctory:
189
+ .
190
+ .IP "" 4
191
+ .
192
+ .nf
193
+
194
+ $ cloudapp download http://cl\.ly/040u2o3X1w0z1z3n2T04
195
+ Downloading screenshot\.png\.\.\. done
196
+ .
197
+ .fi
198
+ .
199
+ .IP "" 0
200
+ .
201
+ .P
202
+ Download a drop to another path:
203
+ .
204
+ .IP "" 4
205
+ .
206
+ .nf
207
+
208
+ $ cloudapp download \-\-output=/Users/Larry/image\.png http://cl\.ly/040u2o3X1w0z1z3n2T04
209
+ Downloading screenshot\.png to /Users/Larry\.\.\. done
210
+ .
211
+ .fi
212
+ .
213
+ .IP "" 0
214
+ .
176
215
  .SH "LICENSE"
177
216
  \fBcloudapp\fR is distributed under the MIT license \fIhttps://github\.com/cloudapp/cloudapp/blob/master/MIT\-LICENSE\fR\.