cloudapp 2.0.0.beta.5 → 2.0.0.beta.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +12 -1
- data/README.md +23 -22
- data/bin/cloudapp +32 -12
- data/cloudapp.gemspec +16 -5
- data/lib/cloudapp/cli/prompt.rb +34 -0
- data/lib/cloudapp/collection_json/collection.rb +21 -0
- data/lib/cloudapp/collection_json/item.rb +26 -0
- data/lib/cloudapp/collection_json/template.rb +26 -0
- data/lib/cloudapp/collection_json.rb +5 -60
- data/lib/cloudapp/credentials.rb +15 -97
- data/lib/cloudapp/service.rb +8 -2
- data/lib/cloudapp.rb +1 -1
- data/man/cloudapp.1 +2 -2
- data/man/cloudapp.1.html +2 -2
- data/man/cloudapp.1.ronn +2 -2
- data/spec/cloudapp/authorized_spec.rb +19 -0
- data/spec/cloudapp/collection_json/collection_spec.rb +69 -0
- data/spec/cloudapp/collection_json/item_spec.rb +55 -0
- data/spec/cloudapp/collection_json/template_spec.rb +35 -0
- data/spec/cloudapp/credentials_spec.rb +61 -0
- metadata +43 -2
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cloudapp (2.0.0.beta.
|
4
|
+
cloudapp (2.0.0.beta.6)
|
5
5
|
leadlight (~> 0.0.7)
|
6
|
+
mime-types (~> 1.19)
|
6
7
|
netrc (~> 0.7.7)
|
7
8
|
typhoeus (~> 0.3.3)
|
8
9
|
|
@@ -10,6 +11,7 @@ GEM
|
|
10
11
|
remote: http://rubygems.org/
|
11
12
|
specs:
|
12
13
|
addressable (2.2.8)
|
14
|
+
diff-lcs (1.1.3)
|
13
15
|
fail-fast (1.0.0)
|
14
16
|
faraday (0.8.1)
|
15
17
|
multipart-post (~> 1.1)
|
@@ -37,6 +39,14 @@ GEM
|
|
37
39
|
hpricot (>= 0.8.2)
|
38
40
|
mustache (>= 0.7.0)
|
39
41
|
rdiscount (>= 1.5.8)
|
42
|
+
rspec (2.12.0)
|
43
|
+
rspec-core (~> 2.12.0)
|
44
|
+
rspec-expectations (~> 2.12.0)
|
45
|
+
rspec-mocks (~> 2.12.0)
|
46
|
+
rspec-core (2.12.2)
|
47
|
+
rspec-expectations (2.12.1)
|
48
|
+
diff-lcs (~> 1.1.3)
|
49
|
+
rspec-mocks (2.12.0)
|
40
50
|
typhoeus (0.3.3)
|
41
51
|
mime-types
|
42
52
|
|
@@ -47,3 +57,4 @@ DEPENDENCIES
|
|
47
57
|
cloudapp!
|
48
58
|
rake
|
49
59
|
ronn
|
60
|
+
rspec
|
data/README.md
CHANGED
@@ -15,20 +15,38 @@ you're willing to lend a hand, we'd love to officially support it.
|
|
15
15
|
|
16
16
|
``` bash
|
17
17
|
$ gem install cloudapp --pre
|
18
|
-
$ cloudapp upload screenshot.png
|
19
18
|
$ cloudapp bookmark http://getcloudapp.com
|
19
|
+
$ cloudapp upload screenshot.png
|
20
|
+
$ cloudapp upload --direct screenshot.png
|
21
|
+
```
|
22
|
+
|
23
|
+
For a good time, install `gem-man` and read the man page or
|
24
|
+
[read it online][man].
|
25
|
+
|
26
|
+
[man]: http://cloudapp.github.com/cloudapp
|
27
|
+
|
28
|
+
|
29
|
+
``` bash
|
30
|
+
$ gem install gem-man
|
31
|
+
$ gem man cloudapp
|
20
32
|
```
|
21
33
|
|
22
34
|
## Commands
|
23
35
|
|
24
|
-
### cloudapp upload
|
36
|
+
### cloudapp upload `<file>`
|
37
|
+
|
38
|
+
Upload `<file>` and print its link to standard out. Use the `--direct` flag to
|
39
|
+
print the file's direct link which is suitable for use in places that expect a
|
40
|
+
link to a file like an HTML IMG tag.
|
25
41
|
|
26
42
|
``` bash
|
27
43
|
$ cloudapp upload screenshot.png
|
28
44
|
http://cl.ly/abc123
|
29
45
|
```
|
30
46
|
|
31
|
-
### cloudapp bookmark
|
47
|
+
### cloudapp bookmark `<url>`
|
48
|
+
|
49
|
+
Bookmark `<url>` and print its link to standard out.
|
32
50
|
|
33
51
|
``` bash
|
34
52
|
$ cloudapp bookmark http://getcloudapp.com
|
@@ -40,29 +58,12 @@ http://cl.ly/abc123
|
|
40
58
|
A few simple commands to allow scripting and input from other Unix programs
|
41
59
|
would be ideal.
|
42
60
|
|
43
|
-
### Phase: Next
|
44
|
-
|
45
61
|
- Share several files: `cloudapp upload *.png`
|
46
62
|
- Bookmark several links: `cloudapp bookmark http://douglasadams.com http://zombo.com`
|
47
63
|
- Handle bookmarks from STDIN: `pbpaste | cloudapp bookmark`
|
48
|
-
- Handle files from STDIN: `find *.png | cloudapp upload`
|
49
64
|
- Download a drop: `cloudapp download http://cl.ly/abc123`
|
50
|
-
|
51
|
-
### Phase: Unstoppable
|
52
|
-
|
53
|
-
- Archive and share several files: `cloudapp upload --archive *.png`
|
54
65
|
- Encrypt and share a file: `cloudapp upload --encrypt launch_codes.txt`
|
55
66
|
- Download and decrypt and encrypted drop: `cloudapp download --key=def456 http://cl.ly/abc123`
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
While we're dreaming, what could you do if `cloudapp` had a database of all your
|
60
|
-
drops? Bonus points for a light weight daemon that kept everything in sync at
|
61
|
-
all times.
|
62
|
-
|
63
|
-
- Find all your screen shots: `cloudapp list /^screen ?shot.*\.png$/`
|
64
|
-
- Trash all your stale drops: `cloudapp delete --last-viewed="> 1 month ago"`
|
65
|
-
- See your drop views in real time: `cloudapp --tail`
|
66
|
-
|
67
|
-
There's bound to be a better way to express some of these commands, but you get
|
68
|
-
the picture.
|
68
|
+
There's be a better way to express some of these commands, but you get the
|
69
|
+
picture.
|
data/bin/cloudapp
CHANGED
@@ -1,30 +1,47 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'cloudapp/credentials'
|
4
|
+
require 'cloudapp/cli/prompt'
|
4
5
|
require 'cloudapp/service'
|
5
6
|
|
6
7
|
def service
|
7
|
-
CloudApp::Service.using_token
|
8
|
+
CloudApp::Service.using_token token
|
9
|
+
end
|
10
|
+
|
11
|
+
def token
|
12
|
+
Credentials.token
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid_token?
|
16
|
+
!token.nil? and service.root.authorized?
|
8
17
|
end
|
9
18
|
|
10
19
|
def error error
|
11
20
|
puts "ERROR: #{error}"
|
12
|
-
exit
|
21
|
+
exit 9
|
13
22
|
end
|
14
23
|
|
15
|
-
def
|
24
|
+
def valid_url? url
|
16
25
|
error 'URL missing' if url.nil?
|
17
26
|
error "#{ url } doesn't look like a valid URL" unless url =~ URI.regexp
|
27
|
+
true
|
18
28
|
end
|
19
29
|
|
20
|
-
def
|
30
|
+
def valid_file? file
|
21
31
|
error "File doesn't exist" if file.is_a? Errno::ENOENT
|
22
32
|
error 'File missing' if file.nil? or file == STDIN
|
33
|
+
not file.closed?
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_file
|
37
|
+
ARGF.file rescue $!
|
23
38
|
end
|
24
39
|
|
25
40
|
|
26
|
-
while
|
27
|
-
|
41
|
+
while not valid_token?
|
42
|
+
credentials = CloudApp::CLI::Prompt.new.ask_for_credentials
|
43
|
+
token = CloudApp::Service.token_for_account *credentials
|
44
|
+
Credentials.save_token token
|
28
45
|
end
|
29
46
|
|
30
47
|
link = ARGV.delete('--direct') || ARGV.delete('-d') ? :embed : :canonical
|
@@ -32,13 +49,16 @@ link = ARGV.delete('--direct') || ARGV.delete('-d') ? :embed : :canonical
|
|
32
49
|
case ARGV.shift
|
33
50
|
when 'bookmark'
|
34
51
|
link = :canonical # No such thing as an embed link for a bookmark.
|
35
|
-
url
|
36
|
-
|
37
|
-
|
52
|
+
while url = ARGV.shift
|
53
|
+
break unless valid_url? url
|
54
|
+
puts service.bookmark(url).link(link)
|
55
|
+
end
|
38
56
|
when 'upload'
|
39
|
-
file =
|
40
|
-
|
41
|
-
|
57
|
+
while file = next_file
|
58
|
+
break unless valid_file? file
|
59
|
+
puts service.upload(file).link(link)
|
60
|
+
ARGF.skip
|
61
|
+
end
|
42
62
|
else
|
43
63
|
puts <<EOS
|
44
64
|
Usage:
|
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 = '2.0.0.beta.
|
17
|
-
s.date = '2012-12-
|
16
|
+
s.version = '2.0.0.beta.6'
|
17
|
+
s.date = '2012-12-23'
|
18
18
|
s.rubyforge_project = 'cloudapp'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -43,14 +43,16 @@ Gem::Specification.new do |s|
|
|
43
43
|
|
44
44
|
## List your runtime dependencies here. Runtime dependencies are those
|
45
45
|
## that are needed for an end user to actually USE your code.
|
46
|
-
s.add_dependency 'netrc',
|
47
|
-
s.add_dependency 'leadlight',
|
48
|
-
s.add_dependency '
|
46
|
+
s.add_dependency 'netrc', '~> 0.7.7'
|
47
|
+
s.add_dependency 'leadlight', '~> 0.0.7'
|
48
|
+
s.add_dependency 'mime-types', '~> 1.19'
|
49
|
+
s.add_dependency 'typhoeus', '~> 0.3.3'
|
49
50
|
|
50
51
|
## List your development dependencies here. Development dependencies are
|
51
52
|
## those that are only needed during development
|
52
53
|
s.add_development_dependency 'rake'
|
53
54
|
s.add_development_dependency 'ronn'
|
55
|
+
s.add_development_dependency 'rspec'
|
54
56
|
|
55
57
|
## Leave this section as-is. It will be automatically generated from the
|
56
58
|
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
@@ -66,12 +68,21 @@ Gem::Specification.new do |s|
|
|
66
68
|
cloudapp.gemspec
|
67
69
|
lib/cloudapp.rb
|
68
70
|
lib/cloudapp/authorized.rb
|
71
|
+
lib/cloudapp/cli/prompt.rb
|
69
72
|
lib/cloudapp/collection_json.rb
|
73
|
+
lib/cloudapp/collection_json/collection.rb
|
74
|
+
lib/cloudapp/collection_json/item.rb
|
75
|
+
lib/cloudapp/collection_json/template.rb
|
70
76
|
lib/cloudapp/credentials.rb
|
71
77
|
lib/cloudapp/service.rb
|
72
78
|
man/cloudapp.1
|
73
79
|
man/cloudapp.1.html
|
74
80
|
man/cloudapp.1.ronn
|
81
|
+
spec/cloudapp/authorized_spec.rb
|
82
|
+
spec/cloudapp/collection_json/collection_spec.rb
|
83
|
+
spec/cloudapp/collection_json/item_spec.rb
|
84
|
+
spec/cloudapp/collection_json/template_spec.rb
|
85
|
+
spec/cloudapp/credentials_spec.rb
|
75
86
|
]
|
76
87
|
# = MANIFEST =
|
77
88
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Lovingly borrowed from the heroku gem:
|
2
|
+
# https://github.com/heroku/heroku/blob/master/lib/heroku/auth.rb
|
3
|
+
module CloudApp
|
4
|
+
module CLI
|
5
|
+
class Prompt
|
6
|
+
def ask_for_credentials
|
7
|
+
puts "Sign into CloudApp."
|
8
|
+
print "Email: "
|
9
|
+
email = ask
|
10
|
+
print "Password (typing will be hidden): "
|
11
|
+
password = ask_for_password
|
12
|
+
[ email, password ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def ask() $stdin.gets.to_s.strip end
|
16
|
+
def ask_for_password
|
17
|
+
echo_off
|
18
|
+
password = ask
|
19
|
+
puts
|
20
|
+
echo_on
|
21
|
+
return password
|
22
|
+
end
|
23
|
+
|
24
|
+
def echo_on() with_tty { system "stty echo" } end
|
25
|
+
def echo_off() with_tty { system "stty -echo" } end
|
26
|
+
def with_tty(&block)
|
27
|
+
return unless $stdin.isatty
|
28
|
+
yield
|
29
|
+
rescue
|
30
|
+
# fails on windows
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'cloudapp/collection_json/item'
|
2
|
+
require 'cloudapp/collection_json/template'
|
3
|
+
|
4
|
+
module CloudApp
|
5
|
+
module CollectionJson
|
6
|
+
class Collection < Item
|
7
|
+
def initialize representation
|
8
|
+
super representation.fetch('collection')
|
9
|
+
end
|
10
|
+
|
11
|
+
def item() items.first end
|
12
|
+
def items
|
13
|
+
fetch('items', []).map {|item| Item.new(item) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def template
|
17
|
+
Template.new(fetch('template'), href)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module CloudApp
|
4
|
+
module CollectionJson
|
5
|
+
class Item < SimpleDelegator
|
6
|
+
def href() fetch('href') end
|
7
|
+
def rel() fetch('rel') end
|
8
|
+
|
9
|
+
def links
|
10
|
+
fetch('links', []).map {|link| Item.new(link) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def link rel
|
14
|
+
links.find {|link| link.rel == rel.to_s }.href
|
15
|
+
end
|
16
|
+
|
17
|
+
def data
|
18
|
+
data = {}
|
19
|
+
fetch('data').each do |datum|
|
20
|
+
data[datum.fetch('name')] = datum.fetch('value')
|
21
|
+
end
|
22
|
+
data
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'cloudapp/collection_json/item'
|
2
|
+
|
3
|
+
module CloudApp
|
4
|
+
module CollectionJson
|
5
|
+
class Template < Item
|
6
|
+
attr_reader :href
|
7
|
+
|
8
|
+
def initialize item, href
|
9
|
+
@href = href
|
10
|
+
super item
|
11
|
+
end
|
12
|
+
|
13
|
+
def fill key, value
|
14
|
+
return self unless data.has_key? key
|
15
|
+
new_data = fetch('data').map {|datum|
|
16
|
+
if datum.fetch('name') == key
|
17
|
+
datum = datum.merge('value' => value)
|
18
|
+
end
|
19
|
+
datum
|
20
|
+
}
|
21
|
+
new_item = self.merge('data' => new_data)
|
22
|
+
Template.new new_item, href
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'cloudapp/collection_json/item'
|
2
|
+
require 'cloudapp/collection_json/collection'
|
3
|
+
require 'cloudapp/collection_json/template'
|
4
|
+
|
1
5
|
module CloudApp
|
2
6
|
module CollectionJson
|
3
7
|
Tint = Leadlight::Tint.new 'collection+json', status: [ :success, 401 ] do
|
@@ -7,6 +11,7 @@ module CloudApp
|
|
7
11
|
|
8
12
|
module Representation
|
9
13
|
extend Forwardable
|
14
|
+
def_delegators :collection, :template, :item
|
10
15
|
|
11
16
|
def self.extended representation
|
12
17
|
Collection.new(representation).links.each do |link|
|
@@ -21,66 +26,6 @@ module CloudApp
|
|
21
26
|
def collection
|
22
27
|
Collection.new(self)
|
23
28
|
end
|
24
|
-
|
25
|
-
def_delegators :collection, :template, :item
|
26
|
-
|
27
|
-
class Item < SimpleDelegator
|
28
|
-
def links
|
29
|
-
fetch('links', []).map {|link| Item.new(link) }
|
30
|
-
end
|
31
|
-
|
32
|
-
def link rel
|
33
|
-
links.find {|link| link.rel == rel.to_s }.href
|
34
|
-
end
|
35
|
-
|
36
|
-
def href() fetch('href') end
|
37
|
-
def rel() fetch('rel') end
|
38
|
-
def data
|
39
|
-
data = {}
|
40
|
-
fetch('data').each do |datum|
|
41
|
-
data[datum.fetch('name')] = datum.fetch('value')
|
42
|
-
end
|
43
|
-
data
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class Collection < Item
|
48
|
-
def initialize representation
|
49
|
-
super representation.fetch('collection')
|
50
|
-
end
|
51
|
-
|
52
|
-
def template() Template.new(fetch('template'), href) end
|
53
|
-
def items
|
54
|
-
fetch('items').map {|item| Item.new(item) }
|
55
|
-
end
|
56
|
-
|
57
|
-
def item
|
58
|
-
items.first
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
class Template < Item
|
63
|
-
attr_reader :href
|
64
|
-
|
65
|
-
def initialize item, href
|
66
|
-
@href = href
|
67
|
-
super item
|
68
|
-
end
|
69
|
-
|
70
|
-
def enctype() fetch('enctype') end
|
71
|
-
|
72
|
-
def fill key, value
|
73
|
-
return self unless data.has_key?(key)
|
74
|
-
new_data = fetch('data').map {|datum|
|
75
|
-
if datum.fetch('name') == key
|
76
|
-
datum = datum.merge 'value' => value
|
77
|
-
end
|
78
|
-
datum
|
79
|
-
}
|
80
|
-
new_item = self.merge('data' => new_data)
|
81
|
-
Template.new new_item, href
|
82
|
-
end
|
83
|
-
end
|
84
29
|
end
|
85
30
|
end
|
86
31
|
end
|
data/lib/cloudapp/credentials.rb
CHANGED
@@ -1,115 +1,33 @@
|
|
1
|
-
# Taken from the heroku gem.
|
2
|
-
# https://github.com/heroku/heroku/blob/master/lib/heroku/auth.rb
|
3
1
|
require 'netrc'
|
4
2
|
|
5
3
|
class Credentials
|
6
|
-
|
7
|
-
new.credentials.last
|
8
|
-
end
|
4
|
+
attr_reader :netrc
|
9
5
|
|
10
|
-
def
|
11
|
-
|
12
|
-
encrypted = default + ".gpg"
|
13
|
-
if File.exists?(encrypted)
|
14
|
-
encrypted
|
15
|
-
else
|
16
|
-
default
|
17
|
-
end
|
6
|
+
def initialize netrc
|
7
|
+
@netrc = netrc
|
18
8
|
end
|
19
9
|
|
20
|
-
def netrc
|
21
|
-
|
22
|
-
File.exists?(netrc_path) && Netrc.read(netrc_path)
|
23
|
-
rescue => error
|
24
|
-
if error.message =~ /^Permission bits for/
|
25
|
-
perm = File.stat(netrc_path).mode & 0777
|
26
|
-
abort("Permissions #{perm} for '#{netrc_path}' are too open. You should run `chmod 0600 #{netrc_path}` so that your credentials are NOT accessible by others.")
|
27
|
-
else
|
28
|
-
raise error
|
29
|
-
end
|
30
|
-
end
|
10
|
+
def self.token netrc = Netrc.read
|
11
|
+
new(netrc).token
|
31
12
|
end
|
32
13
|
|
33
|
-
def
|
34
|
-
|
35
|
-
(@credentials ||= read_credentials) || ask_for_and_save_credentials
|
14
|
+
def self.save_token token, netrc = Netrc.read
|
15
|
+
new(netrc).save_token token
|
36
16
|
end
|
37
17
|
|
38
|
-
def
|
39
|
-
|
40
|
-
# if File.exists?(legacy_credentials_path)
|
41
|
-
# @api, @client = nil
|
42
|
-
# @credentials = File.read(legacy_credentials_path).split("\n")
|
43
|
-
# write_credentials
|
44
|
-
# FileUtils.rm_f(legacy_credentials_path)
|
45
|
-
# end
|
46
|
-
|
47
|
-
# read netrc credentials if they exist
|
48
|
-
self.credentials = netrc['api.getcloudapp.com'] if netrc
|
18
|
+
def token
|
19
|
+
credentials.last
|
49
20
|
end
|
50
21
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
FileUtils.chmod(0600, netrc_path)
|
55
|
-
netrc['api.getcloudapp.com'] = credentials
|
22
|
+
def save_token token
|
23
|
+
return if token.nil? or token =~ /^\s*$/
|
24
|
+
netrc['api.getcloudapp.com'] = [ 'api@getcloudapp.com', token ]
|
56
25
|
netrc.save
|
57
26
|
end
|
58
27
|
|
59
|
-
|
60
|
-
# TODO: delete legacy credentials
|
61
|
-
# if File.exists?(legacy_credentials_path)
|
62
|
-
# FileUtils.rm_f(legacy_credentials_path)
|
63
|
-
# end
|
64
|
-
if netrc
|
65
|
-
netrc.delete('api.getcloudapp.com')
|
66
|
-
netrc.save
|
67
|
-
end
|
68
|
-
self.credentials = nil
|
69
|
-
end
|
70
|
-
|
71
|
-
def ask_for_and_save_credentials
|
72
|
-
self.credentials = ask_for_credentials
|
73
|
-
write_credentials
|
74
|
-
credentials
|
75
|
-
end
|
76
|
-
|
77
|
-
def ask_for_credentials
|
78
|
-
puts "Enter your CloudApp credentials."
|
79
|
-
|
80
|
-
print "Email: "
|
81
|
-
email = ask
|
82
|
-
|
83
|
-
print "Password (typing will be hidden): "
|
84
|
-
password = ask_for_password
|
85
|
-
|
86
|
-
# TODO: Remove this dependency on Service.
|
87
|
-
token = CloudApp::Service.token_for_account email, password
|
88
|
-
unless token
|
89
|
-
puts "Incorrect email or password."
|
90
|
-
puts
|
91
|
-
delete_credentials
|
92
|
-
return ask_for_credentials
|
93
|
-
end
|
94
|
-
|
95
|
-
[ email, token ]
|
96
|
-
end
|
97
|
-
|
98
|
-
def ask() $stdin.gets.to_s.strip end
|
99
|
-
def ask_for_password
|
100
|
-
echo_off
|
101
|
-
password = ask
|
102
|
-
puts
|
103
|
-
echo_on
|
104
|
-
return password
|
105
|
-
end
|
28
|
+
private
|
106
29
|
|
107
|
-
def
|
108
|
-
|
109
|
-
def with_tty(&block)
|
110
|
-
return unless $stdin.isatty
|
111
|
-
yield
|
112
|
-
rescue
|
113
|
-
# fails on windows
|
30
|
+
def credentials
|
31
|
+
Array netrc['api.getcloudapp.com']
|
114
32
|
end
|
115
33
|
end
|
data/lib/cloudapp/service.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'leadlight'
|
2
2
|
require 'cloudapp/authorized'
|
3
3
|
require 'cloudapp/collection_json'
|
4
|
+
require 'mime/types'
|
4
5
|
require 'uri'
|
5
6
|
|
6
7
|
module CloudApp
|
7
8
|
class Service
|
8
9
|
Leadlight.build_service self do
|
9
|
-
url '
|
10
|
+
url 'https://api.getcloudapp.com'
|
10
11
|
tints << CloudApp::CollectionJson::Tint
|
11
12
|
tints << CloudApp::Authorized::Tint
|
12
13
|
|
@@ -70,7 +71,7 @@ module CloudApp
|
|
70
71
|
|
71
72
|
def upload_file(path, template)
|
72
73
|
file = File.open path
|
73
|
-
file_io = Faraday::UploadIO.new file,
|
74
|
+
file_io = Faraday::UploadIO.new file, mime_type_for(path)
|
74
75
|
template = template.fill('file', file_io)
|
75
76
|
uri = Addressable::URI.parse template.href
|
76
77
|
|
@@ -87,5 +88,10 @@ module CloudApp
|
|
87
88
|
end
|
88
89
|
end
|
89
90
|
end
|
91
|
+
|
92
|
+
def mime_type_for path
|
93
|
+
MIME::Types.type_for(File.basename(path)).first ||
|
94
|
+
'application/octet-stream'
|
95
|
+
end
|
90
96
|
end
|
91
97
|
end
|
data/lib/cloudapp.rb
CHANGED
data/man/cloudapp.1
CHANGED
@@ -19,7 +19,7 @@ Upload a file or share a bookmark with CloudApp\. The drop\'s share link will be
|
|
19
19
|
.
|
20
20
|
.TP
|
21
21
|
\fB\-d\fR, \fB\-\-direct\fR
|
22
|
-
Print the drop\'s direct link after creation\. The direct link is suitable for use in places that expect a link to a file
|
22
|
+
Print the drop\'s direct link after creation\. The direct link is suitable for use in places that expect a link to a file like an HTML IMG tag\.
|
23
23
|
.
|
24
24
|
.SH "EXIT STATUS"
|
25
25
|
cloudapp may return one of several error codes if it encouters problems\.
|
@@ -31,7 +31,7 @@ cloudapp may return one of several error codes if it encouters problems\.
|
|
31
31
|
\fB1\fR File exceeds the limits of the account\'s plan
|
32
32
|
.
|
33
33
|
.IP "\(bu" 4
|
34
|
-
\fB9\fR
|
34
|
+
\fB9\fR Some horrible, unexpected thing has happened
|
35
35
|
.
|
36
36
|
.IP "" 0
|
37
37
|
.
|
data/man/cloudapp.1.html
CHANGED
@@ -94,7 +94,7 @@ printed to standard output. Account credentials are stored in <code>~/.netrc</co
|
|
94
94
|
|
95
95
|
<dl>
|
96
96
|
<dt><code>-d</code>, <code>--direct</code></dt><dd>Print the drop's direct link after creation. The direct link is suitable for
|
97
|
-
use in places that expect a link to a file
|
97
|
+
use in places that expect a link to a file like an HTML IMG tag.</dd>
|
98
98
|
</dl>
|
99
99
|
|
100
100
|
|
@@ -105,7 +105,7 @@ use in places that expect a link to a file content like an HTML IMG tag.</dd>
|
|
105
105
|
<ul>
|
106
106
|
<li><code>0</code> Success</li>
|
107
107
|
<li><code>1</code> File exceeds the limits of the account's plan</li>
|
108
|
-
<li><code>9</code>
|
108
|
+
<li><code>9</code> Some horrible, unexpected thing has happened</li>
|
109
109
|
</ul>
|
110
110
|
|
111
111
|
|
data/man/cloudapp.1.ronn
CHANGED
@@ -15,7 +15,7 @@ printed to standard output. Account credentials are stored in `~/.netrc`.
|
|
15
15
|
|
16
16
|
- `-d`, `--direct`:
|
17
17
|
Print the drop's direct link after creation. The direct link is suitable for
|
18
|
-
use in places that expect a link to a file
|
18
|
+
use in places that expect a link to a file like an HTML IMG tag.
|
19
19
|
|
20
20
|
## EXIT STATUS
|
21
21
|
|
@@ -23,7 +23,7 @@ cloudapp may return one of several error codes if it encouters problems.
|
|
23
23
|
|
24
24
|
- `0` Success
|
25
25
|
- `1` File exceeds the limits of the account's plan
|
26
|
-
- `9`
|
26
|
+
- `9` Some horrible, unexpected thing has happened
|
27
27
|
|
28
28
|
## FILES
|
29
29
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'cloudapp/authorized'
|
2
|
+
|
3
|
+
describe CloudApp::Authorized::Representation do
|
4
|
+
let(:representation) { stub :representation, __response__: response }
|
5
|
+
let(:response) { stub :response, status: status }
|
6
|
+
subject { representation.extend described_class }
|
7
|
+
|
8
|
+
context 'an unauthorized response' do
|
9
|
+
let(:status) { 401 }
|
10
|
+
it { should be_unauthorized }
|
11
|
+
it { should_not be_authorized }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'a success response' do
|
15
|
+
let(:status) { 200 }
|
16
|
+
it { should_not be_unauthorized }
|
17
|
+
it { should be_authorized }
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'cloudapp/collection_json/collection'
|
2
|
+
|
3
|
+
describe CloudApp::CollectionJson::Collection do
|
4
|
+
let(:collection) { described_class.new(data) }
|
5
|
+
subject { collection }
|
6
|
+
|
7
|
+
describe 'the collection item' do
|
8
|
+
let(:data) {{ 'collection' => { 'href' => 'http://href.com' }}}
|
9
|
+
its(:href) { should eq 'http://href.com' }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#items' do
|
13
|
+
let(:data) {{
|
14
|
+
'collection' => {
|
15
|
+
'items' => [
|
16
|
+
{ 'href' => 'http://one.com' },
|
17
|
+
{ 'href' => 'http://two.com' } ]}
|
18
|
+
}}
|
19
|
+
subject { collection.items }
|
20
|
+
|
21
|
+
it { should be_an Array }
|
22
|
+
it { should have(2).items }
|
23
|
+
|
24
|
+
it 'returns an array of Items' do
|
25
|
+
subject.each do |item|
|
26
|
+
item.should be_an CloudApp::CollectionJson::Item
|
27
|
+
end
|
28
|
+
|
29
|
+
item = subject.first
|
30
|
+
item.href.should eq 'http://one.com'
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with no items' do
|
34
|
+
let(:data) {{ 'collection' => {} }}
|
35
|
+
it { should be_empty }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#item' do
|
40
|
+
let(:data) {{
|
41
|
+
'collection' => {
|
42
|
+
'items' => [
|
43
|
+
{ 'href' => 'http://one.com' },
|
44
|
+
{ 'href' => 'http://two.com' } ]}}}
|
45
|
+
subject { collection.item }
|
46
|
+
|
47
|
+
it { should eq collection.items.first }
|
48
|
+
|
49
|
+
context 'with no items' do
|
50
|
+
let(:data) {{ 'collection' => {} }}
|
51
|
+
it { should be_nil }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#template' do
|
56
|
+
let(:data) {{
|
57
|
+
'collection' => {
|
58
|
+
'href' => 'http://href.com',
|
59
|
+
'template' => {
|
60
|
+
'data' => [
|
61
|
+
{ 'name' => 'first_name', 'value' => '' },
|
62
|
+
{ 'name' => 'last_name', 'value' => '' } ]}}}}
|
63
|
+
subject { collection.template }
|
64
|
+
|
65
|
+
it { should be_an CloudApp::CollectionJson::Template }
|
66
|
+
its(:href) { should eq 'http://href.com' }
|
67
|
+
its(:data) { should eq 'first_name' => '', 'last_name' => '' }
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'cloudapp/collection_json/item'
|
2
|
+
|
3
|
+
describe CloudApp::CollectionJson::Item do
|
4
|
+
subject { described_class.new(data) }
|
5
|
+
|
6
|
+
describe '#href' do
|
7
|
+
let(:data) {{ 'href' => 'http://getcloudapp.com' }}
|
8
|
+
its(:href) { should eq 'http://getcloudapp.com' }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#rel' do
|
12
|
+
let(:data) {{ 'rel' => 'relation' }}
|
13
|
+
its(:rel) { should eq 'relation' }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#links' do
|
17
|
+
subject { described_class.new(data).links }
|
18
|
+
let(:data) {{ 'links' => [
|
19
|
+
{ 'rel' => 'self', 'href' => 'http://self.com' },
|
20
|
+
{ 'rel' => 'next', 'href' => 'http://next.com' }] }}
|
21
|
+
|
22
|
+
it { should be_an Array }
|
23
|
+
it { should have(2).items }
|
24
|
+
|
25
|
+
it 'returns an array of Items' do
|
26
|
+
subject.each do |item|
|
27
|
+
item.should be_a described_class
|
28
|
+
end
|
29
|
+
|
30
|
+
item = subject.first
|
31
|
+
item.rel.should eq 'self'
|
32
|
+
item.href.should eq 'http://self.com'
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with no links' do
|
36
|
+
let(:data) { {} }
|
37
|
+
it { should be_empty }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#link' do
|
42
|
+
subject { described_class.new(data).link('next') }
|
43
|
+
let(:data) {{ 'links' => [
|
44
|
+
{ 'rel' => 'self', 'href' => 'http://self.com' },
|
45
|
+
{ 'rel' => 'next', 'href' => 'http://next.com' }] }}
|
46
|
+
|
47
|
+
it { should eq 'http://next.com' }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#data' do
|
51
|
+
let(:data) {{ 'data' => [{ 'name' => 'first_name', 'value' => 'Arthur' },
|
52
|
+
{ 'name' => 'last_name', 'value' => 'Dent' }] }}
|
53
|
+
its(:data) { should eq 'first_name' => 'Arthur', 'last_name' => 'Dent' }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cloudapp/collection_json/template'
|
2
|
+
|
3
|
+
describe CloudApp::CollectionJson::Template do
|
4
|
+
let(:template) { described_class.new(data, 'http://href.com') }
|
5
|
+
subject { template }
|
6
|
+
|
7
|
+
describe 'the template item' do
|
8
|
+
let(:data) {{ 'data' => [
|
9
|
+
{ 'name' => 'first_name', 'value' => '' },
|
10
|
+
{ 'name' => 'last_name', 'value' => '' } ]}}
|
11
|
+
|
12
|
+
its(:href) { should eq 'http://href.com' }
|
13
|
+
its(:data) { should eq 'first_name' => '', 'last_name' => '' }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#fill' do
|
17
|
+
let(:data) {{ 'data' => [
|
18
|
+
{ 'name' => 'first_name', 'value' => '' },
|
19
|
+
{ 'name' => 'last_name', 'value' => '' } ]}}
|
20
|
+
subject { template.fill('first_name', 'Arthur') }
|
21
|
+
|
22
|
+
it { should be_a described_class }
|
23
|
+
it { should_not eq template }
|
24
|
+
|
25
|
+
it 'fills the datum' do
|
26
|
+
subject.data.fetch('first_name').should eq 'Arthur'
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'a nonexistent key' do
|
30
|
+
it 'does nothing' do
|
31
|
+
subject.fill('bad', 'value').should eq subject
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'cloudapp/credentials'
|
2
|
+
|
3
|
+
describe Credentials do
|
4
|
+
describe '.token' do
|
5
|
+
let(:netrc) { stub :netrc, :[] => %w( arthur@dent.com towel ) }
|
6
|
+
subject { Credentials.token(netrc) }
|
7
|
+
it { should eq 'towel' }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#token' do
|
11
|
+
let(:netrc) { stub :netrc, :[] => %w( arthur@dent.com towel ) }
|
12
|
+
subject { Credentials.new(netrc).token }
|
13
|
+
|
14
|
+
it { should eq 'towel' }
|
15
|
+
|
16
|
+
it 'fetches token from netrc' do
|
17
|
+
netrc.should_receive(:[]).with('api.getcloudapp.com').once
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'without saved token' do
|
22
|
+
let(:netrc) { stub :netrc, :[] => nil }
|
23
|
+
it { should be_nil }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.save_token' do
|
28
|
+
let(:netrc) { stub :netrc, :[]= => nil }
|
29
|
+
|
30
|
+
it 'saves the token' do
|
31
|
+
netrc.should_receive(:save).once.ordered
|
32
|
+
Credentials.save_token 'new token', netrc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#save_token' do
|
37
|
+
let(:netrc) { stub :netrc }
|
38
|
+
subject { Credentials.new(netrc) }
|
39
|
+
|
40
|
+
it 'saves the token' do
|
41
|
+
credentials = [ 'api@getcloudapp.com', 'new token' ]
|
42
|
+
netrc.should_receive(:[]=).with('api.getcloudapp.com', credentials)
|
43
|
+
.once.ordered
|
44
|
+
netrc.should_receive(:save).once.ordered
|
45
|
+
subject.save_token 'new token'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'ignores a nil token' do
|
49
|
+
netrc.should_not_receive(:[]=)
|
50
|
+
netrc.should_not_receive(:save)
|
51
|
+
subject.save_token nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'ignores an empty token' do
|
55
|
+
netrc.should_not_receive(:[]=)
|
56
|
+
netrc.should_not_receive(:save)
|
57
|
+
subject.save_token ''
|
58
|
+
subject.save_token ' '
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudapp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.beta.
|
4
|
+
version: 2.0.0.beta.6
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: netrc
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.0.7
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mime-types
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.19'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.19'
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: typhoeus
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -91,6 +107,22 @@ dependencies:
|
|
91
107
|
- - ! '>='
|
92
108
|
- !ruby/object:Gem::Version
|
93
109
|
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
94
126
|
description: Experience all the pleasures of sharing with CloudApp now in your terminal.
|
95
127
|
email: larry@marburger.cc
|
96
128
|
executables:
|
@@ -109,12 +141,21 @@ files:
|
|
109
141
|
- cloudapp.gemspec
|
110
142
|
- lib/cloudapp.rb
|
111
143
|
- lib/cloudapp/authorized.rb
|
144
|
+
- lib/cloudapp/cli/prompt.rb
|
112
145
|
- lib/cloudapp/collection_json.rb
|
146
|
+
- lib/cloudapp/collection_json/collection.rb
|
147
|
+
- lib/cloudapp/collection_json/item.rb
|
148
|
+
- lib/cloudapp/collection_json/template.rb
|
113
149
|
- lib/cloudapp/credentials.rb
|
114
150
|
- lib/cloudapp/service.rb
|
115
151
|
- man/cloudapp.1
|
116
152
|
- man/cloudapp.1.html
|
117
153
|
- man/cloudapp.1.ronn
|
154
|
+
- spec/cloudapp/authorized_spec.rb
|
155
|
+
- spec/cloudapp/collection_json/collection_spec.rb
|
156
|
+
- spec/cloudapp/collection_json/item_spec.rb
|
157
|
+
- spec/cloudapp/collection_json/template_spec.rb
|
158
|
+
- spec/cloudapp/credentials_spec.rb
|
118
159
|
homepage: https://github.com/cloudapp/cloudapp
|
119
160
|
licenses: []
|
120
161
|
post_install_message:
|