iostreams 0.20.3 → 1.0.0.beta
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 +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
|