iostreams 1.2.1 → 1.6.2
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/README.md +19 -4
- data/lib/io_streams/builder.rb +27 -10
- data/lib/io_streams/bzip2/reader.rb +3 -3
- data/lib/io_streams/bzip2/writer.rb +3 -3
- data/lib/io_streams/deprecated.rb +1 -1
- data/lib/io_streams/encode/reader.rb +1 -3
- data/lib/io_streams/encode/writer.rb +1 -1
- data/lib/io_streams/errors.rb +22 -0
- data/lib/io_streams/io_streams.rb +1 -5
- data/lib/io_streams/line/reader.rb +28 -16
- data/lib/io_streams/path.rb +3 -1
- data/lib/io_streams/paths/file.rb +4 -4
- data/lib/io_streams/paths/http.rb +6 -3
- data/lib/io_streams/paths/s3.rb +30 -8
- data/lib/io_streams/paths/sftp.rb +34 -13
- data/lib/io_streams/pgp.rb +84 -71
- data/lib/io_streams/stream.rb +78 -12
- data/lib/io_streams/tabular.rb +28 -27
- data/lib/io_streams/tabular/header.rb +14 -12
- data/lib/io_streams/tabular/parser/csv.rb +4 -2
- data/lib/io_streams/tabular/parser/fixed.rb +166 -26
- data/lib/io_streams/tabular/utility/csv_row.rb +1 -4
- data/lib/io_streams/utils.rb +4 -4
- data/lib/io_streams/version.rb +1 -1
- data/lib/io_streams/zip/reader.rb +1 -1
- data/test/builder_test.rb +29 -0
- data/test/bzip2_writer_test.rb +6 -4
- data/test/deprecated_test.rb +2 -0
- data/test/files/test.psv +4 -0
- data/test/files/unclosed_quote_large_test.csv +1658 -0
- data/test/files/unclosed_quote_test2.csv +3 -0
- data/test/io_streams_test.rb +2 -2
- data/test/line_reader_test.rb +30 -4
- data/test/paths/file_test.rb +1 -1
- data/test/paths/s3_test.rb +3 -3
- data/test/paths/sftp_test.rb +4 -4
- data/test/pgp_test.rb +54 -4
- data/test/pgp_writer_test.rb +3 -3
- data/test/stream_test.rb +174 -8
- data/test/tabular_test.rb +100 -40
- data/test/test_helper.rb +1 -1
- metadata +47 -42
@@ -26,12 +26,13 @@ module IOStreams
|
|
26
26
|
include SemanticLogger::Loggable if defined?(SemanticLogger)
|
27
27
|
|
28
28
|
class << self
|
29
|
-
attr_accessor :sshpass_bin, :sftp_bin, :sshpass_wait_seconds
|
29
|
+
attr_accessor :sshpass_bin, :sftp_bin, :sshpass_wait_seconds, :before_password_wait_seconds
|
30
30
|
end
|
31
31
|
|
32
|
-
@sftp_bin
|
33
|
-
@sshpass_bin
|
34
|
-
@
|
32
|
+
@sftp_bin = "sftp"
|
33
|
+
@sshpass_bin = "sshpass"
|
34
|
+
@before_password_wait_seconds = 2
|
35
|
+
@sshpass_wait_seconds = 5
|
35
36
|
|
36
37
|
attr_reader :hostname, :username, :ssh_options, :url, :port
|
37
38
|
|
@@ -71,7 +72,9 @@ module IOStreams
|
|
71
72
|
# end
|
72
73
|
#
|
73
74
|
# # When using the sftp executable use an identity file instead of a password to authenticate:
|
74
|
-
# IOStreams.path("sftp://test.com/path/file_name.csv",
|
75
|
+
# IOStreams.path("sftp://test.com/path/file_name.csv",
|
76
|
+
# username: "jack",
|
77
|
+
# ssh_options: {IdentityFile: "~/.ssh/private_key"}).reader do |io|
|
75
78
|
# puts io.read
|
76
79
|
# end
|
77
80
|
def initialize(url, username: nil, password: nil, ssh_options: {})
|
@@ -122,7 +125,8 @@ module IOStreams
|
|
122
125
|
# end
|
123
126
|
#
|
124
127
|
# Example Output:
|
125
|
-
# sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group",
|
128
|
+
# sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group",
|
129
|
+
# :permissions=>420, :atime=>1572378136, :mtime=>1572378136, :link_count=>1, :extended=>{}}
|
126
130
|
def each_child(pattern = "*", case_sensitive: true, directories: false, hidden: false)
|
127
131
|
Utils.load_soft_dependency("net-sftp", "SFTP glob capability", "net/sftp") unless defined?(Net::SFTP)
|
128
132
|
|
@@ -165,15 +169,23 @@ module IOStreams
|
|
165
169
|
with_sftp_args do |args|
|
166
170
|
Open3.popen2e(*args) do |writer, reader, waith_thr|
|
167
171
|
begin
|
172
|
+
# Give time for remote sftp server to get ready to accept the password.
|
173
|
+
sleep self.class.before_password_wait_seconds
|
174
|
+
|
168
175
|
writer.puts password
|
176
|
+
|
169
177
|
# Give time for password to be processed and stdin to be passed to sftp process.
|
170
178
|
sleep self.class.sshpass_wait_seconds
|
179
|
+
|
171
180
|
writer.puts "get #{remote_file_name} #{local_file_name}"
|
172
181
|
writer.puts "bye"
|
173
182
|
writer.close
|
174
183
|
out = reader.read.chomp
|
175
184
|
unless waith_thr.value.success?
|
176
|
-
raise(
|
185
|
+
raise(
|
186
|
+
Errors::CommunicationsFailure,
|
187
|
+
"Download failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}"
|
188
|
+
)
|
177
189
|
end
|
178
190
|
|
179
191
|
out
|
@@ -183,7 +195,10 @@ module IOStreams
|
|
183
195
|
rescue StandardError
|
184
196
|
nil
|
185
197
|
end
|
186
|
-
raise(
|
198
|
+
raise(
|
199
|
+
Errors::CommunicationsFailure,
|
200
|
+
"Download failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}"
|
201
|
+
)
|
187
202
|
end
|
188
203
|
end
|
189
204
|
end
|
@@ -201,7 +216,10 @@ module IOStreams
|
|
201
216
|
writer.close
|
202
217
|
out = reader.read.chomp
|
203
218
|
unless waith_thr.value.success?
|
204
|
-
raise(
|
219
|
+
raise(
|
220
|
+
Errors::CommunicationsFailure,
|
221
|
+
"Upload failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}"
|
222
|
+
)
|
205
223
|
end
|
206
224
|
|
207
225
|
out
|
@@ -211,7 +229,10 @@ module IOStreams
|
|
211
229
|
rescue StandardError
|
212
230
|
nil
|
213
231
|
end
|
214
|
-
raise(
|
232
|
+
raise(
|
233
|
+
Errors::CommunicationsFailure,
|
234
|
+
"Upload failed calling #{self.class.sftp_bin} via #{self.class.sshpass_bin}: #{out}"
|
235
|
+
)
|
215
236
|
end
|
216
237
|
end
|
217
238
|
end
|
@@ -256,9 +277,9 @@ module IOStreams
|
|
256
277
|
end
|
257
278
|
|
258
279
|
def build_ssh_options
|
259
|
-
options
|
260
|
-
options[:logger]
|
261
|
-
options[:port]
|
280
|
+
options = ssh_options.dup
|
281
|
+
options[:logger] ||= logger if defined?(SemanticLogger)
|
282
|
+
options[:port] ||= port
|
262
283
|
options[:max_pkt_size] ||= 65_536
|
263
284
|
options[:password] ||= @password
|
264
285
|
options
|
data/lib/io_streams/pgp.rb
CHANGED
@@ -46,7 +46,15 @@ module IOStreams
|
|
46
46
|
# `SecureRandom.urlsafe_base64(128)`
|
47
47
|
#
|
48
48
|
# See `man gpg` for the remaining options
|
49
|
-
def self.generate_key(name:,
|
49
|
+
def self.generate_key(name:,
|
50
|
+
email:,
|
51
|
+
comment: nil,
|
52
|
+
passphrase:,
|
53
|
+
key_type: "RSA",
|
54
|
+
key_length: 4096,
|
55
|
+
subkey_type: "RSA",
|
56
|
+
subkey_length: key_length,
|
57
|
+
expire_date: nil)
|
50
58
|
version_check
|
51
59
|
params = ""
|
52
60
|
params << "Key-Type: #{key_type}\n" if key_type
|
@@ -63,13 +71,13 @@ module IOStreams
|
|
63
71
|
|
64
72
|
out, err, status = Open3.capture3(command, binmode: true, stdin_data: params)
|
65
73
|
logger&.debug { "IOStreams::Pgp.generate_key: #{command}\n#{params}\n#{err}#{out}" }
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
|
75
|
+
raise(Pgp::Failure, "GPG Failed to generate key: #{err}#{out}") unless status.success?
|
76
|
+
|
77
|
+
match = err.match(/gpg: key ([0-9A-F]+)\s+/)
|
78
|
+
return unless match
|
79
|
+
|
80
|
+
match[1]
|
73
81
|
end
|
74
82
|
|
75
83
|
# Delete all private and public keys for a particular email.
|
@@ -77,7 +85,8 @@ module IOStreams
|
|
77
85
|
# Returns false if no key was found.
|
78
86
|
# Raises an exception if it fails to delete the key.
|
79
87
|
#
|
80
|
-
# email: [String]
|
88
|
+
# email: [String] Optional email address for the key.
|
89
|
+
# key_id: [String] Optional id for the key.
|
81
90
|
#
|
82
91
|
# public: [true|false]
|
83
92
|
# Whether to delete the public key
|
@@ -86,12 +95,12 @@ module IOStreams
|
|
86
95
|
# private: [true|false]
|
87
96
|
# Whether to delete the private key
|
88
97
|
# Default: false
|
89
|
-
def self.delete_keys(email
|
98
|
+
def self.delete_keys(email: nil, key_id: nil, public: true, private: false)
|
90
99
|
version_check
|
91
100
|
method_name = pgp_version.to_f >= 2.2 ? :delete_public_or_private_keys : :delete_public_or_private_keys_v1
|
92
101
|
status = false
|
93
|
-
status = send(method_name, email: email, private: true) if private
|
94
|
-
status = send(method_name, email: email, private: false) if public
|
102
|
+
status = send(method_name, email: email, key_id: key_id, private: true) if private
|
103
|
+
status = send(method_name, email: email, key_id: key_id, private: false) if public
|
95
104
|
status
|
96
105
|
end
|
97
106
|
|
@@ -102,11 +111,6 @@ module IOStreams
|
|
102
111
|
!list_keys(email: email, key_id: key_id, private: private).empty?
|
103
112
|
end
|
104
113
|
|
105
|
-
# Deprecated
|
106
|
-
def self.has_key?(**args)
|
107
|
-
key?(**args)
|
108
|
-
end
|
109
|
-
|
110
114
|
# Returns [Array<Hash>] the list of keys.
|
111
115
|
# Each Hash consists of:
|
112
116
|
# key_length: [Integer]
|
@@ -150,16 +154,15 @@ module IOStreams
|
|
150
154
|
|
151
155
|
out, err, status = Open3.capture3(command, binmode: true, stdin_data: key)
|
152
156
|
logger&.debug { "IOStreams::Pgp.key_info: #{command}\n#{err}#{out}" }
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
157
|
+
|
158
|
+
raise(Pgp::Failure, "GPG Failed extracting key details: #{err} #{out}") unless status.success? && out.length.positive?
|
159
|
+
|
160
|
+
# Sample Output:
|
161
|
+
#
|
162
|
+
# pub 4096R/3A5456F5 2017-06-07
|
163
|
+
# uid Joe Bloggs <j@bloggs.net>
|
164
|
+
# sub 4096R/2C9B240B 2017-06-07
|
165
|
+
parse_list_output(out)
|
163
166
|
end
|
164
167
|
|
165
168
|
# Returns [String] containing all the public keys for the supplied email address.
|
@@ -181,11 +184,10 @@ module IOStreams
|
|
181
184
|
|
182
185
|
out, err, status = Open3.capture3(command, binmode: true)
|
183
186
|
logger&.debug { "IOStreams::Pgp.export: #{command}\n#{err}" }
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
187
|
+
|
188
|
+
raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err}") unless status.success? && out.length.positive?
|
189
|
+
|
190
|
+
out
|
189
191
|
end
|
190
192
|
|
191
193
|
# Imports the supplied public/private key
|
@@ -226,7 +228,7 @@ module IOStreams
|
|
226
228
|
err.each_line do |line|
|
227
229
|
if line =~ /secret key imported/
|
228
230
|
secret = true
|
229
|
-
elsif match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/)
|
231
|
+
elsif (match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/))
|
230
232
|
results << {
|
231
233
|
key_id: match[1].to_s.strip,
|
232
234
|
private: secret,
|
@@ -251,12 +253,15 @@ module IOStreams
|
|
251
253
|
def self.import_and_trust(key:)
|
252
254
|
raise(ArgumentError, "Key cannot be empty") if key.nil? || (key == "")
|
253
255
|
|
254
|
-
|
255
|
-
|
256
|
+
key_info = key_info(key: key).last
|
257
|
+
|
258
|
+
email = key_info.fetch(:email, nil)
|
259
|
+
key_id = key_info.fetch(:key_id, nil)
|
260
|
+
raise(ArgumentError, "Recipient email or key id cannot be extracted from supplied key") unless email || key_id
|
256
261
|
|
257
262
|
import(key: key)
|
258
|
-
set_trust(email: email)
|
259
|
-
email
|
263
|
+
set_trust(email: email, key_id: key_id)
|
264
|
+
email || key_id
|
260
265
|
end
|
261
266
|
|
262
267
|
# Set the trust level for an existing key.
|
@@ -266,20 +271,19 @@ module IOStreams
|
|
266
271
|
#
|
267
272
|
# After importing keys, they are not trusted and the relevant trust level must be set.
|
268
273
|
# Default: 5 : Ultimate
|
269
|
-
def self.set_trust(email
|
274
|
+
def self.set_trust(email: nil, key_id: nil, level: 5)
|
270
275
|
version_check
|
271
|
-
fingerprint = fingerprint(email: email)
|
276
|
+
fingerprint = key_id || fingerprint(email: email)
|
272
277
|
return unless fingerprint
|
273
278
|
|
274
279
|
command = "#{executable} --import-ownertrust"
|
275
280
|
trust = "#{fingerprint}:#{level + 1}:\n"
|
276
281
|
out, err, status = Open3.capture3(command, stdin_data: trust)
|
277
282
|
logger&.debug { "IOStreams::Pgp.set_trust: #{command}\n#{err}#{out}" }
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
283
|
+
|
284
|
+
raise(Pgp::Failure, "GPG Failed trusting key: #{err} #{out}") unless status.success?
|
285
|
+
|
286
|
+
err
|
283
287
|
end
|
284
288
|
|
285
289
|
# DEPRECATED - Use key_ids instead of fingerprints
|
@@ -287,18 +291,18 @@ module IOStreams
|
|
287
291
|
version_check
|
288
292
|
Open3.popen2e("#{executable} --list-keys --fingerprint --with-colons #{email}") do |_stdin, out, waith_thr|
|
289
293
|
output = out.read.chomp
|
290
|
-
|
291
|
-
output
|
292
|
-
|
293
|
-
return match[1]
|
294
|
-
end
|
294
|
+
unless waith_thr.value.success?
|
295
|
+
unless output =~ /(public key not found|No public key)/i
|
296
|
+
raise(Pgp::Failure, "GPG Failed calling #{executable} to list keys for #{email}: #{output}")
|
295
297
|
end
|
296
|
-
|
297
|
-
else
|
298
|
-
return if output =~ /(public key not found|No public key)/i
|
298
|
+
end
|
299
299
|
|
300
|
-
|
300
|
+
output.each_line do |line|
|
301
|
+
if (match = line.match(/\Afpr.*::([^\:]*):\Z/))
|
302
|
+
return match[1]
|
303
|
+
end
|
301
304
|
end
|
305
|
+
nil
|
302
306
|
end
|
303
307
|
end
|
304
308
|
|
@@ -328,7 +332,7 @@ module IOStreams
|
|
328
332
|
# CAMELLIA128, CAMELLIA192, CAMELLIA256
|
329
333
|
# Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
|
330
334
|
# Compression: Uncompressed, ZIP, ZLIB, BZIP2
|
331
|
-
if match = out.lines.first.match(/(\d+\.\d+.\d+)/)
|
335
|
+
if (match = out.lines.first.match(/(\d+\.\d+.\d+)/))
|
332
336
|
match[1]
|
333
337
|
end
|
334
338
|
else
|
@@ -339,8 +343,6 @@ module IOStreams
|
|
339
343
|
end
|
340
344
|
end
|
341
345
|
|
342
|
-
private
|
343
|
-
|
344
346
|
@logger = nil
|
345
347
|
|
346
348
|
def self.logger
|
@@ -348,9 +350,12 @@ module IOStreams
|
|
348
350
|
end
|
349
351
|
|
350
352
|
def self.version_check
|
351
|
-
|
352
|
-
|
353
|
-
|
353
|
+
return unless pgp_version.to_f >= 2.4
|
354
|
+
|
355
|
+
raise(
|
356
|
+
Pgp::UnsupportedVersion,
|
357
|
+
"Version #{pgp_version} of #{executable} is not yet supported. Please submit a Pull Request to support it."
|
358
|
+
)
|
354
359
|
end
|
355
360
|
|
356
361
|
# v2.2.1 output:
|
@@ -370,7 +375,7 @@ module IOStreams
|
|
370
375
|
results = []
|
371
376
|
hash = {}
|
372
377
|
out.each_line do |line|
|
373
|
-
if match = line.match(/(pub|sec)\s+(\D+)(\d+)\s+(\d+-\d+-\d+)\s+(.*)/)
|
378
|
+
if (match = line.match(/(pub|sec)\s+(\D+)(\d+)\s+(\d+-\d+-\d+)\s+(.*)/))
|
374
379
|
# v2.2: pub rsa1024 2017-10-24 [SCEA]
|
375
380
|
hash = {
|
376
381
|
private: match[1] == "sec",
|
@@ -382,7 +387,7 @@ module IOStreams
|
|
382
387
|
match[4]
|
383
388
|
end)
|
384
389
|
}
|
385
|
-
elsif match = line.match(%r{(pub|sec)\s+(\d+)(.*)/(\w+)\s+(\d+-\d+-\d+)(\s+(.+)<(.+)>)?})
|
390
|
+
elsif (match = line.match(%r{(pub|sec)\s+(\d+)(.*)/(\w+)\s+(\d+-\d+-\d+)(\s+(.+)<(.+)>)?}))
|
386
391
|
# Matches: pub 2048R/C7F9D9CB 2016-10-26
|
387
392
|
# Or: pub 2048R/C7F9D9CB 2016-10-26 Receiver <receiver@example.org>
|
388
393
|
hash = {
|
@@ -403,7 +408,7 @@ module IOStreams
|
|
403
408
|
results << hash
|
404
409
|
hash = {}
|
405
410
|
end
|
406
|
-
elsif match = line.match(/uid\s+(\[(.+)\]\s+)?(.+)<(.+)>/)
|
411
|
+
elsif (match = line.match(/uid\s+(\[(.+)\]\s+)?(.+)<(.+)>/))
|
407
412
|
# Matches: uid [ unknown] Joe Bloggs <j@bloggs.net>
|
408
413
|
# Or: uid Joe Bloggs <j@bloggs.net>
|
409
414
|
# v2.2: uid [ultimate] Joe Bloggs <pgp_test@iostreams.net>
|
@@ -412,7 +417,15 @@ module IOStreams
|
|
412
417
|
hash[:trust] = match[2].to_s.strip if match[1]
|
413
418
|
results << hash
|
414
419
|
hash = {}
|
415
|
-
elsif match = line.match(/([
|
420
|
+
elsif (match = line.match(/uid\s+(\[(.+)\]\s+)?(.+)/))
|
421
|
+
# Matches: uid [ unknown] Joe Bloggs
|
422
|
+
# Or: uid Joe Bloggs
|
423
|
+
# v2.2: uid [ultimate] Joe Bloggs
|
424
|
+
hash[:name] = match[3].to_s.strip
|
425
|
+
hash[:trust] = match[2].to_s.strip if match[1]
|
426
|
+
results << hash
|
427
|
+
hash = {}
|
428
|
+
elsif (match = line.match(/([A-Z0-9]+)/))
|
416
429
|
# v2.2 18A0FC1C09C0D8AE34CE659257DC4AE323C7368C
|
417
430
|
hash[:key_id] ||= match[1]
|
418
431
|
end
|
@@ -420,10 +433,10 @@ module IOStreams
|
|
420
433
|
results
|
421
434
|
end
|
422
435
|
|
423
|
-
def self.delete_public_or_private_keys(email
|
436
|
+
def self.delete_public_or_private_keys(email: nil, key_id: nil, private: false)
|
424
437
|
keys = private ? "secret-keys" : "keys"
|
425
438
|
|
426
|
-
list = list_keys(email: email, private: private)
|
439
|
+
list = email ? list_keys(email: email, private: private) : list_keys(key_id: key_id)
|
427
440
|
return false if list.empty?
|
428
441
|
|
429
442
|
list.each do |key_info|
|
@@ -435,17 +448,17 @@ module IOStreams
|
|
435
448
|
logger&.debug { "IOStreams::Pgp.delete_keys: #{command}\n#{err}#{out}" }
|
436
449
|
|
437
450
|
unless status.success?
|
438
|
-
raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}")
|
451
|
+
raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email || key_id}: #{err}: #{out}")
|
439
452
|
end
|
440
|
-
raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}:#{out}") if out.include?("error")
|
453
|
+
raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email || key_id} #{err.strip}:#{out}") if out.include?("error")
|
441
454
|
end
|
442
455
|
true
|
443
456
|
end
|
444
457
|
|
445
|
-
def self.delete_public_or_private_keys_v1(email
|
458
|
+
def self.delete_public_or_private_keys_v1(email: nil, key_id: nil, private: false)
|
446
459
|
keys = private ? "secret-keys" : "keys"
|
447
460
|
|
448
|
-
command = "for i in `#{executable} --list-#{keys} --with-colons --fingerprint #{email} | grep \"^fpr\" | cut -d: -f10`; do\n"
|
461
|
+
command = "for i in `#{executable} --list-#{keys} --with-colons --fingerprint #{email || key_id} | grep \"^fpr\" | cut -d: -f10`; do\n"
|
449
462
|
command << "#{executable} --batch --no-tty --yes --delete-#{keys} \"$i\" ;\n"
|
450
463
|
command << "done"
|
451
464
|
|
@@ -454,9 +467,9 @@ module IOStreams
|
|
454
467
|
|
455
468
|
return false if err =~ /(not found|no public key)/i
|
456
469
|
unless status.success?
|
457
|
-
raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}")
|
470
|
+
raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email || key_id}: #{err}: #{out}")
|
458
471
|
end
|
459
|
-
raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}: #{out}") if out.include?("error")
|
472
|
+
raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email || key_id} #{err.strip}: #{out}") if out.include?("error")
|
460
473
|
|
461
474
|
true
|
462
475
|
end
|
data/lib/io_streams/stream.rb
CHANGED
@@ -17,7 +17,7 @@ module IOStreams
|
|
17
17
|
#
|
18
18
|
# Example:
|
19
19
|
#
|
20
|
-
# IOStreams.path(
|
20
|
+
# IOStreams.path("tempfile2527").stream(:zip).stream(:pgp, passphrase: "receiver_passphrase").read
|
21
21
|
def stream(stream, **options)
|
22
22
|
builder.stream(stream, **options)
|
23
23
|
self
|
@@ -27,12 +27,12 @@ module IOStreams
|
|
27
27
|
# If the relevant stream is not found for this file it is ignored.
|
28
28
|
# For example, if the file does not have a pgp extension then the pgp option is not relevant.
|
29
29
|
#
|
30
|
-
# IOStreams.path(
|
30
|
+
# IOStreams.path("keep_safe.pgp").option(:pgp, passphrase: "receiver_passphrase").read
|
31
31
|
#
|
32
32
|
# # In this case the file is not pgp so the `passphrase` option is ignored.
|
33
|
-
# IOStreams.path(
|
33
|
+
# IOStreams.path("keep_safe.enc").option(:pgp, passphrase: "receiver_passphrase").read
|
34
34
|
#
|
35
|
-
# IOStreams.path(output_file_name).option(:pgp, passphrase:
|
35
|
+
# IOStreams.path(output_file_name).option(:pgp, passphrase: "receiver_passphrase").read
|
36
36
|
def option(stream, **options)
|
37
37
|
builder.option(stream, **options)
|
38
38
|
self
|
@@ -177,7 +177,7 @@ module IOStreams
|
|
177
177
|
end
|
178
178
|
|
179
179
|
def copy_to(target, convert: true)
|
180
|
-
target = IOStreams.
|
180
|
+
target = IOStreams.new(target)
|
181
181
|
target.copy_from(self, convert: convert)
|
182
182
|
end
|
183
183
|
|
@@ -191,11 +191,41 @@ module IOStreams
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
-
# Set
|
194
|
+
# Set the original file_name
|
195
195
|
def file_name=(file_name)
|
196
196
|
builder.file_name = file_name
|
197
197
|
end
|
198
198
|
|
199
|
+
# Set/get the tabular format_options
|
200
|
+
def format(format = :none)
|
201
|
+
if format == :none
|
202
|
+
builder.format
|
203
|
+
else
|
204
|
+
builder.format = format
|
205
|
+
self
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Set the tabular format
|
210
|
+
def format=(format)
|
211
|
+
builder.format = format
|
212
|
+
end
|
213
|
+
|
214
|
+
# Set/get the tabular format options
|
215
|
+
def format_options(format_options = :none)
|
216
|
+
if format_options == :none
|
217
|
+
builder.format_options
|
218
|
+
else
|
219
|
+
builder.format_options = format_options
|
220
|
+
self
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Set the tabular format_options
|
225
|
+
def format_options=(format_options)
|
226
|
+
builder.format_options = format_options
|
227
|
+
end
|
228
|
+
|
199
229
|
# Returns [String] the last component of this path.
|
200
230
|
# Returns `nil` if no `file_name` was set.
|
201
231
|
#
|
@@ -282,20 +312,37 @@ module IOStreams
|
|
282
312
|
def line_reader(embedded_within: nil, **args)
|
283
313
|
embedded_within = '"' if embedded_within.nil? && builder.file_name&.include?(".csv")
|
284
314
|
|
285
|
-
stream_reader
|
315
|
+
stream_reader do |io|
|
316
|
+
yield IOStreams::Line::Reader.new(io,
|
317
|
+
original_file_name: builder.file_name,
|
318
|
+
embedded_within: embedded_within,
|
319
|
+
**args)
|
320
|
+
end
|
286
321
|
end
|
287
322
|
|
288
323
|
# Iterate over a file / stream returning each line as an array, one at a time.
|
289
324
|
def row_reader(delimiter: nil, embedded_within: nil, **args)
|
290
325
|
line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
|
291
|
-
yield IOStreams::Row::Reader.new(
|
326
|
+
yield IOStreams::Row::Reader.new(
|
327
|
+
io,
|
328
|
+
original_file_name: builder.file_name,
|
329
|
+
format: builder.format,
|
330
|
+
format_options: builder.format_options,
|
331
|
+
**args
|
332
|
+
)
|
292
333
|
end
|
293
334
|
end
|
294
335
|
|
295
336
|
# Iterate over a file / stream returning each line as a hash, one at a time.
|
296
337
|
def record_reader(delimiter: nil, embedded_within: nil, **args)
|
297
338
|
line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
|
298
|
-
yield IOStreams::Record::Reader.new(
|
339
|
+
yield IOStreams::Record::Reader.new(
|
340
|
+
io,
|
341
|
+
original_file_name: builder.file_name,
|
342
|
+
format: builder.format,
|
343
|
+
format_options: builder.format_options,
|
344
|
+
**args
|
345
|
+
)
|
299
346
|
end
|
300
347
|
end
|
301
348
|
|
@@ -306,19 +353,38 @@ module IOStreams
|
|
306
353
|
def line_writer(**args, &block)
|
307
354
|
return block.call(io_stream) if io_stream&.is_a?(IOStreams::Line::Writer)
|
308
355
|
|
309
|
-
writer
|
356
|
+
writer do |io|
|
357
|
+
IOStreams::Line::Writer.stream(io, original_file_name: builder.file_name, **args, &block)
|
358
|
+
end
|
310
359
|
end
|
311
360
|
|
312
361
|
def row_writer(delimiter: $/, **args, &block)
|
313
362
|
return block.call(io_stream) if io_stream&.is_a?(IOStreams::Row::Writer)
|
314
363
|
|
315
|
-
line_writer(delimiter: delimiter)
|
364
|
+
line_writer(delimiter: delimiter) do |io|
|
365
|
+
IOStreams::Row::Writer.stream(
|
366
|
+
io,
|
367
|
+
original_file_name: builder.file_name,
|
368
|
+
format: builder.format,
|
369
|
+
format_options: builder.format_options,
|
370
|
+
**args,
|
371
|
+
&block
|
372
|
+
)
|
373
|
+
end
|
316
374
|
end
|
317
375
|
|
318
376
|
def record_writer(delimiter: $/, **args, &block)
|
319
377
|
return block.call(io_stream) if io_stream&.is_a?(IOStreams::Record::Writer)
|
320
378
|
|
321
|
-
line_writer(delimiter: delimiter)
|
379
|
+
line_writer(delimiter: delimiter) do |io|
|
380
|
+
IOStreams::Record::Writer.stream(
|
381
|
+
io,
|
382
|
+
original_file_name: builder.file_name,
|
383
|
+
format: builder.format,
|
384
|
+
format_options: builder.format_options,
|
385
|
+
**args,
|
386
|
+
&block)
|
387
|
+
end
|
322
388
|
end
|
323
389
|
end
|
324
390
|
end
|