zipline 1.0.1 → 1.2.1

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
  SHA1:
3
- metadata.gz: d870d86f6be0fe78deb3549c7037808bcb91551d
4
- data.tar.gz: fe5c5fac16ca3d02ac37c1fd8981e8eda1052ad6
3
+ metadata.gz: 60e9847b29f301c7d2a2b7ff9614454f0e3a1193
4
+ data.tar.gz: b9b391b9478f96f0fa067918e4368a5415332123
5
5
  SHA512:
6
- metadata.gz: 1622bf22aecbe54887642408a64b2cafa40559d6b17cb7b2f3ec3e42275dca4af77675237145f09537cf64c35cc909d8842fcedc1c632268e76f3d860acb600b
7
- data.tar.gz: fa212e4a62d7c44508b2374b2619b07a97b8db35565f9b6b07541f26306d308f8612ae31bc6d7145fe584c1bbe913f205c5746e5f046a000dd8b6e12df0f861e
6
+ metadata.gz: e25c8bba3c7b7d6c093637c1323d0a4bb6660e94457e4445cc3e6798f7125e8c5c0b99a334de0430ef4ced180e00a4001c29b6ab9c92525dd0a6eca689b0dbe7
7
+ data.tar.gz: db9b45bfb987cf97f025c4c2cbeb818a821bd7968524df56ac44d6eaebea4b8420b634815b6a6cde8ff32976e67b20183f8dfa404a0ffa05b91aef824c645a79
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.3
5
+ - 2.4
6
+ - 2.5
7
+ - 2.6
8
+ - 2.7
data/Gemfile CHANGED
@@ -4,7 +4,6 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
- gem 'rubyzip', '~> 1.2'
8
7
  gem 'rspec', '~> 3'
9
8
  gem 'fog'
10
9
  gem 'fog-aws'
@@ -12,4 +11,6 @@ group :development, :test do
12
11
  gem 'aws-sdk'
13
12
  gem 'carrierwave'
14
13
  gem 'paperclip'
14
+ gem 'rake'
15
+ gem 'rake-rspec'
15
16
  end
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
 
@@ -19,45 +21,84 @@ And then execute:
19
21
 
20
22
  ## Usage
21
23
 
22
- Set up some models with [carrierwave](https://github.com/jnicklas/carrierwave), [paperclip](https://github.com/thoughtbot/paperclip), or [shrine](https://github.com/janko-m/shrine). Right now only plain
23
- file storage and S3 are supported in the case of
24
- [carrierwave](https://github.com/jnicklas/carrierwave) and only plain file
25
- storage and S3 are supported in the case of
26
- [paperclip](https://github.com/thoughtbot/paperclip). [Mutiple file storages](http://shrinerb.com/#external) are supported with [shrine](https://github.com/janko-m/shrine).
24
+ Set up some models with [ActiveStorage](http://edgeguides.rubyonrails.org/active_storage_overview.html)
25
+ [carrierwave](https://github.com/jnicklas/carrierwave), [paperclip](https://github.com/thoughtbot/paperclip), or
26
+ [shrine](https://github.com/janko-m/shrine). Right now only plain file storage and S3 are supported in the case of
27
+ [carrierwave](https://github.com/jnicklas/carrierwave) and only plain file storage and S3 are supported in the case of
28
+ [paperclip](https://github.com/thoughtbot/paperclip). [Mutiple file storages](http://shrinerb.com/#external) are
29
+ supported with [shrine](https://github.com/janko-m/shrine).
27
30
 
28
31
  You'll need to be using puma or some other server that supports streaming output.
29
32
 
30
- class MyController < ApplicationController
31
- # enable streaming responses
32
- include ActionController::Streaming
33
- # enable zipline
34
- include Zipline
35
-
36
- def index
37
- users = User.all
38
- # you can replace user.avatar with any stream or any object that
39
- # responds to :url
40
- files = users.map{ |user| [user.avatar, "#{user.username}.png"] }
41
- zipline( files, 'avatars.zip')
42
- end
43
- end
33
+ ```Ruby
34
+ class MyController < ApplicationController
35
+ # enable zipline
36
+ include Zipline
37
+
38
+ def index
39
+ users = User.all
40
+ # you can replace user.avatar with any stream or any object that
41
+ # responds to :url, :path or :file.
42
+ # :modification_time is an optional third argument you can use.
43
+ files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
44
+ zipline(files, 'avatars.zip')
45
+ end
46
+ end
47
+ ```
48
+
49
+ ### ActiveStorage
50
+
51
+ ```Ruby
52
+ users = User.all
53
+ files = users.map{ |user| [user.avatar, user.avatar.filename] }
54
+ zipline(files, 'avatars.zip')
55
+ ```
56
+
57
+ ### Carrierwave
58
+
59
+ ```Ruby
60
+ users = User.all
61
+ files = users.map{ |user| [user.avatar, user.avatar_identifier] }
62
+ zipline(files, 'avatars.zip')
63
+ ```
64
+
65
+ ### Paperclip ([deprecated](https://thoughtbot.com/blog/closing-the-trombone))
66
+
67
+ ```Ruby
68
+ users = User.all
69
+ files = users.map{ |user| [user.avatar, user.avatar_file_name] }
70
+ zipline(files, 'avatars.zip')
71
+ ```
72
+
73
+ ### Url
74
+
75
+ If you know the URL of the remote file you want to include, you can just pass in the
76
+ URL directly in place of the attachment object.
77
+ ```Ruby
78
+ avatars = [
79
+ ['http://www.example.com/user1.png', 'user1.png']
80
+ ['http://www.example.com/user2.png', 'user2.png']
81
+ ['http://www.example.com/user3.png', 'user3.png']
82
+ ]
83
+ zipline(avatars, 'avatars.zip')
84
+ ```
85
+
86
+ ### Directories
44
87
 
45
88
  For directories, just give the files names like "directory/file".
46
89
 
47
- 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):
48
-
49
- require 'open-uri'
50
- avatars = [
51
- # remote_url zip_path
52
- [ 'http://www.example.com/user1.png', 'avatars/user1.png' ]
53
- [ 'http://www.example.com/user2.png', 'avatars/user2.png' ]
54
- [ 'http://www.example.com/user3.png', 'avatars/user3.png' ]
55
- ]
56
- file_mappings = avatars
57
- .lazy # Lazy allows us to begin sending the download immediately instead of waiting to download everything
58
- .map { |url, path| [open(url), path] }
59
- zipline(file_mappings, 'avatars.zip')
60
-
90
+
91
+ ```Ruby
92
+ avatars = [
93
+ # remote_url zip_path zip_tricks_options
94
+ [ 'http://www.example.com/user1.png', 'avatars/user1.png', modification_time: Time.now.utc ]
95
+ [ 'http://www.example.com/user2.png', 'avatars/user2.png', modification_time: 1.day.ago ]
96
+ [ 'http://www.example.com/user3.png', 'avatars/user3.png' ]
97
+ ]
98
+
99
+ zipline(avatars, 'avatars.zip')
100
+ ```
101
+
61
102
  ## Contributing
62
103
 
63
104
  1. Fork it
data/Rakefile CHANGED
@@ -1,2 +1,4 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require "rake/rspec"
4
+ task :default => :spec
@@ -1,14 +1,13 @@
1
1
  require "zipline/version"
2
- require 'curb'
3
2
  require 'zip_tricks'
4
3
  require "zipline/zip_generator"
5
4
 
6
5
  # class MyController < ApplicationController
7
6
  # include Zipline
8
7
  # def index
9
- # users= User.all
10
- # files = users.map{ |user| [user.avatar, "#{user.username}.png"] }
11
- # zipline( files, 'avatars.zip')
8
+ # users = User.all
9
+ # files = users.map{ |user| [user.avatar, "#{user.username}.png", modification_time: 1.day.ago] }
10
+ # zipline(files, 'avatars.zip')
12
11
  # end
13
12
  # end
14
13
  module Zipline
@@ -1,3 +1,3 @@
1
1
  module Zipline
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -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 {|file, name| handle_file(streamer, file, name) }
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.
@@ -44,31 +46,38 @@ module Zipline
44
46
  {file: File.open(file.path)}
45
47
  elsif is_io?(file)
46
48
  {file: file}
49
+ elsif defined?(ActiveStorage::Blob) && file.is_a?(ActiveStorage::Blob)
50
+ {blob: file}
51
+ elsif is_active_storage_attachment?(file) || is_active_storage_one?(file)
52
+ {blob: file.blob}
47
53
  elsif file.respond_to? :url
48
54
  {url: file.url}
49
55
  elsif file.respond_to? :path
50
56
  {file: File.open(file.path)}
51
57
  elsif file.respond_to? :file
52
58
  {file: File.open(file.file)}
59
+ elsif is_url?(file)
60
+ {url: file}
53
61
  else
54
62
  raise(ArgumentError, 'Bad File/Stream')
55
63
  end
56
64
  end
57
65
 
58
- def write_file(streamer, file, name)
59
- streamer.write_deflated_file(name) do |writer_for_file|
66
+ def write_file(streamer, file, name, options)
67
+ streamer.write_deflated_file(name, options) do |writer_for_file|
60
68
  if file[:url]
61
- the_remote_url = file[:url]
62
- c = Curl::Easy.new(the_remote_url) do |curl|
63
- curl.on_body do |data|
64
- writer_for_file << data
65
- data.bytesize
69
+ the_remote_uri = URI(file[:url])
70
+
71
+ Net::HTTP.get_response(the_remote_uri) do |response|
72
+ response.read_body do |chunk|
73
+ writer_for_file << chunk
66
74
  end
67
75
  end
68
- c.perform
69
76
  elsif file[:file]
70
77
  IO.copy_stream(file[:file], writer_for_file)
71
78
  file[:file].close
79
+ elsif file[:blob]
80
+ file[:blob].download { |chunk| writer_for_file << chunk }
72
81
  else
73
82
  raise(ArgumentError, 'Bad File/Stream')
74
83
  end
@@ -78,5 +87,20 @@ module Zipline
78
87
  def is_io?(io_ish)
79
88
  io_ish.respond_to? :read
80
89
  end
90
+
91
+ private
92
+
93
+ def is_active_storage_attachment?(file)
94
+ defined?(ActiveStorage::Attachment) && file.is_a?(ActiveStorage::Attachment)
95
+ end
96
+
97
+ def is_active_storage_one?(file)
98
+ defined?(ActiveStorage::Attached::One) && file.is_a?(ActiveStorage::Attached::One)
99
+ end
100
+
101
+ def is_url?(url)
102
+ url = URI.parse(url) rescue false
103
+ url.kind_of?(URI::HTTP) || url.kind_of?(URI::HTTPS)
104
+ end
81
105
  end
82
106
  end
@@ -2,7 +2,6 @@ require 'spec_helper'
2
2
  require 'tempfile'
3
3
 
4
4
  describe Zipline::ZipGenerator do
5
-
6
5
  before { Fog.mock! }
7
6
  let(:file_attributes){ {
8
7
  key: 'fog_file_tests',
@@ -67,7 +66,7 @@ describe Zipline::ZipGenerator do
67
66
  end
68
67
  end
69
68
  context "Paperclip" do
70
- context "Local" do
69
+ context "Local" do
71
70
  let(:file){ Paperclip::Attachment.new(:name, :instance) }
72
71
  it "creates a File" do
73
72
  allow(file).to receive(:path).and_return('spec/fakefile.txt')
@@ -76,7 +75,7 @@ describe Zipline::ZipGenerator do
76
75
  expect(normalized[:file]).to be_a File
77
76
  end
78
77
  end
79
- context "Remote" do
78
+ context "Remote" do
80
79
  let(:file){ Paperclip::Attachment.new(:name, :instance, storage: :s3) }
81
80
  it "creates a URL" do
82
81
  allow(file).to receive(:expiring_url).and_return('fakeurl')
@@ -85,6 +84,72 @@ describe Zipline::ZipGenerator do
85
84
  end
86
85
  end
87
86
  end
87
+ context "ActiveStorage" do
88
+ module ActiveStorage
89
+ class Attached
90
+ class One < Attached
91
+ end
92
+ end
93
+ class Attachment; end
94
+ class Blob; end
95
+ end
96
+
97
+ context "Attached::One" do
98
+ it "get blob" 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(:blob)
105
+ expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
106
+ end
107
+ end
108
+
109
+ context "Attachment" do
110
+ it "get blob" 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(:blob)
117
+ expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
118
+ end
119
+ end
120
+
121
+ context "Blob" do
122
+ it "get blob" 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(:blob)
129
+ expect(normalized[:blob]).to be_a(ActiveStorage::Blob)
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
138
+ end
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
145
+ end
146
+
147
+ def create_blob
148
+ blob = ActiveStorage::Blob.new
149
+ allow(blob).to receive(:service_url).and_return('fakeurl')
150
+ blob
151
+ end
152
+ end
88
153
  context "Fog" do
89
154
  it "extracts url" do
90
155
  allow(file).to receive(:url).and_return('fakeurl')
@@ -106,4 +171,46 @@ describe Zipline::ZipGenerator do
106
171
  end
107
172
  end
108
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
109
216
  end
@@ -14,8 +14,8 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "zipline"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Zipline::VERSION
17
+ gem.licenses = ['MIT']
17
18
 
18
- gem.add_dependency 'zip_tricks', ['>= 4.2.1', '<= 5.0.0']
19
- gem.add_dependency 'rails', ['>= 3.2.1', '< 5.3']
20
- gem.add_dependency 'curb', ['>= 0.8.0', '< 0.10']
19
+ gem.add_dependency 'zip_tricks', ['>= 4.2.1', '< 6.0']
20
+ gem.add_dependency 'rails', ['>= 3.2.1', '< 6.1']
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.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ram Dobson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-11 00:00:00.000000000 Z
11
+ date: 2020-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zip_tricks
@@ -17,9 +17,9 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 4.2.1
20
- - - "<="
20
+ - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 5.0.0
22
+ version: '6.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +27,9 @@ dependencies:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: 4.2.1
30
- - - "<="
30
+ - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 5.0.0
32
+ version: '6.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rails
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: 3.2.1
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '5.3'
42
+ version: '6.1'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,27 +49,7 @@ dependencies:
49
49
  version: 3.2.1
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '5.3'
53
- - !ruby/object:Gem::Dependency
54
- name: curb
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: 0.8.0
60
- - - "<"
61
- - !ruby/object:Gem::Version
62
- version: '0.10'
63
- type: :runtime
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: 0.8.0
70
- - - "<"
71
- - !ruby/object:Gem::Version
72
- version: '0.10'
52
+ version: '6.1'
73
53
  description: a module for streaming dynamically generated zip files
74
54
  email:
75
55
  - ram.dobson@solsystemscompany.com
@@ -77,6 +57,7 @@ executables: []
77
57
  extensions: []
78
58
  extra_rdoc_files: []
79
59
  files:
60
+ - ".travis.yml"
80
61
  - Gemfile
81
62
  - LICENSE
82
63
  - README.md
@@ -89,7 +70,8 @@ files:
89
70
  - spec/spec_helper.rb
90
71
  - zipline.gemspec
91
72
  homepage: http://github.com/fringd/zipline
92
- licenses: []
73
+ licenses:
74
+ - MIT
93
75
  metadata: {}
94
76
  post_install_message:
95
77
  rdoc_options: []