zipline 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/README.md +33 -26
- data/lib/zipline.rb +3 -3
- data/lib/zipline/version.rb +1 -1
- data/lib/zipline/zip_generator.rb +19 -5
- data/spec/lib/zipline/zip_generator_spec.rb +100 -17
- data/zipline.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69a1498073cad22c14b800ca508220aadb0f1237
|
4
|
+
data.tar.gz: 1046ab42fb8678bcb2ad08ca4cd8b4c00f6bf8c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 296f7ba0ba6fa91e78a060b40edbda917ed4c839658faf639f50888d9089b9d992003a5e01348b0b3377b998a7b162bc4548c826569894f29ae6a4f0a632459b
|
7
|
+
data.tar.gz: 987a45caed0f8956172dff50e438198ddc8e36fe2f65c5a55bab67c5b24bc6df1aff7320f57bfe4c139d6f491963ca496f0e97fc292a4bdedee39ee677e0bd8b
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# Zipline
|
2
|
+
[![Build Status](https://travis-ci.org/fringd/zipline.svg?branch=master)](https://travis-ci.org/fringd/zipline)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/zipline.svg)](https://badge.fury.io/rb/zipline)
|
2
4
|
|
3
5
|
A gem to stream dynamically generated zip files from a rails application. Unlike other solutions that generate zips for user download, zipline does not wait for the entire zip file to be created (or even for the entire input file in the cloud to be downloaded) before it begins sending the zip file to the user. It does this by never seeking backwards during zip creation, and streaming the zip file over http as it is constructed. The advantages of this are:
|
4
6
|
|
@@ -28,37 +30,42 @@ supported with [shrine](https://github.com/janko-m/shrine).
|
|
28
30
|
|
29
31
|
You'll need to be using puma or some other server that supports streaming output.
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
33
|
+
```Ruby
|
34
|
+
class MyController < ApplicationController
|
35
|
+
# enable streaming responses
|
36
|
+
include ActionController::Streaming
|
37
|
+
# enable zipline
|
38
|
+
include Zipline
|
39
|
+
|
40
|
+
def index
|
41
|
+
users = User.all
|
42
|
+
# you can replace user.avatar with any stream or any object that
|
43
|
+
# responds to :url.
|
44
|
+
# :modification_time is an optional third argument you can use.
|
45
|
+
files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
|
46
|
+
zipline(files, 'avatars.zip')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
45
50
|
|
46
51
|
For directories, just give the files names like "directory/file".
|
47
52
|
|
48
53
|
To stream files from a remote URL, use open-uri with a [lazy enumerator](http://ruby-doc.org/core-2.0.0/Enumerator/Lazy.html):
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
```Ruby
|
56
|
+
require 'open-uri'
|
57
|
+
avatars = [
|
58
|
+
# remote_url zip_path
|
59
|
+
[ 'http://www.example.com/user1.png', 'avatars/user1.png', modification_time: Time.now.utc ]
|
60
|
+
[ 'http://www.example.com/user2.png', 'avatars/user2.png', modification_time: 1.day.ago ]
|
61
|
+
[ 'http://www.example.com/user3.png', 'avatars/user3.png' ]
|
62
|
+
]
|
63
|
+
file_mappings = avatars
|
64
|
+
.lazy # Lazy allows us to begin sending the download immediately instead of waiting to download everything
|
65
|
+
.map { |url, path| [open(url), path] }
|
66
|
+
zipline(file_mappings, 'avatars.zip')
|
67
|
+
```
|
68
|
+
|
62
69
|
## Contributing
|
63
70
|
|
64
71
|
1. Fork it
|
data/lib/zipline.rb
CHANGED
@@ -6,9 +6,9 @@ require "zipline/zip_generator"
|
|
6
6
|
# class MyController < ApplicationController
|
7
7
|
# include Zipline
|
8
8
|
# def index
|
9
|
-
# users= User.all
|
10
|
-
# files =
|
11
|
-
# zipline(
|
9
|
+
# users = User.all
|
10
|
+
# files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
|
11
|
+
# zipline(files, 'avatars.zip')
|
12
12
|
# end
|
13
13
|
# end
|
14
14
|
module Zipline
|
data/lib/zipline/version.rb
CHANGED
@@ -15,13 +15,15 @@ module Zipline
|
|
15
15
|
def each(&block)
|
16
16
|
fake_io_writer = ZipTricks::BlockWrite.new(&block)
|
17
17
|
ZipTricks::Streamer.open(fake_io_writer) do |streamer|
|
18
|
-
@files.each
|
18
|
+
@files.each do |file, name, options = {}|
|
19
|
+
handle_file(streamer, file, name, options)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
def handle_file(streamer, file, name)
|
24
|
+
def handle_file(streamer, file, name, options)
|
23
25
|
file = normalize(file)
|
24
|
-
write_file(streamer, file, name)
|
26
|
+
write_file(streamer, file, name, options)
|
25
27
|
end
|
26
28
|
|
27
29
|
# This extracts either a url or a local file from the provided file.
|
@@ -46,6 +48,8 @@ module Zipline
|
|
46
48
|
{file: file}
|
47
49
|
elsif defined?(ActiveStorage::Blob) && file.is_a?(ActiveStorage::Blob)
|
48
50
|
{url: file.service_url}
|
51
|
+
elsif is_active_storage_attachment?(file) || is_active_storage_one?(file)
|
52
|
+
{url: file.blob.service_url}
|
49
53
|
elsif file.respond_to? :url
|
50
54
|
{url: file.url}
|
51
55
|
elsif file.respond_to? :path
|
@@ -57,8 +61,8 @@ module Zipline
|
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
|
-
def write_file(streamer, file, name)
|
61
|
-
streamer.write_deflated_file(name) do |writer_for_file|
|
64
|
+
def write_file(streamer, file, name, options)
|
65
|
+
streamer.write_deflated_file(name, options) do |writer_for_file|
|
62
66
|
if file[:url]
|
63
67
|
the_remote_url = file[:url]
|
64
68
|
c = Curl::Easy.new(the_remote_url) do |curl|
|
@@ -80,5 +84,15 @@ module Zipline
|
|
80
84
|
def is_io?(io_ish)
|
81
85
|
io_ish.respond_to? :read
|
82
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def is_active_storage_attachment?(file)
|
91
|
+
defined?(ActiveStorage::Attachment) && file.is_a?(ActiveStorage::Attachment)
|
92
|
+
end
|
93
|
+
|
94
|
+
def is_active_storage_one?(file)
|
95
|
+
defined?(ActiveStorage::Attached::One) && file.is_a?(ActiveStorage::Attached::One)
|
96
|
+
end
|
83
97
|
end
|
84
98
|
end
|
@@ -84,29 +84,70 @@ describe Zipline::ZipGenerator do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
|
-
context "ActiveStorage
|
87
|
+
context "ActiveStorage" do
|
88
88
|
module ActiveStorage
|
89
|
-
class
|
89
|
+
class Attached
|
90
|
+
class One < Attached
|
91
|
+
end
|
92
|
+
end
|
93
|
+
class Attachment; end
|
90
94
|
class Blob; end
|
91
95
|
end
|
92
96
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
context "Attached::One" do
|
98
|
+
it "extracts url" do
|
99
|
+
attached = create_attached_one
|
100
|
+
allow_any_instance_of(Object).to receive(:defined?).and_return(true)
|
101
|
+
|
102
|
+
normalized = generator.normalize(attached)
|
103
|
+
|
104
|
+
expect(normalized.keys).to include(:url)
|
105
|
+
expect(normalized[:url]).to eq('fakeurl')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "Attachment" do
|
110
|
+
it "extracts url" do
|
111
|
+
attachment = create_attachment
|
112
|
+
allow_any_instance_of(Object).to receive(:defined?).and_return(true)
|
113
|
+
|
114
|
+
normalized = generator.normalize(attachment)
|
115
|
+
|
116
|
+
expect(normalized.keys).to include(:url)
|
117
|
+
expect(normalized[:url]).to eq('fakeurl')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "Blob" do
|
122
|
+
it "extracts url" do
|
123
|
+
blob = create_blob
|
124
|
+
allow_any_instance_of(Object).to receive(:defined?).and_return(true)
|
125
|
+
|
126
|
+
normalized = generator.normalize(blob)
|
127
|
+
|
128
|
+
expect(normalized.keys).to include(:url)
|
129
|
+
expect(normalized[:url]).to eq('fakeurl')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_attached_one
|
134
|
+
attached = ActiveStorage::Attached::One.new
|
135
|
+
blob = create_blob
|
136
|
+
allow(attached).to receive(:blob).and_return(blob)
|
137
|
+
attached
|
98
138
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
139
|
+
|
140
|
+
def create_attachment
|
141
|
+
attachment = ActiveStorage::Attachment.new
|
142
|
+
blob = create_blob
|
143
|
+
allow(attachment).to receive(:blob).and_return(blob)
|
144
|
+
attachment
|
104
145
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
146
|
+
|
147
|
+
def create_blob
|
148
|
+
blob = ActiveStorage::Blob.new
|
149
|
+
allow(blob).to receive(:service_url).and_return('fakeurl')
|
150
|
+
blob
|
110
151
|
end
|
111
152
|
end
|
112
153
|
context "Fog" do
|
@@ -130,4 +171,46 @@ describe Zipline::ZipGenerator do
|
|
130
171
|
end
|
131
172
|
end
|
132
173
|
|
174
|
+
describe 'passing an options hash' do
|
175
|
+
let(:file) { StringIO.new('passthrough') }
|
176
|
+
|
177
|
+
context 'with optional arguments' do
|
178
|
+
let(:mtime) { 1.day.ago }
|
179
|
+
let(:generator) do
|
180
|
+
Zipline::ZipGenerator.new([[file, 'test', modification_time: mtime]])
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'passes the options hash through handle_file' do
|
184
|
+
expect(generator).to receive(:handle_file)
|
185
|
+
.with(anything, anything, anything, modification_time: mtime)
|
186
|
+
generator.each { |_| 'Test' }
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'passes the options hash to ZipTricks' do
|
190
|
+
allow(file).to receive(:url).and_return('fakeurl')
|
191
|
+
expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
|
192
|
+
.with(anything, modification_time: mtime)
|
193
|
+
generator.each { |_| 'Test' }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'without optional arguments' do
|
198
|
+
let(:generator) do
|
199
|
+
Zipline::ZipGenerator.new([[file, 'test']])
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'passes the options hash through handle_file' do
|
203
|
+
expect(generator).to receive(:handle_file)
|
204
|
+
.with(anything, anything, anything, {})
|
205
|
+
generator.each { |_| 'Test' }
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'passes the options hash to ZipTricks' do
|
209
|
+
allow(file).to receive(:url).and_return('fakeurl')
|
210
|
+
expect_any_instance_of(ZipTricks::Streamer).to receive(:write_deflated_file)
|
211
|
+
.with(anything, {})
|
212
|
+
generator.each { |_| 'Test' }
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
133
216
|
end
|
data/zipline.gemspec
CHANGED
@@ -16,6 +16,6 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = Zipline::VERSION
|
17
17
|
|
18
18
|
gem.add_dependency 'zip_tricks', ['>= 4.2.1', '<= 5.0.0']
|
19
|
-
gem.add_dependency 'rails', ['>= 3.2.1', '<
|
19
|
+
gem.add_dependency 'rails', ['>= 3.2.1', '< 6.1']
|
20
20
|
gem.add_dependency 'curb', ['>= 0.8.0', '< 0.10']
|
21
21
|
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.0
|
4
|
+
version: 1.1.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:
|
11
|
+
date: 2019-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zip_tricks
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
version: 3.2.1
|
40
40
|
- - "<"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
42
|
+
version: '6.1'
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -49,7 +49,7 @@ dependencies:
|
|
49
49
|
version: 3.2.1
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: '
|
52
|
+
version: '6.1'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: curb
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|