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.
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