zip_tricks 4.2.1 → 4.2.2

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: 99c656baa62bbe09d48e090b2971d63ab3bb875a
4
- data.tar.gz: d6b84eb8c621b7039ae205f9e3d9ff5d29887032
3
+ metadata.gz: 62ea03396a8a75e72000952d6c5ff69b9d8f0a80
4
+ data.tar.gz: 6cf0bca2293065a1b5430e73d26a9a0117064adb
5
5
  SHA512:
6
- metadata.gz: fa0dfe2f578443c695be43c3e170b81bc24fdc836401e3b8e50f41379a7583bd425fffbb1ed1d618624988c63e45fd0732f234d57abb249ccea3ef4d20c863d9
7
- data.tar.gz: 30a98f251dd26cde99a62f1cf93ff889aceb30f3ae3eebbba3516b01da3fd56d920e8816d5f742b4c0b17d731b6d8be1f235b4d5bc595b0dc3ecd3960e6b1618
6
+ metadata.gz: c5ca0c25882b1770b663b199bc6f00b4763691f51f9bc34cd9594442be37e942738c1fbbcf6eff0d8aac56e99a81310f4e691a32aed3a84647e0a8e3d48aa9f2
7
+ data.tar.gz: 4d53363180343a0017e4054c2d55b40da0c74213a75a3aaf1e339715a6a3ad5ae9b7150abb79685f9cc9d19822d84e9122a12399a75f5488fccad18e35b31357
data/Gemfile CHANGED
@@ -10,5 +10,5 @@ group :development do
10
10
  gem 'coderay'
11
11
  gem "yard", "~> 0.8"
12
12
  gem "bundler", "~> 1.0"
13
- gem "jeweler", "~> 2.0.1"
13
+ gem "jeweler", "~> 2", '>= 2.1.2'
14
14
  end
data/README.md CHANGED
@@ -4,23 +4,28 @@
4
4
 
5
5
  Allows streaming, non-rewinding ZIP file output from Ruby.
6
6
  Spiritual successor to [zipline](https://github.com/fringd/zipline)
7
-
8
7
  Requires Ruby 2.1+ syntax support and a working zlib (all available to jRuby as well).
9
8
 
9
+ Allows you to write a ZIP archive out to a File, Socket, String or Array without having to rewind it at any
10
+ point. Usable for creating very large ZIP archives for immediate sending out to clients, or for writing
11
+ large ZIP archives without memory inflation.
12
+
10
13
  ## Create a ZIP file without size estimation, compress on-the-fly)
11
14
 
12
15
  When you compress on the fly and use data descriptors it is not really possible to compute the file size upfront.
13
16
  But it is very likely to yield good compression - especially if you send things like CSV files.
14
17
 
15
- out = my_tempfile # can also be a socket
16
- ZipTricks::Streamer.open(out) do |zip|
17
- zip.write_stored_file('mov.mp4.txt') do |sink|
18
- File.open('mov.mp4', 'rb'){|source| IO.copy_stream(source, sink) }
19
- end
20
- zip.write_deflated_file('long-novel.txt') do |sink|
21
- File.open('novel.txt', 'rb'){|source| IO.copy_stream(source, sink) }
22
- end
23
- end
18
+ ```ruby
19
+ out = my_tempfile # can also be a socket
20
+ ZipTricks::Streamer.open(out) do |zip|
21
+ zip.write_stored_file('mov.mp4.txt') do |sink|
22
+ File.open('mov.mp4', 'rb'){|source| IO.copy_stream(source, sink) }
23
+ end
24
+ zip.write_deflated_file('long-novel.txt') do |sink|
25
+ File.open('novel.txt', 'rb'){|source| IO.copy_stream(source, sink) }
26
+ end
27
+ end
28
+ ```
24
29
 
25
30
  ## Send the same ZIP file from a Rack response
26
31
 
@@ -28,35 +33,39 @@ Create a `RackBody` object and give it's constructor a block that adds files.
28
33
  The block will only be called when actually sending the response to the client
29
34
  (unless you are using a buffering Rack webserver, such as Webrick).
30
35
 
31
- body = ZipTricks::RackBody.new do | zip |
32
- zip.write_stored_file('mov.mp4') do |sink| # Those MPEG4 files do not compress that well
33
- File.open('mov.mp4', 'rb'){|source| IO.copy_stream(source, sink) }
34
- end
35
- zip.write_deflated_file('long-novel.txt') do |sink|
36
- File.open('novel.txt', 'rb'){|source| IO.copy_stream(source, sink) }
37
- end
38
- end
39
- [200, {'Transfer-Encoding' => 'chunked'}, body]
36
+ ```ruby
37
+ body = ZipTricks::RackBody.new do | zip |
38
+ zip.write_stored_file('mov.mp4') do |sink| # Those MPEG4 files do not compress that well
39
+ File.open('mov.mp4', 'rb'){|source| IO.copy_stream(source, sink) }
40
+ end
41
+ zip.write_deflated_file('long-novel.txt') do |sink|
42
+ File.open('novel.txt', 'rb'){|source| IO.copy_stream(source, sink) }
43
+ end
44
+ end
45
+ [200, {'Transfer-Encoding' => 'chunked'}, body]
46
+ ```
40
47
 
41
48
  ## Send a ZIP file of known size, with correct headers
42
49
 
43
50
  Use the `SizeEstimator` to compute the correct size of the resulting archive.
44
51
 
45
- # Prepare the response body. The block will only be called when the response starts to be written.
46
- zip_body = ZipTricks::RackBody.new do | zip |
47
- zip.add_stored_entry(filename: "myfile1.bin", size: 9090821, crc32: 12485)
48
- zip << read_file('myfile1.bin')
49
- zip.add_stored_entry(filename: "myfile2.bin", size: 458678, crc32: 89568)
50
- zip << read_file('myfile2.bin')
51
- end
52
-
53
- # Precompute the Content-Length ahead of time
54
- bytesize = ZipTricks::SizeEstimator.estimate do |z|
55
- z.add_stored_entry(filename: 'myfile1.bin', size: 9090821)
56
- z.add_stored_entry(filename: 'myfile2.bin', size: 458678)
57
- end
58
-
59
- [200, {'Content-Length' => bytesize.to_s}, zip_body]
52
+ ```ruby
53
+ # Prepare the response body. The block will only be called when the response starts to be written.
54
+ zip_body = ZipTricks::RackBody.new do | zip |
55
+ zip.add_stored_entry(filename: "myfile1.bin", size: 9090821, crc32: 12485)
56
+ zip << read_file('myfile1.bin')
57
+ zip.add_stored_entry(filename: "myfile2.bin", size: 458678, crc32: 89568)
58
+ zip << read_file('myfile2.bin')
59
+ end
60
+
61
+ # Precompute the Content-Length ahead of time
62
+ bytesize = ZipTricks::SizeEstimator.estimate do |z|
63
+ z.add_stored_entry(filename: 'myfile1.bin', size: 9090821)
64
+ z.add_stored_entry(filename: 'myfile2.bin', size: 458678)
65
+ end
66
+
67
+ [200, {'Content-Length' => bytesize.to_s}, zip_body]
68
+ ```
60
69
 
61
70
  ## Other usage examples
62
71
 
@@ -70,32 +79,36 @@ If the write destination for your use case is a `Socket` (say, you are writing u
70
79
  the metadata of the file upfront (the CRC32 of the uncompressed file and the sizes), you can write directly
71
80
  to that socket using some accelerated writing technique, and only use the Streamer to write out the ZIP metadata.
72
81
 
73
- # io has to be an object that supports #<<
74
- ZipTricks::Streamer.open(io) do | zip |
75
- # raw_file is written "as is" (STORED mode).
76
- # Write the local file header first..
77
- zip.add_stored_entry(filename: "first-file.bin", size: raw_file.size, crc32: raw_file_crc32)
78
-
79
- # then send the actual file contents bypassing the Streamer interface
80
- io.sendfile(my_temp_file)
81
-
82
- # ...and then adjust the ZIP offsets within the Streamer
83
- zip.simulate_write(my_temp_file.size)
84
- end
82
+ ```ruby
83
+ # io has to be an object that supports #<<
84
+ ZipTricks::Streamer.open(io) do | zip |
85
+ # raw_file is written "as is" (STORED mode).
86
+ # Write the local file header first..
87
+ zip.add_stored_entry(filename: "first-file.bin", size: raw_file.size, crc32: raw_file_crc32)
88
+
89
+ # then send the actual file contents bypassing the Streamer interface
90
+ io.sendfile(my_temp_file)
91
+
92
+ # ...and then adjust the ZIP offsets within the Streamer
93
+ zip.simulate_write(my_temp_file.size)
94
+ end
95
+ ```
85
96
 
86
97
  ## Computing the CRC32 value of a large file
87
98
 
88
- `BlockCRC32` computes the CRC32 checksum of an IO in a streaming fashion. It is slightly more convenient for the purpose
89
- than using the raw Zlib library functions.
90
-
91
- crc = ZipTricks::StreamCRC32.new
92
- crc << large_file.read(1024 * 12) until large_file.eof?
93
- ...
94
-
95
- crc.to_i # Returns the actual CRC32 value computed so far
96
- ...
97
- # Append a known CRC32 value that has been computed previosuly
98
- crc.append(precomputed_crc32, size_of_the_blob_computed_from)
99
+ `BlockCRC32` computes the CRC32 checksum of an IO in a streaming fashion.
100
+ It is slightly more convenient for the purpose than using the raw Zlib library functions.
101
+
102
+ ```ruby
103
+ crc = ZipTricks::StreamCRC32.new
104
+ crc << large_file.read(1024 * 12) until large_file.eof?
105
+ ...
106
+
107
+ crc.to_i # Returns the actual CRC32 value computed so far
108
+ ...
109
+ # Append a known CRC32 value that has been computed previosuly
110
+ crc.append(precomputed_crc32, size_of_the_blob_computed_from)
111
+ ```
99
112
 
100
113
  ## Contributing to zip_tricks
101
114
 
data/Rakefile CHANGED
@@ -13,8 +13,6 @@ require 'rake'
13
13
  require_relative 'lib/zip_tricks'
14
14
  require 'jeweler'
15
15
 
16
-
17
-
18
16
  Jeweler::Tasks.new do |gem|
19
17
  # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
20
18
  gem.name = "zip_tricks"
@@ -1,5 +1,5 @@
1
1
  module ZipTricks
2
- VERSION = '4.2.1'
2
+ VERSION = '4.2.2'
3
3
 
4
4
  # Require all the sub-components except myself
5
5
  Dir.glob(__dir__ + '/**/*.rb').sort.each {|p| require p unless p == __FILE__ }
@@ -20,6 +20,9 @@ class ZipTricks::FileReader::InflatingReader
20
20
  return '' if n_bytes.zero?
21
21
 
22
22
  compressed_chunk = @io.read(n_bytes)
23
+
24
+ return if compressed_chunk.nil?
25
+
23
26
  @already_read += compressed_chunk.bytesize
24
27
  @zlib_inflater.inflate(compressed_chunk)
25
28
  end
@@ -19,6 +19,9 @@ class ZipTricks::FileReader::StoredReader
19
19
  return '' if n_bytes.zero?
20
20
 
21
21
  compressed_chunk = @io.read(n_bytes)
22
+
23
+ return if compressed_chunk.nil?
24
+
22
25
  @already_read += compressed_chunk.bytesize
23
26
  compressed_chunk
24
27
  end
@@ -48,11 +48,12 @@ class ZipTricks::ZipWriter
48
48
  C_V = 'V'.freeze # Encode a 4-byte little-endian uint
49
49
  C_v = 'v'.freeze # Encode a 2-byte little-endian uint
50
50
  C_Qe = 'Q<'.freeze # Encode an 8-byte little-endian uint
51
-
51
+ C_es = ''.freeze
52
+
52
53
  private_constant :FOUR_BYTE_MAX_UINT, :TWO_BYTE_MAX_UINT,
53
54
  :VERSION_MADE_BY, :VERSION_NEEDED_TO_EXTRACT, :VERSION_NEEDED_TO_EXTRACT_ZIP64,
54
55
  :DEFAULT_EXTERNAL_ATTRS, :MADE_BY_SIGNATURE,
55
- :C_V, :C_v, :C_Qe, :ZIP_TRICKS_COMMENT
56
+ :C_V, :C_v, :C_Qe, :C_es, :ZIP_TRICKS_COMMENT
56
57
 
57
58
  # Writes the local file header, that precedes the actual file _data_.
58
59
  #
@@ -92,21 +93,21 @@ class ZipTricks::ZipWriter
92
93
  # Filename should not be longer than 0xFFFF otherwise this wont fit here
93
94
  io << [filename.bytesize].pack(C_v) # file name length 2 bytes
94
95
 
95
- extra_size = 0
96
- if requires_zip64
97
- extra_size += bytesize_of {|buf| write_zip_64_extra_for_local_file_header(io: buf, compressed_size: 0, uncompressed_size: 0) }
96
+ extra_fields = if requires_zip64
97
+ zip_64_extra_for_local_file_header(compressed_size: compressed_size, uncompressed_size: uncompressed_size)
98
+ else
99
+ C_es
98
100
  end
99
- io << [extra_size].pack(C_v) # extra field length 2 bytes
100
101
 
101
- io << filename # file name (variable size)
102
+ io << [extra_fields.bytesize].pack(C_v) # extra field length 2 bytes
103
+
104
+ io << filename # file name (variable size)
102
105
 
103
106
  # Interesting tidbit:
104
107
  # https://social.technet.microsoft.com/Forums/windows/en-US/6a60399f-2879-4859-b7ab-6ddd08a70948
105
108
  # TL;DR of it is: Windows 7 Explorer _will_ open Zip64 entries. However, it desires to have the
106
109
  # Zip64 extra field as _the first_ extra field. If we decide to add the Info-ZIP UTF-8 field...
107
- if requires_zip64
108
- write_zip_64_extra_for_local_file_header(io: io, compressed_size: compressed_size, uncompressed_size: uncompressed_size)
109
- end
110
+ io << extra_fields if requires_zip64
110
111
  end
111
112
 
112
113
  # Writes the file header for the central directory, for a particular file in the archive. When writing out this data,
@@ -153,14 +154,13 @@ class ZipTricks::ZipWriter
153
154
  # Filename should not be longer than 0xFFFF otherwise this wont fit here
154
155
  io << [filename.bytesize].pack(C_v) # file name length 2 bytes
155
156
 
156
- extra_size = 0
157
- if add_zip64
158
- extra_size += bytesize_of {|buf|
159
- # Supply zeroes for most values as we obnly care about the size of the data written
160
- write_zip_64_extra_for_central_directory_file_header(io: buf, compressed_size: 0, uncompressed_size: 0, local_file_header_location: 0)
161
- }
157
+ extra_fields = if add_zip64
158
+ zip_64_extra_for_central_directory_file_header(local_file_header_location: local_file_header_location,
159
+ compressed_size: compressed_size, uncompressed_size: uncompressed_size)
160
+ else
161
+ C_es
162
162
  end
163
- io << [extra_size].pack(C_v) # extra field length 2 bytes
163
+ io << [extra_fields.bytesize].pack(C_v) # extra field length 2 bytes
164
164
 
165
165
  io << [0].pack(C_v) # file comment length 2 bytes
166
166
 
@@ -181,11 +181,7 @@ class ZipTricks::ZipWriter
181
181
  io << [local_file_header_location].pack(C_V)
182
182
  end
183
183
  io << filename # file name (variable size)
184
-
185
- if add_zip64 # extra field (variable size)
186
- write_zip_64_extra_for_central_directory_file_header(io: io, local_file_header_location: local_file_header_location,
187
- compressed_size: compressed_size, uncompressed_size: uncompressed_size)
188
- end
184
+ io << extra_fields # extra field (variable size)
189
185
  #(empty) # file comment (variable size)
190
186
  end
191
187
 
@@ -305,38 +301,38 @@ class ZipTricks::ZipWriter
305
301
 
306
302
  # Writes the Zip64 extra field for the local file header. Will be used by `write_local_file_header` when any sizes given to it warrant that.
307
303
  #
308
- # @param io[#<<] the buffer to write the local file header to
309
304
  # @param compressed_size[Fixnum] The size of the compressed (or stored) data - how much space it uses in the ZIP
310
305
  # @param uncompressed_size[Fixnum] The size of the file once extracted
311
- # @return [void]
312
- def write_zip_64_extra_for_local_file_header(io:, compressed_size:, uncompressed_size:)
313
- io << [0x0001].pack(C_v) # 2 bytes Tag for this "extra" block type
314
- io << [16].pack(C_v) # 2 bytes Size of this "extra" block. For us it will always be 16 (2x8)
315
- io << [uncompressed_size].pack(C_Qe) # 8 bytes Original uncompressed file size
316
- io << [compressed_size].pack(C_Qe) # 8 bytes Size of compressed data
306
+ # @return [String]
307
+ def zip_64_extra_for_local_file_header(compressed_size:, uncompressed_size:)
308
+ data_and_packspecs = [
309
+ 0x0001, C_v, # 2 bytes Tag for this "extra" block type
310
+ 16, C_v, # 2 bytes Size of this "extra" block. For us it will always be 16 (2x8)
311
+ uncompressed_size, C_Qe, # 8 bytes Original uncompressed file size
312
+ compressed_size, C_Qe, # 8 bytes Size of compressed data
313
+ ]
314
+ pack_array(data_and_packspecs)
317
315
  end
318
316
 
319
317
  # Writes the Zip64 extra field for the central directory header.It differs from the extra used in the local file header because it
320
318
  # also contains the location of the local file header in the ZIP as an 8-byte int.
321
319
  #
322
- # @param io[#<<] the buffer to write the local file header to
323
320
  # @param compressed_size[Fixnum] The size of the compressed (or stored) data - how much space it uses in the ZIP
324
321
  # @param uncompressed_size[Fixnum] The size of the file once extracted
325
322
  # @param local_file_header_location[Fixnum] Byte offset of the start of the local file header from the beginning of the ZIP archive
326
- # @return [void]
327
- def write_zip_64_extra_for_central_directory_file_header(io:, compressed_size:, uncompressed_size:, local_file_header_location:)
328
- io << [0x0001].pack(C_v) # 2 bytes Tag for this "extra" block type
329
- io << [28].pack(C_v) # 2 bytes Size of this "extra" block. For us it will always be 28
330
- io << [uncompressed_size].pack(C_Qe) # 8 bytes Original uncompressed file size
331
- io << [compressed_size].pack(C_Qe) # 8 bytes Size of compressed data
332
- io << [local_file_header_location].pack(C_Qe) # 8 bytes Offset of local header record
333
- io << [0].pack(C_V) # 4 bytes Number of the disk on which this file starts
323
+ # @return [String]
324
+ def zip_64_extra_for_central_directory_file_header(compressed_size:, uncompressed_size:, local_file_header_location:)
325
+ data_and_packspecs = [
326
+ 0x0001, C_v, # 2 bytes Tag for this "extra" block type
327
+ 28, C_v, # 2 bytes Size of this "extra" block. For us it will always be 28
328
+ uncompressed_size, C_Qe, # 8 bytes Original uncompressed file size
329
+ compressed_size, C_Qe, # 8 bytes Size of compressed data
330
+ local_file_header_location, C_Qe, # 8 bytes Offset of local header record
331
+ 0, C_V, # 4 bytes Number of the disk on which this file starts
332
+ ]
333
+ pack_array(data_and_packspecs)
334
334
  end
335
335
 
336
- def bytesize_of
337
- ''.force_encoding(Encoding::BINARY).tap {|b| yield(b) }.bytesize
338
- end
339
-
340
336
  def to_binary_dos_time(t)
341
337
  (t.sec/2) + (t.min << 5) + (t.hour << 11)
342
338
  end
@@ -344,4 +340,17 @@ class ZipTricks::ZipWriter
344
340
  def to_binary_dos_date(t)
345
341
  (t.day) + (t.month << 5) + ((t.year - 1980) << 9)
346
342
  end
343
+
344
+ # Unzips a given array of tuples of "numeric value, pack specifier" and then packs all the odd
345
+ # values using specifiers from all the even values. It is harder to explain than to show:
346
+ #
347
+ # pack_array([1, 'V', 2, 'v', 148, 'v]) #=> "\x01\x00\x00\x00\x02\x00\x94\x00"
348
+ #
349
+ # will do the following two transforms:
350
+ #
351
+ # [1, 'V', 2, 'v', 148, 'v] -> [1,2,148], ['V','v','v'] -> [1,2,148].pack('Vvv') -> "\x01\x00\x00\x00\x02\x00\x94\x00"
352
+ def pack_array(values_to_packspecs)
353
+ values, packspecs = values_to_packspecs.partition.each_with_index { |_, i| i.even? }
354
+ values.pack(packspecs.join)
355
+ end
347
356
  end
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: zip_tricks 4.2.1 ruby lib
5
+ # stub: zip_tricks 4.2.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "zip_tricks"
9
- s.version = "4.2.1"
9
+ s.version = "4.2.2"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Julik Tarkhanov"]
14
- s.date = "2016-10-10"
14
+ s.date = "2016-11-13"
15
15
  s.description = "Stream out ZIP files from Ruby"
16
16
  s.email = "me@julik.nl"
17
17
  s.extra_rdoc_files = [
@@ -86,7 +86,7 @@ Gem::Specification.new do |s|
86
86
  s.add_development_dependency(%q<coderay>, [">= 0"])
87
87
  s.add_development_dependency(%q<yard>, ["~> 0.8"])
88
88
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
89
- s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
89
+ s.add_development_dependency(%q<jeweler>, [">= 2.1.2", "~> 2"])
90
90
  else
91
91
  s.add_dependency(%q<rubyzip>, ["~> 1.1"])
92
92
  s.add_dependency(%q<terminal-table>, [">= 0"])
@@ -97,7 +97,7 @@ Gem::Specification.new do |s|
97
97
  s.add_dependency(%q<coderay>, [">= 0"])
98
98
  s.add_dependency(%q<yard>, ["~> 0.8"])
99
99
  s.add_dependency(%q<bundler>, ["~> 1.0"])
100
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
100
+ s.add_dependency(%q<jeweler>, [">= 2.1.2", "~> 2"])
101
101
  end
102
102
  else
103
103
  s.add_dependency(%q<rubyzip>, ["~> 1.1"])
@@ -109,7 +109,7 @@ Gem::Specification.new do |s|
109
109
  s.add_dependency(%q<coderay>, [">= 0"])
110
110
  s.add_dependency(%q<yard>, ["~> 0.8"])
111
111
  s.add_dependency(%q<bundler>, ["~> 1.0"])
112
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
112
+ s.add_dependency(%q<jeweler>, [">= 2.1.2", "~> 2"])
113
113
  end
114
114
  end
115
115
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zip_tricks
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.1
4
+ version: 4.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-10 00:00:00.000000000 Z
11
+ date: 2016-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -146,16 +146,22 @@ dependencies:
146
146
  name: jeweler
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 2.1.2
149
152
  - - "~>"
150
153
  - !ruby/object:Gem::Version
151
- version: 2.0.1
154
+ version: '2'
152
155
  type: :development
153
156
  prerelease: false
154
157
  version_requirements: !ruby/object:Gem::Requirement
155
158
  requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 2.1.2
156
162
  - - "~>"
157
163
  - !ruby/object:Gem::Version
158
- version: 2.0.1
164
+ version: '2'
159
165
  description: Stream out ZIP files from Ruby
160
166
  email: me@julik.nl
161
167
  executables: []