tusc 0.6.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.
- checksums.yaml +7 -0
- data/.editorconfig +26 -0
- data/.gitignore +12 -0
- data/.tool-versions +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +83 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +21 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/rackup +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/config.ru +8 -0
- data/lib/core_ext/object/blank.rb +144 -0
- data/lib/core_ext/string/truncate.rb +44 -0
- data/lib/http_service.rb +87 -0
- data/lib/tusc.rb +56 -0
- data/lib/tusc/creation_request.rb +45 -0
- data/lib/tusc/creation_response.rb +26 -0
- data/lib/tusc/offset_request.rb +42 -0
- data/lib/tusc/offset_response.rb +20 -0
- data/lib/tusc/responsorial.rb +15 -0
- data/lib/tusc/upload_request.rb +64 -0
- data/lib/tusc/upload_response.rb +37 -0
- data/lib/tusc/uploader.rb +141 -0
- data/lib/tusc/version.rb +3 -0
- data/tusc.gemspec +48 -0
- metadata +202 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'responsorial'
|
2
|
+
|
3
|
+
# Parses the response from a CreationRequest
|
4
|
+
#
|
5
|
+
# Surfacing important info:
|
6
|
+
# - location -> upload_uri
|
7
|
+
class TusClient::CreationResponse
|
8
|
+
include TusClient::Responsorial
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def location
|
14
|
+
raw.header && raw.header['Location']
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
result = status_code == 201
|
19
|
+
result &= (location =~ URI::ABS_URI) unless location.blank?
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def upload_uri
|
24
|
+
URI.parse(location) unless location.blank?
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative '../http_service'
|
2
|
+
require_relative 'offset_response'
|
3
|
+
|
4
|
+
# Asks tus server for appriopriate offset
|
5
|
+
# for specific file, via upload_url
|
6
|
+
class TusClient::OffsetRequest
|
7
|
+
attr_reader :extra_headers, :upload_uri
|
8
|
+
def initialize(upload_url:, extra_headers: {})
|
9
|
+
upload_uri = upload_url.is_a?(URI) ? upload_url : URI.parse(upload_url)
|
10
|
+
unless upload_uri.is_a?(URI::HTTP) && !upload_uri.host.nil?
|
11
|
+
raise URI::InvalidURIError, "Could NOT parse host from #{upload_url.inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
@upload_uri = upload_uri
|
15
|
+
@extra_headers = extra_headers
|
16
|
+
end
|
17
|
+
|
18
|
+
def headers
|
19
|
+
{
|
20
|
+
'Tus-Resumable' => supported_tus_resumable_version
|
21
|
+
}.merge(extra_headers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ||= TusClient.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
# Retrieves offset via a HEAD request to the tus server
|
29
|
+
# Returns the offset (in a OffsetResponse)
|
30
|
+
def perform
|
31
|
+
response = TusClient::HttpService.head(
|
32
|
+
uri: upload_uri,
|
33
|
+
headers: headers,
|
34
|
+
logger: logger
|
35
|
+
)
|
36
|
+
TusClient::OffsetResponse.new(response)
|
37
|
+
end
|
38
|
+
|
39
|
+
def supported_tus_resumable_version
|
40
|
+
'1.0.0'
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'responsorial'
|
2
|
+
|
3
|
+
# Parses the response from an OffsetRequest
|
4
|
+
#
|
5
|
+
# Surfacing important info:
|
6
|
+
# - offset
|
7
|
+
class TusClient::OffsetResponse
|
8
|
+
include TusClient::Responsorial
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def offset
|
14
|
+
raw.header['Upload-Offset'].to_i # nil.to_i == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
status_code == 204 && (offset >= 0)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../http_service'
|
2
|
+
require_relative 'upload_response'
|
3
|
+
|
4
|
+
module TusClient
|
5
|
+
# Asks tus server to upload a chunk of a file, from a specific offset
|
6
|
+
class UploadRequest
|
7
|
+
attr_reader :chunk_to_upload, :extra_headers, :file_size, :offset, :upload_uri
|
8
|
+
|
9
|
+
def initialize(upload_uri:, chunk_to_upload:, offset:, file_size:, extra_headers: {})
|
10
|
+
upload_uri = upload_uri.is_a?(URI) ? upload_uri : URI.parse(upload_uri)
|
11
|
+
unless upload_uri.is_a?(URI::HTTP) && !upload_uri.host.nil?
|
12
|
+
raise URI::InvalidURIError, "Could NOT parse host from #{upload_uri.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
unless chunk_to_upload.is_a?(String)
|
16
|
+
raise ArgumentError, "chunk_to_upload must be a String, found (#{chunk_to_upload}:#{chunk_to_upload.class.name})"
|
17
|
+
end
|
18
|
+
|
19
|
+
unless file_size.is_a?(Integer)
|
20
|
+
raise ArgumentError, "file_size must be an Integer, found (#{file_size}:#{file_size.class.name})"
|
21
|
+
end
|
22
|
+
|
23
|
+
unless offset.is_a?(Integer)
|
24
|
+
raise ArgumentError, "offset must be an Integer, found (#{offset}:#{offset.class.name})"
|
25
|
+
end
|
26
|
+
|
27
|
+
@chunk_to_upload = chunk_to_upload
|
28
|
+
@extra_headers = extra_headers
|
29
|
+
@file_size = file_size
|
30
|
+
@offset = offset
|
31
|
+
@upload_uri = upload_uri
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_content_type
|
35
|
+
'application/offset+octet-stream'
|
36
|
+
end
|
37
|
+
|
38
|
+
def headers
|
39
|
+
{
|
40
|
+
'Content-Type' => default_content_type,
|
41
|
+
'Tus-Resumable' => supported_tus_resumable_version,
|
42
|
+
'Upload-Offset' => offset.to_s
|
43
|
+
}.merge(extra_headers)
|
44
|
+
end
|
45
|
+
|
46
|
+
def logger
|
47
|
+
@logger ||= TusClient.logger
|
48
|
+
end
|
49
|
+
|
50
|
+
def perform
|
51
|
+
response = TusClient::HttpService.patch(
|
52
|
+
uri: upload_uri,
|
53
|
+
headers: headers,
|
54
|
+
body: chunk_to_upload,
|
55
|
+
logger: logger
|
56
|
+
)
|
57
|
+
TusClient::UploadResponse.new(response, file_size)
|
58
|
+
end
|
59
|
+
|
60
|
+
def supported_tus_resumable_version
|
61
|
+
'1.0.0'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'responsorial'
|
2
|
+
|
3
|
+
# Parses the response from an UploadRequest
|
4
|
+
#
|
5
|
+
# Surfacing important info:
|
6
|
+
# - complete?
|
7
|
+
# - incomplete?
|
8
|
+
# - offset
|
9
|
+
# - success?
|
10
|
+
class TusClient::UploadResponse
|
11
|
+
include TusClient::Responsorial
|
12
|
+
attr_reader :file_size
|
13
|
+
def initialize(response, file_size)
|
14
|
+
@response = response
|
15
|
+
@file_size = file_size
|
16
|
+
end
|
17
|
+
|
18
|
+
def complete?
|
19
|
+
offset >= file_size
|
20
|
+
end
|
21
|
+
|
22
|
+
def incomplete?
|
23
|
+
offset < file_size
|
24
|
+
end
|
25
|
+
|
26
|
+
def offset
|
27
|
+
raw.header['Upload-Offset'].to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def successful_status_codes
|
31
|
+
[200, 204]
|
32
|
+
end
|
33
|
+
|
34
|
+
def success?
|
35
|
+
successful_status_codes.include?(status_code)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require_relative '../core_ext/string/truncate'
|
3
|
+
require_relative 'upload_request'
|
4
|
+
require_relative 'upload_response'
|
5
|
+
require_relative 'offset_request'
|
6
|
+
|
7
|
+
# Uploads a file to a tus server
|
8
|
+
class TusClient::Uploader
|
9
|
+
attr_reader :extra_headers, :io, :upload_url
|
10
|
+
|
11
|
+
def self.chunk_size
|
12
|
+
10 * TusClient::MEGABYTE
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.default_content_type
|
16
|
+
'application/offset+octet-stream'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_file_path(file_path:, upload_url:, extra_headers: {})
|
20
|
+
raise ArgumentError, "file_path is required (#{file_path})" if file_path.blank?
|
21
|
+
|
22
|
+
file_pathname = Pathname.new(file_path)
|
23
|
+
raise ArgumentError, "Passed file does NOT exist: #{file_pathname.inspect}" unless file_pathname.exist?
|
24
|
+
|
25
|
+
new(io: file_pathname.open, upload_url: upload_url, extra_headers: extra_headers)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(io:, upload_url:, extra_headers: {})
|
29
|
+
# fail ArgumentError.new("io must be an IO object") unless io.is_a?(IO) || io.is_a?(StringIO)
|
30
|
+
%i[size read close].each do |required_method|
|
31
|
+
raise ArgumentError, "io must respond to ##{required_method}" unless io.respond_to?(required_method)
|
32
|
+
end
|
33
|
+
raise ArgumentError, "upload_url is required (#{upload_url})" if upload_url.blank?
|
34
|
+
raise ArgumentError, "upload_url must be a valid url (#{upload_url})" unless upload_url =~ URI::ABS_URI
|
35
|
+
|
36
|
+
@io = io
|
37
|
+
@upload_url = upload_url
|
38
|
+
@extra_headers = extra_headers
|
39
|
+
end
|
40
|
+
|
41
|
+
# Optionally, if the client wants to delete an upload because it won’t be needed anymore,
|
42
|
+
# a DELETE request can be sent to the upload URL.
|
43
|
+
# After this, the upload can be cleaned up by the server and resuming the upload is not possible anymore.
|
44
|
+
def cancel_upload
|
45
|
+
delete upload_url
|
46
|
+
end
|
47
|
+
|
48
|
+
def chunk_size
|
49
|
+
@chunk_size ||= TusClient::Uploader.chunk_size
|
50
|
+
end
|
51
|
+
|
52
|
+
def content_type
|
53
|
+
# We used to detect content type, using MimeMagic,
|
54
|
+
# but the videos only uploaded if we used the default (octet-stream)
|
55
|
+
# So... we just use the default
|
56
|
+
default_content_type
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_content_type
|
60
|
+
self.class.default_content_type
|
61
|
+
end
|
62
|
+
|
63
|
+
def headers
|
64
|
+
{
|
65
|
+
'Content-Type' => content_type,
|
66
|
+
'Tus-Resumable' => tus_resumable_version,
|
67
|
+
'Upload-Offset' => 0.to_s
|
68
|
+
}.merge(extra_headers)
|
69
|
+
end
|
70
|
+
|
71
|
+
def logger
|
72
|
+
@logger ||= TusClient.logger
|
73
|
+
end
|
74
|
+
|
75
|
+
def offset_requester
|
76
|
+
@offset_requester ||= TusClient::OffsetRequest.new(upload_url: upload_uri, extra_headers: extra_headers)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Performs the upload, one chunk at a time
|
80
|
+
# Starts by asking for the currnet offset
|
81
|
+
# Each follow-on request, returns the current offset.
|
82
|
+
def perform
|
83
|
+
# TODO: asynch?
|
84
|
+
logger.debug { 'Starting upload...' }
|
85
|
+
|
86
|
+
offset = retrieve_offset
|
87
|
+
begin
|
88
|
+
logger.debug { ['Reading io...', { size: size, offset: offset, chunk_size: chunk_size }] }
|
89
|
+
io.pos = offset
|
90
|
+
chunk = io.read(chunk_size)
|
91
|
+
upload_response = push_chunk(chunk, offset)
|
92
|
+
unless upload_response.success?
|
93
|
+
raise "Issue uploading file: #{{ code: upload_response.status_code, message: upload_response.body }}."
|
94
|
+
end
|
95
|
+
|
96
|
+
offset = upload_response.offset
|
97
|
+
end while upload_response.success? && upload_response.incomplete?
|
98
|
+
|
99
|
+
io.close
|
100
|
+
upload_response
|
101
|
+
end
|
102
|
+
|
103
|
+
def push_chunk(chunk, offset)
|
104
|
+
push_chunk_via_upload_request(chunk, offset)
|
105
|
+
end
|
106
|
+
|
107
|
+
def push_chunk_via_upload_request(chunk, offset)
|
108
|
+
upload_request = TusClient::UploadRequest.new(
|
109
|
+
upload_uri: upload_url,
|
110
|
+
chunk_to_upload: chunk,
|
111
|
+
offset: offset,
|
112
|
+
file_size: size,
|
113
|
+
extra_headers: extra_headers
|
114
|
+
)
|
115
|
+
upload_request.perform
|
116
|
+
end
|
117
|
+
|
118
|
+
def retrieve_offset
|
119
|
+
offset_requester.perform.offset
|
120
|
+
end
|
121
|
+
|
122
|
+
def size
|
123
|
+
@size ||= io.size
|
124
|
+
end
|
125
|
+
|
126
|
+
def tus_resumable_version
|
127
|
+
'1.0.0'
|
128
|
+
end
|
129
|
+
|
130
|
+
def upload_incomplete
|
131
|
+
retrieve_offset < size
|
132
|
+
end
|
133
|
+
|
134
|
+
def upload_uri
|
135
|
+
@upload_uri ||= URI.parse(upload_url)
|
136
|
+
end
|
137
|
+
|
138
|
+
def video_url
|
139
|
+
upload_response.fetch('uri')
|
140
|
+
end
|
141
|
+
end
|
data/lib/tusc/version.rb
ADDED
data/tusc.gemspec
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'tusc/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'tusc'
|
7
|
+
spec.version = TusClient::VERSION
|
8
|
+
spec.authors = ['Matt Scilipoti']
|
9
|
+
spec.email = ['matt@scilipoti.name']
|
10
|
+
|
11
|
+
spec.summary = 'Ruby client for [tus](https://tus.io/): open protocol for resumable file uploads'
|
12
|
+
# spec.description = %q{TODO: Write a longer description or delete this line.}
|
13
|
+
spec.homepage = 'https://github.com/mattscilipoti/tusc.rb'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata['allowed_push_host'] = 'https://rubygems.org/???'
|
20
|
+
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
22
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mattscilipoti/tusc.rb'
|
23
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
24
|
+
else
|
25
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
26
|
+
'public gem pushes.'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
30
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
32
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
end
|
34
|
+
spec.bindir = 'exe'
|
35
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
+
spec.require_paths = ['lib']
|
37
|
+
|
38
|
+
spec.add_dependency 'ougai', '~> 1.8' # for structured logging, child log per file
|
39
|
+
|
40
|
+
spec.add_development_dependency 'amazing_print', '~> 1.2'
|
41
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
42
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.9'
|
43
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
44
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
45
|
+
spec.add_development_dependency 'tus-server', '~> 2.3'
|
46
|
+
spec.add_development_dependency 'webmock', '~> 3.8'
|
47
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tusc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Scilipoti
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ougai
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: amazing_print
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.17'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.17'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tus-server
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.8'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.8'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: yard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.9'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.9'
|
139
|
+
description:
|
140
|
+
email:
|
141
|
+
- matt@scilipoti.name
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".editorconfig"
|
147
|
+
- ".gitignore"
|
148
|
+
- ".tool-versions"
|
149
|
+
- ".travis.yml"
|
150
|
+
- CHANGELOG.md
|
151
|
+
- CODE_OF_CONDUCT.md
|
152
|
+
- Gemfile
|
153
|
+
- Gemfile.lock
|
154
|
+
- LICENSE.txt
|
155
|
+
- README.md
|
156
|
+
- Rakefile
|
157
|
+
- bin/console
|
158
|
+
- bin/rackup
|
159
|
+
- bin/rspec
|
160
|
+
- bin/setup
|
161
|
+
- config.ru
|
162
|
+
- lib/core_ext/object/blank.rb
|
163
|
+
- lib/core_ext/string/truncate.rb
|
164
|
+
- lib/http_service.rb
|
165
|
+
- lib/tusc.rb
|
166
|
+
- lib/tusc/creation_request.rb
|
167
|
+
- lib/tusc/creation_response.rb
|
168
|
+
- lib/tusc/offset_request.rb
|
169
|
+
- lib/tusc/offset_response.rb
|
170
|
+
- lib/tusc/responsorial.rb
|
171
|
+
- lib/tusc/upload_request.rb
|
172
|
+
- lib/tusc/upload_response.rb
|
173
|
+
- lib/tusc/uploader.rb
|
174
|
+
- lib/tusc/version.rb
|
175
|
+
- tusc.gemspec
|
176
|
+
homepage: https://github.com/mattscilipoti/tusc.rb
|
177
|
+
licenses:
|
178
|
+
- MIT
|
179
|
+
metadata:
|
180
|
+
homepage_uri: https://github.com/mattscilipoti/tusc.rb
|
181
|
+
source_code_uri: https://github.com/mattscilipoti/tusc.rb
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubygems_version: 3.1.2
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: 'Ruby client for [tus](https://tus.io/): open protocol for resumable file
|
201
|
+
uploads'
|
202
|
+
test_files: []
|