tui 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
File without changes
@@ -0,0 +1,3 @@
1
+ require "bundler"
2
+
3
+ Bundler::GemHelper.install_tasks
data/bin/tui ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "commander/import"
4
+ require "tui"
5
+
6
+ program :version, Tui::VERSION
7
+ program :description, Tui::DESCRIPTION
8
+
9
+ command :upload do |c|
10
+ c.syntax = "upload [file]"
11
+ c.description = "Uploads a file"
12
+ c.when_called do |args, options|
13
+ upload = Tui::Upload.new(args[0])
14
+
15
+ say "Requesting authorization"
16
+ request = Tui::Authorization::Request.new(upload)
17
+ authorization = request.perform
18
+
19
+ say "File URL will be #{authorization.short_url}"
20
+
21
+ say "Uploading #{upload.path} (#{upload.size} bytes)"
22
+
23
+ bar = Commander::UI::ProgressBar.new(upload.chunks, { :complete_message => "Uploaded" })
24
+
25
+ upload.perform(authorization) do |bytes_written, bytes_total|
26
+ bar.increment
27
+ true
28
+ end
29
+ end
30
+ end
31
+
32
+ alias_command :u, :upload
@@ -0,0 +1,5 @@
1
+ require "tui/version"
2
+ require "tui/patch"
3
+ require "tui/authorization"
4
+ require "tui/upload"
5
+ require "tui/config"
@@ -0,0 +1,2 @@
1
+ require "tui/authorization/request"
2
+ require "tui/authorization/response"
@@ -0,0 +1,41 @@
1
+ require "json/add/rails"
2
+ require "base64"
3
+
4
+ module Tui
5
+ module Authorization
6
+ class Request
7
+
8
+ def initialize(upload)
9
+ @upload = upload
10
+ end
11
+
12
+ attr_reader :upload
13
+
14
+ def perform
15
+ json_request = upload.as_json.to_json
16
+
17
+ connection = Excon.new("http://#{Tui::Config.host}/files")
18
+
19
+ login = Base64.urlsafe_encode64("#{Tui::Config.username}:#{Tui::Config.password}")
20
+
21
+ headers = {
22
+ "Authorization" => "Basic #{login}",
23
+ "Accept" => "application/json",
24
+ "Content-Type" => "application/json",
25
+ }
26
+
27
+ response = connection.request({
28
+ :method => "POST",
29
+ :headers => headers,
30
+ :expects => 200,
31
+ :body => json_request,
32
+ })
33
+
34
+ json_response = JSON.parse(response.body)
35
+
36
+ Response.new(json_response)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ module Tui
2
+ module Authorization
3
+ class Response
4
+
5
+ attr_reader :short_url, :upload_url, :headers
6
+
7
+ def initialize(json)
8
+ @short_url = json.delete("short_url")
9
+ @upload_url = json.delete("upload_url")
10
+ @headers = json.delete("headers")
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require "singleton"
2
+ require "yaml"
3
+
4
+ module Tui
5
+ class Config
6
+
7
+ include Singleton
8
+
9
+ attr_reader :config
10
+
11
+ class << self
12
+ [:host, :username, :password].each do |attribute|
13
+ define_method attribute do
14
+ instance.config[attribute.to_s]
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def initialize
22
+ path = File.expand_path("~/.tui.yml")
23
+
24
+ @config = File.open(path, "r") do |f|
25
+ YAML.load(f.read)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,155 @@
1
+ module Excon
2
+ class Connection
3
+
4
+ CHUNK_SIZE = 128 * 1024
5
+
6
+ def request_with_uploadprogress(params, &block)
7
+ begin
8
+ # connection has defaults, merge in new params to override
9
+ params = @connection.merge(params)
10
+ params[:headers] = @connection[:headers].merge(params[:headers] || {})
11
+ params[:headers]['Host'] ||= '' << params[:host] << ':' << params[:port]
12
+
13
+ # if path is empty or doesn't start with '/', insert one
14
+ unless params[:path][0, 1] == '/'
15
+ params[:path].insert(0, '/')
16
+ end
17
+
18
+ if params[:mock]
19
+ for stub, response in Excon.stubs
20
+ # all specified non-headers params match and no headers were specified or all specified headers match
21
+ if [stub.keys - [:headers]].all? {|key| stub[key] == params[key] } &&
22
+ (!stub.has_key?(:headers) || stub[:headers].keys.all? {|key| stub[:headers][key] == params[:headers][key]})
23
+ case response
24
+ when Proc
25
+ return Excon::Response.new(response.call(params))
26
+ else
27
+ return Excon::Response.new(response)
28
+ end
29
+ end
30
+ end
31
+ # if we reach here no stubs matched
32
+ raise(Excon::Errors::StubNotFound.new('no stubs matched ' << params.inspect))
33
+ end
34
+
35
+ # start with "METHOD /path"
36
+ request = params[:method].to_s.upcase << ' '
37
+ if @proxy
38
+ request << params[:scheme] << '://' << params[:host] << ':' << params[:port]
39
+ end
40
+ request << params[:path]
41
+
42
+ # add query to path, if there is one
43
+ case params[:query]
44
+ when String
45
+ request << '?' << params[:query]
46
+ when Hash
47
+ request << '?'
48
+ for key, values in params[:query]
49
+ if values.nil?
50
+ request << key.to_s << '&'
51
+ else
52
+ for value in [*values]
53
+ request << key.to_s << '=' << CGI.escape(value.to_s) << '&'
54
+ end
55
+ end
56
+ end
57
+ request.chop! # remove trailing '&'
58
+ end
59
+
60
+ # finish first line with "HTTP/1.1\r\n"
61
+ request << HTTP_1_1
62
+
63
+ # calculate content length and set to handle non-ascii
64
+ unless params[:headers].has_key?('Content-Length')
65
+ params[:headers]['Content-Length'] = case params[:body]
66
+ when File
67
+ params[:body].binmode
68
+ File.size(params[:body])
69
+ when String
70
+ if FORCE_ENC
71
+ params[:body].force_encoding('BINARY')
72
+ end
73
+ params[:body].length
74
+ else
75
+ 0
76
+ end
77
+ end
78
+
79
+ # add headers to request
80
+ for key, values in params[:headers]
81
+ for value in [*values]
82
+ request << key.to_s << ': ' << value.to_s << CR_NL
83
+ end
84
+ end
85
+
86
+ # add additional "\r\n" to indicate end of headers
87
+ request << CR_NL
88
+
89
+ # write out the request, sans body
90
+ socket.write(request)
91
+ socket.flush
92
+
93
+ # PATCH BEGINS
94
+
95
+ bytes_written = 0
96
+ bytes_total = params[:headers]['Content-Length']
97
+
98
+ # write out the body
99
+ if params[:body]
100
+ if params[:body].is_a?(String)
101
+ socket.write(params[:body])
102
+ else
103
+ while chunk = params[:body].read(CHUNK_SIZE)
104
+ socket.write(chunk)
105
+ bytes_written += CHUNK_SIZE
106
+ if params[:upload_progress]
107
+ params[:upload_progress].call([bytes_written, bytes_total].min, bytes_total)
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ # PATCH ENDS
114
+
115
+ # read the response
116
+ response = Excon::Response.parse(socket, params, &block)
117
+
118
+ if response.headers['Connection'] == 'close'
119
+ reset
120
+ end
121
+
122
+ response
123
+ rescue Excon::Errors::StubNotFound => stub_not_found
124
+ raise(stub_not_found)
125
+ rescue => socket_error
126
+ reset
127
+ raise(Excon::Errors::SocketError.new(socket_error))
128
+ end
129
+
130
+ if params.has_key?(:expects) && ![*params[:expects]].include?(response.status)
131
+ reset
132
+ raise(Excon::Errors.status_error(params, response))
133
+ else
134
+ response
135
+ end
136
+
137
+ rescue => request_error
138
+ if params[:idempotent] && [Excon::Errors::SocketError, Excon::Errors::HTTPStatusError].include?(request_error)
139
+ retries_remaining ||= 4
140
+ retries_remaining -= 1
141
+ if retries_remaining > 0
142
+ if params[:body].respond_to?(:pos=)
143
+ params[:body].pos = 0
144
+ end
145
+ retry
146
+ else
147
+ raise(request_error)
148
+ end
149
+ else
150
+ raise(request_error)
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,76 @@
1
+ require "digest/md5"
2
+ require "mime/types"
3
+ require "excon"
4
+
5
+ module Tui
6
+
7
+ class Upload
8
+
9
+ attr_reader :path
10
+ attr_accessor :authorization
11
+
12
+ def initialize(path)
13
+ @path = File.realpath(path)
14
+ end
15
+
16
+ def file
17
+ @file ||= File.new(path, "r")
18
+ end
19
+
20
+ def size
21
+ @size ||= File.size(path)
22
+ end
23
+
24
+ def original_filename
25
+ @original_filename ||= File.basename(path)
26
+ end
27
+
28
+ def md5sum
29
+ @digest ||= Digest::MD5.file(path)
30
+ @md5sum ||= @digest.hexdigest
31
+ end
32
+
33
+ def mime_type
34
+ @mime_type ||= MIME::Types.type_for(path).first.to_s
35
+ end
36
+
37
+ def chunks
38
+ @chunks ||= (size / Excon::Connection::CHUNK_SIZE.to_f).ceil
39
+ end
40
+
41
+ def perform(authorization, &block)
42
+ connection = Excon.new(authorization.upload_url)
43
+
44
+ headers = {
45
+ "User-Agent" => user_agent,
46
+ }.merge(authorization.headers)
47
+
48
+ connection.request_with_uploadprogress(
49
+ :method => "PUT",
50
+ :body => file,
51
+ :headers => headers,
52
+ :expects => 200,
53
+ :upload_progress => block
54
+ )
55
+
56
+ true
57
+ end
58
+
59
+ def as_json
60
+ {
61
+ :original_filename => original_filename,
62
+ :md5sum => md5sum,
63
+ :size => size,
64
+ :mime_type => mime_type
65
+ }
66
+ end
67
+
68
+ private
69
+
70
+ def user_agent
71
+ "tui-#{Tui::VERSION}"
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,4 @@
1
+ module Tui
2
+ VERSION = "0.0.1"
3
+ DESCRIPTION = "A collection of command line utilities to interact with tu.is"
4
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "tui/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "tui"
7
+ s.version = Tui::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Steve Hoeksema"]
10
+ s.email = ["steve@seven.net.nz"]
11
+ s.homepage = ""
12
+ s.summary = Tui::DESCRIPTION
13
+ s.description = Tui::DESCRIPTION
14
+
15
+ s.rubyforge_project = "tui"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "commander", "~> 4.0"
23
+ s.add_dependency "excon", "~> 0.6"
24
+ s.add_dependency "mime-types", "~> 1.1"
25
+ s.add_dependency "json"
26
+
27
+ s.add_development_dependency "rspec", "~> 2.6"
28
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tui
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Steve Hoeksema
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-18 00:00:00 +12:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: commander
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "4.0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: excon
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: "0.6"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: mime-types
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: "1.1"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: json
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rspec
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: "2.6"
69
+ type: :development
70
+ version_requirements: *id005
71
+ description: A collection of command line utilities to interact with tu.is
72
+ email:
73
+ - steve@seven.net.nz
74
+ executables:
75
+ - tui
76
+ extensions: []
77
+
78
+ extra_rdoc_files: []
79
+
80
+ files:
81
+ - .gitignore
82
+ - Gemfile
83
+ - README.md
84
+ - Rakefile
85
+ - bin/tui
86
+ - lib/tui.rb
87
+ - lib/tui/authorization.rb
88
+ - lib/tui/authorization/request.rb
89
+ - lib/tui/authorization/response.rb
90
+ - lib/tui/config.rb
91
+ - lib/tui/patch.rb
92
+ - lib/tui/upload.rb
93
+ - lib/tui/version.rb
94
+ - tui.gemspec
95
+ has_rdoc: true
96
+ homepage: ""
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options: []
101
+
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: "0"
116
+ requirements: []
117
+
118
+ rubyforge_project: tui
119
+ rubygems_version: 1.5.0
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: A collection of command line utilities to interact with tu.is
123
+ test_files: []
124
+