iostreams 0.20.3 → 1.0.0.beta
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 +9 -21
- data/lib/io_streams/bzip2/writer.rb +9 -21
- data/lib/io_streams/deprecated.rb +217 -0
- data/lib/io_streams/encode/reader.rb +12 -16
- data/lib/io_streams/encode/writer.rb +9 -13
- data/lib/io_streams/errors.rb +6 -6
- data/lib/io_streams/gzip/reader.rb +7 -14
- data/lib/io_streams/gzip/writer.rb +7 -15
- data/lib/io_streams/io_streams.rb +182 -524
- data/lib/io_streams/line/reader.rb +9 -9
- data/lib/io_streams/line/writer.rb +10 -11
- data/lib/io_streams/path.rb +190 -0
- data/lib/io_streams/paths/file.rb +176 -0
- data/lib/io_streams/paths/http.rb +92 -0
- data/lib/io_streams/paths/matcher.rb +61 -0
- data/lib/io_streams/paths/s3.rb +269 -0
- data/lib/io_streams/paths/sftp.rb +99 -0
- data/lib/io_streams/pgp.rb +47 -19
- data/lib/io_streams/pgp/reader.rb +20 -28
- data/lib/io_streams/pgp/writer.rb +24 -46
- data/lib/io_streams/reader.rb +28 -0
- data/lib/io_streams/record/reader.rb +20 -16
- data/lib/io_streams/record/writer.rb +28 -28
- data/lib/io_streams/row/reader.rb +22 -26
- data/lib/io_streams/row/writer.rb +29 -28
- data/lib/io_streams/stream.rb +400 -0
- data/lib/io_streams/streams.rb +125 -0
- data/lib/io_streams/symmetric_encryption/reader.rb +5 -13
- data/lib/io_streams/symmetric_encryption/writer.rb +16 -15
- data/lib/io_streams/tabular/header.rb +9 -3
- data/lib/io_streams/tabular/parser/array.rb +8 -3
- data/lib/io_streams/tabular/parser/csv.rb +6 -2
- data/lib/io_streams/tabular/parser/hash.rb +4 -1
- data/lib/io_streams/tabular/parser/json.rb +3 -1
- data/lib/io_streams/tabular/parser/psv.rb +3 -1
- data/lib/io_streams/tabular/utility/csv_row.rb +9 -8
- data/lib/io_streams/utils.rb +22 -0
- data/lib/io_streams/version.rb +1 -1
- data/lib/io_streams/writer.rb +28 -0
- data/lib/io_streams/xlsx/reader.rb +7 -19
- data/lib/io_streams/zip/reader.rb +7 -26
- data/lib/io_streams/zip/writer.rb +21 -38
- data/lib/iostreams.rb +15 -15
- data/test/bzip2_reader_test.rb +3 -3
- data/test/bzip2_writer_test.rb +3 -3
- data/test/deprecated_test.rb +123 -0
- data/test/encode_reader_test.rb +3 -3
- data/test/encode_writer_test.rb +6 -6
- data/test/gzip_reader_test.rb +2 -2
- data/test/gzip_writer_test.rb +3 -3
- data/test/io_streams_test.rb +43 -136
- data/test/line_reader_test.rb +20 -20
- data/test/line_writer_test.rb +3 -3
- data/test/path_test.rb +30 -28
- data/test/paths/file_test.rb +206 -0
- data/test/paths/http_test.rb +34 -0
- data/test/paths/matcher_test.rb +111 -0
- data/test/paths/s3_test.rb +207 -0
- data/test/pgp_reader_test.rb +8 -8
- data/test/pgp_writer_test.rb +13 -13
- data/test/record_reader_test.rb +5 -5
- data/test/record_writer_test.rb +4 -4
- data/test/row_reader_test.rb +5 -5
- data/test/row_writer_test.rb +6 -6
- data/test/stream_test.rb +116 -0
- data/test/streams_test.rb +255 -0
- data/test/utils_test.rb +20 -0
- data/test/xlsx_reader_test.rb +3 -3
- data/test/zip_reader_test.rb +12 -12
- data/test/zip_writer_test.rb +5 -5
- metadata +33 -45
- data/lib/io_streams/base_path.rb +0 -72
- data/lib/io_streams/file/path.rb +0 -58
- data/lib/io_streams/file/reader.rb +0 -12
- data/lib/io_streams/file/writer.rb +0 -22
- data/lib/io_streams/http/reader.rb +0 -71
- data/lib/io_streams/s3.rb +0 -26
- data/lib/io_streams/s3/path.rb +0 -40
- data/lib/io_streams/s3/reader.rb +0 -28
- data/lib/io_streams/s3/writer.rb +0 -85
- data/lib/io_streams/sftp/reader.rb +0 -67
- data/lib/io_streams/sftp/writer.rb +0 -68
- data/test/base_path_test.rb +0 -35
- data/test/file_path_test.rb +0 -97
- data/test/file_reader_test.rb +0 -33
- data/test/file_writer_test.rb +0 -50
- data/test/http_reader_test.rb +0 -38
- data/test/s3_reader_test.rb +0 -41
- data/test/s3_writer_test.rb +0 -41
@@ -1,21 +1,13 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module SymmetricEncryption
|
3
|
-
class Reader
|
3
|
+
class Reader < IOStreams::Reader
|
4
4
|
# read from a file/stream using Symmetric Encryption
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
8
|
-
rescue LoadError => e
|
9
|
-
raise(LoadError, "Please install the 'symmetric-encryption' gem for .enc streaming support. #{e.message}")
|
5
|
+
def self.stream(input_stream, **args, &block)
|
6
|
+
unless defined?(SymmetricEncryption)
|
7
|
+
Utils.load_dependency('symmetric-encryption', '.enc streaming')
|
10
8
|
end
|
11
9
|
|
12
|
-
|
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
|
10
|
+
::SymmetricEncryption::Reader.open(input_stream, **args, &block)
|
19
11
|
end
|
20
12
|
end
|
21
13
|
end
|
@@ -1,24 +1,25 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module SymmetricEncryption
|
3
|
-
class Writer
|
4
|
-
# Write to
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
class Writer < IOStreams::Writer
|
4
|
+
# Write to stream using Symmetric Encryption
|
5
|
+
# By default the output stream is compressed.
|
6
|
+
# If the input_stream is already compressed consider setting compress: false.
|
7
|
+
def self.stream(input_stream, compress: true, **args, &block)
|
8
|
+
unless defined?(SymmetricEncryption)
|
9
|
+
Utils.load_dependency('symmetric-encryption', '.enc streaming')
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
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?
|
12
|
+
::SymmetricEncryption::Writer.open(input_stream, compress: compress, **args, &block)
|
13
|
+
end
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
# Write to stream using Symmetric Encryption
|
16
|
+
# By default the output stream is compressed unless the file_name extension indicates the file is already compressed.
|
17
|
+
def self.file(file_name, compress: nil, **args, &block)
|
18
|
+
unless defined?(SymmetricEncryption)
|
19
|
+
Utils.load_dependency('symmetric-encryption', '.enc streaming')
|
21
20
|
end
|
21
|
+
|
22
|
+
::SymmetricEncryption::Writer.open(file_name, compress: compress, **args, &block)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -90,7 +90,10 @@ module IOStreams
|
|
90
90
|
|
91
91
|
case row
|
92
92
|
when Array
|
93
|
-
|
93
|
+
unless columns
|
94
|
+
raise(IOStreams::Errors::InvalidHeader, 'Missing mandatory header when trying to convert a row into a hash')
|
95
|
+
end
|
96
|
+
|
94
97
|
array_to_hash(row)
|
95
98
|
when Hash
|
96
99
|
cleanse && columns ? cleanse_hash(row) : row
|
@@ -104,7 +107,11 @@ module IOStreams
|
|
104
107
|
row = cleanse_hash(row) if cleanse
|
105
108
|
row = columns.collect { |column| row[column] }
|
106
109
|
end
|
107
|
-
|
110
|
+
|
111
|
+
unless row.is_a?(Array)
|
112
|
+
raise(IOStreams::Errors::TypeMismatch, "Don't know how to convert #{row.class.name} to an Array without the header columns being set.")
|
113
|
+
end
|
114
|
+
|
108
115
|
row
|
109
116
|
end
|
110
117
|
|
@@ -140,7 +147,6 @@ module IOStreams
|
|
140
147
|
cleansed.gsub!(/\W+/, '')
|
141
148
|
cleansed
|
142
149
|
end
|
143
|
-
|
144
150
|
end
|
145
151
|
end
|
146
152
|
end
|
@@ -6,20 +6,25 @@ module IOStreams
|
|
6
6
|
# Returns [Array<String>] the header row.
|
7
7
|
# Returns nil if the row is blank.
|
8
8
|
def parse_header(row)
|
9
|
-
|
9
|
+
unless row.is_a?(::Array)
|
10
|
+
raise(IOStreams::Errors::InvalidHeader, "Format is :array. Invalid input header: #{row.class.name}")
|
11
|
+
end
|
12
|
+
|
10
13
|
row
|
11
14
|
end
|
12
15
|
|
13
16
|
# Returns Array
|
14
17
|
def parse(row)
|
15
|
-
|
18
|
+
unless row.is_a?(::Array)
|
19
|
+
raise(IOStreams::Errors::TypeMismatch, "Format is :array. Invalid input: #{row.class.name}")
|
20
|
+
end
|
21
|
+
|
16
22
|
row
|
17
23
|
end
|
18
24
|
|
19
25
|
def render(row, header)
|
20
26
|
header.to_array(row)
|
21
27
|
end
|
22
|
-
|
23
28
|
end
|
24
29
|
end
|
25
30
|
end
|
@@ -14,7 +14,9 @@ module IOStreams
|
|
14
14
|
def parse_header(row)
|
15
15
|
return row if row.is_a?(::Array)
|
16
16
|
|
17
|
-
|
17
|
+
unless row.is_a?(String)
|
18
|
+
raise(IOStreams::Errors::InvalidHeader, "Format is :csv. Invalid input header: #{row.class.name}")
|
19
|
+
end
|
18
20
|
|
19
21
|
parse_line(row)
|
20
22
|
end
|
@@ -23,7 +25,9 @@ module IOStreams
|
|
23
25
|
def parse(row)
|
24
26
|
return row if row.is_a?(::Array)
|
25
27
|
|
26
|
-
|
28
|
+
unless row.is_a?(String)
|
29
|
+
raise(IOStreams::Errors::TypeMismatch, "Format is :csv. Invalid input: #{row.class.name}")
|
30
|
+
end
|
27
31
|
|
28
32
|
parse_line(row)
|
29
33
|
end
|
@@ -4,7 +4,10 @@ module IOStreams
|
|
4
4
|
module Parser
|
5
5
|
class Hash < Base
|
6
6
|
def parse(row)
|
7
|
-
|
7
|
+
unless row.is_a?(::Hash)
|
8
|
+
raise(IOStreams::Errors::TypeMismatch, "Format is :hash. Invalid input: #{row.class.name}")
|
9
|
+
end
|
10
|
+
|
8
11
|
row
|
9
12
|
end
|
10
13
|
|
@@ -7,7 +7,9 @@ module IOStreams
|
|
7
7
|
def parse(row)
|
8
8
|
return row if row.is_a?(::Hash)
|
9
9
|
|
10
|
-
|
10
|
+
unless row.is_a?(String)
|
11
|
+
raise(IOStreams::Errors::TypeMismatch, "Format is :json. Invalid input: #{row.class.name}")
|
12
|
+
end
|
11
13
|
|
12
14
|
JSON.parse(row)
|
13
15
|
end
|
@@ -19,7 +19,9 @@ module IOStreams
|
|
19
19
|
def parse(row)
|
20
20
|
return row if row.is_a?(::Array)
|
21
21
|
|
22
|
-
|
22
|
+
unless row.is_a?(String)
|
23
|
+
raise(IOStreams::Errors::TypeMismatch, "Format is :psv. Invalid input: #{row.class.name}")
|
24
|
+
end
|
23
25
|
|
24
26
|
row.split('|')
|
25
27
|
end
|
@@ -24,10 +24,10 @@ module IOStreams
|
|
24
24
|
# A single line of CSV data without any line terminators
|
25
25
|
def parse(line)
|
26
26
|
return if IOStreams::Utils.blank?(line)
|
27
|
-
return if @skip_lines
|
27
|
+
return if @skip_lines&.match(line)
|
28
28
|
|
29
29
|
in_extended_col = false
|
30
|
-
csv =
|
30
|
+
csv = []
|
31
31
|
parts = line.split(@col_sep, -1)
|
32
32
|
csv << nil if parts.empty?
|
33
33
|
|
@@ -36,12 +36,13 @@ module IOStreams
|
|
36
36
|
parts.each do |part|
|
37
37
|
if in_extended_col
|
38
38
|
# If we are continuing a previous column
|
39
|
-
if part[-1] == @quote_char && part.count(@quote_char)
|
39
|
+
if part[-1] == @quote_char && part.count(@quote_char).odd?
|
40
40
|
# extended column ends
|
41
41
|
csv.last << part[0..-2]
|
42
42
|
if csv.last =~ @parsers[:stray_quote]
|
43
43
|
raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}"
|
44
44
|
end
|
45
|
+
|
45
46
|
csv.last.gsub!(@quote_char * 2, @quote_char)
|
46
47
|
in_extended_col = false
|
47
48
|
else
|
@@ -50,7 +51,7 @@ module IOStreams
|
|
50
51
|
end
|
51
52
|
elsif part[0] == @quote_char
|
52
53
|
# If we are starting a new quoted column
|
53
|
-
if part[-1] != @quote_char || part.count(@quote_char)
|
54
|
+
if part[-1] != @quote_char || part.count(@quote_char).odd?
|
54
55
|
# start an extended column
|
55
56
|
csv << part[1..-1]
|
56
57
|
csv.last << @col_sep
|
@@ -61,6 +62,7 @@ module IOStreams
|
|
61
62
|
if csv.last =~ @parsers[:stray_quote]
|
62
63
|
raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}"
|
63
64
|
end
|
65
|
+
|
64
66
|
csv.last.gsub!(@quote_char * 2, @quote_char)
|
65
67
|
end
|
66
68
|
elsif part =~ @parsers[:quote_or_nl]
|
@@ -90,12 +92,12 @@ module IOStreams
|
|
90
92
|
unconverted = csv.dup if @unconverted_fields
|
91
93
|
|
92
94
|
# convert fields, if needed...
|
93
|
-
csv = convert_fields(csv) unless @use_headers
|
95
|
+
csv = convert_fields(csv) unless @use_headers || @converters.empty?
|
94
96
|
# parse out header rows and handle CSV::Row conversions...
|
95
97
|
csv = parse_headers(csv) if @use_headers
|
96
98
|
|
97
99
|
# inject unconverted fields and accessor, if requested...
|
98
|
-
if @unconverted_fields
|
100
|
+
if @unconverted_fields && (!csv.respond_to? :unconverted_fields)
|
99
101
|
add_unconverted_fields(csv, unconverted)
|
100
102
|
end
|
101
103
|
|
@@ -107,8 +109,7 @@ module IOStreams
|
|
107
109
|
row.map(&@quote).join(@col_sep) + @row_sep # quote and separate
|
108
110
|
end
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
+
alias to_csv render
|
112
113
|
end
|
113
114
|
end
|
114
115
|
end
|
data/lib/io_streams/utils.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module Utils
|
3
|
+
# Lazy load dependent gem so that it remains a soft dependency.
|
4
|
+
def self.load_dependency(gem_name, stream_type, require_name = gem_name)
|
5
|
+
require require_name
|
6
|
+
rescue LoadError => e
|
7
|
+
raise(LoadError, "Please install the gem '#{gem_name}' to support #{stream_type}. #{e.message}")
|
8
|
+
end
|
9
|
+
|
3
10
|
# Helper method: Returns [true|false] if a value is blank?
|
4
11
|
def self.blank?(value)
|
5
12
|
if value.nil?
|
@@ -10,5 +17,20 @@ module IOStreams
|
|
10
17
|
value.respond_to?(:empty?) ? value.empty? : !value
|
11
18
|
end
|
12
19
|
end
|
20
|
+
|
21
|
+
# Yields the path to a temporary file_name.
|
22
|
+
#
|
23
|
+
# File is deleted upon completion if present.
|
24
|
+
def self.temp_file_name(basename, extension = '')
|
25
|
+
result = nil
|
26
|
+
::Dir::Tmpname.create([basename, extension]) do |tmpname|
|
27
|
+
begin
|
28
|
+
result = yield(tmpname)
|
29
|
+
ensure
|
30
|
+
::File.unlink(tmpname) if ::File.exist?(tmpname)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
13
35
|
end
|
14
36
|
end
|
data/lib/io_streams/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
module IOStreams
|
2
|
+
class Writer
|
3
|
+
# When a Writer does not support streams, we copy the stream to a local temp file
|
4
|
+
# and then pass that filename in for this reader.
|
5
|
+
def self.stream(output_stream, original_file_name: nil, **args, &block)
|
6
|
+
Utils.temp_file_name("iostreams_writer") do |file_name|
|
7
|
+
file(file_name, original_file_name: original_file_name, **args, &block)
|
8
|
+
::File.open(file_name, 'rb') { |source| ::IO.copy_stream(source, output_stream) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# When a Writer supports streams, also allow it to simply support a file
|
13
|
+
def self.file(file_name, original_file_name: file_name, **args, &block)
|
14
|
+
::File.open(file_name, "wb") { |file| stream(file, original_file_name: original_file_name, **args, &block) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# For processing by either a file name or an open IO stream.
|
18
|
+
def self.open(file_name_or_io, **args, &block)
|
19
|
+
file_name_or_io.is_a?(String) ? file(file_name_or_io, **args, &block) : stream(file_name_or_io, **args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :output_stream
|
23
|
+
|
24
|
+
def initialize(output_stream)
|
25
|
+
@output_stream = output_stream
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -2,25 +2,13 @@ require 'csv'
|
|
2
2
|
|
3
3
|
module IOStreams
|
4
4
|
module Xlsx
|
5
|
-
class Reader
|
6
|
-
# Convert a xlsx, or xlsm file
|
7
|
-
def self.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
IOStreams.copy(file_name_or_io, temp_file_name, target_options: {streams: []})
|
13
|
-
extract_csv(temp_file_name, &block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Convert the spreadsheet to csv in a tempfile
|
18
|
-
def self.extract_csv(file_name, &block)
|
19
|
-
IOStreams::File::Path.temp_file_name('iostreams_csv') do |temp_file_name|
|
20
|
-
IOStreams::File::Writer.open(temp_file_name) do |io|
|
21
|
-
new(file_name).each { |lines| io << lines.to_csv }
|
22
|
-
end
|
23
|
-
IOStreams::File::Reader.open(temp_file_name, &block)
|
5
|
+
class Reader < IOStreams::Reader
|
6
|
+
# Convert a xlsx, or xlsm file into CSV format.
|
7
|
+
def self.file(file_name, original_file_name: file_name, &block)
|
8
|
+
# Stream into a temp file as csv
|
9
|
+
Utils.temp_file_name('iostreams_csv') do |temp_file_name|
|
10
|
+
::File.open(temp_file_name, 'wb') { |io| new(file_name).each { |lines| io << lines.to_csv } }
|
11
|
+
::File.open(temp_file_name, 'rb', &block)
|
24
12
|
end
|
25
13
|
end
|
26
14
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module Zip
|
3
|
-
class Reader
|
3
|
+
class Reader < IOStreams::Reader
|
4
4
|
# Read from a zip file or stream, decompressing the contents as it is read
|
5
5
|
# The input stream from the first file found in the zip file is passed
|
6
6
|
# to the supplied block.
|
@@ -17,46 +17,27 @@ module IOStreams
|
|
17
17
|
# puts data
|
18
18
|
# end
|
19
19
|
# end
|
20
|
-
def self.open(file_name_or_io, entry_file_name: nil, &block)
|
21
|
-
# File name supplied
|
22
|
-
return read_file(file_name_or_io, entry_file_name, &block) unless IOStreams.reader_stream?(file_name_or_io)
|
23
|
-
|
24
|
-
# Ruby ZIP gem uses `#seek` so can only work against a file, not a stream, so create temp file.
|
25
|
-
# JRuby ZIP requires an InputStream.
|
26
|
-
IOStreams::File::Path.temp_file_name('iostreams_zip') do |temp_file_name|
|
27
|
-
IOStreams.copy(file_name_or_io, temp_file_name, target_options: {streams: []})
|
28
|
-
read_file(temp_file_name, entry_file_name, &block)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
20
|
if defined?(JRuby)
|
33
21
|
# Java has built-in support for Zip files
|
34
|
-
def self.
|
22
|
+
def self.file(file_name, entry_file_name: nil)
|
35
23
|
fin = Java::JavaIo::FileInputStream.new(file_name)
|
36
24
|
zin = Java::JavaUtilZip::ZipInputStream.new(fin)
|
37
25
|
|
38
26
|
get_entry(zin, entry_file_name) ||
|
39
|
-
raise(Java::JavaUtilZip::ZipException
|
27
|
+
raise(Java::JavaUtilZip::ZipException, "File #{entry_file_name} not found within zip file.")
|
40
28
|
|
41
29
|
yield(zin.to_io)
|
42
30
|
ensure
|
43
|
-
zin
|
44
|
-
fin
|
31
|
+
zin&.close
|
32
|
+
fin&.close
|
45
33
|
end
|
46
34
|
|
47
35
|
else
|
48
36
|
# Read from a zip file or stream, decompressing the contents as it is read
|
49
37
|
# The input stream from the first file found in the zip file is passed
|
50
38
|
# to the supplied block
|
51
|
-
def self.
|
52
|
-
|
53
|
-
# MRI needs Ruby Zip, since it only has native support for GZip
|
54
|
-
begin
|
55
|
-
require 'zip'
|
56
|
-
rescue LoadError => exc
|
57
|
-
raise(LoadError, "Install gem 'rubyzip' to read and write Zip files: #{exc.message}")
|
58
|
-
end
|
59
|
-
end
|
39
|
+
def self.file(file_name, entry_file_name: nil)
|
40
|
+
Utils.load_dependency('rubyzip', 'Zip', 'zip') unless defined?(::Zip)
|
60
41
|
|
61
42
|
::Zip::InputStream.open(file_name) do |zin|
|
62
43
|
get_entry(zin, entry_file_name) ||
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module IOStreams
|
2
2
|
module Zip
|
3
|
-
class Writer
|
3
|
+
class Writer < IOStreams::Writer
|
4
4
|
# Write a single file in Zip format to the supplied output file name
|
5
5
|
#
|
6
6
|
# Parameters
|
7
|
-
#
|
8
|
-
# Full path and filename for the output zip file
|
7
|
+
# file_name [String]
|
8
|
+
# Full path and filename for the output zip file.
|
9
9
|
#
|
10
|
-
#
|
11
|
-
# Name of the file within the Zip
|
10
|
+
# entry_file_name: [String]
|
11
|
+
# Name of the file entry within the Zip file.
|
12
12
|
#
|
13
13
|
# The stream supplied to the block only responds to #write
|
14
14
|
#
|
@@ -21,57 +21,40 @@ module IOStreams
|
|
21
21
|
# Notes:
|
22
22
|
# - Since Zip cannot write to streams, if a stream is supplied, a temp file
|
23
23
|
# is automatically created under the covers
|
24
|
-
def self.
|
24
|
+
def self.file(file_name, original_file_name: file_name, zip_file_name: nil, entry_file_name: zip_file_name, &block)
|
25
25
|
# Default the name of the file within the zip to the supplied file_name without the zip extension
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
if !defined?(JRuby) && !defined?(::Zip)
|
30
|
-
# MRI needs Ruby Zip, since it only has native support for GZip
|
31
|
-
begin
|
32
|
-
require 'zip'
|
33
|
-
rescue LoadError => exc
|
34
|
-
raise(LoadError, "Install gem 'rubyzip' to read and write Zip files: #{exc.message}")
|
35
|
-
end
|
26
|
+
if entry_file_name.nil? && (original_file_name =~ /\.(zip)\z/i)
|
27
|
+
entry_file_name = original_file_name.to_s[0..-5]
|
36
28
|
end
|
29
|
+
entry_file_name ||= 'file'
|
37
30
|
|
38
|
-
|
39
|
-
return write_file(file_name_or_io, zip_file_name, &block) unless IOStreams.writer_stream?(file_name_or_io)
|
40
|
-
|
41
|
-
# ZIP can only work against a file, not a stream, so create temp file.
|
42
|
-
IOStreams::File::Path.temp_file_name('iostreams_zip') do |temp_file_name|
|
43
|
-
write_file(temp_file_name, zip_file_name, &block)
|
44
|
-
IOStreams.copy(temp_file_name, file_name_or_io, source_options: {streams: []})
|
45
|
-
end
|
31
|
+
write_file(file_name, entry_file_name, &block)
|
46
32
|
end
|
47
33
|
|
48
34
|
private
|
49
35
|
|
50
36
|
if defined?(JRuby)
|
51
|
-
|
52
|
-
def self.write_file(file_name, zip_file_name, &block)
|
37
|
+
def self.write_file(file_name, entry_file_name)
|
53
38
|
out = Java::JavaIo::FileOutputStream.new(file_name)
|
54
39
|
zout = Java::JavaUtilZip::ZipOutputStream.new(out)
|
55
|
-
zout.put_next_entry(Java::JavaUtilZip::ZipEntry.new(
|
40
|
+
zout.put_next_entry(Java::JavaUtilZip::ZipEntry.new(entry_file_name))
|
56
41
|
io = zout.to_io
|
57
|
-
|
42
|
+
yield(io)
|
58
43
|
ensure
|
59
|
-
io
|
60
|
-
out
|
44
|
+
io&.close
|
45
|
+
out&.close
|
61
46
|
end
|
62
|
-
|
63
47
|
else
|
64
|
-
def self.write_file(file_name,
|
65
|
-
|
48
|
+
def self.write_file(file_name, entry_file_name)
|
49
|
+
Utils.load_dependency('rubyzip', 'Zip', 'zip') unless defined?(::Zip)
|
50
|
+
|
66
51
|
zos = ::Zip::OutputStream.new(file_name)
|
67
|
-
zos.put_next_entry(
|
68
|
-
|
52
|
+
zos.put_next_entry(entry_file_name)
|
53
|
+
yield(zos)
|
69
54
|
ensure
|
70
|
-
zos
|
55
|
+
zos&.close
|
71
56
|
end
|
72
|
-
|
73
57
|
end
|
74
|
-
|
75
58
|
end
|
76
59
|
end
|
77
60
|
end
|