zipline 1.4.1 → 1.6.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: b56c703c9733a9c781d4d81eb83826a8de4a647238a83d9ade835fa034c8a84c
4
- data.tar.gz: 0f1cd2b80086cb0a3fcd572aa18d03f03228820870f93b8ec9da7cb6591c165c
3
+ metadata.gz: ca44760a8a5c6019544aa77857692b43d4cd9f429395173a54b779f276f214f2
4
+ data.tar.gz: c9aec0dc791f84a315658ae9405e88f1d5b7348822befa69e61c92e261ff3a69
5
5
  SHA512:
6
- metadata.gz: 0b97a0e674b144d21eeacf5ed955dcf1e62639479f63e73c988d405d3ed707c8d45e64f33bf3bb98eeebc4fabeacf991f2e60c5c23367152a677e2a9e616e43c
7
- data.tar.gz: e54fb0c40ad64959b1c1965a45312aafa5fe0fa6547d626f54795dab8027c0208a4550589fb933266620c0e00d98eb1685daede1b3395539da81f2bf24887a85
6
+ metadata.gz: 2c46de9ba175df42d54c36a37dceddb77cf7816e058528750fe0b05c54313aba1992b8ba12d40b9f029a33c088c79a214d3e50e56cbd0b295c74062fe49c7781
7
+ data.tar.gz: 5022768afbe367ca08c586b33d16f8a044ad79c7cde80b79be4a3d1c9b18c16d2b0d9cdb9d82f75e3df338d101eda5dce80a099c1261e2e3d828ac637569d790
@@ -11,7 +11,6 @@ jobs:
11
11
  fail-fast: false
12
12
  matrix:
13
13
  ruby-version:
14
- - head
15
14
  - '3.1'
16
15
  - '3.0'
17
16
  - '2.7'
data/Gemfile CHANGED
@@ -2,13 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in zipline.gemspec
4
4
  gemspec
5
-
6
- group :development, :test do
7
- gem 'rspec', '~> 3'
8
- gem 'fog-aws'
9
- gem 'activesupport'
10
- gem 'aws-sdk-s3'
11
- gem 'carrierwave'
12
- gem 'paperclip'
13
- gem 'rake'
14
- end
data/README.md CHANGED
@@ -41,7 +41,10 @@ class MyController < ApplicationController
41
41
  # responds to :url, :path or :file.
42
42
  # :modification_time is an optional third argument you can use.
43
43
  files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
44
- zipline(files, 'avatars.zip')
44
+
45
+ # we can force duplicate file names to be renamed, or raise an error
46
+ # we can also pass in our own writer if required to conform with the Delegated [ZipTricks::Streamer object](https://github.com/WeTransfer/zip_tricks/blob/main/lib/zip_tricks/streamer.rb#L147) object.
47
+ zipline(files, 'avatars.zip', auto_rename_duplicate_filenames: true)
45
48
  end
46
49
  end
47
50
  ```
@@ -0,0 +1,31 @@
1
+ module Zipline
2
+ # A body wrapper that emits chunked responses, creating valid
3
+ # "Transfer-Encoding: chunked" HTTP response body. This is copied from Rack::Chunked::Body,
4
+ # because Rack is not going to include that class after version 3.x
5
+ # Rails has a substitute class for this inside ActionController::Streaming,
6
+ # but that module is a private constant in the Rails codebase, and is thus
7
+ # considered "private" from the Rails standpoint. It is not that much code to
8
+ # carry, so we copy it into our code.
9
+ class Chunked
10
+ TERM = "\r\n"
11
+ TAIL = "0#{TERM}"
12
+
13
+ def initialize(body)
14
+ @body = body
15
+ end
16
+
17
+ # For each string yielded by the response body, yield
18
+ # the element in chunked encoding - and finish off with a terminator
19
+ def each
20
+ term = TERM
21
+ @body.each do |chunk|
22
+ size = chunk.bytesize
23
+ next if size == 0
24
+
25
+ yield [size.to_s(16), term, chunk.b, term].join
26
+ end
27
+ yield TAIL
28
+ yield term
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ module Zipline
2
+ # Contains a file handle which can be closed once the response finishes sending.
3
+ # It supports `to_path` so that `Rack::Sendfile` can intercept it
4
+ class TempfileBody
5
+ TEMPFILE_NAME_PREFIX = "zipline-tf-body-"
6
+ attr_reader :tempfile
7
+
8
+ # @param body[#each] the enumerable that yields bytes, usually a `RackBody`.
9
+ # The `body` will be read in full immediately and closed.
10
+ def initialize(env, body)
11
+ @tempfile = Tempfile.new(TEMPFILE_NAME_PREFIX)
12
+ # Rack::TempfileReaper calls close! on tempfiles which get buffered
13
+ # We wil assume that it works fine with Rack::Sendfile (i.e. the path
14
+ # to the file getting served gets used before we unlink the tempfile)
15
+ env['rack.tempfiles'] ||= []
16
+ env['rack.tempfiles'] << @tempfile
17
+
18
+ @tempfile.binmode
19
+
20
+ body.each { |bytes| @tempfile << bytes }
21
+ body.close if body.respond_to?(:close)
22
+
23
+ @tempfile.flush
24
+ end
25
+
26
+ # Returns the size of the contained `Tempfile` so that a correct
27
+ # Content-Length header can be set
28
+ #
29
+ # @return [Integer]
30
+ def size
31
+ @tempfile.size
32
+ end
33
+
34
+ # Returns the path to the `Tempfile`, so that Rack::Sendfile can send this response
35
+ # using the downstream webserver
36
+ #
37
+ # @return [String]
38
+ def to_path
39
+ @tempfile.to_path
40
+ end
41
+
42
+ # Stream the file's contents if `Rack::Sendfile` isn't present.
43
+ #
44
+ # @return [void]
45
+ def each
46
+ @tempfile.rewind
47
+ while chunk = @tempfile.read(16384)
48
+ yield chunk
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module Zipline
2
- VERSION = "1.4.1"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -3,43 +3,35 @@
3
3
  module Zipline
4
4
  class ZipGenerator
5
5
  # takes an array of pairs [[uploader, filename], ... ]
6
- def initialize(files)
7
- @files = files
8
- end
9
-
10
- #this is supposed to be streamed!
11
- def to_s
12
- throw "stop!"
13
- end
14
-
15
- def each(&block)
16
- fake_io_writer = ZipTricks::BlockWrite.new(&block)
17
- # ZipTricks outputs lots of strings in rapid succession, and with
18
- # servers it can be beneficial to avoid doing too many tiny writes so that
19
- # the number of syscalls is minimized. See https://github.com/WeTransfer/zip_tricks/issues/78
20
- # There is a built-in facility for this in ZipTricks which can be used to implement
21
- # some cheap buffering here (it exists both in version 4 and version 5). The buffer is really
22
- # tiny and roughly equal to the medium Linux socket buffer size (16 KB). Although output
23
- # will be not so immediate with this buffering the overall performance will be better,
24
- # especially with multiple clients being serviced at the same time.
25
- # Note that the WriteBuffer writes the same, retained String object - but the contents
26
- # of that object changes between calls. This should work fine with servers where the
27
- # contents of the string gets written to a socket immediately before the execution inside
28
- # the WriteBuffer resumes), but if the strings get retained somewhere - like in an Array -
29
- # this might pose a problem. Unlikely that it will be an issue here though.
30
- write_buffer_size = 16 * 1024
31
- write_buffer = ZipTricks::WriteBuffer.new(fake_io_writer, write_buffer_size)
32
- ZipTricks::Streamer.open(write_buffer) do |streamer|
33
- @files.each do |file, name, options = {}|
6
+ def initialize(files, **kwargs_for_streamer)
7
+ # Use RackBody as it has buffering built-in in zip_tricks 5.x+
8
+ @body = ZipTricks::RackBody.new(**kwargs_for_streamer) do |streamer|
9
+ files.each do |file, name, options = {}|
34
10
  handle_file(streamer, file, name.to_s, options)
35
11
  end
36
12
  end
37
- write_buffer.flush! # for any remaining writes
13
+ end
14
+
15
+ def each(&block)
16
+ return to_enum(:each) unless block_given?
17
+ @body.each(&block)
18
+ rescue => e
19
+ # Since most APM packages do not trace errors occurring within streaming
20
+ # Rack bodies, it can be helpful to print the error to the Rails log at least
21
+ error_message = "zipline: an exception (#{e.inspect}) was raised when serving the ZIP body."
22
+ error_message += " The error occurred when handling #{@filename.inspect}" if @filename
23
+ logger.error(error_message)
24
+ raise
38
25
  end
39
26
 
40
27
  def handle_file(streamer, file, name, options)
41
28
  file = normalize(file)
29
+
30
+ # Store the filename so that a sensible error message can be displayed in the log
31
+ # if writing this particular file fails
32
+ @filename = name
42
33
  write_file(streamer, file, name, options)
34
+ @filename = nil
43
35
  end
44
36
 
45
37
  # This extracts either a url or a local file from the provided file.
@@ -106,6 +98,16 @@ module Zipline
106
98
 
107
99
  private
108
100
 
101
+ def logger
102
+ # Rails is not defined in our tests, and might as well not be defined
103
+ # elsewhere - or the logger might not be configured correctly
104
+ if defined?(Rails.logger) && Rails.logger
105
+ Rails.logger
106
+ else
107
+ Logger.new($stderr)
108
+ end
109
+ end
110
+
109
111
  def is_active_storage_attachment?(file)
110
112
  defined?(ActiveStorage::Attachment) && file.is_a?(ActiveStorage::Attachment)
111
113
  end
data/lib/zipline.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'content_disposition'
2
- require "zipline/version"
2
+ require 'zipline/version'
3
3
  require 'zip_tricks'
4
- require "zipline/zip_generator"
4
+ require 'zipline/zip_generator'
5
+ require 'zipline/chunked_body'
6
+ require 'zipline/tempfile_body'
5
7
 
6
8
  # class MyController < ApplicationController
7
9
  # include Zipline
@@ -12,13 +14,42 @@ require "zipline/zip_generator"
12
14
  # end
13
15
  # end
14
16
  module Zipline
15
- def zipline(files, zipname = 'zipline.zip')
16
- zip_generator = ZipGenerator.new(files)
17
+ def zipline(files, zipname = 'zipline.zip', **kwargs_for_new)
18
+ zip_generator = ZipGenerator.new(files, **kwargs_for_new)
17
19
  headers['Content-Disposition'] = ContentDisposition.format(disposition: 'attachment', filename: zipname)
18
20
  headers['Content-Type'] = Mime::Type.lookup_by_extension('zip').to_s
19
21
  response.sending_file = true
20
22
  response.cache_control[:public] ||= false
21
- self.response_body = zip_generator
23
+
24
+ # Disables Rack::ETag if it is enabled (prevent buffering)
25
+ # see https://github.com/rack/rack/issues/1619#issuecomment-606315714
22
26
  self.response.headers['Last-Modified'] = Time.now.httpdate
27
+
28
+ if request.get_header("HTTP_VERSION") == "HTTP/1.0"
29
+ # If HTTP/1.0 is used it is not possible to stream, and if that happens it usually will be
30
+ # unclear why buffering is happening. Some info in the log is the least one can do.
31
+ logger.warn { "The downstream HTTP proxy/LB insists on HTTP/1.0 protocol, ZIP response will be buffered." } if logger
32
+
33
+ # If we use Rack::ContentLength it would run through our ZIP block twice - once to calculate the content length
34
+ # of the response, and once - to serve. We can trade performance for disk space and buffer the response into a Tempfile
35
+ # since we are already buffering.
36
+ tempfile_body = TempfileBody.new(request.env, zip_generator)
37
+ headers["Content-Length"] = tempfile_body.size.to_s
38
+ headers["X-Zipline-Output"] = "buffered"
39
+ self.response_body = tempfile_body
40
+ else
41
+ # Disable buffering for both nginx and Google Load Balancer, see
42
+ # https://cloud.google.com/appengine/docs/flexible/how-requests-are-handled?tab=python#x-accel-buffering
43
+ response.headers["X-Accel-Buffering"] = "no"
44
+
45
+ # Make sure Rack::ContentLength does not try to compute a content length,
46
+ # and remove the one already set
47
+ headers.delete("Content-Length")
48
+
49
+ # and send out in chunked encoding
50
+ headers["Transfer-Encoding"] = "chunked"
51
+ headers["X-Zipline-Output"] = "streamed"
52
+ self.response_body = Chunked.new(zip_generator)
53
+ end
23
54
  end
24
55
  end
@@ -185,86 +185,4 @@ describe Zipline::ZipGenerator do
185
185
  end
186
186
  end
187
187
  end
188
-
189
- describe '.write_file' do
190
- let(:file) { StringIO.new('passthrough') }
191
-
192
- context 'when passing an ActiveStorage::Filename object as filename' do
193
- let(:filename) { ActiveStorage::Filename.new('test') }
194
-
195
- let(:generator) do
196
- Zipline::ZipGenerator.new([[file, filename]])
197
- end
198
-
199
- it 'passes a string as filename to ZipTricks' do
200
- allow(file).to receive(:url).and_return('fakeurl')
201
- expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
202
- .with('test')
203
- generator.each { |_| 'Test' }
204
- end
205
- end
206
- end
207
-
208
- describe 'passing an options hash' do
209
- let(:file) { StringIO.new('passthrough') }
210
-
211
- context 'with optional arguments' do
212
- let(:mtime) { 1.day.ago }
213
- let(:generator) do
214
- Zipline::ZipGenerator.new([[file, 'test', modification_time: mtime]])
215
- end
216
-
217
- it 'passes the options hash through handle_file' do
218
- expect(generator).to receive(:handle_file)
219
- .with(anything, anything, anything, modification_time: mtime)
220
- generator.each { |_| 'Test' }
221
- end
222
-
223
- it 'passes the options hash to ZipTricks as kwargs' do
224
- allow(file).to receive(:url).and_return('fakeurl')
225
- expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
226
- .with(anything, modification_time: mtime)
227
- generator.each { |_| 'Test' }
228
- end
229
- end
230
-
231
- context 'without optional arguments' do
232
- let(:generator) do
233
- Zipline::ZipGenerator.new([[file, 'test']])
234
- end
235
-
236
- it 'passes the options hash through handle_file' do
237
- expect(generator).to receive(:handle_file)
238
- .with(anything, anything, anything, {})
239
- generator.each { |_| 'Test' }
240
- end
241
-
242
- it 'passes the options hash to ZipTricks as kwargs' do
243
- allow(file).to receive(:url).and_return('fakeurl')
244
- expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
245
- .with(anything)
246
- generator.each { |_| 'Test' }
247
- end
248
- end
249
-
250
- context 'with extra invalid options' do
251
- let(:mtime) { 1.day.ago }
252
- let(:generator) do
253
- Zipline::ZipGenerator.new([[file, 'test', modification_time: mtime, extra: 'invalid']])
254
- end
255
-
256
- it 'passes the whole options hash through handle_file' do
257
- expect(generator).to receive(:handle_file)
258
- .with(anything, anything, anything, { modification_time: mtime, extra: 'invalid' })
259
- generator.each { |_| 'Test' }
260
- end
261
-
262
- it 'only passes the kwargs to ZipTricks that it expects (i.e., :modification_time)' do
263
- allow(file).to receive(:url).and_return('fakeurl')
264
- expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
265
- .with(anything, modification_time: mtime)
266
- generator.each { |_| 'Test' }
267
- end
268
- end
269
- end
270
188
  end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'action_controller'
3
+
4
+ describe Zipline do
5
+ before { Fog.mock! }
6
+
7
+ class FakeController < ActionController::Base
8
+ include Zipline
9
+ def download_zip
10
+ files = [
11
+ [StringIO.new("File content goes here"), "one.txt"],
12
+ [StringIO.new("Some other content goes here"), "two.txt"]
13
+ ]
14
+ zipline(files, 'myfiles.zip', auto_rename_duplicate_filenames: false)
15
+ end
16
+
17
+ class FailingIO < StringIO
18
+ def read(*)
19
+ raise "Something wonky"
20
+ end
21
+ end
22
+
23
+ def download_zip_with_error_during_streaming
24
+ files = [
25
+ [StringIO.new("File content goes here"), "one.txt"],
26
+ [FailingIO.new("This will fail half-way"), "two.txt"]
27
+ ]
28
+ zipline(files, 'myfiles.zip', auto_rename_duplicate_filenames: false)
29
+ end
30
+ end
31
+
32
+ it 'passes keyword parameters to ZipTricks::Streamer' do
33
+ fake_rack_env = {
34
+ "HTTP_VERSION" => "HTTP/1.0",
35
+ "REQUEST_METHOD" => "GET",
36
+ "SCRIPT_NAME" => "",
37
+ "PATH_INFO" => "/download",
38
+ "QUERY_STRING" => "",
39
+ "SERVER_NAME" => "host.example",
40
+ "rack.input" => StringIO.new,
41
+ }
42
+ expect(ZipTricks::Streamer).to receive(:new).with(anything, auto_rename_duplicate_filenames: false).and_call_original
43
+
44
+ status, headers, body = FakeController.action(:download_zip).call(fake_rack_env)
45
+
46
+ expect(headers['Content-Disposition']).to eq("attachment; filename=\"myfiles.zip\"; filename*=UTF-8''myfiles.zip")
47
+ end
48
+
49
+ it 'sends the exception raised in the streaming body to the Rails logger' do
50
+ fake_rack_env = {
51
+ "HTTP_VERSION" => "HTTP/1.0",
52
+ "REQUEST_METHOD" => "GET",
53
+ "SCRIPT_NAME" => "",
54
+ "PATH_INFO" => "/download",
55
+ "QUERY_STRING" => "",
56
+ "SERVER_NAME" => "host.example",
57
+ "rack.input" => StringIO.new,
58
+ }
59
+ expect(ZipTricks::Streamer).to receive(:new).with(anything, auto_rename_duplicate_filenames: false).and_call_original
60
+ fake_logger = double()
61
+ expect(Logger).to receive(:new).and_return(fake_logger)
62
+ expect(fake_logger).to receive(:error).with(instance_of(String))
63
+
64
+ expect {
65
+ FakeController.action(:download_zip_with_error_during_streaming).call(fake_rack_env)
66
+ }.to raise_error(/Something wonky/)
67
+ end
68
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,22 +1,23 @@
1
1
  require 'rspec'
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext'
4
+ require 'action_dispatch'
5
+
4
6
  require 'zipline'
5
7
  require 'paperclip'
6
8
  require 'fog-aws'
7
9
  require 'carrierwave'
8
10
 
9
- Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].each { |f| require f }
11
+ Dir["#{File.expand_path('..', __FILE__)}/support/**/*.rb"].sort.each { |f| require f }
10
12
 
11
13
  CarrierWave.configure do |config|
12
14
  config.fog_provider = 'fog/aws'
13
15
  config.fog_credentials = {
14
- provider: 'AWS',
15
- aws_access_key_id: 'dummy',
16
- aws_secret_access_key: 'data',
17
- region: 'us-west-2',
18
- }
19
-
16
+ provider: 'AWS',
17
+ aws_access_key_id: 'dummy',
18
+ aws_secret_access_key: 'data',
19
+ region: 'us-west-2',
20
+ }
20
21
  end
21
22
 
22
23
  RSpec.configure do |config|
data/zipline.gemspec CHANGED
@@ -20,8 +20,15 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_dependency 'actionpack', ['>= 6.0', '< 8.0']
22
22
  gem.add_dependency 'content_disposition', '~> 1.0'
23
- gem.add_dependency 'zip_tricks', ['>= 4.2.1', '< 6.0']
23
+ gem.add_dependency 'zip_tricks', ['~> 4.8', '< 6'] # Minimum to 4.8.3 which is the last-released MIT version
24
+
25
+ gem.add_development_dependency 'rspec', '~> 3'
26
+ gem.add_development_dependency 'fog-aws'
27
+ gem.add_development_dependency 'aws-sdk-s3'
28
+ gem.add_development_dependency 'carrierwave'
29
+ gem.add_development_dependency 'paperclip'
30
+ gem.add_development_dependency 'rake'
24
31
 
25
32
  # https://github.com/rspec/rspec-mocks/issues/1457
26
- gem.add_development_dependency 'rspec-mocks', ['~> 3.10', '!= 3.10.3']
33
+ gem.add_development_dependency 'rspec-mocks', '~> 3.12'
27
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zipline
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ram Dobson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-08 00:00:00.000000000 Z
11
+ date: 2024-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -48,42 +48,120 @@ dependencies:
48
48
  name: zip_tricks
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 4.2.1
53
+ version: '4.8'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '6.0'
56
+ version: '6'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - ">="
61
+ - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 4.2.1
63
+ version: '4.8'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '6.0'
66
+ version: '6'
67
67
  - !ruby/object:Gem::Dependency
68
- name: rspec-mocks
68
+ name: rspec
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '3.10'
74
- - - "!="
73
+ version: '3'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3'
81
+ - !ruby/object:Gem::Dependency
82
+ name: fog-aws
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: aws-sdk-s3
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: carrierwave
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: paperclip
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rake
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
75
142
  - !ruby/object:Gem::Version
76
- version: 3.10.3
143
+ version: '0'
77
144
  type: :development
78
145
  prerelease: false
79
146
  version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: rspec-mocks
153
+ requirement: !ruby/object:Gem::Requirement
80
154
  requirements:
81
155
  - - "~>"
82
156
  - !ruby/object:Gem::Version
83
- version: '3.10'
84
- - - "!="
157
+ version: '3.12'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
85
163
  - !ruby/object:Gem::Version
86
- version: 3.10.3
164
+ version: '3.12'
87
165
  description: a module for streaming dynamically generated zip files
88
166
  email:
89
167
  - ram.dobson@solsystemscompany.com
@@ -97,10 +175,13 @@ files:
97
175
  - README.md
98
176
  - Rakefile
99
177
  - lib/zipline.rb
178
+ - lib/zipline/chunked_body.rb
179
+ - lib/zipline/tempfile_body.rb
100
180
  - lib/zipline/version.rb
101
181
  - lib/zipline/zip_generator.rb
102
182
  - spec/fakefile.txt
103
183
  - spec/lib/zipline/zip_generator_spec.rb
184
+ - spec/lib/zipline/zipline_spec.rb
104
185
  - spec/spec_helper.rb
105
186
  - zipline.gemspec
106
187
  homepage: http://github.com/fringd/zipline
@@ -122,11 +203,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
203
  - !ruby/object:Gem::Version
123
204
  version: '0'
124
205
  requirements: []
125
- rubygems_version: 3.2.3
206
+ rubygems_version: 3.3.26
126
207
  signing_key:
127
208
  specification_version: 4
128
209
  summary: stream zip files from rails
129
210
  test_files:
130
211
  - spec/fakefile.txt
131
212
  - spec/lib/zipline/zip_generator_spec.rb
213
+ - spec/lib/zipline/zipline_spec.rb
132
214
  - spec/spec_helper.rb