iostreams 0.7.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dbaf3d597b1fc3ad00a9c371f932236332d3a1b4
4
+ data.tar.gz: ee333efa4fe7737d77dfdaeff353e4811fa4f298
5
+ SHA512:
6
+ metadata.gz: 801f73a031c8fbadd38bf7939fa90dd6fca5360fef7c8f77b08c65ed72bdb142deaecccf8c9a3dd2c7f47af4725397635959cdb14a87e4af603967ced96709a2
7
+ data.tar.gz: 7abef1b3814194b1fa69d05e5c007aaca2b386329fb2d4af3a228dcedccd465501c5a2036f510b7e3602c96ad54b030fe7385dbb096a566e3a26ead686d3f167
@@ -0,0 +1,56 @@
1
+ # iostreams
2
+
3
+ Ruby Input and Output streaming with support for Zip, Gzip, and Encryption.
4
+
5
+ ## Status
6
+
7
+ Alpha - Feedback on the API is welcome. API will change.
8
+
9
+ ## Introduction
10
+
11
+ `iostreams` allows files to be read and written in a streaming fashion to reduce
12
+ memory overhead. It supports reading and writing of Zip, GZip and encrypted files.
13
+
14
+ These streams can be chained together just like piped programs in linux.
15
+ This allows one stream to read the file, another stream to decrypt the file and
16
+ then a third stream to decompress the result.
17
+
18
+ The objective is that all of these streaming processes are performed used streaming
19
+ so that only portions of the file are loaded into memory at a time.
20
+ Where possible each stream never goes to disk, which for example could expose
21
+ un-encrypted data.
22
+
23
+ ## Notes
24
+
25
+ * Due to the nature of Zip, both its Reader and Writer methods will create
26
+ a temp file when reading from or writing to a stream.
27
+ Recommended to use Gzip over Zip since it can be streamed.
28
+
29
+ ## Meta
30
+
31
+ * Code: `git clone git://github.com/rocketjob/iostreams.git`
32
+ * Home: <https://github.com/rocketjob/iostreams>
33
+ * Issues: <http://github.com/rocketjob/iostreams/issues>
34
+ * Gems: <http://rubygems.org/gems/iostreams>
35
+
36
+ This project uses [Semantic Versioning](http://semver.org/).
37
+
38
+ ## Author
39
+
40
+ [Reid Morrison](https://github.com/reidmorrison)
41
+
42
+ ## License
43
+
44
+ Copyright 2015 Reid Morrison
45
+
46
+ Licensed under the Apache License, Version 2.0 (the "License");
47
+ you may not use this file except in compliance with the License.
48
+ You may obtain a copy of the License at
49
+
50
+ http://www.apache.org/licenses/LICENSE-2.0
51
+
52
+ Unless required by applicable law or agreed to in writing, software
53
+ distributed under the License is distributed on an "AS IS" BASIS,
54
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
55
+ See the License for the specific language governing permissions and
56
+ limitations under the License.
@@ -0,0 +1,28 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+
4
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
5
+ require 'io_streams/version'
6
+
7
+ task :gem do
8
+ system "gem build iostreams.gemspec"
9
+ end
10
+
11
+ task :publish => :gem do
12
+ system "git tag -a v#{IOStreams::VERSION} -m 'Tagging #{IOStreams::VERSION}'"
13
+ system "git push --tags"
14
+ system "gem push iostreams-#{IOStreams::VERSION}.gem"
15
+ system "rm iostreams-#{IOStreams::VERSION}.gem"
16
+ end
17
+
18
+ desc "Run Test Suite"
19
+ task :test do
20
+ Rake::TestTask.new(:functional) do |t|
21
+ t.test_files = FileList['test/**/*_test.rb']
22
+ t.verbose = true
23
+ end
24
+
25
+ Rake::Task['functional'].invoke
26
+ end
27
+
28
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ module IOStreams
2
+ module File
3
+ class Reader
4
+ # Read from a file or stream
5
+ def self.open(file_name_or_io, _=nil, &block)
6
+ unless file_name_or_io.respond_to?(:read)
7
+ ::File.open(file_name_or_io, 'rb', &block)
8
+ else
9
+ block.call(file_name_or_io)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module IOStreams
2
+ module File
3
+ class Writer
4
+ # Write to a file or stream
5
+ def self.open(file_name_or_io, _=nil, &block)
6
+ unless file_name_or_io.respond_to?(:write)
7
+ ::File.open(file_name_or_io, 'wb', &block)
8
+ else
9
+ block.call(file_name_or_io)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module IOStreams
2
+ module Gzip
3
+ class Reader
4
+ # Read from a gzip file or stream, decompressing the contents as it is read
5
+ def self.open(file_name_or_io, _=nil, &block)
6
+ unless file_name_or_io.respond_to?(:read)
7
+ ::Zlib::GzipReader.open(file_name_or_io, &block)
8
+ else
9
+ begin
10
+ io = ::Zlib::GzipReader.new(file_name_or_io)
11
+ block.call(io)
12
+ ensure
13
+ io.close if io && (io.respond_to?(:closed?) && !io.closed?)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module IOStreams
2
+ module Gzip
3
+ class Writer
4
+ # Write to a file / stream, compressing with GZip
5
+ def self.open(file_name_or_io, _=nil, &block)
6
+ unless file_name_or_io.respond_to?(:write)
7
+ Zlib::GzipWriter.open(file_name_or_io, &block)
8
+ else
9
+ begin
10
+ io = Zlib::GzipWriter.new(file_name_or_io)
11
+ block.call(io)
12
+ ensure
13
+ io.close if io && (io.respond_to?(:closed?) && !io.closed?)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,243 @@
1
+ require 'thread_safe'
2
+ module IOStreams
3
+ # A registry to hold formats for processing files during upload or download
4
+ @@extensions = ThreadSafe::Hash.new
5
+
6
+ # Returns [Array] the formats required to process the file by looking at
7
+ # its extension(s)
8
+ #
9
+ # Extensions supported:
10
+ # .zip Zip File [ :zip ]
11
+ # .gz, .gzip GZip File [ :gzip ]
12
+ # .enc File Encrypted using symmetric encryption [ :enc ]
13
+ # other All other extensions will be returned as: [ :file ]
14
+ #
15
+ # When a file is encrypted, it may also be compressed:
16
+ # .zip.enc [ :zip, :enc ]
17
+ # .gz.enc [ :gz, :enc ]
18
+ #
19
+ # Example Zip file:
20
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.zip')
21
+ # => [ :zip ]
22
+ #
23
+ # Example Encrypted Gzip file:
24
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.csv.gz.enc')
25
+ # => [ :gz, :enc ]
26
+ #
27
+ # Example plain text / binary file:
28
+ # RocketJob::Formatter::Formats.streams_for_file_name('myfile.csv')
29
+ # => [ :file ]
30
+ def self.streams_for_file_name(file_name)
31
+ raise ArgumentError.new("File name cannot be nil") if file_name.nil?
32
+ raise ArgumentError.new("RocketJob Cannot detect file format when uploading to stream: #{file_name.inspect}") if file_name.respond_to?(:read)
33
+ parts = file_name.split('.')
34
+ extensions = []
35
+ while extension = parts.pop
36
+ break unless @@extensions[extension.to_sym]
37
+ extensions.unshift(extension.to_sym)
38
+ end
39
+ extensions << :file if extensions.size == 0
40
+ extensions
41
+ end
42
+
43
+ Extension = Struct.new(:reader_class, :writer_class)
44
+
45
+ # Register a file extension and the reader and writer classes to use to format it
46
+ #
47
+ # Example:
48
+ # # MyXls::Reader and MyXls::Writer must implement .open
49
+ # register_extension(:xls, MyXls::Reader, MyXls::Writer)
50
+ def self.register_extension(extension, reader_class, writer_class)
51
+ raise "Invalid extension #{extension.inspect}" unless extension.to_s =~ /\A\w+\Z/
52
+ @@extensions[extension.to_sym] = Extension.new(reader_class, writer_class)
53
+ end
54
+
55
+ # De-Register a file extension
56
+ #
57
+ # Returns [Symbol] the extension removed, or nil if the extension was not registered
58
+ #
59
+ # Example:
60
+ # register_extension(:xls)
61
+ def self.deregister_extension(extension)
62
+ raise "Invalid extension #{extension.inspect}" unless extension.to_s =~ /\A\w+\Z/
63
+ @@extensions.delete(extension.to_sym)
64
+ end
65
+
66
+ # Returns a Reader for reading a file / stream
67
+ #
68
+ # Parameters
69
+ # file_name_or_io [String|IO]
70
+ # The file_name of the file to write to, or an IO Stream that implements
71
+ # #read.
72
+ #
73
+ # streams [Symbol|Array]
74
+ # The formats/streams that be used to convert the data whilst it is
75
+ # being read.
76
+ # When nil, the file_name will be inspected to try and determine what
77
+ # streams should be applied.
78
+ # Default: nil
79
+ #
80
+ # Stream types / extensions supported:
81
+ # .zip Zip File [ :zip ]
82
+ # .gz, .gzip GZip File [ :gzip ]
83
+ # .enc File Encrypted using symmetric encryption [ :enc ]
84
+ # other All other extensions will be returned as: [ :file ]
85
+ #
86
+ # When a file is encrypted, it may also be compressed:
87
+ # .zip.enc [ :zip, :enc ]
88
+ # .gz.enc [ :gz, :enc ]
89
+ #
90
+ # Example: Zip
91
+ # IOStreams.reader('myfile.zip') do |stream|
92
+ # puts stream.read
93
+ # end
94
+ #
95
+ # Example: Encrypted Zip
96
+ # IOStreams.reader('myfile.zip.enc') do |stream|
97
+ # puts stream.read
98
+ # end
99
+ #
100
+ # Example: Explicitly set the streams
101
+ # IOStreams.reader('myfile.zip.enc', [:zip, :enc]) do |stream|
102
+ # puts stream.read
103
+ # end
104
+ #
105
+ # Example: Supply custom options
106
+ # # Encrypt the file and get Symmetric Encryption to also compress it
107
+ # IOStreams.reader('myfile.csv.enc', [:enc]) do |stream|
108
+ # puts stream.read
109
+ # end
110
+ def self.reader(file_name_or_io, streams=nil, &block)
111
+ stream(:reader, file_name_or_io, streams, &block)
112
+ end
113
+
114
+ # Returns a Writer for writing to a file / stream
115
+ #
116
+ # Parameters
117
+ # file_name_or_io [String|IO]
118
+ # The file_name of the file to write to, or an IO Stream that implements
119
+ # #write.
120
+ #
121
+ # streams [Symbol|Array]
122
+ # The formats/streams that be used to convert the data whilst it is
123
+ # being written.
124
+ # When nil, the file_name will be inspected to try and determine what
125
+ # streams should be applied.
126
+ # Default: nil
127
+ #
128
+ # Stream types / extensions supported:
129
+ # .zip Zip File [ :zip ]
130
+ # .gz, .gzip GZip File [ :gzip ]
131
+ # .enc File Encrypted using symmetric encryption [ :enc ]
132
+ # other All other extensions will be returned as: [ :file ]
133
+ #
134
+ # When a file is encrypted, it may also be compressed:
135
+ # .zip.enc [ :zip, :enc ]
136
+ # .gz.enc [ :gz, :enc ]
137
+ #
138
+ # Example: Zip
139
+ # IOStreams.writer('myfile.zip') do |stream|
140
+ # stream.write(data)
141
+ # end
142
+ #
143
+ # Example: Encrypted Zip
144
+ # IOStreams.writer('myfile.zip.enc') do |stream|
145
+ # stream.write(data)
146
+ # end
147
+ #
148
+ # Example: Explicitly set the streams
149
+ # IOStreams.writer('myfile.zip.enc', [:zip, :enc]) do |stream|
150
+ # stream.write(data)
151
+ # end
152
+ #
153
+ # Example: Supply custom options
154
+ # IOStreams.writer('myfile.csv.enc', [enc: { compress: true }]) do |stream|
155
+ # stream.write(data)
156
+ # end
157
+ #
158
+ # Example: Set internal filename when creating a zip file
159
+ # IOStreams.writer('myfile.csv.zip', zip: { zip_file_name: 'myfile.csv' }) do |stream|
160
+ # stream.write(data)
161
+ # end
162
+ def self.writer(file_name_or_io, streams=nil, &block)
163
+ stream(:writer, file_name_or_io, streams, &block)
164
+ end
165
+
166
+ # Copies the source stream to the target stream
167
+ # Returns [Integer] the number of bytes copied
168
+ #
169
+ # Example:
170
+ # IOStreams.reader('a.csv') do |source_stream|
171
+ # IOStreams.writer('b.csv.enc') do |target_stream|
172
+ # IOStreams.copy(source_stream, target_stream)
173
+ # end
174
+ # end
175
+ def self.copy(source_stream, target_stream, buffer_size=65536)
176
+ bytes = 0
177
+ while data = source_stream.read(buffer_size)
178
+ break if data.size == 0
179
+ bytes += data.size
180
+ target_stream.write(data)
181
+ end
182
+ bytes
183
+ end
184
+
185
+ ##########################################################################
186
+ private
187
+
188
+ # Struct to hold the Stream and options if any
189
+ StreamStruct = Struct.new(:klass, :options)
190
+
191
+ # Returns a reader or writer stream
192
+ def self.stream(type, file_name_or_io, streams=nil, &block)
193
+ unless streams
194
+ respond_to = type == :reader ? :read : :write
195
+ streams = file_name_or_io.respond_to?(respond_to) ? [ :file ] : streams_for_file_name(file_name_or_io)
196
+ end
197
+ stream_structs = streams_for(type, streams)
198
+ if stream_structs.size == 1
199
+ stream_struct = stream_structs.first
200
+ stream_struct.klass.open(file_name_or_io, stream_struct.options, &block)
201
+ else
202
+ # Daisy chain multiple streams together
203
+ last = stream_structs.inject(block){ |inner, stream_struct| -> io { stream_struct.klass.open(io, stream_struct.options, &inner) } }
204
+ last.call(file_name_or_io)
205
+ end
206
+ end
207
+
208
+ # type: :reader or :writer
209
+ def self.streams_for(type, params)
210
+ if params.is_a?(Symbol)
211
+ [ stream_struct_for_stream(type, params) ]
212
+ elsif params.is_a?(Array)
213
+ a = []
214
+ params.each do |stream|
215
+ if stream.is_a?(Hash)
216
+ stream.each_pair { |stream_sym, options| a << stream_struct_for_stream(type, stream_sym, options) }
217
+ else
218
+ a << stream_struct_for_stream(type, stream)
219
+ end
220
+ end
221
+ a
222
+ elsif params.is_a?(Hash)
223
+ a = []
224
+ params.each_pair { |stream, options| a << stream_struct_for_stream(type, stream, options) }
225
+ a
226
+ else
227
+ raise ArgumentError, "Invalid params supplied: #{params.inspect}"
228
+ end
229
+ end
230
+
231
+ def self.stream_struct_for_stream(type, stream, options={})
232
+ ext = @@extensions[stream.to_sym] || raise(ArgumentError, "Unknown Stream type: #{stream.inspect}")
233
+ klass = ext.send("#{type}_class")
234
+ StreamStruct.new(klass, options)
235
+ end
236
+
237
+ # Register File extensions
238
+ register_extension(:enc, SymmetricEncryption::Reader, SymmetricEncryption::Writer) if defined?(SymmetricEncryption)
239
+ register_extension(:file, IOStreams::File::Reader, IOStreams::File::Writer)
240
+ register_extension(:gz, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
241
+ register_extension(:gzip, IOStreams::Gzip::Reader, IOStreams::Gzip::Writer)
242
+ register_extension(:zip, IOStreams::Zip::Reader, IOStreams::Zip::Writer)
243
+ end
@@ -0,0 +1,3 @@
1
+ module IOStreams #:nodoc
2
+ VERSION = "0.7.0"
3
+ end
@@ -0,0 +1,82 @@
1
+ module IOStreams
2
+ module Zip
3
+ class Reader
4
+ # Read from a zip file or stream, decompressing the contents as it is read
5
+ # The input stream from the first file found in the zip file is passed
6
+ # to the supplied block
7
+ #
8
+ # Example:
9
+ # IOStreams::ZipReader.open('abc.zip') do |io_stream|
10
+ # # Read 256 bytes at a time
11
+ # while data = io_stream.read(256)
12
+ # puts data
13
+ # end
14
+ # end
15
+ def self.open(file_name_or_io, options={}, &block)
16
+ options = options.dup
17
+ buffer_size = options.delete(:buffer_size) || 65536
18
+ raise(ArgumentError, "Unknown IOStreams::Zip::Reader option: #{options.inspect}") if options.size > 0
19
+
20
+ # File name supplied
21
+ return read_file(file_name_or_io, &block) unless file_name_or_io.respond_to?(:read)
22
+
23
+ # Stream supplied
24
+ begin
25
+ # Since ZIP cannot be streamed, download un-zipped data to a local file before streaming
26
+ temp_file = Tempfile.new('rocket_job')
27
+ file_name = temp_file.to_path
28
+
29
+ # Stream zip stream into temp file
30
+ ::File.open(file_name, 'wb') do |file|
31
+ IOStreams.copy(file_name_or_io, file, buffer_size)
32
+ end
33
+
34
+ read_file(file_name, &block)
35
+ ensure
36
+ temp_file.delete if temp_file
37
+ end
38
+ end
39
+
40
+ if defined?(JRuby)
41
+ # Java has built-in support for Zip files
42
+ def self.read_file(file_name, &block)
43
+ fin = Java::JavaIo::FileInputStream.new(file_name)
44
+ zin = Java::JavaUtilZip::ZipInputStream.new(fin)
45
+ zin.get_next_entry
46
+ block.call(zin.to_io)
47
+ ensure
48
+ zin.close if zin
49
+ fin.close if fin
50
+ end
51
+
52
+ else
53
+ # MRI needs Ruby Zip, since it only has native support for GZip
54
+ begin
55
+ require 'zip'
56
+ rescue LoadError => exc
57
+ puts "Please install gem rubyzip so that RocketJob can read Zip files in Ruby MRI"
58
+ raise(exc)
59
+ end
60
+
61
+ # Read from a zip file or stream, decompressing the contents as it is read
62
+ # The input stream from the first file found in the zip file is passed
63
+ # to the supplied block
64
+ def self.read_file(file_name, &block)
65
+ begin
66
+ zin = ::Zip::InputStream.new(file_name)
67
+ zin.get_next_entry
68
+ block.call(zin)
69
+ ensure
70
+ begin
71
+ zin.close if zin
72
+ rescue IOError
73
+ # Ignore file already closed errors since Zip::InputStream
74
+ # does not have a #closed? method
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,86 @@
1
+ module IOStreams
2
+ module Zip
3
+ class Writer
4
+ # Write a single file in Zip format to the supplied output file name
5
+ #
6
+ # Parameters
7
+ # zip_file_name [String]
8
+ # Full path and filename for the output zip file
9
+ #
10
+ # file_name [String]
11
+ # Name of the file within the Zip Stream
12
+ #
13
+ # The stream supplied to the block only responds to #write
14
+ #
15
+ # Example:
16
+ # IOStreams::ZipWriter.open('myfile.zip', zip_file_name: 'myfile.txt') do |io_stream|
17
+ # io_stream.write("hello world\n")
18
+ # io_stream.write("and more\n")
19
+ # end
20
+ #
21
+ # Notes:
22
+ # - Since Zip cannot write to streams, if a stream is supplied, a temp file
23
+ # is automatically created under the covers
24
+ def self.open(file_name_or_io, options={}, &block)
25
+ options = options.dup
26
+ zip_file_name = options.delete(:zip_file_name)
27
+ buffer_size = options.delete(:buffer_size) || 65536
28
+ raise(ArgumentError, "Unknown IOStreams::Zip::Writer option: #{options.inspect}") if options.size > 0
29
+
30
+ # Default the name of the file within the zip to the supplied file_name without the zip extension
31
+ zip_file_name = file_name_or_io.to_s[0..-5] if zip_file_name.nil? && !file_name_or_io.respond_to?(:write) && (file_name_or_io =~ /\.(zip)\z/)
32
+ zip_file_name ||= 'file'
33
+
34
+ # File name supplied
35
+ return write_file(file_name_or_io, zip_file_name, &block) unless file_name_or_io.respond_to?(:write)
36
+
37
+ # Stream supplied
38
+ begin
39
+ # Since ZIP cannot be streamed, download to a local file before streaming
40
+ temp_file = Tempfile.new('rocket_job')
41
+ write_file(temp_file.to_path, zip_file_name, &block)
42
+
43
+ # Stream temp file into output stream
44
+ IOStreams.copy(temp_file, file_name_or_io, buffer_size)
45
+ ensure
46
+ temp_file.delete if temp_file
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ if defined?(JRuby)
53
+
54
+ def self.write_file(file_name, zip_file_name, &block)
55
+ out = Java::JavaIo::FileOutputStream.new(file_name)
56
+ zout = Java::JavaUtilZip::ZipOutputStream.new(out)
57
+ zout.put_next_entry(Java::JavaUtilZip::ZipEntry.new(zip_file_name))
58
+ io = zout.to_io
59
+ block.call(io)
60
+ ensure
61
+ io.close if io && !io.closed?
62
+ out.close if out
63
+ end
64
+
65
+ else
66
+ # MRI needs Ruby Zip, since it only has native support for GZip
67
+ begin
68
+ require 'zip'
69
+ rescue LoadError => exc
70
+ puts "Please install gem rubyzip so that RocketJob can read Zip files in Ruby MRI"
71
+ raise(exc)
72
+ end
73
+
74
+ def self.write_file(file_name, zip_file_name, &block)
75
+ zos = ::Zip::OutputStream.new(file_name)
76
+ zos.put_next_entry(zip_file_name)
77
+ block.call(zos)
78
+ ensure
79
+ zos.close if zos
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,16 @@
1
+ require 'io_streams/version'
2
+ module IOStreams
3
+ module File
4
+ autoload :Reader, 'io_streams/file/reader'
5
+ autoload :Writer, 'io_streams/file/writer'
6
+ end
7
+ module Gzip
8
+ autoload :Reader, 'io_streams/gzip/reader'
9
+ autoload :Writer, 'io_streams/gzip/writer'
10
+ end
11
+ module Zip
12
+ autoload :Reader, 'io_streams/zip/reader'
13
+ autoload :Writer, 'io_streams/zip/writer'
14
+ end
15
+ end
16
+ require 'io_streams/io_streams'
@@ -0,0 +1,31 @@
1
+ require_relative 'test_helper'
2
+
3
+ # Unit Test for IOStreams::File
4
+ module Streams
5
+ class FileReaderTest < Minitest::Test
6
+ context IOStreams::File::Reader do
7
+ setup do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt')
9
+ @data = File.read(@file_name)
10
+ end
11
+
12
+ context '.open' do
13
+ should 'file' do
14
+ result = IOStreams::File::Reader.open(@file_name) do |io|
15
+ io.read
16
+ end
17
+ assert_equal @data, result
18
+ end
19
+ should 'stream' do
20
+ result = File.open(@file_name) do |file|
21
+ IOStreams::File::Reader.open(file) do |io|
22
+ io.read
23
+ end
24
+ end
25
+ assert_equal @data, result
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'test_helper'
2
+
3
+ # Unit Test for IOStreams::File
4
+ module Streams
5
+ class FileWriterTest < Minitest::Test
6
+ context IOStreams::File::Writer do
7
+ setup do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt')
9
+ @data = File.read(@file_name)
10
+ end
11
+
12
+ context '.open' do
13
+ should 'file' do
14
+ temp_file = Tempfile.new('rocket_job')
15
+ file_name = temp_file.to_path
16
+ IOStreams::File::Writer.open(file_name) do |io|
17
+ io.write(@data)
18
+ end
19
+ result = File.read(file_name)
20
+ assert_equal @data, result
21
+ end
22
+ should 'stream' do
23
+ io_string = StringIO.new
24
+ IOStreams::File::Writer.open(io_string) do |io|
25
+ io.write(@data)
26
+ end
27
+ assert_equal @data, io_string.string
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ Hello World
2
+ Line2
3
+ Line3
Binary file
Binary file
@@ -0,0 +1,32 @@
1
+ require_relative 'test_helper'
2
+
3
+ # Unit Test for IOStreams::Gzip
4
+ module Streams
5
+ class GzipReaderTest < Minitest::Test
6
+ context IOStreams::Gzip::Reader do
7
+ setup do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt.gz')
9
+ @gzip_data = File.open(@file_name, 'rb') { |f| f.read }
10
+ @data = Zlib::GzipReader.open(@file_name) {|gz| gz.read }
11
+ end
12
+
13
+ context '.open' do
14
+ should 'file' do
15
+ result = IOStreams::Gzip::Reader.open(@file_name) do |io|
16
+ io.read
17
+ end
18
+ assert_equal @data, result
19
+ end
20
+ should 'stream' do
21
+ result = File.open(@file_name) do |file|
22
+ IOStreams::Gzip::Reader.open(file) do |io|
23
+ io.read
24
+ end
25
+ end
26
+ assert_equal @data, result
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'test_helper'
2
+
3
+ # Unit Test for IOStreams::Gzip
4
+ module Streams
5
+ class GzipWriterTest < Minitest::Test
6
+ context IOStreams::Gzip::Writer do
7
+ setup do
8
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt.gz')
9
+ @data = Zlib::GzipReader.open(@file_name) {|gz| gz.read }
10
+ end
11
+
12
+ context '.open' do
13
+ should 'file' do
14
+ temp_file = Tempfile.new('rocket_job')
15
+ file_name = temp_file.to_path
16
+ IOStreams::Gzip::Writer.open(file_name) do |io|
17
+ io.write(@data)
18
+ end
19
+ result = Zlib::GzipReader.open(file_name) {|gz| gz.read }
20
+ temp_file.delete
21
+ assert_equal @data, result
22
+ end
23
+ should 'stream' do
24
+ io_string = StringIO.new(''.force_encoding('ASCII-8BIT'))
25
+ IOStreams::Gzip::Writer.open(io_string) do |io|
26
+ io.write(@data)
27
+ end
28
+ io = StringIO.new(io_string.string)
29
+ gz = Zlib::GzipReader.new(io)
30
+ data = gz.read
31
+ gz.close
32
+ assert_equal @data, data
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+
3
+ require 'yaml'
4
+ require 'minitest/autorun'
5
+ require 'minitest/reporters'
6
+ require 'shoulda/context'
7
+ require 'iostreams'
8
+ require 'awesome_print'
9
+ require 'symmetric-encryption'
10
+
11
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
12
+
13
+ # Test cipher
14
+ SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new(
15
+ cipher_name: 'aes-128-cbc',
16
+ key: '1234567890ABCDEF1234567890ABCDEF',
17
+ iv: '1234567890ABCDEF',
18
+ encoding: :base64strict
19
+ )
20
+
@@ -0,0 +1,33 @@
1
+ require_relative 'test_helper'
2
+ require 'zip'
3
+
4
+ # Unit Test for IOStreams::Zip
5
+ module Streams
6
+ class ZipReaderTest < Minitest::Test
7
+ context IOStreams::Zip::Reader do
8
+ setup do
9
+ @file_name = File.join(File.dirname(__FILE__), 'files', 'text.zip')
10
+ @zip_data = File.open(@file_name, 'rb') { |f| f.read }
11
+ @data = Zip::File.open(@file_name) { |zip_file| zip_file.first.get_input_stream.read }
12
+ end
13
+
14
+ context '.open' do
15
+ should 'file' do
16
+ result = IOStreams::Zip::Reader.open(@file_name) do |io|
17
+ io.read
18
+ end
19
+ assert_equal @data, result
20
+ end
21
+ should 'stream' do
22
+ result = File.open(@file_name) do |file|
23
+ IOStreams::Zip::Reader.open(file) do |io|
24
+ io.read
25
+ end
26
+ end
27
+ assert_equal @data, result
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'test_helper'
2
+ require 'zip'
3
+
4
+ # Unit Test for IOStreams::Zip
5
+ module Streams
6
+ class ZipWriterTest < Minitest::Test
7
+ context IOStreams::Zip::Writer do
8
+ setup do
9
+ file_name = File.join(File.dirname(__FILE__), 'files', 'text.txt')
10
+ @data = File.read(file_name)
11
+ end
12
+
13
+ context '.open' do
14
+ should 'file' do
15
+ temp_file = Tempfile.new('rocket_job')
16
+ file_name = temp_file.to_path
17
+ IOStreams::Zip::Writer.open(file_name, zip_file_name: 'text.txt') do |io|
18
+ io.write(@data)
19
+ end
20
+ result = Zip::File.open(file_name) do |zip_file|
21
+ zip_file.first.get_input_stream.read
22
+ end
23
+ temp_file.delete
24
+ assert_equal @data, result
25
+ end
26
+
27
+ should 'stream' do
28
+ io_string = StringIO.new(''.force_encoding('ASCII-8BIT'))
29
+ IOStreams::Zip::Writer.open(io_string) do |io|
30
+ io.write(@data)
31
+ end
32
+ io = StringIO.new(io_string.string)
33
+ result = nil
34
+ begin
35
+ zin = ::Zip::InputStream.new(io)
36
+ entry = zin.get_next_entry
37
+ result = zin.read
38
+ ensure
39
+ zin.close if zin
40
+ end
41
+ assert_equal @data, result
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iostreams
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Reid Morrison
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: symmetric-encryption
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thread_safe
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - reidmo@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - Rakefile
50
+ - lib/io_streams/file/reader.rb
51
+ - lib/io_streams/file/writer.rb
52
+ - lib/io_streams/gzip/reader.rb
53
+ - lib/io_streams/gzip/writer.rb
54
+ - lib/io_streams/io_streams.rb
55
+ - lib/io_streams/version.rb
56
+ - lib/io_streams/zip/reader.rb
57
+ - lib/io_streams/zip/writer.rb
58
+ - lib/iostreams.rb
59
+ - test/file_reader_test.rb
60
+ - test/file_writer_test.rb
61
+ - test/files/text.txt
62
+ - test/files/text.txt.gz
63
+ - test/files/text.txt.gz.zip
64
+ - test/files/text.zip
65
+ - test/gzip_reader_test.rb
66
+ - test/gzip_writer_test.rb
67
+ - test/test_helper.rb
68
+ - test/zip_reader_test.rb
69
+ - test/zip_writer_test.rb
70
+ homepage: https://github.com/rocketjob/streams
71
+ licenses:
72
+ - Apache License V2.0
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.4.8
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Ruby Input and Output streaming with support for Zip, Gzip, and Encryption.
94
+ test_files:
95
+ - test/file_reader_test.rb
96
+ - test/file_writer_test.rb
97
+ - test/files/text.txt
98
+ - test/files/text.txt.gz
99
+ - test/files/text.txt.gz.zip
100
+ - test/files/text.zip
101
+ - test/gzip_reader_test.rb
102
+ - test/gzip_writer_test.rb
103
+ - test/test_helper.rb
104
+ - test/zip_reader_test.rb
105
+ - test/zip_writer_test.rb