iostreams 1.0.0.beta2 → 1.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/io_streams/bzip2/reader.rb +1 -1
- data/lib/io_streams/bzip2/writer.rb +1 -1
- data/lib/io_streams/io_streams.rb +14 -4
- data/lib/io_streams/path.rb +4 -1
- data/lib/io_streams/paths/file.rb +0 -6
- data/lib/io_streams/paths/s3.rb +1 -1
- data/lib/io_streams/paths/sftp.rb +137 -36
- data/lib/io_streams/pgp.rb +7 -4
- data/lib/io_streams/streams.rb +1 -1
- data/lib/io_streams/symmetric_encryption/reader.rb +1 -1
- data/lib/io_streams/symmetric_encryption/writer.rb +2 -2
- data/lib/io_streams/utils.rb +1 -1
- data/lib/io_streams/version.rb +1 -1
- data/lib/io_streams/zip/reader.rb +1 -1
- data/lib/io_streams/zip/writer.rb +1 -1
- data/test/io_streams_test.rb +17 -0
- data/test/paths/file_test.rb +0 -17
- data/test/paths/sftp_test.rb +78 -0
- data/test/stream_test.rb +2 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ecd282f3e3fb3dbbb1cc912458ad80f1286a4921bc36c1797d84ef3863a9b57
|
4
|
+
data.tar.gz: 8f695a423c861f7899d0a20b0be370787486d83f2c2741c03a71fc801ae06e44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f46b8cfccf5b45099465ed4d9169a124fe427d55c0168d03c22fedb73e092d3e41e1f481c955276f04b02377041a9e277bd40c9732e6521cfd368afa7f6b935
|
7
|
+
data.tar.gz: 18cc3db574ed6a61005f4743b74da70f787f740e039dda2127d62a02f150e1b90fc7555f6d64e0bb436258c8ef1d2bab64d7fb49615cb6262b1b5d41469e3211
|
@@ -3,7 +3,7 @@ module IOStreams
|
|
3
3
|
class Reader < IOStreams::Reader
|
4
4
|
# Read from a Bzip2 stream, decompressing the contents as it is read
|
5
5
|
def self.stream(input_stream, **_args)
|
6
|
-
Utils.
|
6
|
+
Utils.load_soft_dependency('rbzip2', 'Bzip2') unless defined?(RBzip2)
|
7
7
|
|
8
8
|
begin
|
9
9
|
io = RBzip2.default_adapter::Decompressor.new(input_stream)
|
@@ -3,7 +3,7 @@ module IOStreams
|
|
3
3
|
class Writer < IOStreams::Writer
|
4
4
|
# Write to a stream, compressing with Bzip2
|
5
5
|
def self.stream(input_stream, original_file_name: nil, **_args)
|
6
|
-
Utils.
|
6
|
+
Utils.load_soft_dependency('rbzip2', 'Bzip2') unless defined?(RBzip2)
|
7
7
|
|
8
8
|
begin
|
9
9
|
io = RBzip2.default_adapter::Compressor.new(input_stream)
|
@@ -99,10 +99,20 @@ module IOStreams
|
|
99
99
|
root(root).join(*elements)
|
100
100
|
end
|
101
101
|
|
102
|
-
# Returns a path to a
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
# Returns a path to a temporary file.
|
103
|
+
# Temporary file is deleted upon block completion if present.
|
104
|
+
#
|
105
|
+
# Parameters:
|
106
|
+
# basename: [String]
|
107
|
+
# Base file name to include in the temp file name.
|
108
|
+
#
|
109
|
+
# extension: [String]
|
110
|
+
# Optional extension to add to the tempfile.
|
111
|
+
#
|
112
|
+
# Example:
|
113
|
+
# IOStreams.temp_file
|
114
|
+
def self.temp_file(basename, extension = "", &block)
|
115
|
+
Utils.temp_file_name(basename, extension) { |file_name| yield(Paths::File.new(file_name).stream(:none)) }
|
106
116
|
end
|
107
117
|
|
108
118
|
# Returns [IOStreams::Paths::File] current or named users home path
|
data/lib/io_streams/path.rb
CHANGED
@@ -3,12 +3,6 @@ require "fileutils"
|
|
3
3
|
module IOStreams
|
4
4
|
module Paths
|
5
5
|
class File < IOStreams::Path
|
6
|
-
# Returns a path to a temporary file.
|
7
|
-
# Temporary file is deleted upon block completion if present.
|
8
|
-
def self.temp_file(basename, extension = "")
|
9
|
-
Utils.temp_file_name(basename, extension) { |file_name| yield(new(file_name).stream(:none)) }
|
10
|
-
end
|
11
|
-
|
12
6
|
# Yields Paths within the current path.
|
13
7
|
#
|
14
8
|
# Examples:
|
data/lib/io_streams/paths/s3.rb
CHANGED
@@ -125,7 +125,7 @@ module IOStreams
|
|
125
125
|
# @option params [String] :object_lock_legal_hold_status
|
126
126
|
# The Legal Hold status that you want to apply to the specified object.
|
127
127
|
def initialize(url, client: nil, **args)
|
128
|
-
Utils.
|
128
|
+
Utils.load_soft_dependency('aws-sdk-s3', 'AWS S3') unless defined?(::Aws::S3::Client)
|
129
129
|
|
130
130
|
uri = URI.parse(url)
|
131
131
|
raise "Invalid URI. Required Format: 's3://<bucket_name>/<key>'" unless uri.scheme == 's3'
|
@@ -1,9 +1,19 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
1
3
|
module IOStreams
|
2
4
|
module Paths
|
3
5
|
class SFTP < IOStreams::Path
|
4
6
|
include SemanticLogger::Loggable if defined?(SemanticLogger)
|
5
7
|
|
6
|
-
|
8
|
+
class << self
|
9
|
+
attr_accessor :sshpass_bin, :sftp_bin, :sshpass_wait_seconds
|
10
|
+
end
|
11
|
+
|
12
|
+
@sftp_bin = 'sftp'
|
13
|
+
@sshpass_bin = 'sshpass'
|
14
|
+
@sshpass_wait_seconds = 5
|
15
|
+
|
16
|
+
attr_reader :hostname, :username, :ssh_options, :url
|
7
17
|
|
8
18
|
# Stream to a remote file over sftp.
|
9
19
|
#
|
@@ -16,40 +26,46 @@ module IOStreams
|
|
16
26
|
# password: [String]
|
17
27
|
# Password for the user.
|
18
28
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# port: [Integer]
|
23
|
-
# Port to connect to at the above host.
|
24
|
-
#
|
25
|
-
# **args
|
26
|
-
# Any other options supported by Net::SSH.start
|
29
|
+
# **ssh_options
|
30
|
+
# Any other options supported by ssh_config.
|
31
|
+
# `man ssh_config` to see all available options.
|
27
32
|
#
|
28
33
|
# Examples:
|
29
34
|
#
|
30
|
-
#
|
31
|
-
# sftp://
|
35
|
+
# # Display the contents of a remote file
|
36
|
+
# IOStreams.path("sftp://test.com/path/file_name.csv", username: "jack", password: "OpenSesame").reader do |io|
|
37
|
+
# puts io.read
|
38
|
+
# end
|
32
39
|
#
|
33
|
-
#
|
40
|
+
# # Full url showing all the optional elements that can be set via the url:
|
34
41
|
# sftp://username:password@hostname:22/path/file_name
|
35
|
-
|
36
|
-
|
37
|
-
|
42
|
+
#
|
43
|
+
# # Display the contents of a remote file, supplying the username and password in the url:
|
44
|
+
# IOStreams.path("sftp://jack:OpenSesame@test.com:22/path/file_name.csv").reader do |io|
|
45
|
+
# puts io.read
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # Display the contents of a remote file, supplying the username and password as arguments:
|
49
|
+
# IOStreams.path("sftp://test.com/path/file_name.csv", username: "jack", password: "OpenSesame").reader do |io|
|
50
|
+
# puts io.read
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # When using the sftp executable use an identity file instead of a password to authenticate:
|
54
|
+
# IOStreams.path("sftp://test.com/path/file_name.csv", username: "jack", IdentityFile: "~/.ssh/private_key").reader do |io|
|
55
|
+
# puts io.read
|
56
|
+
# end
|
57
|
+
def initialize(url, username: nil, password: nil, ruby: true, ssh_options: {})
|
38
58
|
uri = URI.parse(url)
|
39
59
|
raise(ArgumentError, "Invalid URL. Required Format: 'sftp://<host_name>/<file_name>'") unless uri.scheme == 'sftp'
|
40
60
|
|
41
|
-
@hostname
|
42
|
-
@mkdir
|
43
|
-
@username
|
44
|
-
@
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
options[:port] = port || uri.port || 22
|
50
|
-
options[:max_pkt_size] = max_pkt_size
|
51
|
-
options[:password] = password || uri.password
|
52
|
-
@options = options
|
61
|
+
@hostname = uri.hostname
|
62
|
+
@mkdir = false
|
63
|
+
@username = username || uri.user
|
64
|
+
@url = url
|
65
|
+
@password = password || uri.password
|
66
|
+
@port = uri.port || 22
|
67
|
+
@ssh_options = ssh_options
|
68
|
+
|
53
69
|
super(uri.path)
|
54
70
|
end
|
55
71
|
|
@@ -57,6 +73,7 @@ module IOStreams
|
|
57
73
|
url
|
58
74
|
end
|
59
75
|
|
76
|
+
# Note that mkdir is delayed and only executed when the file write is performed.
|
60
77
|
def mkdir
|
61
78
|
@mkdir = true
|
62
79
|
self
|
@@ -74,11 +91,10 @@ module IOStreams
|
|
74
91
|
# Note:
|
75
92
|
# - raises Net::SFTP::StatusException when the file could not be read.
|
76
93
|
def reader(&block)
|
77
|
-
|
78
|
-
|
79
|
-
|
94
|
+
IOStreams.temp_file("iostreams-sftp-reader") do |temp_file|
|
95
|
+
sftp_download(path, temp_file.to_s)
|
96
|
+
temp_file.reader(&block)
|
80
97
|
end
|
81
|
-
result
|
82
98
|
end
|
83
99
|
|
84
100
|
# Write to a file on a remote sftp server.
|
@@ -90,12 +106,97 @@ module IOStreams
|
|
90
106
|
# output.write('Hello World')
|
91
107
|
# end
|
92
108
|
def writer(&block)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
109
|
+
IOStreams.temp_file("iostreams-sftp-writer") do |temp_file|
|
110
|
+
temp_file.writer(&block)
|
111
|
+
sftp_upload(temp_file.to_s, path)
|
112
|
+
temp_file.size
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO: Add #copy_from shortcut to detect when a file is supplied that does not require conversion.
|
117
|
+
|
118
|
+
# Search for files on the remote sftp server that match the provided pattern.
|
119
|
+
#
|
120
|
+
# The pattern matching works like Net::SFTP::Operations::Dir.glob and Dir.glob
|
121
|
+
# Each child also returns attributes that contain the file size, ownership, file dates and other details.
|
122
|
+
#
|
123
|
+
# Example Code:
|
124
|
+
# IOStreams.
|
125
|
+
# path("sftp://sftp.example.org/my_files", username: username, password: password).
|
126
|
+
# each_child('**/*.{csv,txt}') do |input, attributes|
|
127
|
+
# puts "#{input.to_s} #{attributes}"
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# Example Output:
|
131
|
+
# sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group", :permissions=>420, :atime=>1572378136, :mtime=>1572378136, :link_count=>1, :extended=>{}}
|
132
|
+
def each_child(pattern = "*", case_sensitive: true, directories: false, hidden: false)
|
133
|
+
Utils.load_soft_dependency("net-sftp", "SFTP glob capability", "net/sftp") unless defined?(Net::SFTP)
|
134
|
+
|
135
|
+
flags = ::File::FNM_EXTGLOB
|
136
|
+
flags |= ::File::FNM_CASEFOLD unless case_sensitive
|
137
|
+
flags |= ::File::FNM_DOTMATCH if hidden
|
138
|
+
|
139
|
+
Net::SFTP.start(hostname, username, build_ssh_options) do |sftp|
|
140
|
+
sftp.dir.glob(".", pattern, flags) do |path|
|
141
|
+
next if !directories && !path.file?
|
142
|
+
new_path = self.class.new("sftp://#{hostname}/#{path.name}", username: username, password: password, ruby: ruby, **ssh_options)
|
143
|
+
yield(new_path, path.attributes.attributes)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
attr_reader :password
|
152
|
+
|
153
|
+
# Use sftp and sshpass executables to download to a local file
|
154
|
+
def sftp_download(remote_file_name, local_file_name)
|
155
|
+
Open3.popen2e(*sftp_args) do |writer, reader, waith_thr|
|
156
|
+
writer.puts password
|
157
|
+
# Give time for password to be processed and stdin to be passed to sftp process.
|
158
|
+
sleep self.class.sshpass_wait_seconds
|
159
|
+
writer.puts "get #{remote_file_name} #{local_file_name}"
|
160
|
+
writer.puts 'bye'
|
161
|
+
writer.close
|
162
|
+
out = reader.read.chomp
|
163
|
+
raise(Errors::CommunicationsFailure, "Failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}") unless waith_thr.value.success?
|
164
|
+
out
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def sftp_upload(local_file_name, remote_file_name)
|
169
|
+
Open3.popen2e(*sftp_args) do |writer, reader, waith_thr|
|
170
|
+
writer.puts(password) if password
|
171
|
+
# Give time for password to be processed and stdin to be passed to sftp process.
|
172
|
+
sleep self.class.sshpass_wait_seconds
|
173
|
+
writer.puts "put #{local_file_name.inspect} #{remote_file_name.inspect}"
|
174
|
+
writer.puts 'bye'
|
175
|
+
writer.close
|
176
|
+
out = reader.read.chomp
|
177
|
+
raise(Errors::CommunicationsFailure, "Failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}") unless waith_thr.value.success?
|
178
|
+
out
|
97
179
|
end
|
98
|
-
|
180
|
+
end
|
181
|
+
|
182
|
+
def sftp_args
|
183
|
+
args = [self.class.sshpass_bin, self.class.sftp_bin, '-oBatchMode=no']
|
184
|
+
# Force it to use the password when supplied.
|
185
|
+
args << "-oPubkeyAuthentication=no" if password
|
186
|
+
ssh_options.each_pair { |key, value| args << "-o#{key}=#{value}" }
|
187
|
+
args << '-b'
|
188
|
+
args << '-'
|
189
|
+
args << "#{username}@#{hostname}"
|
190
|
+
args
|
191
|
+
end
|
192
|
+
|
193
|
+
def build_ssh_options
|
194
|
+
options = ssh_options.dup
|
195
|
+
options[:logger] ||= self.logger if defined?(SemanticLogger)
|
196
|
+
options[:port] ||= @port
|
197
|
+
options[:max_pkt_size] ||= 65_536
|
198
|
+
options[:password] ||= @password
|
199
|
+
options
|
99
200
|
end
|
100
201
|
end
|
101
202
|
end
|
data/lib/io_streams/pgp.rb
CHANGED
@@ -239,12 +239,15 @@ module IOStreams
|
|
239
239
|
# ascii: [true|false]
|
240
240
|
# Whether to export as ASCII text instead of binary format
|
241
241
|
# Default: true
|
242
|
-
def self.export(email:, ascii: true)
|
242
|
+
def self.export(email:, ascii: true, private: false, passphrase: nil)
|
243
243
|
version_check
|
244
244
|
|
245
|
-
|
246
|
-
loopback
|
247
|
-
command
|
245
|
+
command = "#{executable} "
|
246
|
+
command << '--pinentry-mode loopback ' if pgp_version.to_f >= 2.1
|
247
|
+
command << '--armor ' if ascii
|
248
|
+
command << "--no-tty --batch --passphrase"
|
249
|
+
command << (passphrase ? " #{passphrase} " : "-fd 0 ")
|
250
|
+
command << (private ? "--export-secret-keys #{email}" : "--export #{email}")
|
248
251
|
|
249
252
|
out, err, status = Open3.capture3(command, binmode: true)
|
250
253
|
logger.debug { "IOStreams::Pgp.export: #{command}\n#{err}" } if logger
|
data/lib/io_streams/streams.rb
CHANGED
@@ -117,7 +117,7 @@ module IOStreams
|
|
117
117
|
class_for_stream(type, stream).stream(io_stream, opts, &block)
|
118
118
|
else
|
119
119
|
# Daisy chain multiple streams together
|
120
|
-
last = pipeline.keys.inject(block) { |inner,
|
120
|
+
last = pipeline.keys.inject(block) { |inner, stream_sym| ->(io) { class_for_stream(type, stream_sym).stream(io, pipeline[stream_sym], &inner) } }
|
121
121
|
last.call(io_stream)
|
122
122
|
end
|
123
123
|
end
|
@@ -4,7 +4,7 @@ module IOStreams
|
|
4
4
|
# read from a file/stream using Symmetric Encryption
|
5
5
|
def self.stream(input_stream, **args, &block)
|
6
6
|
unless defined?(SymmetricEncryption)
|
7
|
-
Utils.
|
7
|
+
Utils.load_soft_dependency('symmetric-encryption', '.enc streaming')
|
8
8
|
end
|
9
9
|
|
10
10
|
::SymmetricEncryption::Reader.open(input_stream, **args, &block)
|
@@ -6,7 +6,7 @@ module IOStreams
|
|
6
6
|
# If the input_stream is already compressed consider setting compress: false.
|
7
7
|
def self.stream(input_stream, compress: true, **args, &block)
|
8
8
|
unless defined?(SymmetricEncryption)
|
9
|
-
Utils.
|
9
|
+
Utils.load_soft_dependency('symmetric-encryption', '.enc streaming')
|
10
10
|
end
|
11
11
|
|
12
12
|
::SymmetricEncryption::Writer.open(input_stream, compress: compress, **args, &block)
|
@@ -16,7 +16,7 @@ module IOStreams
|
|
16
16
|
# By default the output stream is compressed unless the file_name extension indicates the file is already compressed.
|
17
17
|
def self.file(file_name, compress: nil, **args, &block)
|
18
18
|
unless defined?(SymmetricEncryption)
|
19
|
-
Utils.
|
19
|
+
Utils.load_soft_dependency('symmetric-encryption', '.enc streaming')
|
20
20
|
end
|
21
21
|
|
22
22
|
::SymmetricEncryption::Writer.open(file_name, compress: compress, **args, &block)
|
data/lib/io_streams/utils.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module Utils
|
3
3
|
# Lazy load dependent gem so that it remains a soft dependency.
|
4
|
-
def self.
|
4
|
+
def self.load_soft_dependency(gem_name, stream_type, require_name = gem_name)
|
5
5
|
require require_name
|
6
6
|
rescue LoadError => e
|
7
7
|
raise(LoadError, "Please install the gem '#{gem_name}' to support #{stream_type}. #{e.message}")
|
data/lib/io_streams/version.rb
CHANGED
@@ -37,7 +37,7 @@ module IOStreams
|
|
37
37
|
# The input stream from the first file found in the zip file is passed
|
38
38
|
# to the supplied block
|
39
39
|
def self.file(file_name, entry_file_name: nil)
|
40
|
-
Utils.
|
40
|
+
Utils.load_soft_dependency('rubyzip', 'Zip', 'zip') unless defined?(::Zip)
|
41
41
|
|
42
42
|
::Zip::InputStream.open(file_name) do |zin|
|
43
43
|
get_entry(zin, entry_file_name) ||
|
@@ -46,7 +46,7 @@ module IOStreams
|
|
46
46
|
end
|
47
47
|
else
|
48
48
|
def self.write_file(file_name, entry_file_name)
|
49
|
-
Utils.
|
49
|
+
Utils.load_soft_dependency('rubyzip', 'Zip', 'zip') unless defined?(::Zip)
|
50
50
|
|
51
51
|
zos = ::Zip::OutputStream.new(file_name)
|
52
52
|
zos.put_next_entry(entry_file_name)
|
data/test/io_streams_test.rb
CHANGED
@@ -61,6 +61,23 @@ module IOStreams
|
|
61
61
|
assert_equal :s3, path
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
describe '.temp_file' do
|
66
|
+
it 'returns value from block' do
|
67
|
+
result = IOStreams.temp_file('base', '.ext') { |_path| 257 }
|
68
|
+
assert_equal 257, result
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'supplies new temp file_name' do
|
72
|
+
path1 = nil
|
73
|
+
path2 = nil
|
74
|
+
IOStreams.temp_file('base', '.ext') { |path| path1 = path }
|
75
|
+
IOStreams.temp_file('base', '.ext') { |path| path2 = path }
|
76
|
+
refute_equal path1.to_s, path2.to_s
|
77
|
+
assert path1.is_a?(IOStreams::Paths::File), path1
|
78
|
+
assert path2.is_a?(IOStreams::Paths::File), path2
|
79
|
+
end
|
80
|
+
end
|
64
81
|
end
|
65
82
|
end
|
66
83
|
end
|
data/test/paths/file_test.rb
CHANGED
@@ -17,23 +17,6 @@ module Paths
|
|
17
17
|
path
|
18
18
|
end
|
19
19
|
|
20
|
-
describe '.temp_file' do
|
21
|
-
it 'returns value from block' do
|
22
|
-
result = IOStreams::Paths::File.temp_file('base', '.ext') { |_path| 257 }
|
23
|
-
assert_equal 257, result
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'supplies new temp file_name' do
|
27
|
-
path1 = nil
|
28
|
-
path2 = nil
|
29
|
-
IOStreams::Paths::File.temp_file('base', '.ext') { |path| path1 = path }
|
30
|
-
IOStreams::Paths::File.temp_file('base', '.ext') { |path| path2 = path }
|
31
|
-
refute_equal path1.to_s, path2.to_s
|
32
|
-
assert path1.is_a?(IOStreams::Paths::File), path1
|
33
|
-
assert path2.is_a?(IOStreams::Paths::File), path2
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
20
|
describe '#each_child' do
|
38
21
|
it 'iterates an empty path' do
|
39
22
|
none = nil
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
module Paths
|
4
|
+
class SFTPTest < Minitest::Test
|
5
|
+
describe IOStreams::Paths::SFTP do
|
6
|
+
before do
|
7
|
+
unless ENV["SFTP_HOSTNAME"]
|
8
|
+
skip "Supply environment variables to test SFTP paths: SFTP_HOSTNAME, SFTP_USERNAME, SFTP_PASSWORD, and optional SFTP_DIR"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:host_name) { ENV["SFTP_HOSTNAME"] }
|
13
|
+
let(:username) { ENV["SFTP_USERNAME"] }
|
14
|
+
let(:password) { ENV["SFTP_PASSWORD"] }
|
15
|
+
let(:ftp_dir) { ENV["SFTP_DIR"] || "iostreams_test"}
|
16
|
+
let(:url) { File.join("sftp://", host_name, ftp_dir) }
|
17
|
+
|
18
|
+
let :file_name do
|
19
|
+
File.join(File.dirname(__FILE__), '..', 'files', 'text.txt')
|
20
|
+
end
|
21
|
+
|
22
|
+
let :raw do
|
23
|
+
File.read(file_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:root_path) { IOStreams::Paths::SFTP.new(url, username: username, password: password, ruby: false) }
|
27
|
+
|
28
|
+
let :existing_path do
|
29
|
+
path = root_path.join('test.txt')
|
30
|
+
path.write(raw)
|
31
|
+
path
|
32
|
+
end
|
33
|
+
|
34
|
+
let :missing_path do
|
35
|
+
root_path.join("unknown_path", "test_file.txt")
|
36
|
+
end
|
37
|
+
|
38
|
+
let :missing_file_path do
|
39
|
+
root_path.join("test_file.txt")
|
40
|
+
end
|
41
|
+
|
42
|
+
let :write_path do
|
43
|
+
root_path.join("writer_test.txt")
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#reader' do
|
47
|
+
it 'reads' do
|
48
|
+
assert_equal raw, existing_path.reader { |io| io.read }
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'fails when the file does not exist' do
|
52
|
+
assert_raises IOStreams::Errors::CommunicationsFailure do
|
53
|
+
missing_file_path.read
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'fails when the directory does not exist' do
|
58
|
+
assert_raises IOStreams::Errors::CommunicationsFailure do
|
59
|
+
missing_path.read
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#writer' do
|
65
|
+
it 'writes' do
|
66
|
+
assert_equal raw.size, write_path.writer { |io| io.write(raw) }
|
67
|
+
assert_equal raw, write_path.read
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'fails when the directory does not exist' do
|
71
|
+
assert_raises IOStreams::Errors::CommunicationsFailure do
|
72
|
+
missing_path.write("Bad path")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/test/stream_test.rb
CHANGED
@@ -47,7 +47,7 @@ class StreamTest < Minitest::Test
|
|
47
47
|
result = IOStreams::Stream.new(io).
|
48
48
|
file_name(multiple_zip_file_name).
|
49
49
|
option(:zip, entry_file_name: 'test.json').
|
50
|
-
|
50
|
+
read
|
51
51
|
assert_equal contents_test_json, result
|
52
52
|
end
|
53
53
|
end
|
@@ -56,7 +56,7 @@ class StreamTest < Minitest::Test
|
|
56
56
|
File.open(zip_gz_file_name, 'rb') do |io|
|
57
57
|
result = IOStreams::Stream.new(io).
|
58
58
|
file_name(zip_gz_file_name).
|
59
|
-
|
59
|
+
read
|
60
60
|
assert_equal contents_test_txt, result
|
61
61
|
end
|
62
62
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iostreams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- test/paths/http_test.rb
|
95
95
|
- test/paths/matcher_test.rb
|
96
96
|
- test/paths/s3_test.rb
|
97
|
+
- test/paths/sftp_test.rb
|
97
98
|
- test/pgp_reader_test.rb
|
98
99
|
- test/pgp_test.rb
|
99
100
|
- test/pgp_writer_test.rb
|
@@ -147,6 +148,7 @@ test_files:
|
|
147
148
|
- test/stream_test.rb
|
148
149
|
- test/paths/matcher_test.rb
|
149
150
|
- test/paths/s3_test.rb
|
151
|
+
- test/paths/sftp_test.rb
|
150
152
|
- test/paths/file_test.rb
|
151
153
|
- test/paths/http_test.rb
|
152
154
|
- test/record_reader_test.rb
|