iostreams 0.7.0

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