iostreams 1.0.0.beta2 → 1.0.0.beta3
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 +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
|