zipline 1.6.0 → 2.0.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: 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