iostreams 0.8.2 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f17b7f95e61b93d4e1e69390deaa85511c3765fc
4
- data.tar.gz: 29b174a1959de6a887040c89af1f63a072807f7b
3
+ metadata.gz: 48ec90a51a7475ae218a9758190fe4916d3af4a6
4
+ data.tar.gz: 6d5d33b93ab5b17884fdd79ae8e7d00adbe8adc9
5
5
  SHA512:
6
- metadata.gz: b2edf62d725b21fcb292ffbb025113462083a1f6156097e496c2d67e7a2cf77f10cb482b8094b431a5521577a29bb37bb19466e068acc6f1286f55a5e846acd9
7
- data.tar.gz: 7bb62d42bb61a520132188d9f8304b1eb1923bf7b7d9b119b675a0c8cb980884b24818011e805d57a82532e6c13ab32f3a4593a03fa7ff82103b59cb5381569c
6
+ metadata.gz: 99d8654a734acc8af11acaaba49c61904f71d055695279ebb912d0501c20ad812e8e8ba1a906f84586e8c72e67dcae590922fb5d9df100487e1c87ac72d533f7
7
+ data.tar.gz: 708bcf9a33706391aa38a3314e668149bd14c86964dd235281c1ec512f4ac1ff11a80a232ba996789d7d04dae08cf70541637837965ec10ac92b8152f8b87539
@@ -0,0 +1,21 @@
1
+ require 'csv'
2
+ module IOStreams
3
+ module CSV
4
+ class Reader
5
+ # Read from a file or stream
6
+ def self.open(file_name_or_io, options = Hash.new, &block)
7
+ unless IOStreams.reader_stream?(file_name_or_io)
8
+ ::CSV.open(file_name_or_io, options, &block)
9
+ else
10
+ begin
11
+ csv = ::CSV.new(file_name_or_io, options)
12
+ block.call(csv)
13
+ ensure
14
+ csv.close if csv
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module IOStreams
2
+ module CSV
3
+ class Writer
4
+ # Write to a file / stream, compressing with GZip
5
+ def self.open(file_name_or_io, options = {}, &block)
6
+ unless IOStreams.writer_stream?(file_name_or_io)
7
+ ::CSV.open(file_name_or_io, 'wb', options, &block)
8
+ else
9
+ begin
10
+ csv = ::CSV.new(file_name_or_io, options)
11
+ block.call(csv)
12
+ ensure
13
+ csv.close if csv
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -5,7 +5,7 @@ module IOStreams
5
5
 
6
6
  # Read from a file or stream
7
7
  def self.open(file_name_or_io, options={}, &block)
8
- if file_name_or_io.respond_to?(:read)
8
+ if IOStreams.reader_stream?(file_name_or_io)
9
9
  block.call(new(file_name_or_io, options))
10
10
  else
11
11
  ::File.open(file_name_or_io, 'rb') do |io|
@@ -25,16 +25,14 @@ module IOStreams
25
25
  # The input stream that implements #read
26
26
  #
27
27
  # options
28
- # :delimiter[Symbol|String]
28
+ # :delimiter[String]
29
29
  # Line / Record delimiter to use to break the stream up into records
30
- # nil
31
- # Automatically detect line endings and break up by line
32
- # Searches for the first "\r\n" or "\n" and then uses that as the
33
- # delimiter for all subsequent records
34
- # String:
35
- # Any string to break the stream up by
36
- # The records when saved will not include this delimiter
30
+ # Any string to break the stream up by
31
+ # The records when saved will not include this delimiter
37
32
  # Default: nil
33
+ # Automatically detect line endings and break up by line
34
+ # Searches for the first "\r\n" or "\n" and then uses that as the
35
+ # delimiter for all subsequent records
38
36
  #
39
37
  # :buffer_size [Integer]
40
38
  # Maximum size of the buffer into which to read the stream into for
@@ -44,7 +42,7 @@ module IOStreams
44
42
  #
45
43
  # :strip_non_printable [true|false]
46
44
  # Strip all non-printable characters read from the file
47
- # Default: true iff :encoding is UTF8_ENCODING, otherwise false
45
+ # Default: false
48
46
  #
49
47
  # :encoding
50
48
  # Force encoding to this encoding for all data being read
@@ -65,7 +63,7 @@ module IOStreams
65
63
  end
66
64
 
67
65
  # Returns each line at a time to to the supplied block
68
- def each_line(&block)
66
+ def each(&block)
69
67
  partial = nil
70
68
  loop do
71
69
  if read_chunk == 0
@@ -89,9 +87,29 @@ module IOStreams
89
87
  end
90
88
  end
91
89
 
90
+ alias_method :each_line, :each
91
+
92
+ # Reads length bytes from the I/O stream.
93
+ # Not recommended, but available if someone calls #read on this delimited reader
94
+ def read(length = nil, outbuf = nil)
95
+ if length
96
+ while (@buffer.size < length) && (read_chunk > 0)
97
+ end
98
+ data = @buffer.slice!(0, length)
99
+ outbuf << data if outbuf
100
+ data
101
+ else
102
+ while read_chunk > 0
103
+ end
104
+ @buffer
105
+ end
106
+ end
107
+
92
108
  ##########################################################################
93
109
  private
94
110
 
111
+ NOT_PRINTABLE = Regexp.compile(/[^[:print:]|\r|\n]/)
112
+
95
113
  # Returns [Integer] the number of bytes read into the internal buffer
96
114
  # Returns 0 on EOF
97
115
  def read_chunk
@@ -100,7 +118,7 @@ module IOStreams
100
118
  return 0 unless chunk
101
119
 
102
120
  # Strip out non-printable characters before converting to UTF-8
103
- chunk = chunk.scan(/[[:print:]]|\r|\n/).join if @strip_non_printable
121
+ chunk.gsub!(NOT_PRINTABLE, '') if @strip_non_printable
104
122
 
105
123
  @buffer << (@encoding ? chunk.force_encoding(@encoding) : chunk)
106
124
  chunk.size
@@ -0,0 +1,71 @@
1
+ module IOStreams
2
+ module Delimited
3
+ class Writer
4
+ attr_accessor :delimiter
5
+
6
+ # Write delimited records/lines to a file or stream
7
+ def self.open(file_name_or_io, options={}, &block)
8
+ if IOStreams.writer_stream?(file_name_or_io)
9
+ block.call(new(file_name_or_io, options))
10
+ else
11
+ ::File.open(file_name_or_io, 'wb') do |io|
12
+ block.call(new(io, options))
13
+ end
14
+ end
15
+ end
16
+
17
+ NOT_PRINTABLE = Regexp.compile(/[^[:print:]]/)
18
+
19
+ # A delimited stream writer that will write to the supplied output stream
20
+ #
21
+ # The output stream should be binary with no text conversions performed
22
+ # since `strip_non_printable` will be applied to the binary stream before
23
+ # converting to UTF-8
24
+ #
25
+ # Parameters
26
+ # output_stream
27
+ # The output stream that implements #write
28
+ #
29
+ # options
30
+ # delimiter: [String]
31
+ # Add the specified delimiter after every record when writing it
32
+ # to the output stream
33
+ # Default: OS Specific. Linux: "\n"
34
+ #
35
+ # :encoding
36
+ # Force encoding to this encoding for all data being read
37
+ # Default: UTF8_ENCODING
38
+ # Set to nil to disable encoding
39
+ #
40
+ # :strip_non_printable [true|false]
41
+ # Strip all non-printable characters read from the file
42
+ # Default: false
43
+ def initialize(output_stream, options={})
44
+ @output_stream = output_stream
45
+ options = options.dup
46
+ @delimiter = options.has_key?(:delimiter) ? options.delete(:delimiter) : $/.dup
47
+ @encoding = options.has_key?(:encoding) ? options.delete(:encoding) : UTF8_ENCODING
48
+ @strip_non_printable = options.delete(:strip_non_printable)
49
+ @strip_non_printable = @strip_non_printable.nil? && (@encoding == UTF8_ENCODING)
50
+ raise ArgumentError.new("Unknown IOStreams::Delimited::Writer#initialize options: #{options.inspect}") if options.size > 0
51
+ @delimiter.force_encoding(UTF8_ENCODING) if @delimiter
52
+ end
53
+
54
+ # Write a record or line to the output stream
55
+ def <<(record)
56
+ chunk = record.to_s
57
+ # Strip out non-printable characters before converting to UTF-8
58
+ chunk = chunk.gsub(NOT_PRINTABLE, '') if @strip_non_printable
59
+ @output_stream.write((@encoding ? chunk.force_encoding(@encoding) : chunk))
60
+ @output_stream.write(@delimiter)
61
+ end
62
+
63
+ # Write the given string to the underlying stream
64
+ # Note: Use of this method not recommended
65
+ def write(string)
66
+ @output_stream.write(string)
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -3,7 +3,7 @@ module IOStreams
3
3
  class Reader
4
4
  # Read from a file or stream
5
5
  def self.open(file_name_or_io, _=nil, &block)
6
- unless file_name_or_io.respond_to?(:read)
6
+ unless IOStreams.reader_stream?(file_name_or_io)
7
7
  ::File.open(file_name_or_io, 'rb', &block)
8
8
  else
9
9
  block.call(file_name_or_io)
@@ -12,4 +12,4 @@ module IOStreams
12
12
 
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -3,7 +3,7 @@ module IOStreams
3
3
  class Writer
4
4
  # Write to a file or stream
5
5
  def self.open(file_name_or_io, _=nil, &block)
6
- unless file_name_or_io.respond_to?(:write)
6
+ unless IOStreams.writer_stream?(file_name_or_io)
7
7
  ::File.open(file_name_or_io, 'wb', &block)
8
8
  else
9
9
  block.call(file_name_or_io)
@@ -12,4 +12,4 @@ module IOStreams
12
12
 
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -3,7 +3,7 @@ module IOStreams
3
3
  class Reader
4
4
  # Read from a gzip file or stream, decompressing the contents as it is read
5
5
  def self.open(file_name_or_io, _=nil, &block)
6
- unless file_name_or_io.respond_to?(:read)
6
+ unless IOStreams.reader_stream?(file_name_or_io)
7
7
  ::Zlib::GzipReader.open(file_name_or_io, &block)
8
8
  else
9
9
  begin
@@ -17,4 +17,4 @@ module IOStreams
17
17
 
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -3,7 +3,7 @@ module IOStreams
3
3
  class Writer
4
4
  # Write to a file / stream, compressing with GZip
5
5
  def self.open(file_name_or_io, _=nil, &block)
6
- unless file_name_or_io.respond_to?(:write)
6
+ unless IOStreams.writer_stream?(file_name_or_io)
7
7
  Zlib::GzipWriter.open(file_name_or_io, &block)
8
8
  else
9
9
  begin
@@ -17,4 +17,4 @@ module IOStreams
17
17
 
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -1,7 +1,7 @@
1
- require 'thread_safe'
1
+ require 'concurrent'
2
2
  module IOStreams
3
3
  # A registry to hold formats for processing files during upload or download
4
- @@extensions = ThreadSafe::Hash.new
4
+ @@extensions = Concurrent::Hash.new
5
5
 
6
6
  UTF8_ENCODING = Encoding.find('UTF-8').freeze
7
7
  BINARY_ENCODING = Encoding.find('BINARY').freeze
@@ -110,7 +110,7 @@ module IOStreams
110
110
  # IOStreams.reader('myfile.csv.enc', [:enc]) do |stream|
111
111
  # puts stream.read
112
112
  # end
113
- def self.reader(file_name_or_io, streams=nil, &block)
113
+ def self.reader(file_name_or_io, streams = nil, &block)
114
114
  stream(:reader, file_name_or_io, streams, &block)
115
115
  end
116
116
 
@@ -162,7 +162,7 @@ module IOStreams
162
162
  # IOStreams.writer('myfile.csv.zip', zip: { zip_file_name: 'myfile.csv' }) do |stream|
163
163
  # stream.write(data)
164
164
  # end
165
- def self.writer(file_name_or_io, streams=nil, &block)
165
+ def self.writer(file_name_or_io, streams = nil, &block)
166
166
  stream(:writer, file_name_or_io, streams, &block)
167
167
  end
168
168
 
@@ -185,6 +185,42 @@ module IOStreams
185
185
  bytes
186
186
  end
187
187
 
188
+ # Returns [true|false] whether the supplied file_name_or_io is a reader stream
189
+ def self.reader_stream?(file_name_or_io)
190
+ file_name_or_io.respond_to?(:read)
191
+ end
192
+
193
+ # Returns [true|false] whether the supplied file_name_or_io is a reader stream
194
+ def self.writer_stream?(file_name_or_io)
195
+ file_name_or_io.respond_to?(:write)
196
+ end
197
+
198
+ # Returns [true|false] whether the file is compressed
199
+ # Note: Currently only looks at the file name extension
200
+ def self.compressed?(file_name)
201
+ !(file_name =~ /\.(zip|gz|gzip|xls.|)\z/i).nil?
202
+ end
203
+
204
+ # Deletes the specified stream from the supplied streams if present
205
+ # Returns deleted stream, or nil if not found
206
+ def self.delete_stream(stream, streams)
207
+ raise(ArgumentError, "Argument :stream must be a symbol: #{stream.inspect}") unless stream.is_a?(Symbol)
208
+
209
+ Array(streams).delete_if do |_stream|
210
+ stream_key = _stream.is_a?(Symbol) ? _stream : _stream.keys.first
211
+ stream == stream_key
212
+ end
213
+ end
214
+
215
+ # Returns [true|false] whether the stream starts with a delimited reader or writer
216
+ def self.delimited_stream?(streams)
217
+ stream = Array(streams).first
218
+ return false unless stream
219
+
220
+ # TODO Need to figure out a way so that this is not hard-coded
221
+ [:xlsx, :xlsm, :delimited].include?(stream.is_a?(Symbol) ? stream : stream.keys.first)
222
+ end
223
+
188
224
  ##########################################################################
189
225
  private
190
226
 
@@ -192,7 +228,7 @@ module IOStreams
192
228
  StreamStruct = Struct.new(:klass, :options)
193
229
 
194
230
  # Returns a reader or writer stream
195
- def self.stream(type, file_name_or_io, streams=nil, &block)
231
+ def self.stream(type, file_name_or_io, streams = nil, &block)
196
232
  unless streams
197
233
  respond_to = type == :reader ? :read : :write
198
234
  streams = file_name_or_io.respond_to?(respond_to) ? [:file] : streams_for_file_name(file_name_or_io)
@@ -239,9 +275,13 @@ module IOStreams
239
275
 
240
276
  # Register File extensions
241
277
  # @formatter:off
242
- register_extension(:enc, SymmetricEncryption::Reader, SymmetricEncryption::Writer) if defined?(SymmetricEncryption)
243
- register_extension(:file, IOStreams::File::Reader, IOStreams::File::Writer)
244
- register_extension(:gz, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
245
- register_extension(:gzip, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
246
- register_extension(:zip, IOStreams::Zip::Reader, IOStreams::Zip::Writer)
278
+ register_extension(:enc, SymmetricEncryption::Reader, SymmetricEncryption::Writer) if defined?(SymmetricEncryption)
279
+ register_extension(:file, IOStreams::File::Reader, IOStreams::File::Writer)
280
+ register_extension(:gz, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
281
+ register_extension(:gzip, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
282
+ register_extension(:zip, IOStreams::Zip::Reader, IOStreams::Zip::Writer)
283
+ register_extension(:delimited, IOStreams::Delimited::Reader, IOStreams::Delimited::Writer)
284
+ register_extension(:xlsx, IOStreams::Xlsx::Reader, nil)
285
+ register_extension(:xlsm, IOStreams::Xlsx::Reader, nil)
286
+ #register_extension(:csv, IOStreams::CSV::Reader, IOStreams::CSV::Writer)
247
287
  end
@@ -0,0 +1,109 @@
1
+ module IOStreams
2
+ # Contains behavior for streams
3
+ #
4
+ # When a file is being read the streams are processed from right to left.
5
+ # When writing a file streams are processed left to right.
6
+ # For example:
7
+ # file.gz.enc ==> [:gz, :enc]
8
+ # Read: Unencrypt, then Gunzip
9
+ # Write: GZip, then Encrypt
10
+ class Streams
11
+
12
+ # Returns [Streams] collection of streams to process against the file
13
+ #
14
+ def self.streams_for_file_name(file_name)
15
+
16
+ end
17
+
18
+ # Create a processing stream given:
19
+ # - No stream. Defaults to :file
20
+ # - A single String implies a file_name and the streams will be created based on the file_name
21
+ # - One or more symbols or hashes for a stream
22
+ # - One or more arrays for streams
23
+ def initialize(*args)
24
+ if args.size == 0
25
+ @streams = [:file]
26
+ elsif args.size == 1
27
+ stream = args.first
28
+ if stream
29
+ @stream = stream.is_a?(String) ? streams_for_file_name(stream) : Array(stream)
30
+ else
31
+ @streams = [:file]
32
+ end
33
+ else
34
+ @streams = streams
35
+ end
36
+ @streams.flatten!
37
+ end
38
+
39
+ def delimited?
40
+
41
+ end
42
+
43
+ def delete(stream)
44
+
45
+ end
46
+
47
+ # Add another stream for processing
48
+ def <<(stream)
49
+
50
+ end
51
+
52
+ # Add a stream for processing
53
+ def unshift(stream)
54
+
55
+ end
56
+
57
+ private
58
+
59
+ # Return the Stream klass for the specified hash or symbol
60
+ # Parameters
61
+ # stream [Hash|Symbol]
62
+ def stream_for(stream)
63
+ if stream.is_a?(Symbol)
64
+ registered_klass(stream, {})
65
+ else
66
+ registered_klass(@stream.first)
67
+ end
68
+ end
69
+
70
+ # Returns [Array] the formats required to process the file by looking at
71
+ # its extension(s)
72
+ #
73
+ # Extensions supported:
74
+ # .zip Zip File [ :zip ]
75
+ # .gz, .gzip GZip File [ :gzip ]
76
+ # .enc File Encrypted using symmetric encryption [ :enc ]
77
+ # other All other extensions will be returned as: [ :file ]
78
+ #
79
+ # When a file is encrypted, it may also be compressed:
80
+ # .zip.enc [ :zip, :enc ]
81
+ # .gz.enc [ :gz, :enc ]
82
+ #
83
+ # Example Zip file:
84
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.zip')
85
+ # => [ :zip ]
86
+ #
87
+ # Example Encrypted Gzip file:
88
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.csv.gz.enc')
89
+ # => [ :gz, :enc ]
90
+ #
91
+ # Example plain text / binary file:
92
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.csv')
93
+ # => [ :file ]
94
+ def streams_for_file_name(file_name)
95
+ raise ArgumentError.new("RocketJob Cannot detect file format when uploading to stream: #{file_name.inspect}") if file_name.respond_to?(:read)
96
+
97
+ parts = file_name.split('.')
98
+ extensions = []
99
+ while extension = parts.pop
100
+ break unless @@extensions[extension.to_sym]
101
+ extensions.unshift(extension.to_sym)
102
+ end
103
+ extensions << :file if extensions.size == 0
104
+ extensions
105
+ end
106
+
107
+
108
+ end
109
+ end
@@ -1,3 +1,3 @@
1
1
  module IOStreams #:nodoc
2
- VERSION = '0.8.2'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -1,9 +1,3 @@
1
- begin
2
- require 'creek'
3
- rescue LoadError => e
4
- puts "Install the 'creek' gem for xlsx streaming support"
5
- raise(e)
6
- end
7
1
  require 'csv'
8
2
 
9
3
  module IOStreams
@@ -11,17 +5,7 @@ module IOStreams
11
5
  class Reader
12
6
  attr_reader :worksheet
13
7
 
14
- def initialize(workbook)
15
- @worksheet = workbook.sheets[0]
16
- end
17
-
18
- def each_line(&block)
19
- worksheet.rows.each do |row|
20
- block.call(row.values.to_csv(row_sep: nil))
21
- end
22
- end
23
-
24
- # Read from a xlsx file or stream.
8
+ # Read from a xlsx, or xlsm file or stream.
25
9
  #
26
10
  # Example:
27
11
  # IOStreams::Xlsx::Reader.open('spreadsheet.xlsx') do |spreadsheet_stream|
@@ -30,11 +14,17 @@ module IOStreams
30
14
  # end
31
15
  # end
32
16
  def self.open(file_name_or_io, options={}, &block)
17
+ begin
18
+ require 'creek' unless defined?(Creek::Book)
19
+ rescue LoadError => e
20
+ raise(LoadError, "Please install the 'creek' gem for xlsx streaming support. #{e.message}")
21
+ end
22
+
33
23
  options = options.dup
34
24
  buffer_size = options.delete(:buffer_size) || 65536
35
25
  raise(ArgumentError, "Unknown IOStreams::Xlsx::Reader option: #{options.inspect}") if options.size > 0
36
26
 
37
- if file_name_or_io.respond_to?(:read)
27
+ if IOStreams.reader_stream?(file_name_or_io)
38
28
  temp_file = Tempfile.new('rocket_job_xlsx')
39
29
  file_name = temp_file.to_path
40
30
 
@@ -50,6 +40,17 @@ module IOStreams
50
40
  temp_file.delete if temp_file
51
41
  end
52
42
 
43
+ def initialize(workbook)
44
+ @worksheet = workbook.sheets[0]
45
+ end
46
+
47
+ # Returns each [Array] row from the spreadsheet
48
+ def each(&block)
49
+ worksheet.rows.each { |row| block.call(row.values) }
50
+ end
51
+
52
+ alias_method :each_line, :each
53
+
53
54
  end
54
55
  end
55
56
  end
@@ -18,7 +18,7 @@ module IOStreams
18
18
  raise(ArgumentError, "Unknown IOStreams::Zip::Reader option: #{options.inspect}") if options.size > 0
19
19
 
20
20
  # File name supplied
21
- return read_file(file_name_or_io, &block) unless file_name_or_io.respond_to?(:read)
21
+ return read_file(file_name_or_io, &block) unless IOStreams.reader_stream?(file_name_or_io)
22
22
 
23
23
  # Stream supplied
24
24
  begin
@@ -4,10 +4,11 @@ module IOStreams
4
4
  # Write a single file in Zip format to the supplied output file name
5
5
  #
6
6
  # Parameters
7
- # zip_file_name [String]
7
+ # file_name_or_io [String]
8
8
  # Full path and filename for the output zip file
9
9
  #
10
- # file_name [String]
10
+ # Options
11
+ # :file_name [String]
11
12
  # Name of the file within the Zip Stream
12
13
  #
13
14
  # The stream supplied to the block only responds to #write
@@ -23,7 +24,7 @@ module IOStreams
23
24
  # is automatically created under the covers
24
25
  def self.open(file_name_or_io, options={}, &block)
25
26
  options = options.dup
26
- zip_file_name = options.delete(:zip_file_name)
27
+ zip_file_name = options.delete(:file_name) || options.delete(:zip_file_name)
27
28
  buffer_size = options.delete(:buffer_size) || 65536
28
29
  raise(ArgumentError, "Unknown IOStreams::Zip::Writer option: #{options.inspect}") if options.size > 0
29
30
 
@@ -32,7 +33,7 @@ module IOStreams
32
33
  zip_file_name ||= 'file'
33
34
 
34
35
  # File name supplied
35
- return write_file(file_name_or_io, zip_file_name, &block) unless file_name_or_io.respond_to?(:write)
36
+ return write_file(file_name_or_io, zip_file_name, &block) unless IOStreams.writer_stream?(file_name_or_io)
36
37
 
37
38
  # Stream supplied
38
39
  begin
@@ -83,4 +84,4 @@ module IOStreams
83
84
 
84
85
  end
85
86
  end
86
- end
87
+ end
data/lib/iostreams.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'io_streams/version'
2
2
  module IOStreams
3
+ module CSV
4
+ autoload :Reader, 'io_streams/csv/reader'
5
+ autoload :Writer, 'io_streams/csv/writer'
6
+ end
3
7
  module File
4
8
  autoload :Reader, 'io_streams/file/reader'
5
9
  autoload :Writer, 'io_streams/file/writer'
@@ -0,0 +1,34 @@
1
+ require_relative 'test_helper'
2
+
3
+ # Unit Test for IOStreams::File
4
+ module Streams
5
+ class CSVReaderTest < Minitest::Test
6
+ describe IOStreams::CSV::Reader do
7
+ before do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'test.csv')
9
+ @data = CSV.read(@file_name)
10
+ end
11
+
12
+ describe '.open' do
13
+ it 'file' do
14
+ rows = []
15
+ IOStreams::CSV::Reader.open(@file_name) do |io|
16
+ io.each { |row| rows << row }
17
+ end
18
+ assert_equal @data, rows
19
+ end
20
+
21
+ it 'stream' do
22
+ rows = []
23
+ File.open(@file_name) do |file|
24
+ IOStreams::CSV::Reader.open(file) do |io|
25
+ io.each { |row| rows << row }
26
+ end
27
+ end
28
+ assert_equal @data, rows
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'test_helper'
2
+ require 'csv'
3
+
4
+ module Streams
5
+ class CSVWriterTest < Minitest::Test
6
+ describe IOStreams::CSV::Writer do
7
+ before do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'test.csv')
9
+ @data = ::CSV.read(@file_name)
10
+ @raw_csv_data = ::File.read(@file_name)
11
+ end
12
+
13
+ describe '.open' do
14
+ it 'file' do
15
+ temp_file = Tempfile.new('rocket_job')
16
+ file_name = temp_file.to_path
17
+ IOStreams::CSV::Writer.open(file_name) do |io|
18
+ @data.each { |row| io << row }
19
+ end
20
+ result = File.read(file_name)
21
+ assert_equal @raw_csv_data, result
22
+ end
23
+
24
+ it 'stream' do
25
+ io_string = StringIO.new
26
+ IOStreams::CSV::Writer.open(io_string) do |io|
27
+ @data.each { |row| io << row }
28
+ end
29
+ assert_equal @raw_csv_data, io_string.string
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -3,7 +3,7 @@ require_relative 'test_helper'
3
3
  # Unit Test for IOStreams::File
4
4
  module Streams
5
5
  class DelimitedReaderTest < Minitest::Test
6
- describe IOStreams::File::Reader do
6
+ describe IOStreams::Delimited::Reader do
7
7
  before do
8
8
  @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt')
9
9
  @data = []
@@ -14,11 +14,11 @@ module Streams
14
14
  end
15
15
  end
16
16
 
17
- describe '.open' do
17
+ describe '#each' do
18
18
  it 'each_line file' do
19
19
  lines = []
20
20
  IOStreams::Delimited::Reader.open(@file_name) do |io|
21
- io.each_line { |line| lines << line }
21
+ io.each { |line| lines << line }
22
22
  end
23
23
  assert_equal @data, lines
24
24
  end
@@ -27,7 +27,7 @@ module Streams
27
27
  lines = []
28
28
  File.open(@file_name) do |file|
29
29
  IOStreams::Delimited::Reader.open(file) do |io|
30
- io.each_line { |line| lines << line }
30
+ io.each { |line| lines << line }
31
31
  end
32
32
  end
33
33
  assert_equal @data, lines
@@ -38,34 +38,44 @@ module Streams
38
38
  lines = []
39
39
  stream = StringIO.new(@data.join(delimiter))
40
40
  IOStreams::Delimited::Reader.open(stream, buffer_size: 15) do |io|
41
- io.each_line { |line| lines << line }
41
+ io.each { |line| lines << line }
42
42
  end
43
43
  assert_equal @data, lines
44
44
  end
45
45
  end
46
46
 
47
47
  ['@', 'BLAH'].each do |delimiter|
48
- it "read delimited #{delimiter.inspect}" do
48
+ it "reads delimited #{delimiter.inspect}" do
49
49
  lines = []
50
50
  stream = StringIO.new(@data.join(delimiter))
51
51
  IOStreams::Delimited::Reader.open(stream, buffer_size: 15, delimiter: delimiter) do |io|
52
- io.each_line { |line| lines << line }
52
+ io.each { |line| lines << line }
53
53
  end
54
54
  assert_equal @data, lines
55
55
  end
56
56
  end
57
57
 
58
- it "read binary delimited" do
58
+ it 'reads binary delimited' do
59
59
  delimiter = "\x01"
60
60
  lines = []
61
61
  stream = StringIO.new(@data.join(delimiter))
62
62
  IOStreams::Delimited::Reader.open(stream, buffer_size: 15, delimiter: delimiter, encoding: IOStreams::BINARY_ENCODING) do |io|
63
- io.each_line { |line| lines << line }
63
+ io.each { |line| lines << line }
64
64
  end
65
65
  assert_equal @data, lines
66
66
  end
67
67
  end
68
68
 
69
+ describe '.read' do
70
+ it 'reads without delimiter' do
71
+ result = IOStreams::Delimited::Reader.open(@file_name) do |io|
72
+ io.read
73
+ end
74
+ file = File.read(@file_name)
75
+ assert_equal file, result
76
+ end
77
+ end
78
+
69
79
  end
70
80
  end
71
81
  end
@@ -0,0 +1,44 @@
1
+ require_relative 'test_helper'
2
+
3
+ module Streams
4
+ class DelimitedWriterTest < Minitest::Test
5
+ describe IOStreams::Delimited::Writer do
6
+ before do
7
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt')
8
+ @file = File.read(@file_name)
9
+ @data = @file.lines
10
+ end
11
+
12
+ describe '#<<' do
13
+ it 'file' do
14
+ temp_file = Tempfile.new('rocket_job')
15
+ file_name = temp_file.to_path
16
+ IOStreams::Delimited::Writer.open(file_name) do |io|
17
+ @data.each { |line| io << line.strip }
18
+ end
19
+ result = File.read(file_name)
20
+ assert_equal @file, result
21
+ end
22
+
23
+ it 'stream' do
24
+ io_string = StringIO.new
25
+ IOStreams::Delimited::Writer.open(io_string) do |io|
26
+ @data.each { |line| io << line.strip }
27
+ end
28
+ assert_equal @file, io_string.string
29
+ end
30
+ end
31
+
32
+ describe '.write' do
33
+ it 'writes without delimiter' do
34
+ io_string = StringIO.new
35
+ IOStreams::File::Writer.open(io_string) do |io|
36
+ io.write(@file)
37
+ end
38
+ assert_equal @file, io_string.string
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -16,6 +16,7 @@ module Streams
16
16
  end
17
17
  assert_equal @data, result
18
18
  end
19
+
19
20
  it 'stream' do
20
21
  result = File.open(@file_name) do |file|
21
22
  IOStreams::File::Reader.open(file) do |io|
@@ -19,6 +19,7 @@ module Streams
19
19
  result = File.read(file_name)
20
20
  assert_equal @data, result
21
21
  end
22
+
22
23
  it 'stream' do
23
24
  io_string = StringIO.new
24
25
  IOStreams::File::Writer.open(io_string) do |io|
@@ -0,0 +1,4 @@
1
+ name, address, state, zip
2
+ Joe Black, 23 Where Rd, FL, 12452
3
+ Who is this, 42 Able Rd, GA, 23432
4
+ Another's Person, 42 and 2nd St, NY, 17449
@@ -17,6 +17,7 @@ module Streams
17
17
  end
18
18
  assert_equal @data, result
19
19
  end
20
+
20
21
  it 'stream' do
21
22
  result = File.open(@file_name) do |file|
22
23
  IOStreams::Gzip::Reader.open(file) do |io|
@@ -20,6 +20,7 @@ module Streams
20
20
  temp_file.delete
21
21
  assert_equal @data, result
22
22
  end
23
+
23
24
  it 'stream' do
24
25
  io_string = StringIO.new(''.force_encoding('ASCII-8BIT'))
25
26
  IOStreams::Gzip::Writer.open(io_string) do |io|
@@ -2,10 +2,12 @@ require_relative 'test_helper'
2
2
 
3
3
  module Streams
4
4
  describe IOStreams::Xlsx::Reader do
5
- XLSX_CONTENTS = [
6
- "first column,second column,third column",
7
- "data 1,data 2,more data",
8
- ]
5
+ before do
6
+ @xlsx_contents = [
7
+ ['first column', 'second column', 'third column'],
8
+ ['data 1', 'data 2', 'more data']
9
+ ]
10
+ end
9
11
 
10
12
  describe '.open' do
11
13
  let(:file_name) { File.join(File.dirname(__FILE__), 'files', 'spreadsheet.xlsx') }
@@ -18,9 +20,9 @@ module Streams
18
20
  it 'returns the contents of the file' do
19
21
  rows = []
20
22
  IOStreams::Xlsx::Reader.open(@file) do |spreadsheet|
21
- spreadsheet.each_line { |row| rows << row }
23
+ spreadsheet.each { |row| rows << row }
22
24
  end
23
- assert_equal(XLSX_CONTENTS, rows)
25
+ assert_equal(@xlsx_contents, rows)
24
26
  end
25
27
  end
26
28
 
@@ -30,11 +32,11 @@ module Streams
30
32
  rows = []
31
33
  File.open(file_name) do |file|
32
34
  IOStreams::Xlsx::Reader.open(file) do |spreadsheet|
33
- spreadsheet.each_line { |row| rows << row }
35
+ spreadsheet.each { |row| rows << row }
34
36
  end
35
37
  end
36
38
 
37
- assert_equal(XLSX_CONTENTS, rows)
39
+ assert_equal(@xlsx_contents, rows)
38
40
  end
39
41
  end
40
42
  end
@@ -18,6 +18,7 @@ module Streams
18
18
  end
19
19
  assert_equal @data, result
20
20
  end
21
+
21
22
  it 'stream' do
22
23
  result = File.open(@file_name) do |file|
23
24
  IOStreams::Zip::Reader.open(file) do |io|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iostreams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-25 00:00:00.000000000 Z
11
+ date: 2016-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: symmetric-encryption
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: thread_safe
28
+ name: concurrent-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -47,21 +47,29 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - README.md
49
49
  - Rakefile
50
+ - lib/io_streams/csv/reader.rb
51
+ - lib/io_streams/csv/writer.rb
50
52
  - lib/io_streams/delimited/reader.rb
53
+ - lib/io_streams/delimited/writer.rb
51
54
  - lib/io_streams/file/reader.rb
52
55
  - lib/io_streams/file/writer.rb
53
56
  - lib/io_streams/gzip/reader.rb
54
57
  - lib/io_streams/gzip/writer.rb
55
58
  - lib/io_streams/io_streams.rb
59
+ - lib/io_streams/streams.rb
56
60
  - lib/io_streams/version.rb
57
61
  - lib/io_streams/xlsx/reader.rb
58
62
  - lib/io_streams/zip/reader.rb
59
63
  - lib/io_streams/zip/writer.rb
60
64
  - lib/iostreams.rb
65
+ - test/csv_reader_test.rb
66
+ - test/csv_writer_test.rb
61
67
  - test/delimited_reader_test.rb
68
+ - test/delimited_writer_test.rb
62
69
  - test/file_reader_test.rb
63
70
  - test/file_writer_test.rb
64
71
  - test/files/spreadsheet.xlsx
72
+ - test/files/test.csv
65
73
  - test/files/text.txt
66
74
  - test/files/text.txt.gz
67
75
  - test/files/text.txt.gz.zip
@@ -97,10 +105,14 @@ signing_key:
97
105
  specification_version: 4
98
106
  summary: Ruby Input and Output streaming with support for Zip, Gzip, and Encryption.
99
107
  test_files:
108
+ - test/csv_reader_test.rb
109
+ - test/csv_writer_test.rb
100
110
  - test/delimited_reader_test.rb
111
+ - test/delimited_writer_test.rb
101
112
  - test/file_reader_test.rb
102
113
  - test/file_writer_test.rb
103
114
  - test/files/spreadsheet.xlsx
115
+ - test/files/test.csv
104
116
  - test/files/text.txt
105
117
  - test/files/text.txt.gz
106
118
  - test/files/text.txt.gz.zip
@@ -111,4 +123,3 @@ test_files:
111
123
  - test/xlsx_reader_test.rb
112
124
  - test/zip_reader_test.rb
113
125
  - test/zip_writer_test.rb
114
- has_rdoc: true