iostreams 1.11.0 → 2.0.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -2
  3. data/Rakefile +7 -0
  4. data/lib/io_streams/builder.rb +9 -9
  5. data/lib/io_streams/bzip2/writer.rb +1 -1
  6. data/lib/io_streams/encode/reader.rb +2 -2
  7. data/lib/io_streams/encode/writer.rb +5 -5
  8. data/lib/io_streams/gzip/reader.rb +1 -1
  9. data/lib/io_streams/gzip/writer.rb +1 -1
  10. data/lib/io_streams/io_streams.rb +45 -19
  11. data/lib/io_streams/line/reader.rb +2 -2
  12. data/lib/io_streams/line/writer.rb +1 -1
  13. data/lib/io_streams/path.rb +2 -2
  14. data/lib/io_streams/paths/file.rb +10 -10
  15. data/lib/io_streams/paths/http.rb +80 -7
  16. data/lib/io_streams/paths/matcher.rb +3 -3
  17. data/lib/io_streams/paths/s3.rb +3 -3
  18. data/lib/io_streams/paths/sftp.rb +7 -8
  19. data/lib/io_streams/pgp/reader.rb +23 -10
  20. data/lib/io_streams/pgp/writer.rb +93 -32
  21. data/lib/io_streams/pgp.rb +188 -60
  22. data/lib/io_streams/reader.rb +4 -4
  23. data/lib/io_streams/record/reader.rb +3 -4
  24. data/lib/io_streams/record/writer.rb +3 -4
  25. data/lib/io_streams/row/reader.rb +1 -1
  26. data/lib/io_streams/row/writer.rb +1 -1
  27. data/lib/io_streams/stream.rb +36 -30
  28. data/lib/io_streams/symmetric_encryption/reader.rb +2 -2
  29. data/lib/io_streams/symmetric_encryption/writer.rb +4 -4
  30. data/lib/io_streams/tabular/header.rb +18 -6
  31. data/lib/io_streams/tabular/parser/array.rb +0 -10
  32. data/lib/io_streams/tabular/parser/csv.rb +6 -38
  33. data/lib/io_streams/tabular/parser/fixed.rb +5 -5
  34. data/lib/io_streams/tabular/parser/psv.rb +0 -12
  35. data/lib/io_streams/tabular.rb +5 -10
  36. data/lib/io_streams/utils.rb +3 -2
  37. data/lib/io_streams/version.rb +1 -1
  38. data/lib/io_streams/writer.rb +6 -6
  39. data/lib/io_streams/xlsx/reader.rb +1 -1
  40. data/lib/io_streams/zip/writer.rb +22 -10
  41. data/lib/iostreams.rb +0 -1
  42. metadata +28 -111
  43. data/lib/io_streams/deprecated.rb +0 -216
  44. data/lib/io_streams/tabular/utility/csv_row.rb +0 -105
  45. data/test/builder_test.rb +0 -311
  46. data/test/bzip2_reader_test.rb +0 -27
  47. data/test/bzip2_writer_test.rb +0 -56
  48. data/test/deprecated_test.rb +0 -121
  49. data/test/encode_reader_test.rb +0 -51
  50. data/test/encode_writer_test.rb +0 -90
  51. data/test/files/embedded_lines_test.csv +0 -7
  52. data/test/files/multiple_files.zip +0 -0
  53. data/test/files/spreadsheet.xlsx +0 -0
  54. data/test/files/test.csv +0 -4
  55. data/test/files/test.json +0 -3
  56. data/test/files/test.psv +0 -4
  57. data/test/files/text file.txt +0 -3
  58. data/test/files/text.txt +0 -3
  59. data/test/files/text.txt.bz2 +0 -0
  60. data/test/files/text.txt.gz +0 -0
  61. data/test/files/text.txt.gz.zip +0 -0
  62. data/test/files/text.zip +0 -0
  63. data/test/files/text.zip.gz +0 -0
  64. data/test/files/unclosed_quote_large_test.csv +0 -1658
  65. data/test/files/unclosed_quote_test.csv +0 -4
  66. data/test/files/unclosed_quote_test2.csv +0 -3
  67. data/test/gzip_reader_test.rb +0 -27
  68. data/test/gzip_writer_test.rb +0 -52
  69. data/test/io_streams_test.rb +0 -132
  70. data/test/line_reader_test.rb +0 -325
  71. data/test/line_writer_test.rb +0 -59
  72. data/test/minimal_file_reader.rb +0 -25
  73. data/test/path_test.rb +0 -55
  74. data/test/paths/file_test.rb +0 -213
  75. data/test/paths/http_test.rb +0 -34
  76. data/test/paths/matcher_test.rb +0 -120
  77. data/test/paths/s3_test.rb +0 -220
  78. data/test/paths/sftp_test.rb +0 -106
  79. data/test/pgp_reader_test.rb +0 -46
  80. data/test/pgp_test.rb +0 -267
  81. data/test/pgp_writer_test.rb +0 -130
  82. data/test/record_reader_test.rb +0 -60
  83. data/test/record_writer_test.rb +0 -82
  84. data/test/row_reader_test.rb +0 -35
  85. data/test/row_writer_test.rb +0 -56
  86. data/test/stream_test.rb +0 -577
  87. data/test/tabular_test.rb +0 -338
  88. data/test/test_helper.rb +0 -40
  89. data/test/utils_test.rb +0 -20
  90. data/test/xlsx_reader_test.rb +0 -37
  91. data/test/zip_reader_test.rb +0 -53
  92. data/test/zip_writer_test.rb +0 -48
@@ -1,216 +0,0 @@
1
- module IOStreams
2
- UTF8_ENCODING = Encoding.find("UTF-8").freeze
3
- BINARY_ENCODING = Encoding.find("BINARY").freeze
4
-
5
- # Deprecated IOStreams from v0.x. Do not use, will be removed soon.
6
- module Deprecated
7
- def self.included(base)
8
- base.extend ClassMethods
9
- end
10
-
11
- module ClassMethods
12
- # DEPRECATED. Use `#path` or `#io`
13
- # Examples:
14
- # IOStreams.path("data.zip").reader { |f| f.read(100) }
15
- #
16
- # IOStreams.path(file_name).option(:encode, encoding: "BINARY").reader { |f| f.read(100) }
17
- #
18
- # io_stream = StringIO.new("Hello World")
19
- # IOStreams.stream(io_stream).reader { |f| f.read(100) }
20
- def reader(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, &block)
21
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
22
- path.reader(&block)
23
- end
24
-
25
- # DEPRECATED
26
- def each_line(file_name_or_io, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
27
- path = build_path(file_name_or_io, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
28
- path.each(:line, **args, &block)
29
- end
30
-
31
- # DEPRECATED
32
- def each_row(file_name_or_io, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
33
- path = build_path(file_name_or_io, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
34
- path.each(:array, **args, &block)
35
- end
36
-
37
- # DEPRECATED
38
- def each_record(file_name_or_io, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
39
- path = build_path(file_name_or_io, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
40
- path.each(:hash, **args, &block)
41
- end
42
-
43
- # DEPRECATED. Use `#path` or `#io`
44
- # Examples:
45
- # IOStreams.path("data.zip").writer { |f| f.write("Hello World") }
46
- #
47
- # IOStreams.path(file_name).option(:encode, encoding: "BINARY").writer { |f| f.write("Hello World") }
48
- #
49
- # io_stream = StringIO.new("Hello World")
50
- # IOStreams.stream(io_stream).writer { |f| f.write("Hello World") }
51
- def writer(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, &block)
52
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
53
- path.writer(&block)
54
- end
55
-
56
- # DEPRECATED
57
- def line_writer(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
58
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
59
- path.writer(:line, **args, &block)
60
- end
61
-
62
- # DEPRECATED
63
- def row_writer(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
64
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
65
- path.writer(:array, **args, &block)
66
- end
67
-
68
- # DEPRECATED
69
- def record_writer(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
70
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
71
- path.writer(:hash, **args, &block)
72
- end
73
-
74
- # Copies the source file/stream to the target file/stream.
75
- # Returns [Integer] the number of bytes copied
76
- #
77
- # Example: Copy between 2 files
78
- # IOStreams.copy('a.csv', 'b.csv')
79
- #
80
- # Example: Read content from a Xlsx file and write it out in CSV form.
81
- # IOStreams.copy('a.xlsx', 'b.csv')
82
- #
83
- # Example:
84
- # # Read content from a JSON file and write it out in CSV form.
85
- # #
86
- # # The output header for the CSV file is extracted from the first row in the JSON file.
87
- # # If the first JSON row does not contain all the column names then they will be ignored
88
- # # for the rest of the file.
89
- # IOStreams.copy('a.json', 'b.csv')
90
- #
91
- # Example:
92
- # # Read a PSV file and write out a CSV file from it.
93
- # IOStreams.copy('a.psv', 'b.csv')
94
- #
95
- # Example:
96
- # # Copy between 2 files, encrypting the target file with Symmetric Encryption
97
- # # Since the target file_name already includes `.enc` in the filename, it is automatically
98
- # # encrypted.
99
- # IOStreams.copy('a.csv', 'b.csv.enc')
100
- #
101
- # Example:
102
- # # Copy between 2 files, encrypting the target file with Symmetric Encryption
103
- # # Since the target file_name does not include `.enc` in the filename, to encrypt it
104
- # # the encryption stream is added.
105
- # IOStreams.copy('a.csv', 'b', target_options: [:enc])
106
- #
107
- # Example:
108
- # # Copy between 2 files, encrypting the target file with Symmetric Encryption
109
- # # Since the target file_name does not include `.enc` in the filename, to encrypt it
110
- # # the encryption stream is added, along with the optional compress option.
111
- # IOStreams.copy('a.csv', 'b', target_options: [enc: { compress: true }])
112
- #
113
- # Example:
114
- # # Create a pgp encrypted file.
115
- # # For PGP Encryption the recipients email address is required.
116
- # IOStreams.copy('a.xlsx', 'b.csv.pgp', target_options: [:csv, pgp: { recipient_email: 'user@nospam.org' }])
117
- #
118
- # Example: Copy between 2 existing streams
119
- # IOStreams.reader('a.csv') do |source_stream|
120
- # IOStreams.writer('b.csv.enc') do |target_stream|
121
- # IOStreams.copy(source_stream, target_stream)
122
- # end
123
- # end
124
- #
125
- # Example:
126
- # # Copy between 2 csv files, reducing the number of columns present and encrypting the
127
- # # target file with Symmetric Encryption
128
- # output_headers = %w[name address]
129
- # IOStreams.copy(
130
- # 'a.csv',
131
- # 'b.csv.enc',
132
- # target_options: [csv:{headers: output_headers}, enc: {compress: true}]
133
- # )
134
- #
135
- # Example:
136
- # # Copy a locally encrypted file to AWS S3.
137
- # # Decrypts the file, then compresses it with gzip as it is being streamed into S3.
138
- # # Useful for when the entire bucket is encrypted on S3.
139
- # IOStreams.copy('a.csv.enc', 's3://my_bucket/b.csv.gz')
140
- def copy(source_file_name_or_io, target_file_name_or_io, buffer_size: nil, source_options: {}, target_options: {})
141
- # TODO: prevent stream conversions when reader and writer streams are the same!
142
- reader(source_file_name_or_io, **source_options) do |source_stream|
143
- writer(target_file_name_or_io, **target_options) do |target_stream|
144
- IO.copy_stream(source_stream, target_stream)
145
- end
146
- end
147
- end
148
-
149
- # DEPRECATED
150
- def reader_stream?(file_name_or_io)
151
- file_name_or_io.respond_to?(:read)
152
- end
153
-
154
- # DEPRECATED
155
- def writer_stream?(file_name_or_io)
156
- file_name_or_io.respond_to?(:write)
157
- end
158
-
159
- # DEPRECATED. Use Path#compressed?
160
- def compressed?(file_name)
161
- Path.new(file_name).compressed?
162
- end
163
-
164
- # DEPRECATED. Use Path#encrypted?
165
- def encrypted?(file_name)
166
- Path.new(file_name).encrypted?
167
- end
168
-
169
- # DEPRECATED
170
- def line_reader(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
171
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
172
- path.reader(:line, **args, &block)
173
- end
174
-
175
- # DEPRECATED
176
- def row_reader(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
177
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
178
- path.reader(:line, **args, &block)
179
- end
180
-
181
- # DEPRECATED
182
- def record_reader(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil, **args, &block)
183
- path = build_path(file_name_or_io, streams: streams, file_name: file_name, encoding: encoding, encode_cleaner: encode_cleaner, encode_replace: encode_replace)
184
- path.reader(:hash, **args, &block)
185
- end
186
-
187
- private
188
-
189
- def build_path(file_name_or_io, streams: nil, file_name: nil, encoding: nil, encode_cleaner: nil, encode_replace: nil)
190
- path = new(file_name_or_io)
191
- path.file_name(file_name) if file_name
192
-
193
- apply_old_style_streams(path, streams) if streams
194
-
195
- if encoding || encode_cleaner || encode_replace
196
- path.option_or_stream(:encode, encoding: encoding, cleaner: encode_cleaner, replace: encode_replace)
197
- end
198
-
199
- path
200
- end
201
-
202
- # Applies old form streams to the path
203
- def apply_old_style_streams(path, streams)
204
- if streams.is_a?(Symbol)
205
- path.stream(streams)
206
- elsif streams.is_a?(Array)
207
- streams.each { |stream| apply_old_style_streams(path, stream) }
208
- elsif streams.is_a?(Hash)
209
- streams.each_pair { |stream, options| path.stream(stream, **options) }
210
- else
211
- raise ArgumentError, "Invalid old style stream supplied: #{params.inspect}"
212
- end
213
- end
214
- end
215
- end
216
- end
@@ -1,105 +0,0 @@
1
- require "csv"
2
- module IOStreams
3
- class Tabular
4
- module Utility
5
- # For parsing a single line of CSV at a time
6
- # 2 to 3 times better performance than CSV.parse_line and considerably less
7
- # garbage collection required.
8
- #
9
- # Note: Only used prior to Ruby 2.6
10
- class CSVRow < ::CSV
11
- UTF8_ENCODING = Encoding.find("UTF-8").freeze
12
-
13
- def initialize(encoding = UTF8_ENCODING)
14
- @io = StringIO.new("".force_encoding(encoding))
15
- super(@io, row_sep: "")
16
- end
17
-
18
- # Parse a single line of CSV data
19
- # Parameters
20
- # line [String]
21
- # A single line of CSV data without any line terminators
22
- def parse(line)
23
- return if IOStreams::Utils.blank?(line)
24
- return if @skip_lines&.match(line)
25
-
26
- in_extended_col = false
27
- csv = []
28
- parts = line.split(@col_sep, -1)
29
- csv << nil if parts.empty?
30
-
31
- # This loop is the hot path of csv parsing. Some things may be non-dry
32
- # for a reason. Make sure to benchmark when refactoring.
33
- parts.each do |part|
34
- if in_extended_col
35
- # If we are continuing a previous column
36
- if part[-1] == @quote_char && part.count(@quote_char).odd?
37
- # extended column ends
38
- csv.last << part[0..-2]
39
- raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}" if csv.last =~ @parsers[:stray_quote]
40
-
41
- csv.last.gsub!(@quote_char * 2, @quote_char)
42
- in_extended_col = false
43
- else
44
- csv.last << part
45
- csv.last << @col_sep
46
- end
47
- elsif part[0] == @quote_char
48
- # If we are starting a new quoted column
49
- if part[-1] != @quote_char || part.count(@quote_char).odd?
50
- # start an extended column
51
- csv << part[1..-1]
52
- csv.last << @col_sep
53
- in_extended_col = true
54
- else
55
- # regular quoted column
56
- csv << part[1..-2]
57
- raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}" if csv.last =~ @parsers[:stray_quote]
58
-
59
- csv.last.gsub!(@quote_char * 2, @quote_char)
60
- end
61
- elsif part =~ @parsers[:quote_or_nl]
62
- # Unquoted field with bad characters.
63
- if part =~ @parsers[:nl_or_lf]
64
- raise MalformedCSVError, "Unquoted fields do not allow \\r or \\n (line #{lineno + 1})."
65
- else
66
- raise MalformedCSVError, "Illegal quoting in line #{lineno + 1}."
67
- end
68
- else
69
- # Regular ole unquoted field.
70
- csv << (part.empty? ? nil : part)
71
- end
72
- end
73
-
74
- # Replace tacked on @col_sep with @row_sep if we are still in an extended
75
- # column.
76
- csv[-1][-1] = @row_sep if in_extended_col
77
-
78
- raise MalformedCSVError, "Unclosed quoted field on line #{lineno + 1}." if in_extended_col
79
-
80
- @lineno += 1
81
-
82
- # save fields unconverted fields, if needed...
83
- unconverted = csv.dup if @unconverted_fields
84
-
85
- # convert fields, if needed...
86
- csv = convert_fields(csv) unless @use_headers || @converters.empty?
87
- # parse out header rows and handle CSV::Row conversions...
88
- csv = parse_headers(csv) if @use_headers
89
-
90
- # inject unconverted fields and accessor, if requested...
91
- add_unconverted_fields(csv, unconverted) if @unconverted_fields && (!csv.respond_to? :unconverted_fields)
92
-
93
- csv
94
- end
95
-
96
- # Return the supplied array as a single line CSV string.
97
- def render(row)
98
- row.map(&@quote).join(@col_sep) + @row_sep # quote and separate
99
- end
100
-
101
- alias to_csv render
102
- end
103
- end
104
- end
105
- end
data/test/builder_test.rb DELETED
@@ -1,311 +0,0 @@
1
- require_relative "test_helper"
2
-
3
- class BuilderTest < Minitest::Test
4
- describe IOStreams::Builder do
5
- let(:file_name) { "my/path/abc.bcd.xlsx.zip.gz.pgp" }
6
- let(:streams) { IOStreams::Builder.new(file_name) }
7
-
8
- describe "#option" do
9
- it "adds one option" do
10
- streams.option(:pgp, passphrase: "unlock-me")
11
- assert_equal({pgp: {passphrase: "unlock-me"}}, streams.options)
12
- end
13
-
14
- it "adds options in order" do
15
- streams.option(:pgp, passphrase: "unlock-me")
16
- streams.option(:enc, compress: false)
17
- assert_equal({pgp: {passphrase: "unlock-me"}, enc: {compress: false}}, streams.options)
18
- end
19
-
20
- it "will not add an option if a stream was already set" do
21
- streams.stream(:pgp, passphrase: "unlock-me")
22
- assert_raises ArgumentError do
23
- streams.option(:pgp, passphrase: "unlock-me")
24
- end
25
- end
26
-
27
- it "will not add an invalid option" do
28
- assert_raises ArgumentError do
29
- streams.option(:blah, value: 23)
30
- end
31
- end
32
-
33
- describe "with no file_name" do
34
- let(:file_name) { nil }
35
-
36
- it "prevents options being set" do
37
- assert_raises ArgumentError do
38
- streams.option(:pgp, passphrase: "unlock-me")
39
- end
40
- end
41
- end
42
- end
43
-
44
- describe "#format" do
45
- it "detects the format from the file name" do
46
- streams = IOStreams::Builder.new("abc.json")
47
- assert_equal :json, streams.format
48
- end
49
-
50
- it "is nil if the file name has no meaningful format" do
51
- assert_nil streams.format
52
- end
53
-
54
- it "returns set format with no file_name" do
55
- streams = IOStreams::Builder.new
56
- streams.format = :csv
57
- assert_equal :csv, streams.format
58
- end
59
-
60
- it "returns set format with file_name" do
61
- streams = IOStreams::Builder.new("abc.json")
62
- streams.format = :csv
63
- assert_equal :csv, streams.format
64
- end
65
-
66
- it "validates bad format" do
67
- assert_raises ArgumentError do
68
- streams.format = :blah
69
- end
70
- end
71
- end
72
-
73
- describe "#stream" do
74
- it "adds one stream" do
75
- streams.stream(:pgp, passphrase: "unlock-me")
76
- assert_equal({pgp: {passphrase: "unlock-me"}}, streams.streams)
77
- end
78
-
79
- it "adds streams in order" do
80
- streams.stream(:pgp, passphrase: "unlock-me")
81
- streams.stream(:enc, compress: false)
82
- assert_equal({pgp: {passphrase: "unlock-me"}, enc: {compress: false}}, streams.streams)
83
- end
84
-
85
- it "will not add a stream if an option was already set" do
86
- streams.option(:pgp, passphrase: "unlock-me")
87
- assert_raises ArgumentError do
88
- streams.stream(:pgp, passphrase: "unlock-me")
89
- end
90
- end
91
-
92
- it "will not add an invalid stream" do
93
- assert_raises ArgumentError do
94
- streams.stream(:blah, value: 23)
95
- end
96
- end
97
- end
98
-
99
- describe "#reader" do
100
- let :gzip_string do
101
- io_string = StringIO.new("".b)
102
- IOStreams::Gzip::Writer.stream(io_string) do |io|
103
- io.write("Hello World")
104
- end
105
- io_string.string
106
- end
107
-
108
- it "directly calls block for an empty stream" do
109
- string_io = StringIO.new
110
- value = nil
111
- streams.stream(:none)
112
- streams.reader(string_io) do |io|
113
- assert_equal io, string_io
114
- value = 32
115
- end
116
- assert_equal 32, value
117
- end
118
-
119
- it "returns the reader" do
120
- string_io = StringIO.new(gzip_string)
121
- streams.stream(:gz)
122
- streams.reader(string_io) do |io|
123
- assert io.is_a?(::Zlib::GzipReader), io
124
- end
125
- end
126
-
127
- it "returns the last reader" do
128
- string_io = StringIO.new(gzip_string)
129
- streams.stream(:encode)
130
- streams.stream(:gz)
131
- streams.reader(string_io) do |io|
132
- assert io.is_a?(IOStreams::Encode::Reader), io
133
- end
134
- end
135
- end
136
-
137
- describe "#writer" do
138
- it "directly calls block for an empty stream" do
139
- string_io = StringIO.new
140
- value = nil
141
- streams.stream(:none)
142
- streams.writer(string_io) do |io|
143
- assert_equal io, string_io
144
- value = 32
145
- end
146
- assert_equal 32, value
147
- end
148
-
149
- it "returns the reader" do
150
- string_io = StringIO.new
151
- streams.stream(:zip)
152
- streams.writer(string_io) do |io|
153
- assert io.is_a?(ZipTricks::Streamer::Writable), io
154
- end
155
- end
156
-
157
- it "returns the last reader" do
158
- string_io = StringIO.new
159
- streams.stream(:encode)
160
- streams.stream(:zip)
161
- streams.writer(string_io) do |io|
162
- assert io.is_a?(IOStreams::Encode::Writer), io
163
- end
164
- end
165
- end
166
-
167
- # Internal methods
168
-
169
- describe "#class_for_stream" do
170
- it "xlsx" do
171
- assert_equal IOStreams::Xlsx::Reader, streams.send(:class_for_stream, :reader, :xlsx)
172
- end
173
-
174
- it "gzip" do
175
- assert_equal IOStreams::Gzip::Writer, streams.send(:class_for_stream, :writer, :gzip)
176
- end
177
-
178
- it "unknown" do
179
- assert_raises ArgumentError do
180
- streams.send(:class_for_stream, :reader, :unknown)
181
- end
182
- end
183
- end
184
-
185
- describe "#parse_extensions" do
186
- it "single stream" do
187
- streams = IOStreams::Builder.new("my/path/abc.xlsx")
188
- assert_equal %i[xlsx], streams.send(:parse_extensions)
189
- end
190
-
191
- it "empty" do
192
- streams = IOStreams::Builder.new("my/path/abc.csv")
193
- assert_equal [], streams.send(:parse_extensions)
194
- end
195
-
196
- it "handles multiple extensions" do
197
- assert_equal %i[xlsx zip gz pgp], streams.send(:parse_extensions)
198
- end
199
-
200
- describe "case-insensitive" do
201
- let(:file_name) { "a.XlsX.GzIp" }
202
-
203
- it "is case-insensitive" do
204
- assert_equal %i[xlsx gzip], streams.send(:parse_extensions)
205
- end
206
- end
207
- end
208
-
209
- describe "#pipeline" do
210
- it "with stream and file name" do
211
- expected = {enc: {compress: false}}
212
- streams.stream(:enc, compress: false)
213
- assert_equal expected, streams.pipeline
214
- end
215
-
216
- it "no file name, streams, or options" do
217
- expected = {}
218
- streams = IOStreams::Builder.new
219
- assert_equal expected, streams.pipeline
220
- end
221
-
222
- it "file name without options" do
223
- expected = {xlsx: {}, zip: {}, gz: {}, pgp: {}}
224
- assert_equal expected, streams.pipeline
225
- end
226
-
227
- it "file name with encode option" do
228
- expected = {encode: {encoding: "BINARY"}, xlsx: {}, zip: {}, gz: {}, pgp: {}}
229
- streams.option(:encode, encoding: "BINARY")
230
- assert_equal expected, streams.pipeline
231
- end
232
-
233
- it "file name with option" do
234
- expected = {xlsx: {}, zip: {}, gz: {}, pgp: {passphrase: "unlock-me"}}
235
- streams.option(:pgp, passphrase: "unlock-me")
236
- assert_equal expected, streams.pipeline
237
- end
238
- end
239
-
240
- describe "#remove_from_pipeline" do
241
- let(:file_name) { "my/path/abc.bz2.pgp" }
242
- it "removes a named stream from the pipeline" do
243
- assert_equal({bz2: {}, pgp: {}}, streams.pipeline)
244
- streams.remove_from_pipeline(:bz2)
245
- assert_equal({pgp: {}}, streams.pipeline)
246
- end
247
- it "removes a named stream from the pipeline with options" do
248
- streams.option(:pgp, passphrase: "unlock-me")
249
- assert_equal({bz2: {}, pgp: {passphrase: "unlock-me"}}, streams.pipeline)
250
- streams.remove_from_pipeline(:bz2)
251
- assert_equal({pgp: {passphrase: "unlock-me"}}, streams.pipeline)
252
- end
253
- end
254
-
255
- describe "#execute" do
256
- it "directly calls block for an empty stream" do
257
- string_io = StringIO.new
258
- value = nil
259
- streams.send(:execute, :writer, {}, string_io) do |io|
260
- assert_equal io, string_io
261
- value = 32
262
- end
263
- assert_equal 32, value
264
- end
265
-
266
- it "calls last block in one element stream" do
267
- pipeline = {simple: {arg: "first"}}
268
- string_io = StringIO.new
269
- streams.send(:execute, :writer, pipeline, string_io) { |io| io.write("last") }
270
- assert_equal "first>last", string_io.string
271
- end
272
-
273
- it "chains blocks in 2 element stream" do
274
- pipeline = {simple: {arg: "first"}, simple2: {arg: "second"}}
275
- string_io = StringIO.new
276
- streams.send(:execute, :writer, pipeline, string_io) { |io| io.write("last") }
277
- assert_equal "second>first>last", string_io.string
278
- end
279
-
280
- it "chains blocks in 3 element stream" do
281
- pipeline = {simple: {arg: "first"}, simple2: {arg: "second"}, simple3: {arg: "third"}}
282
- string_io = StringIO.new
283
- streams.send(:execute, :writer, pipeline, string_io) { |io| io.write("last") }
284
- assert_equal "third>second>first>last", string_io.string
285
- end
286
- end
287
-
288
- class SimpleStream
289
- def self.stream(io, **args)
290
- yield new(io, **args)
291
- end
292
-
293
- def self.open(file_name_or_io, **args, &block)
294
- file_name_or_io.is_a?(String) ? file(file_name_or_io, **args, &block) : stream(file_name_or_io, **args, &block)
295
- end
296
-
297
- def initialize(io, arg:)
298
- @io = io
299
- @arg = arg
300
- end
301
-
302
- def write(data)
303
- @io.write("#{@arg}>#{data}")
304
- end
305
- end
306
-
307
- IOStreams.register_extension(:simple, nil, SimpleStream)
308
- IOStreams.register_extension(:simple2, nil, SimpleStream)
309
- IOStreams.register_extension(:simple3, nil, SimpleStream)
310
- end
311
- end
@@ -1,27 +0,0 @@
1
- require_relative "test_helper"
2
-
3
- class Bzip2ReaderTest < Minitest::Test
4
- describe IOStreams::Bzip2::Reader do
5
- let :file_name do
6
- File.join(File.dirname(__FILE__), "files", "text.txt.bz2")
7
- end
8
-
9
- let :decompressed do
10
- File.read(File.join(File.dirname(__FILE__), "files", "text.txt"))
11
- end
12
-
13
- describe ".file" do
14
- it "file" do
15
- result = IOStreams::Bzip2::Reader.file(file_name, &:read)
16
- assert_equal decompressed, result
17
- end
18
-
19
- it "stream" do
20
- result = File.open(file_name) do |file|
21
- IOStreams::Bzip2::Reader.stream(file, &:read)
22
- end
23
- assert_equal decompressed, result
24
- end
25
- end
26
- end
27
- end