zipline 1.6.0 → 2.0.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: ca44760a8a5c6019544aa77857692b43d4cd9f429395173a54b779f276f214f2
4
- data.tar.gz: c9aec0dc791f84a315658ae9405e88f1d5b7348822befa69e61c92e261ff3a69
3
+ metadata.gz: 954c769aaf6aa7780a8770fcc9991a1beea5b326f1920b6bfdcc921dcef1a34d
4
+ data.tar.gz: 7e40d818c5b4e7da8c8167f42e9318d0d7cfee2384657a84fa3ee294184f9819
5
5
  SHA512:
6
- metadata.gz: 2c46de9ba175df42d54c36a37dceddb77cf7816e058528750fe0b05c54313aba1992b8ba12d40b9f029a33c088c79a214d3e50e56cbd0b295c74062fe49c7781
7
- data.tar.gz: 5022768afbe367ca08c586b33d16f8a044ad79c7cde80b79be4a3d1c9b18c16d2b0d9cdb9d82f75e3df338d101eda5dce80a099c1261e2e3d828ac637569d790
6
+ metadata.gz: 5a39143123fc75b44b94d88ca0e8d31a41e2b63f9368ff70692c502c21171baf406e4eb8bb7a9f9ef0885d2883d8c5e5d40995499068a2a0a9a9906c9135bc68
7
+ data.tar.gz: 84c7b320023d7195f619638b87acaa14121852a643a727a9ba5c1d848ed3d4bc045832347586ca3b454dfb753ad189f2bfe96d5e97bd6140065a1fa0ef4675b8
data/README.md CHANGED
@@ -7,7 +7,7 @@ A gem to stream dynamically generated zip files from a rails application. Unlike
7
7
  - Removes need for large disk space or memory allocation to generate zips, even huge zips. So it works on Heroku.
8
8
  - The user begins downloading immediately, which decreaceses latency, download time, and timeouts on Heroku.
9
9
 
10
- Zipline now depends on [zip tricks](https://github.com/WeTransfer/zip_tricks), and you might want to just use that directly if you have more advanced use cases.
10
+ Zipline now depends on [zip_kit](https://github.com/julik/zip_kit), and you might want to just use that directly if you have more advanced use cases.
11
11
 
12
12
  ## Installation
13
13
 
@@ -43,7 +43,7 @@ class MyController < ApplicationController
43
43
  files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
44
44
 
45
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.
46
+ # we can also pass in our own writer if required to conform with the delegated [ZipKit::Streamer object](https://github.com/julik/zip_kit/blob/main/lib/zip_kit/streamer.rb#L147) object.
47
47
  zipline(files, 'avatars.zip', auto_rename_duplicate_filenames: true)
48
48
  end
49
49
  end
@@ -93,7 +93,7 @@ For directories, just give the files names like "directory/file".
93
93
 
94
94
  ```Ruby
95
95
  avatars = [
96
- # remote_url zip_path zip_tricks_options
96
+ # remote_url zip_path write_file options for Streamer
97
97
  [ 'http://www.example.com/user1.png', 'avatars/user1.png', modification_time: Time.now.utc ]
98
98
  [ 'http://www.example.com/user2.png', 'avatars/user2.png', modification_time: 1.day.ago ]
99
99
  [ 'http://www.example.com/user3.png', 'avatars/user3.png' ]
@@ -1,3 +1,3 @@
1
1
  module Zipline
2
- VERSION = "1.6.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -1,39 +1,23 @@
1
- # this class acts as a streaming body for rails
2
- # initialize it with an array of the files you want to zip
3
1
  module Zipline
4
- class ZipGenerator
2
+ class ZipHandler
5
3
  # takes an array of pairs [[uploader, filename], ... ]
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 = {}|
10
- handle_file(streamer, file, name.to_s, options)
11
- end
12
- end
4
+ def initialize(streamer, logger)
5
+ @streamer = streamer
6
+ @logger = logger
13
7
  end
14
8
 
15
- def each(&block)
16
- return to_enum(:each) unless block_given?
17
- @body.each(&block)
9
+ def handle_file(file, name, options)
10
+ normalized_file = normalize(file)
11
+ write_file(normalized_file, name, options)
18
12
  rescue => e
19
13
  # Since most APM packages do not trace errors occurring within streaming
20
14
  # Rack bodies, it can be helpful to print the error to the Rails log at least
21
15
  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)
16
+ error_message += " The error occurred when handling file #{name.inspect}"
17
+ @logger.error(error_message) if @logger
24
18
  raise
25
19
  end
26
20
 
27
- def handle_file(streamer, file, name, options)
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
33
- write_file(streamer, file, name, options)
34
- @filename = nil
35
- end
36
-
37
21
  # This extracts either a url or a local file from the provided file.
38
22
  # Currently support carrierwave and paperclip local and remote storage.
39
23
  # returns a hash of the form {url: aUrl} or {file: anIoObject}
@@ -71,8 +55,8 @@ module Zipline
71
55
  end
72
56
  end
73
57
 
74
- def write_file(streamer, file, name, options)
75
- streamer.write_deflated_file(name, **options.slice(:modification_time)) do |writer_for_file|
58
+ def write_file(file, name, options)
59
+ @streamer.write_file(name, **options.slice(:modification_time)) do |writer_for_file|
76
60
  if file[:url]
77
61
  the_remote_uri = URI(file[:url])
78
62
 
@@ -92,20 +76,10 @@ module Zipline
92
76
  end
93
77
  end
94
78
 
95
- def is_io?(io_ish)
96
- io_ish.respond_to? :read
97
- end
98
-
99
79
  private
100
80
 
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
81
+ def is_io?(io_ish)
82
+ io_ish.respond_to? :read
109
83
  end
110
84
 
111
85
  def is_active_storage_attachment?(file)
data/lib/zipline.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  require 'content_disposition'
2
+ require 'zip_kit'
2
3
  require 'zipline/version'
3
- require 'zip_tricks'
4
- require 'zipline/zip_generator'
5
- require 'zipline/chunked_body'
6
- require 'zipline/tempfile_body'
4
+ require 'zipline/zip_handler'
7
5
 
8
6
  # class MyController < ApplicationController
9
7
  # include Zipline
@@ -14,42 +12,17 @@ require 'zipline/tempfile_body'
14
12
  # end
15
13
  # end
16
14
  module Zipline
17
- def zipline(files, zipname = 'zipline.zip', **kwargs_for_new)
18
- zip_generator = ZipGenerator.new(files, **kwargs_for_new)
19
- headers['Content-Disposition'] = ContentDisposition.format(disposition: 'attachment', filename: zipname)
20
- headers['Content-Type'] = Mime::Type.lookup_by_extension('zip').to_s
21
- response.sending_file = true
22
- response.cache_control[:public] ||= false
23
-
24
- # Disables Rack::ETag if it is enabled (prevent buffering)
25
- # see https://github.com/rack/rack/issues/1619#issuecomment-606315714
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")
15
+ def self.included(into_controller)
16
+ into_controller.include(ZipKit::RailsStreaming)
17
+ super
18
+ end
48
19
 
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)
20
+ def zipline(files, zipname = 'zipline.zip', **kwargs_for_zip_kit_stream)
21
+ zip_kit_stream(filename: zipname, **kwargs_for_zip_kit_stream) do |zip_kit_streamer|
22
+ handler = Zipline::ZipHandler.new(zip_kit_streamer, logger)
23
+ files.each do |file, name, options = {}|
24
+ handler.handle_file(file, name.to_s, options)
25
+ end
53
26
  end
54
27
  end
55
28
  end
@@ -18,7 +18,7 @@ module ActiveStorage
18
18
  end
19
19
  end
20
20
 
21
- describe Zipline::ZipGenerator do
21
+ describe Zipline::ZipHandler do
22
22
  before { Fog.mock! }
23
23
  let(:file_attributes){ {
24
24
  key: 'fog_file_tests',
@@ -38,21 +38,21 @@ describe Zipline::ZipGenerator do
38
38
  let(:file){ directory.files.create(file_attributes) }
39
39
 
40
40
  describe '.normalize' do
41
- let(:generator){ Zipline::ZipGenerator.new([])}
41
+ let(:handler){ Zipline::ZipHandler.new(_streamer = double(), _logger = nil)}
42
42
  context "CarrierWave" do
43
43
  context "Remote" do
44
44
  let(:file){ CarrierWave::Storage::Fog::File.new(nil,nil,nil) }
45
45
  it "extracts the url" do
46
46
  allow(file).to receive(:url).and_return('fakeurl')
47
47
  expect(File).not_to receive(:open)
48
- expect(generator.normalize(file)).to eq({url: 'fakeurl'})
48
+ expect(handler.normalize(file)).to eq({url: 'fakeurl'})
49
49
  end
50
50
  end
51
51
  context "Local" do
52
52
  let(:file){ CarrierWave::SanitizedFile.new(Tempfile.new('t')) }
53
53
  it "creates a File" do
54
54
  allow(file).to receive(:path).and_return('spec/fakefile.txt')
55
- normalized = generator.normalize(file)
55
+ normalized = handler.normalize(file)
56
56
  expect(normalized.keys).to include(:file)
57
57
  expect(normalized[:file]).to be_a File
58
58
  end
@@ -66,7 +66,7 @@ describe Zipline::ZipGenerator do
66
66
  allow(uploader).to receive(:file).and_return(file)
67
67
  allow(file).to receive(:url).and_return('fakeurl')
68
68
  expect(File).not_to receive(:open)
69
- expect(generator.normalize(uploader)).to eq({url: 'fakeurl'})
69
+ expect(handler.normalize(uploader)).to eq({url: 'fakeurl'})
70
70
  end
71
71
  end
72
72
 
@@ -75,7 +75,7 @@ describe Zipline::ZipGenerator do
75
75
  it "creates a File" do
76
76
  allow(uploader).to receive(:file).and_return(file)
77
77
  allow(file).to receive(:path).and_return('spec/fakefile.txt')
78
- normalized = generator.normalize(uploader)
78
+ normalized = handler.normalize(uploader)
79
79
  expect(normalized.keys).to include(:file)
80
80
  expect(normalized[:file]).to be_a File
81
81
  end
@@ -87,7 +87,7 @@ describe Zipline::ZipGenerator do
87
87
  let(:file){ Paperclip::Attachment.new(:name, :instance) }
88
88
  it "creates a File" do
89
89
  allow(file).to receive(:path).and_return('spec/fakefile.txt')
90
- normalized = generator.normalize(file)
90
+ normalized = handler.normalize(file)
91
91
  expect(normalized.keys).to include(:file)
92
92
  expect(normalized[:file]).to be_a File
93
93
  end
@@ -97,7 +97,7 @@ describe Zipline::ZipGenerator do
97
97
  it "creates a URL" do
98
98
  allow(file).to receive(:expiring_url).and_return('fakeurl')
99
99
  expect(File).to_not receive(:open)
100
- expect(generator.normalize(file)).to include(url: 'fakeurl')
100
+ expect(handler.normalize(file)).to include(url: 'fakeurl')
101
101
  end
102
102
  end
103
103
  end
@@ -107,7 +107,7 @@ describe Zipline::ZipGenerator do
107
107
  attached = create_attached_one
108
108
  allow_any_instance_of(Object).to receive(:defined?).and_return(true)
109
109
 
110
- normalized = generator.normalize(attached)
110
+ normalized = handler.normalize(attached)
111
111
 
112
112
  expect(normalized.keys).to include(:blob)
113
113
  expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
@@ -119,7 +119,7 @@ describe Zipline::ZipGenerator do
119
119
  attachment = create_attachment
120
120
  allow_any_instance_of(Object).to receive(:defined?).and_return(true)
121
121
 
122
- normalized = generator.normalize(attachment)
122
+ normalized = handler.normalize(attachment)
123
123
 
124
124
  expect(normalized.keys).to include(:blob)
125
125
  expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
@@ -131,7 +131,7 @@ describe Zipline::ZipGenerator do
131
131
  blob = create_blob
132
132
  allow_any_instance_of(Object).to receive(:defined?).and_return(true)
133
133
 
134
- normalized = generator.normalize(blob)
134
+ normalized = handler.normalize(blob)
135
135
 
136
136
  expect(normalized.keys).to include(:blob)
137
137
  expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
@@ -169,19 +169,19 @@ describe Zipline::ZipGenerator do
169
169
  it "extracts url" do
170
170
  allow(file).to receive(:url).and_return('fakeurl')
171
171
  expect(File).not_to receive(:open)
172
- expect(generator.normalize(file)).to eq(url: 'fakeurl')
172
+ expect(handler.normalize(file)).to eq(url: 'fakeurl')
173
173
  end
174
174
  end
175
175
  context "IOStream" do
176
176
  let(:file){ StringIO.new('passthrough')}
177
177
  it "passes through" do
178
- expect(generator.normalize(file)).to eq(file: file)
178
+ expect(handler.normalize(file)).to eq(file: file)
179
179
  end
180
180
  end
181
181
  context "invalid" do
182
182
  let(:file){ Thread.new{} }
183
183
  it "raises error" do
184
- expect{generator.normalize(file)}.to raise_error(ArgumentError)
184
+ expect{handler.normalize(file)}.to raise_error(ArgumentError)
185
185
  end
186
186
  end
187
187
  end
@@ -2,7 +2,10 @@ require 'spec_helper'
2
2
  require 'action_controller'
3
3
 
4
4
  describe Zipline do
5
- before { Fog.mock! }
5
+ before do
6
+ Fog.mock!
7
+ FakeController.logger = nil
8
+ end
6
9
 
7
10
  class FakeController < ActionController::Base
8
11
  include Zipline
@@ -29,7 +32,7 @@ describe Zipline do
29
32
  end
30
33
  end
31
34
 
32
- it 'passes keyword parameters to ZipTricks::Streamer' do
35
+ it 'passes keyword parameters to ZipKit::OutputEnumerator' do
33
36
  fake_rack_env = {
34
37
  "HTTP_VERSION" => "HTTP/1.0",
35
38
  "REQUEST_METHOD" => "GET",
@@ -39,7 +42,7 @@ describe Zipline do
39
42
  "SERVER_NAME" => "host.example",
40
43
  "rack.input" => StringIO.new,
41
44
  }
42
- expect(ZipTricks::Streamer).to receive(:new).with(anything, auto_rename_duplicate_filenames: false).and_call_original
45
+ expect(ZipKit::OutputEnumerator).to receive(:new).with(auto_rename_duplicate_filenames: false).and_call_original
43
46
 
44
47
  status, headers, body = FakeController.action(:download_zip).call(fake_rack_env)
45
48
 
@@ -56,13 +59,15 @@ describe Zipline do
56
59
  "SERVER_NAME" => "host.example",
57
60
  "rack.input" => StringIO.new,
58
61
  }
59
- expect(ZipTricks::Streamer).to receive(:new).with(anything, auto_rename_duplicate_filenames: false).and_call_original
60
62
  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
+ allow(fake_logger).to receive(:warn)
64
+ expect(fake_logger).to receive(:error).with(a_string_matching(/when serving the ZIP/))
65
+
66
+ FakeController.logger = fake_logger
63
67
 
64
68
  expect {
65
- FakeController.action(:download_zip_with_error_during_streaming).call(fake_rack_env)
69
+ status, headers, body = FakeController.action(:download_zip_with_error_during_streaming).call(fake_rack_env)
70
+ body.each { }
66
71
  }.to raise_error(/Something wonky/)
67
72
  end
68
73
  end
data/zipline.gemspec CHANGED
@@ -20,7 +20,7 @@ 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.8', '< 6'] # Minimum to 4.8.3 which is the last-released MIT version
23
+ gem.add_dependency 'zip_kit', ['~> 6', '>= 6.2.0', '< 7']
24
24
 
25
25
  gem.add_development_dependency 'rspec', '~> 3'
26
26
  gem.add_development_dependency 'fog-aws'
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.6.0
4
+ version: 2.0.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: 2024-02-12 00:00:00.000000000 Z
11
+ date: 2024-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -45,25 +45,31 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: '1.0'
47
47
  - !ruby/object:Gem::Dependency
48
- name: zip_tricks
48
+ name: zip_kit
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '4.8'
53
+ version: '6'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 6.2.0
54
57
  - - "<"
55
58
  - !ruby/object:Gem::Version
56
- version: '6'
59
+ version: '7'
57
60
  type: :runtime
58
61
  prerelease: false
59
62
  version_requirements: !ruby/object:Gem::Requirement
60
63
  requirements:
61
64
  - - "~>"
62
65
  - !ruby/object:Gem::Version
63
- version: '4.8'
66
+ version: '6'
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 6.2.0
64
70
  - - "<"
65
71
  - !ruby/object:Gem::Version
66
- version: '6'
72
+ version: '7'
67
73
  - !ruby/object:Gem::Dependency
68
74
  name: rspec
69
75
  requirement: !ruby/object:Gem::Requirement
@@ -175,12 +181,10 @@ files:
175
181
  - README.md
176
182
  - Rakefile
177
183
  - lib/zipline.rb
178
- - lib/zipline/chunked_body.rb
179
- - lib/zipline/tempfile_body.rb
180
184
  - lib/zipline/version.rb
181
- - lib/zipline/zip_generator.rb
185
+ - lib/zipline/zip_handler.rb
182
186
  - spec/fakefile.txt
183
- - spec/lib/zipline/zip_generator_spec.rb
187
+ - spec/lib/zipline/zip_handler_spec.rb
184
188
  - spec/lib/zipline/zipline_spec.rb
185
189
  - spec/spec_helper.rb
186
190
  - zipline.gemspec
@@ -209,6 +213,6 @@ specification_version: 4
209
213
  summary: stream zip files from rails
210
214
  test_files:
211
215
  - spec/fakefile.txt
212
- - spec/lib/zipline/zip_generator_spec.rb
216
+ - spec/lib/zipline/zip_handler_spec.rb
213
217
  - spec/lib/zipline/zipline_spec.rb
214
218
  - spec/spec_helper.rb
@@ -1,31 +0,0 @@
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
@@ -1,52 +0,0 @@
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