zip_kit 6.2.1 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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.