iostreams 0.17.3 → 0.18.0
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/writer.rb +2 -3
- data/lib/io_streams/file/reader.rb +0 -2
- data/lib/io_streams/file/writer.rb +11 -1
- data/lib/io_streams/gzip/writer.rb +1 -0
- data/lib/io_streams/io_streams.rb +9 -12
- data/lib/io_streams/pgp/writer.rb +29 -28
- data/lib/io_streams/s3/writer.rb +5 -2
- data/lib/io_streams/symmetric_encryption/reader.rb +22 -0
- data/lib/io_streams/symmetric_encryption/writer.rb +25 -0
- data/lib/io_streams/version.rb +1 -1
- data/lib/io_streams/zip/writer.rb +1 -0
- data/lib/iostreams.rb +4 -0
- metadata +4 -3
- data/lib/io_streams/streams.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9c9876f8e66281fabd3a26188f1e51d23a12620de461147c9125286d58950cb
|
4
|
+
data.tar.gz: 81f7b6b198f4dfa37b5349de57df74d2004e7984fd176172ac61cce2c7dac9d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91c03cfc1c218d235b2bda1ab304ebc78a8d02dbb9902372a2b5c6b5dd0afb53fcee99ed335cc9361ff2e9db9213cced9fad901a9ecf48bd56f53043792e6096
|
7
|
+
data.tar.gz: 9773ffd737484920543182b8b6a32c4704cc42e83fb2be014e86447eedf184458aa6a570b129b3fa55fc44148c25862730c9af937578469467e4f54f8628ef60
|
@@ -9,7 +9,7 @@ module IOStreams
|
|
9
9
|
raise(LoadError, "Please install the 'rbzip2' gem for Bzip2 streaming support. #{e.message}")
|
10
10
|
end
|
11
11
|
|
12
|
-
if IOStreams.
|
12
|
+
if IOStreams.writer_stream?(file_name_or_io)
|
13
13
|
begin
|
14
14
|
io = RBzip2.default_adapter::Compressor.new(file_name_or_io)
|
15
15
|
block.call(io)
|
@@ -17,13 +17,12 @@ module IOStreams
|
|
17
17
|
io.close
|
18
18
|
end
|
19
19
|
else
|
20
|
-
::File.open(file_name_or_io
|
20
|
+
IOStreams::File::Writer.open(file_name_or_io) do |file|
|
21
21
|
io = RBzip2.default_adapter::Compressor.new(file)
|
22
22
|
block.call(io)
|
23
23
|
io.close
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|
@@ -2,8 +2,6 @@ module IOStreams
|
|
2
2
|
module File
|
3
3
|
class Reader
|
4
4
|
# Read from a named file
|
5
|
-
# TODO: Add support for mode (text / binary)
|
6
|
-
# TODO: Add encoding support: external_encoding, internal_encoding
|
7
5
|
def self.open(file_name, **args, &block)
|
8
6
|
raise(ArgumentError, 'File name must be a string') unless file_name.is_a?(String)
|
9
7
|
|
@@ -2,10 +2,20 @@ module IOStreams
|
|
2
2
|
module File
|
3
3
|
class Writer
|
4
4
|
# Write to a named file
|
5
|
+
#
|
6
|
+
# Note:
|
7
|
+
# If an exception is raised whilst the file is being written to the file is removed to
|
8
|
+
# prevent incomplete / partial files from being created.
|
5
9
|
def self.open(file_name, **args, &block)
|
6
10
|
raise(ArgumentError, 'File name must be a string') unless file_name.is_a?(String)
|
7
11
|
|
8
|
-
|
12
|
+
IOStreams.mkpath(file_name)
|
13
|
+
begin
|
14
|
+
::File.open(file_name, 'wb', &block)
|
15
|
+
rescue StandardError => e
|
16
|
+
File.unlink(file_name) if File.exist?(file_name)
|
17
|
+
raise(e)
|
18
|
+
end
|
9
19
|
end
|
10
20
|
end
|
11
21
|
end
|
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'concurrent'
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'symmetric-encryption'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
2
|
+
require 'fileutils'
|
7
3
|
|
8
4
|
# Streaming library for Ruby
|
9
5
|
#
|
@@ -473,6 +469,12 @@ module IOStreams
|
|
473
469
|
end
|
474
470
|
end
|
475
471
|
|
472
|
+
# Used by writers that can write directly to file to create the target path
|
473
|
+
def self.mkpath(file_name)
|
474
|
+
path = ::File.dirname(file_name)
|
475
|
+
FileUtils.mkdir_p(path) unless ::File.exist?(path)
|
476
|
+
end
|
477
|
+
|
476
478
|
private
|
477
479
|
|
478
480
|
# A registry to hold formats for processing files during upload or download
|
@@ -484,7 +486,7 @@ module IOStreams
|
|
484
486
|
|
485
487
|
# Returns a reader or writer stream
|
486
488
|
def self.stream(type, file_name_or_io, streams:, file_name:, encoding: nil, encode_cleaner: nil, encode_replace: nil, &block)
|
487
|
-
|
489
|
+
raise(ArgumentError, 'IOStreams call is missing mandatory block') if block.nil?
|
488
490
|
|
489
491
|
streams = streams_for_file_name(file_name) if streams.nil? && file_name
|
490
492
|
|
@@ -565,6 +567,7 @@ module IOStreams
|
|
565
567
|
|
566
568
|
# Register File extensions
|
567
569
|
register_extension(:bz2, IOStreams::Bzip2::Reader, IOStreams::Bzip2::Writer)
|
570
|
+
register_extension(:enc, IOStreams::SymmetricEncryption::Reader, IOStreams::SymmetricEncryption::Writer)
|
568
571
|
register_extension(:gz, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
|
569
572
|
register_extension(:gzip, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
|
570
573
|
register_extension(:zip, IOStreams::Zip::Reader, IOStreams::Zip::Writer)
|
@@ -573,12 +576,6 @@ module IOStreams
|
|
573
576
|
register_extension(:xlsx, IOStreams::Xlsx::Reader, nil)
|
574
577
|
register_extension(:xlsm, IOStreams::Xlsx::Reader, nil)
|
575
578
|
|
576
|
-
# Use Symmetric Encryption to encrypt of decrypt files with the `enc` extension
|
577
|
-
# when the gem `symmetric-encryption` has been loaded.
|
578
|
-
if defined?(SymmetricEncryption)
|
579
|
-
register_extension(:enc, SymmetricEncryption::Reader, SymmetricEncryption::Writer)
|
580
|
-
end
|
581
|
-
|
582
579
|
# Support URI schemes
|
583
580
|
#
|
584
581
|
# Examples:
|
@@ -45,38 +45,39 @@ module IOStreams
|
|
45
45
|
# compress_level: [Integer]
|
46
46
|
# Compression level
|
47
47
|
# Default: 6
|
48
|
-
def self.open(
|
48
|
+
def self.open(file_name, recipient:, signer: default_signer, signer_passphrase: default_signer_passphrase, binary: true, compression: :zip, compress_level: 6)
|
49
49
|
compress_level = 0 if compression == :none
|
50
|
-
if IOStreams.writer_stream?(
|
50
|
+
if IOStreams.writer_stream?(file_name)
|
51
51
|
raise(NotImplementedError, 'Can only PGP Encrypt directly to a file name. Output to streams are not yet supported.')
|
52
|
-
|
53
|
-
|
54
|
-
command = "#{IOStreams::Pgp.executable} --batch --no-tty --yes --encrypt"
|
55
|
-
command << " --sign --local-user \"#{signer}\"" if signer
|
56
|
-
if signer_passphrase
|
57
|
-
command << " --pinentry-mode loopback" if IOStreams::Pgp.pgp_version.to_f >= 2.1
|
58
|
-
command << " --passphrase \"#{signer_passphrase}\""
|
59
|
-
end
|
60
|
-
command << " -z #{compress_level}" if compress_level != 6
|
61
|
-
command << " --compress-algo #{compression}" unless compression == :none
|
62
|
-
command << " --recipient \"#{recipient}\" -o \"#{file_name_or_io}\""
|
52
|
+
end
|
53
|
+
IOStreams.mkpath(file_name)
|
63
54
|
|
64
|
-
|
55
|
+
# Write to stdin, with encrypted contents being written to the file
|
56
|
+
command = "#{IOStreams::Pgp.executable} --batch --no-tty --yes --encrypt"
|
57
|
+
command << " --sign --local-user \"#{signer}\"" if signer
|
58
|
+
if signer_passphrase
|
59
|
+
command << " --pinentry-mode loopback" if IOStreams::Pgp.pgp_version.to_f >= 2.1
|
60
|
+
command << " --passphrase \"#{signer_passphrase}\""
|
61
|
+
end
|
62
|
+
command << " -z #{compress_level}" if compress_level != 6
|
63
|
+
command << " --compress-algo #{compression}" unless compression == :none
|
64
|
+
command << " --recipient \"#{recipient}\" -o \"#{file_name}\""
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
66
|
+
IOStreams::Pgp.logger.debug { "IOStreams::Pgp::Writer.open: #{command}" } if IOStreams::Pgp.logger
|
67
|
+
|
68
|
+
Open3.popen2e(command) do |stdin, out, waith_thr|
|
69
|
+
begin
|
70
|
+
stdin.binmode if binary
|
71
|
+
yield(stdin)
|
72
|
+
stdin.close
|
73
|
+
rescue Errno::EPIPE
|
74
|
+
# Ignore broken pipe because gpg terminates early due to an error
|
75
|
+
::File.delete(file_name) if ::File.exist?(file_name)
|
76
|
+
raise(Pgp::Failure, "GPG Failed writing to encrypted file: #{file_name}: #{out.read.chomp}")
|
77
|
+
end
|
78
|
+
unless waith_thr.value.success?
|
79
|
+
::File.delete(file_name) if ::File.exist?(file_name)
|
80
|
+
raise(Pgp::Failure, "GPG Failed to create encrypted file: #{file_name}: #{out.read.chomp}")
|
80
81
|
end
|
81
82
|
end
|
82
83
|
end
|
data/lib/io_streams/s3/writer.rb
CHANGED
@@ -67,7 +67,7 @@ module IOStreams
|
|
67
67
|
# aborted and this error is raised. The raised error has a `#errors`
|
68
68
|
# method that returns the failures that caused the upload to be
|
69
69
|
# aborted.
|
70
|
-
def self.open(uri, region: nil, **args
|
70
|
+
def self.open(uri, region: nil, **args)
|
71
71
|
raise(ArgumentError, 'file_name must be a URI string') unless uri.is_a?(String)
|
72
72
|
|
73
73
|
IOStreams::S3.load_dependencies
|
@@ -75,7 +75,10 @@ module IOStreams
|
|
75
75
|
options = IOStreams::S3.parse_uri(uri)
|
76
76
|
s3 = region.nil? ? Aws::S3::Resource.new : Aws::S3::Resource.new(region: region)
|
77
77
|
object = s3.bucket(options[:bucket]).object(options[:key])
|
78
|
-
object.upload_stream(args
|
78
|
+
object.upload_stream(args) do |s3|
|
79
|
+
s3.binmode
|
80
|
+
yield(s3)
|
81
|
+
end
|
79
82
|
end
|
80
83
|
end
|
81
84
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module IOStreams
|
2
|
+
module SymmetricEncryption
|
3
|
+
class Reader
|
4
|
+
# read from a file/stream using Symmetric Encryption
|
5
|
+
def self.open(file_name_or_io, **args, &block)
|
6
|
+
begin
|
7
|
+
require 'symmetric-encryption' unless defined?(SymmetricEncryption)
|
8
|
+
rescue LoadError => e
|
9
|
+
raise(LoadError, "Please install the 'symmetric-encryption' gem for .enc streaming support. #{e.message}")
|
10
|
+
end
|
11
|
+
|
12
|
+
if IOStreams.reader_stream?(file_name_or_io)
|
13
|
+
::SymmetricEncryption::Reader.open(file_name_or_io, **args, &block)
|
14
|
+
else
|
15
|
+
IOStreams::File::Reader.open(file_name_or_io) do |file|
|
16
|
+
::SymmetricEncryption::Reader.open(file, **args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module IOStreams
|
2
|
+
module SymmetricEncryption
|
3
|
+
class Writer
|
4
|
+
# Write to file/stream using Symmetric Encryption
|
5
|
+
def self.open(file_name_or_io, compress: nil, **args, &block)
|
6
|
+
begin
|
7
|
+
require 'symmetric-encryption' unless defined?(SymmetricEncryption)
|
8
|
+
rescue LoadError => e
|
9
|
+
raise(LoadError, "Please install the 'symmetric-encryption' gem for .enc streaming support. #{e.message}")
|
10
|
+
end
|
11
|
+
|
12
|
+
if IOStreams.writer_stream?(file_name_or_io)
|
13
|
+
compress = true if compress.nil?
|
14
|
+
::SymmetricEncryption::Writer.open(file_name_or_io, compress: compress, **args, &block)
|
15
|
+
else
|
16
|
+
compress = !IOStreams.compressed?(file_name_or_io) if compress.nil?
|
17
|
+
|
18
|
+
IOStreams::File::Writer.open(file_name_or_io) do |file|
|
19
|
+
::SymmetricEncryption::Writer.open(file, compress: compress, **args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/io_streams/version.rb
CHANGED
data/lib/iostreams.rb
CHANGED
@@ -42,6 +42,10 @@ module IOStreams
|
|
42
42
|
autoload :Reader, 'io_streams/row/reader'
|
43
43
|
autoload :Writer, 'io_streams/row/writer'
|
44
44
|
end
|
45
|
+
module SymmetricEncryption
|
46
|
+
autoload :Reader, 'io_streams/symmetric_encryption/reader'
|
47
|
+
autoload :Writer, 'io_streams/symmetric_encryption/writer'
|
48
|
+
end
|
45
49
|
module Xlsx
|
46
50
|
autoload :Reader, 'io_streams/xlsx/reader'
|
47
51
|
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: 0.
|
4
|
+
version: 0.18.0
|
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-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -58,7 +58,8 @@ files:
|
|
58
58
|
- lib/io_streams/s3/writer.rb
|
59
59
|
- lib/io_streams/sftp/reader.rb
|
60
60
|
- lib/io_streams/sftp/writer.rb
|
61
|
-
- lib/io_streams/
|
61
|
+
- lib/io_streams/symmetric_encryption/reader.rb
|
62
|
+
- lib/io_streams/symmetric_encryption/writer.rb
|
62
63
|
- lib/io_streams/tabular.rb
|
63
64
|
- lib/io_streams/tabular/header.rb
|
64
65
|
- lib/io_streams/tabular/parser/array.rb
|
data/lib/io_streams/streams.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
module IOStreams
|
2
|
-
# Contains behavior for streams
|
3
|
-
#
|
4
|
-
# When a file is being read the streams are processed from right to left.
|
5
|
-
# When writing a file streams are processed left to right.
|
6
|
-
# For example:
|
7
|
-
# file.gz.enc ==> [:gz, :enc]
|
8
|
-
# Read: Unencrypt, then Gunzip
|
9
|
-
# Write: GZip, then Encrypt
|
10
|
-
class Streams
|
11
|
-
|
12
|
-
# Returns [Streams] collection of streams to process against the file
|
13
|
-
#
|
14
|
-
def self.streams_for_file_name(file_name)
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
# Create a processing stream given:
|
19
|
-
# - No stream. Defaults to :file
|
20
|
-
# - A single String implies a file_name and the streams will be created based on the file_name
|
21
|
-
# - One or more symbols or hashes for a stream
|
22
|
-
# - One or more arrays for streams
|
23
|
-
def initialize(*args)
|
24
|
-
if args.size == 0
|
25
|
-
@streams = [:file]
|
26
|
-
elsif args.size == 1
|
27
|
-
stream = args.first
|
28
|
-
if stream
|
29
|
-
@stream = stream.is_a?(String) ? streams_for_file_name(stream) : Array(stream)
|
30
|
-
else
|
31
|
-
@streams = [:file]
|
32
|
-
end
|
33
|
-
else
|
34
|
-
@streams = streams
|
35
|
-
end
|
36
|
-
@streams.flatten!
|
37
|
-
end
|
38
|
-
|
39
|
-
def delimited?
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def delete(stream)
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
# Add another stream for processing
|
48
|
-
def <<(stream)
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
# Add a stream for processing
|
53
|
-
def unshift(stream)
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
# Return the Stream klass for the specified hash or symbol
|
60
|
-
# Parameters
|
61
|
-
# stream [Hash|Symbol]
|
62
|
-
def stream_for(stream)
|
63
|
-
if stream.is_a?(Symbol)
|
64
|
-
registered_klass(stream, {})
|
65
|
-
else
|
66
|
-
registered_klass(@stream.first)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Returns [Array] the formats required to process the file by looking at
|
71
|
-
# its extension(s)
|
72
|
-
#
|
73
|
-
# Extensions supported:
|
74
|
-
# .zip Zip File [ :zip ]
|
75
|
-
# .gz, .gzip GZip File [ :gzip ]
|
76
|
-
# .enc File Encrypted using symmetric encryption [ :enc ]
|
77
|
-
# other All other extensions will be returned as: [ :file ]
|
78
|
-
#
|
79
|
-
# When a file is encrypted, it may also be compressed:
|
80
|
-
# .zip.enc [ :zip, :enc ]
|
81
|
-
# .gz.enc [ :gz, :enc ]
|
82
|
-
#
|
83
|
-
# Example Zip file:
|
84
|
-
# IOStreams.streams_for_file_name('myfile.zip')
|
85
|
-
# => [ :zip ]
|
86
|
-
#
|
87
|
-
# Example Encrypted Gzip file:
|
88
|
-
# IOStreams.streams_for_file_name('myfile.csv.gz.enc')
|
89
|
-
# => [ :gz, :enc ]
|
90
|
-
#
|
91
|
-
# Example plain text / binary file:
|
92
|
-
# IOStreams.streams_for_file_name('myfile.csv')
|
93
|
-
# => [ :file ]
|
94
|
-
def streams_for_file_name(file_name)
|
95
|
-
raise ArgumentError.new("Cannot auto-detect streams when already a stream: #{file_name.inspect}") if reader_stream?(file_name)
|
96
|
-
|
97
|
-
parts = file_name.split('.')
|
98
|
-
extensions = []
|
99
|
-
while extension = parts.pop
|
100
|
-
break unless @extensions[extension.to_sym]
|
101
|
-
extensions.unshift(extension.to_sym)
|
102
|
-
end
|
103
|
-
extensions << :file if extensions.size == 0
|
104
|
-
extensions
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|