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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/lib/io_streams/bzip2/reader.rb +9 -21
  3. data/lib/io_streams/bzip2/writer.rb +9 -21
  4. data/lib/io_streams/deprecated.rb +217 -0
  5. data/lib/io_streams/encode/reader.rb +12 -16
  6. data/lib/io_streams/encode/writer.rb +9 -13
  7. data/lib/io_streams/errors.rb +6 -6
  8. data/lib/io_streams/gzip/reader.rb +7 -14
  9. data/lib/io_streams/gzip/writer.rb +7 -15
  10. data/lib/io_streams/io_streams.rb +182 -524
  11. data/lib/io_streams/line/reader.rb +9 -9
  12. data/lib/io_streams/line/writer.rb +10 -11
  13. data/lib/io_streams/path.rb +190 -0
  14. data/lib/io_streams/paths/file.rb +176 -0
  15. data/lib/io_streams/paths/http.rb +92 -0
  16. data/lib/io_streams/paths/matcher.rb +61 -0
  17. data/lib/io_streams/paths/s3.rb +269 -0
  18. data/lib/io_streams/paths/sftp.rb +99 -0
  19. data/lib/io_streams/pgp.rb +47 -19
  20. data/lib/io_streams/pgp/reader.rb +20 -28
  21. data/lib/io_streams/pgp/writer.rb +24 -46
  22. data/lib/io_streams/reader.rb +28 -0
  23. data/lib/io_streams/record/reader.rb +20 -16
  24. data/lib/io_streams/record/writer.rb +28 -28
  25. data/lib/io_streams/row/reader.rb +22 -26
  26. data/lib/io_streams/row/writer.rb +29 -28
  27. data/lib/io_streams/stream.rb +400 -0
  28. data/lib/io_streams/streams.rb +125 -0
  29. data/lib/io_streams/symmetric_encryption/reader.rb +5 -13
  30. data/lib/io_streams/symmetric_encryption/writer.rb +16 -15
  31. data/lib/io_streams/tabular/header.rb +9 -3
  32. data/lib/io_streams/tabular/parser/array.rb +8 -3
  33. data/lib/io_streams/tabular/parser/csv.rb +6 -2
  34. data/lib/io_streams/tabular/parser/hash.rb +4 -1
  35. data/lib/io_streams/tabular/parser/json.rb +3 -1
  36. data/lib/io_streams/tabular/parser/psv.rb +3 -1
  37. data/lib/io_streams/tabular/utility/csv_row.rb +9 -8
  38. data/lib/io_streams/utils.rb +22 -0
  39. data/lib/io_streams/version.rb +1 -1
  40. data/lib/io_streams/writer.rb +28 -0
  41. data/lib/io_streams/xlsx/reader.rb +7 -19
  42. data/lib/io_streams/zip/reader.rb +7 -26
  43. data/lib/io_streams/zip/writer.rb +21 -38
  44. data/lib/iostreams.rb +15 -15
  45. data/test/bzip2_reader_test.rb +3 -3
  46. data/test/bzip2_writer_test.rb +3 -3
  47. data/test/deprecated_test.rb +123 -0
  48. data/test/encode_reader_test.rb +3 -3
  49. data/test/encode_writer_test.rb +6 -6
  50. data/test/gzip_reader_test.rb +2 -2
  51. data/test/gzip_writer_test.rb +3 -3
  52. data/test/io_streams_test.rb +43 -136
  53. data/test/line_reader_test.rb +20 -20
  54. data/test/line_writer_test.rb +3 -3
  55. data/test/path_test.rb +30 -28
  56. data/test/paths/file_test.rb +206 -0
  57. data/test/paths/http_test.rb +34 -0
  58. data/test/paths/matcher_test.rb +111 -0
  59. data/test/paths/s3_test.rb +207 -0
  60. data/test/pgp_reader_test.rb +8 -8
  61. data/test/pgp_writer_test.rb +13 -13
  62. data/test/record_reader_test.rb +5 -5
  63. data/test/record_writer_test.rb +4 -4
  64. data/test/row_reader_test.rb +5 -5
  65. data/test/row_writer_test.rb +6 -6
  66. data/test/stream_test.rb +116 -0
  67. data/test/streams_test.rb +255 -0
  68. data/test/utils_test.rb +20 -0
  69. data/test/xlsx_reader_test.rb +3 -3
  70. data/test/zip_reader_test.rb +12 -12
  71. data/test/zip_writer_test.rb +5 -5
  72. metadata +33 -45
  73. data/lib/io_streams/base_path.rb +0 -72
  74. data/lib/io_streams/file/path.rb +0 -58
  75. data/lib/io_streams/file/reader.rb +0 -12
  76. data/lib/io_streams/file/writer.rb +0 -22
  77. data/lib/io_streams/http/reader.rb +0 -71
  78. data/lib/io_streams/s3.rb +0 -26
  79. data/lib/io_streams/s3/path.rb +0 -40
  80. data/lib/io_streams/s3/reader.rb +0 -28
  81. data/lib/io_streams/s3/writer.rb +0 -85
  82. data/lib/io_streams/sftp/reader.rb +0 -67
  83. data/lib/io_streams/sftp/writer.rb +0 -68
  84. data/test/base_path_test.rb +0 -35
  85. data/test/file_path_test.rb +0 -97
  86. data/test/file_reader_test.rb +0 -33
  87. data/test/file_writer_test.rb +0 -50
  88. data/test/http_reader_test.rb +0 -38
  89. data/test/s3_reader_test.rb +0 -41
  90. 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.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}")
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
- 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
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 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}")
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
- 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?
12
+ ::SymmetricEncryption::Writer.open(input_stream, compress: compress, **args, &block)
13
+ end
17
14
 
18
- IOStreams::File::Writer.open(file_name_or_io) do |file|
19
- ::SymmetricEncryption::Writer.open(file, compress: compress, **args, &block)
20
- end
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
- raise(IOStreams::Errors::InvalidHeader, "Missing mandatory header when trying to convert a row into a hash") unless columns
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
- raise(IOStreams::Errors::TypeMismatch, "Don't know how to convert #{row.class.name} to an Array without the header columns being set.") unless row.is_a?(Array)
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
- raise(IOStreams::Errors::InvalidHeader, "Format is :array. Invalid input header: #{row.class.name}") unless row.is_a?(::Array)
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
- raise(IOStreams::Errors::TypeMismatch, "Format is :array. Invalid input: #{row.class.name}") unless row.is_a?(::Array)
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
- raise(IOStreams::Errors::InvalidHeader, "Format is :csv. Invalid input header: #{row.class.name}") unless row.is_a?(String)
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
- raise(IOStreams::Errors::TypeMismatch, "Format is :csv. Invalid input: #{row.class.name}") unless row.is_a?(String)
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
- raise(IOStreams::Errors::TypeMismatch, "Format is :hash. Invalid input: #{row.class.name}") unless row.is_a?(::Hash)
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
- raise(IOStreams::Errors::TypeMismatch, "Format is :json. Invalid input: #{row.class.name}") unless row.is_a?(String)
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
- raise(IOStreams::Errors::TypeMismatch, "Format is :psv. Invalid input: #{row.class.name}") unless row.is_a?(String)
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 and @skip_lines.match line
27
+ return if @skip_lines&.match(line)
28
28
 
29
29
  in_extended_col = false
30
- csv = Array.new
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) % 2 != 0
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) % 2 != 0
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 or @converters.empty?
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 and not csv.respond_to? :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
- alias_method :to_csv, :render
111
-
112
+ alias to_csv render
112
113
  end
113
114
  end
114
115
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module IOStreams
2
- VERSION = '0.20.3'
2
+ VERSION = "1.0.0.beta".freeze
3
3
  end
@@ -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 or stream into CSV format.
7
- def self.open(file_name_or_io, _ = nil, &block)
8
- return extract_csv(file_name_or_io, &block) if file_name_or_io.is_a?(String)
9
-
10
- # Creek gem can only work against a file, not a stream, so create temp file.
11
- IOStreams::File::Path.temp_file_name('iostreams_xlsx') do |temp_file_name|
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.read_file(file_name, entry_file_name)
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.new("File #{entry_file_name} not found within zip file."))
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.close if zin
44
- fin.close if 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.read_file(file_name, entry_file_name)
52
- if !defined?(::Zip)
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
- # file_name_or_io [String]
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
- # zip_file_name: [String]
11
- # Name of the file within the Zip Stream
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.open(file_name_or_io, zip_file_name: nil, buffer_size: 65536, &block)
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
- zip_file_name = file_name_or_io.to_s[0..-5] if zip_file_name.nil? && !IOStreams.writer_stream?(file_name_or_io) && (file_name_or_io =~ /\.(zip)\z/)
27
- zip_file_name ||= 'file'
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
- # File name supplied
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(zip_file_name))
40
+ zout.put_next_entry(Java::JavaUtilZip::ZipEntry.new(entry_file_name))
56
41
  io = zout.to_io
57
- block.call(io)
42
+ yield(io)
58
43
  ensure
59
- io.close if io && !io.closed?
60
- out.close if out
44
+ io&.close
45
+ out&.close
61
46
  end
62
-
63
47
  else
64
- def self.write_file(file_name, zip_file_name, &block)
65
- IOStreams::File::Path.mkpath(file_name)
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(zip_file_name)
68
- block.call(zos)
52
+ zos.put_next_entry(entry_file_name)
53
+ yield(zos)
69
54
  ensure
70
- zos.close if zos
55
+ zos&.close
71
56
  end
72
-
73
57
  end
74
-
75
58
  end
76
59
  end
77
60
  end