tui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+