cloudapp 2.0.0.beta.4 → 2.0.0.beta.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/Gemfile.lock +12 -19
  2. data/README.md +60 -6
  3. data/Rakefile +1 -19
  4. data/bin/cloudapp +48 -0
  5. data/cloudapp.gemspec +11 -42
  6. data/lib/cloudapp/authorized.rb +14 -0
  7. data/lib/cloudapp/collection_json.rb +86 -3
  8. data/lib/cloudapp/credentials.rb +115 -0
  9. data/lib/cloudapp/service.rb +43 -146
  10. data/lib/cloudapp.rb +1 -3
  11. data/man/cloudapp.1 +100 -0
  12. data/man/cloudapp.1.html +157 -0
  13. data/man/cloudapp.1.ronn +56 -0
  14. metadata +24 -69
  15. data/CHANGELOG.md +0 -34
  16. data/lib/cloudapp/authorized_representation.rb +0 -17
  17. data/lib/cloudapp/collection_json/item.rb +0 -20
  18. data/lib/cloudapp/collection_json/representation.rb +0 -74
  19. data/lib/cloudapp/collection_json/template.rb +0 -17
  20. data/lib/cloudapp/collection_json/tint.rb +0 -11
  21. data/lib/cloudapp/drop.rb +0 -37
  22. data/lib/cloudapp/drop_collection.rb +0 -37
  23. data/spec/cassettes/account_token.yml +0 -88
  24. data/spec/cassettes/create_bookmark.yml +0 -132
  25. data/spec/cassettes/create_bookmark_with_name.yml +0 -133
  26. data/spec/cassettes/create_bookmark_with_privacy.yml +0 -133
  27. data/spec/cassettes/list_drops.yml +0 -87
  28. data/spec/cassettes/list_drops_with_bad_token.yml +0 -45
  29. data/spec/cassettes/list_drops_with_filter.yml +0 -129
  30. data/spec/cassettes/list_drops_with_limit.yml +0 -129
  31. data/spec/cassettes/purge_drops.yml +0 -466
  32. data/spec/cassettes/setup_drops.yml +0 -620
  33. data/spec/cassettes/token_for_account.yml +0 -88
  34. data/spec/cassettes/token_for_account_with_bad_credentials.yml +0 -86
  35. data/spec/cassettes/upload_file.yml +0 -276
  36. data/spec/cassettes/upload_file_with_name.yml +0 -278
  37. data/spec/cassettes/upload_file_with_privacy.yml +0 -277
  38. data/spec/cassettes/view_drop.yml +0 -45
  39. data/spec/cloudapp/authorized_representation_spec.rb +0 -32
  40. data/spec/cloudapp/collection_json/item_spec.rb +0 -45
  41. data/spec/cloudapp/collection_json/representation_spec.rb +0 -118
  42. data/spec/cloudapp/collection_json/template_spec.rb +0 -53
  43. data/spec/cloudapp/drop_collection_spec.rb +0 -127
  44. data/spec/cloudapp/drop_spec.rb +0 -142
  45. data/spec/cloudapp/service_spec.rb +0 -335
  46. data/spec/helper.rb +0 -8
  47. data/spec/integration_spec.rb +0 -68
  48. data/spec/support/files/favicon.ico +0 -0
  49. data/spec/support/stub_class_or_module.rb +0 -22
  50. data/spec/support/vcr.rb +0 -24
data/Gemfile.lock CHANGED
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cloudapp (2.0.0.beta.2)
4
+ cloudapp (2.0.0.beta.5)
5
5
  leadlight (~> 0.0.7)
6
+ netrc (~> 0.7.7)
6
7
  typhoeus (~> 0.3.3)
7
8
 
8
9
  GEM
9
10
  remote: http://rubygems.org/
10
11
  specs:
11
12
  addressable (2.2.8)
12
- crack (0.3.1)
13
- diff-lcs (1.1.3)
14
13
  fail-fast (1.0.0)
15
14
  faraday (0.8.1)
16
15
  multipart-post (~> 1.1)
17
16
  fattr (2.2.1)
18
17
  hookr (1.1.1)
19
18
  fail-fast (= 1.0.0)
19
+ hpricot (0.8.6)
20
20
  leadlight (0.0.7)
21
21
  addressable (~> 2.2.0)
22
22
  faraday (= 0.8.1)
@@ -29,21 +29,16 @@ GEM
29
29
  mime-types (1.19)
30
30
  multi_json (1.0.4)
31
31
  multipart-post (1.1.5)
32
- rake (0.9.2.2)
33
- rspec (2.11.0)
34
- rspec-core (~> 2.11.0)
35
- rspec-expectations (~> 2.11.0)
36
- rspec-mocks (~> 2.11.0)
37
- rspec-core (2.11.1)
38
- rspec-expectations (2.11.2)
39
- diff-lcs (~> 1.1.3)
40
- rspec-mocks (2.11.2)
32
+ mustache (0.99.4)
33
+ netrc (0.7.7)
34
+ rake (10.0.3)
35
+ rdiscount (1.6.8)
36
+ ronn (0.7.3)
37
+ hpricot (>= 0.8.2)
38
+ mustache (>= 0.7.0)
39
+ rdiscount (>= 1.5.8)
41
40
  typhoeus (0.3.3)
42
41
  mime-types
43
- vcr (2.0.1)
44
- webmock (1.8.9)
45
- addressable (>= 2.2.7)
46
- crack (>= 0.1.7)
47
42
 
48
43
  PLATFORMS
49
44
  ruby
@@ -51,6 +46,4 @@ PLATFORMS
51
46
  DEPENDENCIES
52
47
  cloudapp!
53
48
  rake
54
- rspec
55
- vcr (~> 2.0.0)
56
- webmock
49
+ ronn
data/README.md CHANGED
@@ -1,14 +1,68 @@
1
- # CloudApp Ruby Client [![Build Status](https://secure.travis-ci.org/cloudapp/cloudapp.png)](http://travis-ci.org/cloudapp/cloudapp)
1
+ # CloudApp CLI
2
2
 
3
- Interact with the [CloudApp API][] from Ruby.
3
+ Experience all the pleasures of sharing with [CloudApp][] from your terminal.
4
+
5
+ [cloudapp]: http://getcloudapp.com
4
6
 
5
- [cloudapp api]: http://developer.getcloudapp.com
6
7
 
7
8
  ## Requirements
8
9
 
9
- `cloudapp` requires Ruby 1.9.2 or greater.
10
+ `cloudapp` requires Ruby 1.9.2 or greater. Windows is not yet supported. If
11
+ you're willing to lend a hand, we'd love to officially support it.
12
+
13
+
14
+ ## Getting Started
15
+
16
+ ``` bash
17
+ $ gem install cloudapp --pre
18
+ $ cloudapp upload screenshot.png
19
+ $ cloudapp bookmark http://getcloudapp.com
20
+ ```
21
+
22
+ ## Commands
23
+
24
+ ### cloudapp upload
25
+
26
+ ``` bash
27
+ $ cloudapp upload screenshot.png
28
+ http://cl.ly/abc123
29
+ ```
30
+
31
+ ### cloudapp bookmark
32
+
33
+ ``` bash
34
+ $ cloudapp bookmark http://getcloudapp.com
35
+ http://cl.ly/abc123
36
+ ```
37
+
38
+ ## Wish List
39
+
40
+ A few simple commands to allow scripting and input from other Unix programs
41
+ would be ideal.
42
+
43
+ ### Phase: Next
44
+
45
+ - Share several files: `cloudapp upload *.png`
46
+ - Bookmark several links: `cloudapp bookmark http://douglasadams.com http://zombo.com`
47
+ - Handle bookmarks from STDIN: `pbpaste | cloudapp bookmark`
48
+ - Handle files from STDIN: `find *.png | cloudapp upload`
49
+ - 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
+ - Encrypt and share a file: `cloudapp upload --encrypt launch_codes.txt`
55
+ - Download and decrypt and encrypted drop: `cloudapp download --key=def456 http://cl.ly/abc123`
56
+
57
+ ### Phase: World Domination
10
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.
11
62
 
12
- ## Usage
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`
13
66
 
14
- _Usage from Ruby is still a work in progress._
67
+ There's bound to be a better way to express some of these commands, but you get
68
+ the picture.
data/Rakefile CHANGED
@@ -37,24 +37,6 @@ def replace_header(head, header_name)
37
37
  head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
38
  end
39
39
 
40
- #############################################################################
41
- #
42
- # Standard tasks
43
- #
44
- #############################################################################
45
-
46
- require 'rspec'
47
- require 'rspec/core/rake_task'
48
-
49
- desc "Run all specs"
50
- task RSpec::Core::RakeTask.new('spec')
51
- task :default => "spec"
52
-
53
- desc 'Run non-integration specs'
54
- RSpec::Core::RakeTask.new('spec:unit') do |t|
55
- t.rspec_opts = "--tag ~integration"
56
- end
57
-
58
40
  #############################################################################
59
41
  #
60
42
  # Custom tasks (add your own tasks here)
@@ -79,7 +61,7 @@ task :release => :build do
79
61
  sh "git tag v#{version}"
80
62
  sh "git push origin master"
81
63
  sh "git push origin v#{version}"
82
- sh "gem push pkg/#{name}-#{version}.gem"
64
+ sh "gem push pkg/#{gem_file}"
83
65
  end
84
66
 
85
67
  desc "Build #{gem_file} into the pkg directory"
data/bin/cloudapp ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cloudapp/credentials'
4
+ require 'cloudapp/service'
5
+
6
+ def service
7
+ CloudApp::Service.using_token Credentials.token
8
+ end
9
+
10
+ def error error
11
+ puts "ERROR: #{error}"
12
+ exit 1
13
+ end
14
+
15
+ def validate_url url
16
+ error 'URL missing' if url.nil?
17
+ error "#{ url } doesn't look like a valid URL" unless url =~ URI.regexp
18
+ end
19
+
20
+ def validate_file file
21
+ error "File doesn't exist" if file.is_a? Errno::ENOENT
22
+ error 'File missing' if file.nil? or file == STDIN
23
+ end
24
+
25
+
26
+ while service.root.unauthorized?
27
+ Credentials.new.ask_for_and_save_credentials
28
+ end
29
+
30
+ link = ARGV.delete('--direct') || ARGV.delete('-d') ? :embed : :canonical
31
+
32
+ case ARGV.shift
33
+ when 'bookmark'
34
+ link = :canonical # No such thing as an embed link for a bookmark.
35
+ url = ARGV.shift
36
+ validate_url url
37
+ puts service.bookmark(url).link(link)
38
+ when 'upload'
39
+ file = ARGF.file rescue $!
40
+ validate_file file
41
+ puts service.upload(file).link(link)
42
+ else
43
+ puts <<EOS
44
+ Usage:
45
+ cloudapp bookmark http://getcloudapp.com
46
+ cloudapp upload screenshot.png
47
+ EOS
48
+ end
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.4'
17
- s.date = '2012-08-25'
16
+ s.version = '2.0.0.beta.5'
17
+ s.date = '2012-12-17'
18
18
  s.rubyforge_project = 'cloudapp'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.require_paths = %w[lib]
35
35
 
36
36
  ## If your gem includes any executables, list them here.
37
- # s.executables = ["cloudapp"]
37
+ s.executables = ["cloudapp"]
38
38
 
39
39
  ## Specify any RDoc options here. You'll want to add your README and
40
40
  ## LICENSE files to the extra_rdoc_files list.
@@ -43,66 +43,35 @@ 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', '~> 0.7.7'
46
47
  s.add_dependency 'leadlight', '~> 0.0.7'
47
48
  s.add_dependency 'typhoeus', '~> 0.3.3'
48
49
 
49
50
  ## List your development dependencies here. Development dependencies are
50
51
  ## those that are only needed during development
51
52
  s.add_development_dependency 'rake'
52
- s.add_development_dependency 'rspec'
53
- s.add_development_dependency 'vcr', '~> 2.0.0'
54
- s.add_development_dependency 'webmock'
53
+ s.add_development_dependency 'ronn'
55
54
 
56
55
  ## Leave this section as-is. It will be automatically generated from the
57
56
  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
58
57
  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
59
58
  # = MANIFEST =
60
59
  s.files = %w[
61
- CHANGELOG.md
62
60
  Gemfile
63
61
  Gemfile.lock
64
62
  MIT-LICENSE
65
63
  README.md
66
64
  Rakefile
65
+ bin/cloudapp
67
66
  cloudapp.gemspec
68
67
  lib/cloudapp.rb
69
- lib/cloudapp/authorized_representation.rb
68
+ lib/cloudapp/authorized.rb
70
69
  lib/cloudapp/collection_json.rb
71
- lib/cloudapp/collection_json/item.rb
72
- lib/cloudapp/collection_json/representation.rb
73
- lib/cloudapp/collection_json/template.rb
74
- lib/cloudapp/collection_json/tint.rb
75
- lib/cloudapp/drop.rb
76
- lib/cloudapp/drop_collection.rb
70
+ lib/cloudapp/credentials.rb
77
71
  lib/cloudapp/service.rb
78
- spec/cassettes/account_token.yml
79
- spec/cassettes/create_bookmark.yml
80
- spec/cassettes/create_bookmark_with_name.yml
81
- spec/cassettes/create_bookmark_with_privacy.yml
82
- spec/cassettes/list_drops.yml
83
- spec/cassettes/list_drops_with_bad_token.yml
84
- spec/cassettes/list_drops_with_filter.yml
85
- spec/cassettes/list_drops_with_limit.yml
86
- spec/cassettes/purge_drops.yml
87
- spec/cassettes/setup_drops.yml
88
- spec/cassettes/token_for_account.yml
89
- spec/cassettes/token_for_account_with_bad_credentials.yml
90
- spec/cassettes/upload_file.yml
91
- spec/cassettes/upload_file_with_name.yml
92
- spec/cassettes/upload_file_with_privacy.yml
93
- spec/cassettes/view_drop.yml
94
- spec/cloudapp/authorized_representation_spec.rb
95
- spec/cloudapp/collection_json/item_spec.rb
96
- spec/cloudapp/collection_json/representation_spec.rb
97
- spec/cloudapp/collection_json/template_spec.rb
98
- spec/cloudapp/drop_collection_spec.rb
99
- spec/cloudapp/drop_spec.rb
100
- spec/cloudapp/service_spec.rb
101
- spec/helper.rb
102
- spec/integration_spec.rb
103
- spec/support/files/favicon.ico
104
- spec/support/stub_class_or_module.rb
105
- spec/support/vcr.rb
72
+ man/cloudapp.1
73
+ man/cloudapp.1.html
74
+ man/cloudapp.1.ronn
106
75
  ]
107
76
  # = MANIFEST =
108
77
 
@@ -0,0 +1,14 @@
1
+ require 'leadlight'
2
+
3
+ module CloudApp
4
+ module Authorized
5
+ Tint = Leadlight::Tint.new 'authorized', status: [ :success, 401 ] do
6
+ extend CloudApp::Authorized::Representation
7
+ end
8
+
9
+ module Representation
10
+ def unauthorized?() __response__.status == 401 end
11
+ def authorized?() not unauthorized? end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,86 @@
1
- require 'cloudapp/collection_json/item'
2
- require 'cloudapp/collection_json/representation'
3
- require 'cloudapp/collection_json/template'
1
+ module CloudApp
2
+ module CollectionJson
3
+ Tint = Leadlight::Tint.new 'collection+json', status: [ :success, 401 ] do
4
+ match_content_type 'application/vnd.collection+json'
5
+ extend CloudApp::CollectionJson::Representation
6
+ end
7
+
8
+ module Representation
9
+ extend Forwardable
10
+
11
+ def self.extended representation
12
+ Collection.new(representation).links.each do |link|
13
+ representation.add_link link.href, link.rel
14
+ end
15
+ end
16
+
17
+ def fill_template attributes
18
+ collection.template.fill(attributes)
19
+ end
20
+
21
+ def collection
22
+ Collection.new(self)
23
+ 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
+ end
85
+ end
86
+ end
@@ -0,0 +1,115 @@
1
+ # Taken from the heroku gem.
2
+ # https://github.com/heroku/heroku/blob/master/lib/heroku/auth.rb
3
+ require 'netrc'
4
+
5
+ class Credentials
6
+ def self.token
7
+ new.credentials.last
8
+ end
9
+
10
+ def netrc_path
11
+ default = Netrc.default_path
12
+ encrypted = default + ".gpg"
13
+ if File.exists?(encrypted)
14
+ encrypted
15
+ else
16
+ default
17
+ end
18
+ end
19
+
20
+ def netrc
21
+ @netrc ||= begin
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
31
+ end
32
+
33
+ def credentials=(credentials) @credentials = credentials end
34
+ def credentials()
35
+ (@credentials ||= read_credentials) || ask_for_and_save_credentials
36
+ end
37
+
38
+ def read_credentials
39
+ # TODO: convert legacy credentials to netrc
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
49
+ end
50
+
51
+ def write_credentials
52
+ FileUtils.mkdir_p(File.dirname(netrc_path))
53
+ FileUtils.touch(netrc_path)
54
+ FileUtils.chmod(0600, netrc_path)
55
+ netrc['api.getcloudapp.com'] = credentials
56
+ netrc.save
57
+ end
58
+
59
+ def delete_credentials
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
106
+
107
+ def echo_on() with_tty { system "stty echo" } end
108
+ def echo_off() with_tty { system "stty -echo" } end
109
+ def with_tty(&block)
110
+ return unless $stdin.isatty
111
+ yield
112
+ rescue
113
+ # fails on windows
114
+ end
115
+ end