iostreams 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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