zip_kit 6.2.1 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1d33b58f4501d3ddbae7abcab3957fde0549abf734eae72ca1a7ce45601f479
4
- data.tar.gz: e9126924e6fe75329237ba551a1a65218676c7d2b3757f4ad91e73eb0bce154e
3
+ metadata.gz: dcd59b7f9d367a40895c9bac2f909f3e22ebd0a96ee737073ef5153c4d486c70
4
+ data.tar.gz: 05665ca021d401cad02f80c38d8965c2e5fef1a477317f7ee92246d1c54a21ae
5
5
  SHA512:
6
- metadata.gz: 011e57f856ebe7f625b0bfa5eeb4a240c6c38f2b07ff0434b7e89805516b2d47b6d4230ac404203d00562586909c72d7ea225a7238d47af70fb99ed97b3d50bc
7
- data.tar.gz: b68fbaae2e57314c47e7971aeef2150341ba80308c7dee1536718250f5cac01b4b387fe3f73cde5f84fb1ffcd93500343ec451d0fccf9f19b2c0c4a58e74aa2f
6
+ metadata.gz: b13d15a467565ef66ce6a21ac3891e8a26b30246d538c232aca193c3fe24a24a8f3a2d844d48eaeaeeb5ccc2f92cfcaab20e4afa1a2717b89ba27f55bb4635e4
7
+ data.tar.gz: 29aacf905b670323861812a4aece1446917ac2f94ece22b948f5d2f77a6eb7574c39598177e7c4f33cc230fd863b4f1c02eb961c5b48e4823d40854a25368101
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup markdown
1
+ --markup markdown --no-private --protected lib/**/*.rb - LICENSE.txt IMPLEMENTATION_DETAILS.md RUBYZIP_DIFFERENCES.md CONTRIBUTING.md CODE_OF_CONDUCT.md CHANGELOG.md
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 6.3.0
2
+
3
+ * Include `RailsStreaming` automatically via a Railtie. It is not really necessary to force people to manage it manually.
4
+
5
+ ## 6.2.2
6
+
7
+ * Make sure "zlib" gets required at the top, as it is used everywhere
8
+ * Improve documentation
9
+ * Make sure `zip_kit_stream` honors the custom `Content-Type` parameter
10
+ * Add a streaming example with Sinatra (and add a Sinatra app to the test harness)
11
+
1
12
  ## 6.2.1
2
13
 
3
14
  * Make `RailsStreaming` compatible with `ActionController::Live` (previously the response would hang)
data/CONTRIBUTING.md CHANGED
@@ -16,7 +16,7 @@ If you are interested in contributing code and would like to learn more about th
16
16
 
17
17
  - [ruby](https://ruby-doc.org)
18
18
  - [rubyzip](https://github.com/rubyzip/rubyzip) as we like to test "our implementation against theirs"
19
- - [rspec](http://rspec.info/) and [appraisal]() https://github.com/thoughtbot/appraisal) for testing
19
+ - [rspec](http://rspec.info/) for testing
20
20
  - [zip files](https://en.wikipedia.org/wiki/Zip_(file_format))
21
21
 
22
22
  # How do I make a contribution?
@@ -78,8 +78,8 @@ you can try this:
78
78
 
79
79
  [Encoding::CP437, Encoding::ISO_8859_1, Encoding::UTF_8]
80
80
 
81
- We don't want no such thing, and sorry Windows users, you are going to need a decent unarchiver
82
- that honors the standard. Alas, alas.
81
+ While this could work, we found it to be broken in practice as the decoding of the filename
82
+ also depends on the system locale.
83
83
 
84
84
  Additionally, the tests with the unarchivers we _do_ support have shown that including the InfoZIP
85
85
  extra field does not actually help any of them recognize the file name correctly. And the use of
data/README.md CHANGED
@@ -14,8 +14,13 @@ point. Usable for creating very large ZIP archives for immediate sending out to
14
14
  large ZIP archives without memory inflation.
15
15
 
16
16
  The original gem (zip_tricks) handled all the zipping needs (millions of ZIP files generated per day),
17
- for a large file transfer service, so we are pretty confident it is widely compatible with a large number
18
- of unarchiving end-user applications and is well tested.
17
+ for WeTransfer, it is widely compatible with a large number of unarchiving end-user applications.
18
+
19
+ ## How does it work? How is it different from Rubyzip?
20
+
21
+ Check out [the implementation details](IMPLEMENTATION_DETAILS.md) on the design of the library, and
22
+ we have a separate [reference](RUBYZIP_DIFFERENCES.md) on why you might want to use ZipKit over
23
+ Rubyzip and vice versa.
19
24
 
20
25
  ## Requirements
21
26
 
@@ -23,13 +28,11 @@ Ruby 2.6+ syntax support is required, as well as a a working zlib (all available
23
28
 
24
29
  ## Diving in: send some large CSV reports from Rails
25
30
 
26
- The easiest is to include the `ZipKit::RailsStreaming` module into your
27
- controller. You will then have a `zip_kit_stream` method available which accepts a block:
31
+ The included `Railtie` will automatically include `ZipKit::RailsStreaming` into the
32
+ `ActionController::Base` class. You will then have a `zip_kit_stream` method available which accepts a block:
28
33
 
29
34
  ```ruby
30
35
  class ZipsController < ActionController::Base
31
- include ZipKit::RailsStreaming
32
-
33
36
  def download
34
37
  zip_kit_stream do |zip|
35
38
  zip.write_file('report1.csv') do |sink|
@@ -48,6 +51,8 @@ class ZipsController < ActionController::Base
48
51
  end
49
52
  ```
50
53
 
54
+ The block receives the `ZipKit::Streamer` object you can write your files through.
55
+
51
56
  The `write_file` method will use some heuristics to determine whether your output file would benefit
52
57
  from compression, and pick the appropriate storage mode for the file accordingly.
53
58
 
@@ -55,9 +60,9 @@ If you want some more conveniences you can also use [zipline](https://github.com
55
60
  will automatically process and stream attachments (Carrierwave, Shrine, ActiveStorage) and remote objects
56
61
  via HTTP.
57
62
 
58
- `RailsStreaming` will *not* use [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html)
59
- and the ZIP output will run in the same thread as your main request. Your testing flows (be it minitest or
60
- RSpec) should work normally with controller actions returning ZIPs.
63
+ `RailsStreaming` does *not* require [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html)
64
+ and will stream without it. See {ZipKit::RailsStreaming#zip_kit_stream} for more details on this. You can use it
65
+ together with `Live` just fine if you need to.
61
66
 
62
67
  ## Writing into streaming destinations
63
68
 
@@ -108,11 +113,17 @@ zip.write_file('line_items.csv') do |sink|
108
113
  end
109
114
  ```
110
115
 
111
- ## Create a ZIP file without size estimation, compress on-the-fly during writes
116
+ ## Automatic storage mode (stored vs. deflated)
117
+
118
+ The ZIP file format allows storage in both compressed and raw storage modes. The raw ("stored")
119
+ mode does not require decompression and unarchives faster.
120
+
121
+ ZipKit will buffer a small amount of output and attempt to compress it using deflate compression.
122
+ If this turns out to be significantly smaller than raw data, it is then going to proceed with
123
+ all further output using deflate compression. Memory use is going to be very modest, but it allows
124
+ you to not have to think about the appropriate storage mode.
112
125
 
113
- Basic use case is compressing on the fly. Some data will be buffered by the Zlib deflater, but
114
- memory inflation is going to be very constrained. Data will be written to destination at fairly regular
115
- intervals. Deflate compression will work best for things like text files. For example, here is how to
126
+ Deflate compression will work great for JSONs, CSVs and other text- or text-like formats. For example, here is how to
116
127
  output direct to STDOUT (so that you can run `$ ruby archive.rb > file.zip` in your terminal):
117
128
 
118
129
  ```ruby
@@ -126,8 +137,8 @@ ZipKit::Streamer.open($stdout) do |zip|
126
137
  end
127
138
  ```
128
139
 
129
- Unfortunately with this approach it is impossible to compute the size of the ZIP file being output,
130
- since you do not know how large the compressed data segments are going to be.
140
+ If you want to use specific storage modes, use `write_deflated_file` and `write_stored_file` instead of
141
+ `write_file`.
131
142
 
132
143
  ## Send a ZIP from a Rack response
133
144
 
@@ -152,7 +163,11 @@ end
152
163
 
153
164
  ## Send a ZIP file of known size, with correct headers
154
165
 
155
- Use the `SizeEstimator` to compute the correct size of the resulting archive.
166
+ Sending a file with data descriptors is not always desirable - you don't really know how large your ZIP is going to be.
167
+ If you want to present your users with proper download progress, you would need to set a `Content-Length` header - and
168
+ know ahead of time how large your download is going to be. This can be done with ZipKit, provided you know how large
169
+ the compressed versions of your file are going to be. Use the {ZipKit::SizeEstimator} to do the pre-calculation - it
170
+ is not going to produce any large amounts of output, and will give you a to-the-byte value for your future archive:
156
171
 
157
172
  ```ruby
158
173
  bytesize = ZipKit::SizeEstimator.estimate do |z|
@@ -0,0 +1,56 @@
1
+ # Key differences between Rubyzip and ZipKit
2
+
3
+ Please bear in mind that this is written down not to disparage [Rubyzip.](https://github.com/rubyzip/rubyzip)
4
+
5
+ Rubyzip is, by all means, a very mature, fully featured library, and while ZipKit does have overlap with it in some functionality there are
6
+ differences in supported features which may be important for you when choosing.
7
+
8
+ ## What ZipKit supports and Rubyzip does not
9
+
10
+ * ZipKit outputs archives in a streaming fashion, using data descriptors.
11
+ This allows output of archives of very large size, without buffering.
12
+ Rubyzip will seek in the already written archive to overwrite the local entry after the write of every file into the output ZIP.
13
+ At the moment, it is difficult to build a streaming Rubyzip solution due to the output of extra field placeholders.
14
+ * ZipKit supports block-deflate which allows for distributed compression of files
15
+ * ZipKit reads files from the central directory, which allows for very rapid reading. Reading works well with data descriptors
16
+ and Zip64, and is economical enough to enable "remote uncapping" where pieces of a ZIP file get read over HTTP to reconstruct
17
+ the archive structure. Actual reading can then be done on a per-entry basis. Rubyzip reads entry data from local entries, which
18
+ is error prone and much less economical than using the central directory
19
+ * ZipKit deliberately _does not_ allow you to crawl directories to add to an archive, as this has been used for security exploits
20
+ in Rubyzip.
21
+ * ZipKit deliberately _does not_ allow you to extract a ZIP archive directly to the filesystem, as this has been used for security
22
+ exploits in Rubyzip.
23
+ * When writing, ZipKit applies careful buffering to speed up CRC32 calculations. Rubyzip combines CRC32 values at every write, which
24
+ can be slow if there are many small writes.
25
+ * ZipKit comes with a Rails helper and a Rack-compatible response body for facilitating streaming. Rubyzip has no Rails integration
26
+ and no Rack integration.
27
+ * ZipKit allows you to estimate the exact size of an archive ahead of time
28
+ * ZipKit has a heuristic module which picks the storage mode (stored or deflated) depending on how well your input compresses
29
+ * ZipKit requires components using autoloading, which means that your application will likely boot faster as you will almost never
30
+ need all of the features in one codebase. Rubyzip requires its components eagerly.
31
+ * ZipKit comes with exhaustive YARD documentation and `.rbi` typedefs for [Sorbet/Tapioca](https://sorbet.org/blog/2022/07/27/srb-tapioca)
32
+
33
+ ## What Rubyzip supports and ZipKit does not
34
+
35
+ * Rubyzip allows limited manipulation of existing ZIP files by overwriting the archive entries
36
+ * Rubyzip supports "classic" ZIP encryption - both for reading and writing. ZipKit has no encryption support.
37
+ * Rubyzip allows extraction into a directory, ZipKit avoids implementing this for security reasons
38
+ * Rubyzip allows archiving a directory, ZipKit avoids implementing this for security reasons
39
+ * Rubyzip supports separate atime and ctime in the `UniversalTime` extra fields. ZipKit outputs just one timestamp
40
+ * Rubyzip attempts to faithfully replicate UNIX permissions on the files being output. ZipKit does not attempt that
41
+ because it turned out that these permissions can break unarchiving of folders on macOS.
42
+
43
+ ## Where there is currently feature parity
44
+
45
+ These used to be different, but Rubyzip has made great progress in addressing.
46
+
47
+ * ZipKit automatically applies the EFS flag for Unicode filenames in the archive. This used to be optional in RubyZip
48
+ * ZipKit automatically enables Zip64 and does so only when necessary. applies the EFS flag for Unicode filenames in the archive. This used to be optional in RubyZip.
49
+ * ZipKit outputs the `UT` precise time extra field
50
+ * ZipKit used to be distributed under the MIT-Hippocratic license which is much more restrictive than the Rubyzip BSD-2-Clause. ZipKit is now MIT-licensed.
51
+
52
+ ## Code style differences
53
+
54
+ Rubyzip is written in a somewhat Java-like style, with a lot of encapsulation and data hiding. ZipKit aims to be more approachable and have "less" of everything.
55
+ Less modules, less classes, less OOP - just enough to be useful. But that is a matter of taste, and as such should not matter to you all that much
56
+ when picking one over the other. Or it might, you never know ;-)
data/Rakefile CHANGED
@@ -11,11 +11,7 @@ task :format do
11
11
  `bundle exec magic_frozen_string_literal ./lib`
12
12
  end
13
13
 
14
- YARD::Rake::YardocTask.new(:doc) do |t|
15
- # The dash has to be between the two to "divide" the source files and
16
- # miscellaneous documentation files that contain no code
17
- t.files = ["lib/**/*.rb", "-", "LICENSE.txt", "IMPLEMENTATION_DETAILS.md"]
18
- end
14
+ YARD::Rake::YardocTask.new(:doc)
19
15
  RSpec::Core::RakeTask.new(:spec)
20
16
 
21
17
  task :generate_typedefs do
@@ -0,0 +1,16 @@
1
+ require "sinatra/base"
2
+
3
+ class SinatraApp < Sinatra::Base
4
+ get "/" do
5
+ content_type :zip
6
+ stream do |out|
7
+ ZipKit::Streamer.open(out) do |z|
8
+ z.write_file(File.basename(__FILE__)) do |io|
9
+ File.open(__FILE__, "r") do |f|
10
+ IO.copy_stream(f, io)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zlib"
4
-
5
3
  # Permits Deflate compression in independent blocks. The workflow is as follows:
6
4
  #
7
5
  # * Run every block to compress through deflate_chunk, remove the header,
@@ -34,6 +34,15 @@ require "time" # for .httpdate
34
34
  #
35
35
  # to bypass things like `Rack::ETag` and the nginx buffering.
36
36
  class ZipKit::OutputEnumerator
37
+ # With HTTP output it is better to apply a small amount of buffering. While Streamer
38
+ # output does not buffer at all, the `OutputEnumerator` does as it is going to
39
+ # be used as a Rack response body. Applying some buffering helps reduce the number
40
+ # of syscalls for otherwise tiny writes, which relieves the app webserver from
41
+ # doing too much work managing those writes. While we recommend buffering, the
42
+ # buffer size is configurable via the constructor - so you can disable buffering
43
+ # if you really need to. While ZipKit ams not to buffer, in this instance this
44
+ # buffering is justified. See https://github.com/WeTransfer/zip_tricks/issues/78
45
+ # for the background on buffering.
37
46
  DEFAULT_WRITE_BUFFER_SIZE = 64 * 1024
38
47
 
39
48
  # Creates a new OutputEnumerator enumerator. The enumerator can be read from using `each`,
@@ -3,16 +3,30 @@
3
3
  # Should be included into a Rails controller for easy ZIP output from any action.
4
4
  module ZipKit::RailsStreaming
5
5
  # Opens a {ZipKit::Streamer} and yields it to the caller. The output of the streamer
6
- # gets automatically forwarded to the Rails response stream. When the output completes,
7
- # the Rails response stream is going to be closed automatically.
6
+ # will be sent through to the HTTP response body as it gets produced.
7
+ #
8
+ # Note that there is an important difference in how this method works, depending whether
9
+ # you use it in a controller which includes `ActionController::Live` vs. one that does not.
10
+ # With a standard `ActionController` this method will assign a response body, but streaming
11
+ # will begin when your action method returns. With `ActionController::Live` the streaming
12
+ # will begin immediately, before the method returns. In all other aspects the method should
13
+ # stream correctly in both types of controllers.
14
+ #
15
+ # If you encounter buffering (streaming does not start for a very long time) you probably
16
+ # have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
17
+ # `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
18
+ # always possible. If you encounter buffering, examine your middleware stack and try to suss
19
+ # out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
20
+ # to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
21
+ #
8
22
  # @param filename[String] name of the file for the Content-Disposition header
9
23
  # @param type[String] the content type (MIME type) of the archive being output
10
24
  # @param use_chunked_transfer_encoding[Boolean] whether to forcibly encode output as chunked. Normally you should not need this.
11
- # @param zip_streamer_options[Hash] options that will be passed to the Streamer.
12
- # See {ZipKit::Streamer#initialize} for the full list of options.
13
- # @yieldparam [ZipKit::Streamer] the streamer that can be written to
14
- # @return [ZipKit::OutputEnumerator] The output enumerator assigned to the response body
15
- def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **zip_streamer_options, &zip_streaming_blk)
25
+ # @param output_enumerator_options[Hash] options that will be passed to the OutputEnumerator - these include
26
+ # options for the Streamer. See {ZipKit::OutputEnumerator#initialize} for the full list of options.
27
+ # @yieldparam [ZipKit::Streamer] zip the {ZipKit::Streamer} that can be written to
28
+ # @return [Boolean] always returns true
29
+ def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk)
16
30
  # We want some common headers for file sending. Rails will also set
17
31
  # self.sending_file = true for us when we call send_file_headers!
18
32
  send_file_headers!(type: type, filename: filename)
@@ -26,38 +40,45 @@ module ZipKit::RailsStreaming
26
40
  end
27
41
 
28
42
  headers = ZipKit::OutputEnumerator.streaming_http_headers
29
- response.headers.merge!(headers)
43
+
44
+ # Allow Rails headers to override ours. This is important if, for example, a content type gets
45
+ # set to something else than "application/zip"
46
+ response.headers.reverse_merge!(headers)
30
47
 
31
48
  # The output enumerator yields chunks of bytes generated from the Streamer,
32
- # with some buffering
33
- output_enum = ZipKit::OutputEnumerator.new(**zip_streamer_options, &zip_streaming_blk)
49
+ # with some buffering. See OutputEnumerator docs for more.
50
+ rack_zip_body = ZipKit::OutputEnumerator.new(**output_enumerator_options, &zip_streaming_blk)
51
+
52
+ # Chunked encoding may be forced if, for example, you _need_ to bypass Rack::ContentLength.
53
+ # Rack::ContentLength is normally not in a Rails middleware stack, but it might get
54
+ # introduced unintentionally - for example, "rackup" adds the ContentLength middleware for you.
55
+ # There is a recommendation to leave the chunked encoding to the app server, so that servers
56
+ # that support HTTP/2 can use native framing and not have to deal with the chunked encoding,
57
+ # see https://github.com/julik/zip_kit/issues/7
58
+ # But it is not to be excluded that a user may need to force the chunked encoding to bypass
59
+ # some especially pesky Rack middleware that just would not cooperate. Those include
60
+ # Rack::MiniProfiler and the above-mentioned Rack::ContentLength.
61
+ if use_chunked_transfer_encoding
62
+ response.headers["Transfer-Encoding"] = "chunked"
63
+ rack_zip_body = ZipKit::RackChunkedBody.new(rack_zip_body)
64
+ end
34
65
 
35
66
  # Time for some branching, which mostly has to do with the 999 flavours of
36
67
  # "how to make both Rails and Rack stream"
37
68
  if self.class.ancestors.include?(ActionController::Live)
38
69
  # If this controller includes Live it will not work correctly with a Rack
39
- # response body assignment - we need to write into the Live output stream instead
70
+ # response body assignment - the action will just hang. We need to read out the response
71
+ # body ourselves and write it into the Rails stream.
40
72
  begin
41
- output_enum.each { |bytes| response.stream.write(bytes) }
73
+ rack_zip_body.each { |bytes| response.stream.write(bytes) }
42
74
  ensure
43
75
  response.stream.close
44
76
  end
45
- elsif use_chunked_transfer_encoding
46
- # Chunked encoding may be forced if, for example, you _need_ to bypass Rack::ContentLength.
47
- # Rack::ContentLength is normally not in a Rails middleware stack, but it might get
48
- # introduced unintentionally - for example, "rackup" adds the ContentLength middleware for you.
49
- # There is a recommendation to leave the chunked encoding to the app server, so that servers
50
- # that support HTTP/2 can use native framing and not have to deal with the chunked encoding,
51
- # see https://github.com/julik/zip_kit/issues/7
52
- # But it is not to be excluded that a user may need to force the chunked encoding to bypass
53
- # some especially pesky Rack middleware that just would not cooperate. Those include
54
- # Rack::MiniProfiler and the above-mentioned Rack::ContentLength.
55
- response.headers["Transfer-Encoding"] = "chunked"
56
- self.response_body = ZipKit::RackChunkedBody.new(output_enum)
57
77
  else
58
- # Stream using a Rack body assigned to the ActionController response body, without
59
- # doing explicit chunked encoding. See above for the reasoning.
60
- self.response_body = output_enum
78
+ # Stream using a Rack body assigned to the ActionController response body
79
+ self.response_body = rack_zip_body
61
80
  end
81
+
82
+ true
62
83
  end
63
84
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ZipKit::Railtie < ::Rails::Railtie
4
+ initializer "zip_kit.install_extensions" do |app|
5
+ ActionController::Base.include(ZipKit::RailsStreaming)
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ZipKit
4
- VERSION = "6.2.1"
4
+ VERSION = "6.3.0"
5
5
  end
data/lib/zip_kit.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "zip_kit/version"
4
+ require "zlib"
4
5
 
5
6
  module ZipKit
6
7
  autoload :OutputEnumerator, File.dirname(__FILE__) + "/zip_kit/rack_body.rb"
@@ -23,4 +24,6 @@ module ZipKit
23
24
  autoload :WriteShovel, File.dirname(__FILE__) + "/zip_kit/write_shovel.rb"
24
25
  autoload :RackChunkedBody, File.dirname(__FILE__) + "/zip_kit/rack_chunked_body.rb"
25
26
  autoload :RackTempfileBody, File.dirname(__FILE__) + "/zip_kit/rack_tempfile_body.rb"
27
+
28
+ require_relative "zip_kit/railtie" if defined?(::Rails)
26
29
  end
data/rbi/zip_kit.rbi CHANGED
@@ -1,6 +1,9 @@
1
1
  # typed: strong
2
2
  module ZipKit
3
- VERSION = T.let("6.2.1", T.untyped)
3
+ VERSION = T.let("6.3.0", T.untyped)
4
+
5
+ class Railtie < Rails::Railtie
6
+ end
4
7
 
5
8
  # A ZIP archive contains a flat list of entries. These entries can implicitly
6
9
  # create directories when the archive is expanded. For example, an entry with
@@ -1976,8 +1979,21 @@ end, T.untyped)
1976
1979
  # Should be included into a Rails controller for easy ZIP output from any action.
1977
1980
  module RailsStreaming
1978
1981
  # Opens a {ZipKit::Streamer} and yields it to the caller. The output of the streamer
1979
- # gets automatically forwarded to the Rails response stream. When the output completes,
1980
- # the Rails response stream is going to be closed automatically.
1982
+ # will be sent through to the HTTP response body as it gets produced.
1983
+ #
1984
+ # Note that there is an important difference in how this method works, depending whether
1985
+ # you use it in a controller which includes `ActionController::Live` vs. one that does not.
1986
+ # With a standard `ActionController` this method will assign a response body, but streaming
1987
+ # will begin when your action method returns. With `ActionController::Live` the streaming
1988
+ # will begin immediately, before the method returns. In all other aspects the method should
1989
+ # stream correctly in both types of controllers.
1990
+ #
1991
+ # If you encounter buffering (streaming does not start for a very long time) you probably
1992
+ # have a piece of Rack middleware in your stack which buffers. Known offenders are `Rack::ContentLength`,
1993
+ # `Rack::MiniProfiler` and `Rack::ETag`. ZipKit will try to work around these but it is not
1994
+ # always possible. If you encounter buffering, examine your middleware stack and try to suss
1995
+ # out whether any middleware might be buffering. You can also try setting `use_chunked_transfer_encoding`
1996
+ # to `true` - this is not recommended but sometimes necessary, for example to bypass `Rack::ContentLength`.
1981
1997
  #
1982
1998
  # _@param_ `filename` — name of the file for the Content-Disposition header
1983
1999
  #
@@ -1985,19 +2001,19 @@ end, T.untyped)
1985
2001
  #
1986
2002
  # _@param_ `use_chunked_transfer_encoding` — whether to forcibly encode output as chunked. Normally you should not need this.
1987
2003
  #
1988
- # _@param_ `zip_streamer_options` — options that will be passed to the Streamer. See {ZipKit::Streamer#initialize} for the full list of options.
2004
+ # _@param_ `output_enumerator_options` — options that will be passed to the OutputEnumerator - these include options for the Streamer. See {ZipKit::OutputEnumerator#initialize} for the full list of options.
1989
2005
  #
1990
- # _@return_ — The output enumerator assigned to the response body
2006
+ # _@return_ — always returns true
1991
2007
  sig do
1992
2008
  params(
1993
2009
  filename: String,
1994
2010
  type: String,
1995
2011
  use_chunked_transfer_encoding: T::Boolean,
1996
- zip_streamer_options: T::Hash[T.untyped, T.untyped],
1997
- zip_streaming_blk: T.proc.params(the: ZipKit::Streamer).void
1998
- ).returns(ZipKit::OutputEnumerator)
2012
+ output_enumerator_options: T::Hash[T.untyped, T.untyped],
2013
+ zip_streaming_blk: T.proc.params(zip: ZipKit::Streamer).void
2014
+ ).returns(T::Boolean)
1999
2015
  end
2000
- def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **zip_streamer_options, &zip_streaming_blk); end
2016
+ def zip_kit_stream(filename: "download.zip", type: "application/zip", use_chunked_transfer_encoding: false, **output_enumerator_options, &zip_streaming_blk); end
2001
2017
  end
2002
2018
 
2003
2019
  # The output enumerator makes it possible to "pull" from a ZipKit streamer
data/zip_kit.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  # zip_kit does not use any runtime dependencies (besides zlib). However, for testing
28
28
  # things quite a few things are used - and for a good reason.
29
29
 
30
- spec.add_development_dependency "rubyzip", "~> 1" # We test our output with _another_ ZIP library, which is the way to go here
30
+ spec.add_development_dependency "rubyzip", "~> 2" # We test our output with _another_ ZIP library, which is the way to go here
31
31
  spec.add_development_dependency "rack" # For tests where we spin up a server. Both for streaming out and for testing reads over HTTP
32
32
  spec.add_development_dependency "rake", "~> 12.2"
33
33
  spec.add_development_dependency "rspec", "~> 3"
@@ -40,7 +40,9 @@ Gem::Specification.new do |spec|
40
40
  spec.add_development_dependency "standard", "1.28.5" # Very specific version of standard for 2.6 with _known_ settings
41
41
  spec.add_development_dependency "magic_frozen_string_literal"
42
42
  spec.add_development_dependency "puma"
43
+ spec.add_development_dependency "rails", "~> 5" # For testing RailsStreaming against an actual Rails controller
43
44
  spec.add_development_dependency "actionpack", "~> 5" # For testing RailsStreaming against an actual Rails controller
44
45
  spec.add_development_dependency "nokogiri", "~> 1", ">= 1.13" # Rails 5 does by mistake use an older Nokogiri otherwise
46
+ spec.add_development_dependency "sinatra"
45
47
  spec.add_development_dependency "sord"
46
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zip_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.1
4
+ version: 6.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: exe
14
14
  cert_chain: []
15
- date: 2024-03-23 00:00:00.000000000 Z
15
+ date: 2024-04-01 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bundler
@@ -34,14 +34,14 @@ dependencies:
34
34
  requirements:
35
35
  - - "~>"
36
36
  - !ruby/object:Gem::Version
37
- version: '1'
37
+ version: '2'
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  requirements:
42
42
  - - "~>"
43
43
  - !ruby/object:Gem::Version
44
- version: '1'
44
+ version: '2'
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: rack
47
47
  requirement: !ruby/object:Gem::Requirement
@@ -216,6 +216,20 @@ dependencies:
216
216
  - - ">="
217
217
  - !ruby/object:Gem::Version
218
218
  version: '0'
219
+ - !ruby/object:Gem::Dependency
220
+ name: rails
221
+ requirement: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - "~>"
224
+ - !ruby/object:Gem::Version
225
+ version: '5'
226
+ type: :development
227
+ prerelease: false
228
+ version_requirements: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - "~>"
231
+ - !ruby/object:Gem::Version
232
+ version: '5'
219
233
  - !ruby/object:Gem::Dependency
220
234
  name: actionpack
221
235
  requirement: !ruby/object:Gem::Requirement
@@ -250,6 +264,20 @@ dependencies:
250
264
  - - ">="
251
265
  - !ruby/object:Gem::Version
252
266
  version: '1.13'
267
+ - !ruby/object:Gem::Dependency
268
+ name: sinatra
269
+ requirement: !ruby/object:Gem::Requirement
270
+ requirements:
271
+ - - ">="
272
+ - !ruby/object:Gem::Version
273
+ version: '0'
274
+ type: :development
275
+ prerelease: false
276
+ version_requirements: !ruby/object:Gem::Requirement
277
+ requirements:
278
+ - - ">="
279
+ - !ruby/object:Gem::Version
280
+ version: '0'
253
281
  - !ruby/object:Gem::Dependency
254
282
  name: sord
255
283
  requirement: !ruby/object:Gem::Requirement
@@ -284,6 +312,7 @@ files:
284
312
  - IMPLEMENTATION_DETAILS.md
285
313
  - LICENSE.txt
286
314
  - README.md
315
+ - RUBYZIP_DIFFERENCES.md
287
316
  - Rakefile
288
317
  - bench/buffered_crc32_bench.rb
289
318
  - examples/archive_size_estimate.rb
@@ -292,6 +321,7 @@ files:
292
321
  - examples/parallel_compression_with_block_deflate.rb
293
322
  - examples/rack_application.rb
294
323
  - examples/s3_upload.rb
324
+ - examples/sinatra_application.rb
295
325
  - lib/zip_kit.rb
296
326
  - lib/zip_kit/block_deflate.rb
297
327
  - lib/zip_kit/block_write.rb
@@ -304,6 +334,7 @@ files:
304
334
  - lib/zip_kit/rack_chunked_body.rb
305
335
  - lib/zip_kit/rack_tempfile_body.rb
306
336
  - lib/zip_kit/rails_streaming.rb
337
+ - lib/zip_kit/railtie.rb
307
338
  - lib/zip_kit/remote_io.rb
308
339
  - lib/zip_kit/remote_uncap.rb
309
340
  - lib/zip_kit/size_estimator.rb
@@ -343,7 +374,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
374
  - !ruby/object:Gem::Version
344
375
  version: '0'
345
376
  requirements: []
346
- rubygems_version: 3.3.7
377
+ rubygems_version: 3.0.3
347
378
  signing_key:
348
379
  specification_version: 4
349
380
  summary: Stream out ZIP files from Ruby. Successor to zip_tricks.