streamy_csv 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +4 -0
- data/README.md +6 -3
- data/lib/streamy_csv/version.rb +1 -1
- data/lib/streamy_csv.rb +27 -7
- data/spec/streamy_csv_spec.rb +34 -0
- metadata +12 -15
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 24325070ca75478285318b9b6e0123ef9984cca3
|
4
|
+
data.tar.gz: 47cda2132e14791c76233c269ad967d906b81cdb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b816608ce3cac24b1f930b44b05229fcaf1400b36dd673b47632f4c7df258971bcb1132357a2b0311be5a1bcc357baf362835a8de2ce440fecf81d5b30c9cc27
|
7
|
+
data.tar.gz: 201fa74a9696bacdbfa0928c27674a58b30f65cae3c13f2a50b41bef7741d1c13bbd194e1686b77a77ad30903a8e25f5efa3465805e08f4108560b500e5132fa
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# StreamyCsv
|
2
2
|
|
3
|
-
|
3
|
+
StreamyCSV streams CSV files one row at a time as live data is generated instead of waiting for the whole file to be created and then sent to the client. Works on most standard web servers including Nginx, Passenger, Unicorn, Thin etc., but does NOT work on Webrick.
|
4
|
+
|
5
|
+
## Build Status
|
6
|
+
[![Build Status](https://travis-ci.org/smsohan/streamy_csv.png)](https://travis-ci.org/smsohan/streamy_csv)
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -22,11 +25,11 @@ In your model:
|
|
22
25
|
class MyModel
|
23
26
|
|
24
27
|
def self.header_row
|
25
|
-
CSV::Row([:name, :title], ['Name', 'Title'], true)
|
28
|
+
CSV::Row.new([:name, :title], ['Name', 'Title'], true)
|
26
29
|
end
|
27
30
|
|
28
31
|
def to_csv_row
|
29
|
-
CSV::Row([:name, :title], ['John', 'Mr'])
|
32
|
+
CSV::Row.new([:name, :title], ['John', 'Mr'])
|
30
33
|
end
|
31
34
|
|
32
35
|
end
|
data/lib/streamy_csv/version.rb
CHANGED
data/lib/streamy_csv.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "streamy_csv/version"
|
2
2
|
|
3
3
|
module StreamyCsv
|
4
|
+
CSV_OPERATORS = ['+','-','=','@','%']
|
5
|
+
UNESCAPED_PIPES_RGX = /(?<!\\)(?:\\{2})*\K\|/
|
4
6
|
|
5
7
|
# stream_csv('data.csv', MyModel.header_row) do |rows|
|
6
8
|
# MyModel.find_each do |my_model|
|
@@ -10,13 +12,13 @@ module StreamyCsv
|
|
10
12
|
#
|
11
13
|
#
|
12
14
|
|
13
|
-
def stream_csv(file_name, header_row, &block)
|
15
|
+
def stream_csv(file_name, header_row, sanitize = true, &block)
|
14
16
|
set_streaming_headers
|
15
17
|
set_file_headers(file_name)
|
16
18
|
|
17
19
|
response.status = 200
|
18
20
|
|
19
|
-
self.response_body = csv_lines(header_row, &block)
|
21
|
+
self.response_body = csv_lines(header_row, sanitize, &block)
|
20
22
|
end
|
21
23
|
|
22
24
|
protected
|
@@ -27,13 +29,31 @@ module StreamyCsv
|
|
27
29
|
headers.delete("Content-Length")
|
28
30
|
end
|
29
31
|
|
30
|
-
def csv_lines(header_row, &block)
|
31
|
-
|
32
|
-
|
33
|
-
rows << header_row.to_s if header_row
|
32
|
+
def csv_lines(header_row, sanitize, &block)
|
33
|
+
Enumerator.new do |yielder|
|
34
|
+
rows = appendHeader([], header_row, sanitize)
|
34
35
|
block.call(rows)
|
36
|
+
rows.each do |row|
|
37
|
+
sanitize!(row) if sanitize
|
38
|
+
yielder.yield row
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def appendHeader(rows, header_row, sanitize)
|
44
|
+
if header_row && header_row.any?
|
45
|
+
sanitize! header_row if sanitize
|
46
|
+
rows << header_row.to_s
|
35
47
|
end
|
48
|
+
rows
|
49
|
+
end
|
36
50
|
|
51
|
+
def sanitize!(enumerable)
|
52
|
+
return unless enumerable && enumerable.is_a?(Enumerable)
|
53
|
+
enumerable = enumerable.fields if enumerable.is_a?(CSV::Row)
|
54
|
+
enumerable.each do |field|
|
55
|
+
field.gsub!(UNESCAPED_PIPES_RGX,'\|') if field.is_a?(String) && field.start_with?(*CSV_OPERATORS)
|
56
|
+
end
|
37
57
|
end
|
38
58
|
|
39
59
|
def set_file_headers(file_name)
|
@@ -43,4 +63,4 @@ module StreamyCsv
|
|
43
63
|
|
44
64
|
end
|
45
65
|
|
46
|
-
ActionController::Base.send :include, StreamyCsv
|
66
|
+
ActionController::Base.send :include, StreamyCsv
|
data/spec/streamy_csv_spec.rb
CHANGED
@@ -59,6 +59,40 @@ describe StreamyCsv do
|
|
59
59
|
@controller.response.status.should == 200
|
60
60
|
@controller.response_body.is_a?(Enumerator).should == true
|
61
61
|
end
|
62
|
+
it 'sanitizes header and contents and streams the csv file' do
|
63
|
+
row_1 = CSV::Row.new([:name, :title], ['AB', 'Mr'])
|
64
|
+
row_2 = CSV::Row.new([:name, :title], ["=cmd|' /C", 'Pres'])
|
65
|
+
header = [:name, "=cmd|' /C"]
|
66
|
+
rows = [header, row_1]
|
67
|
+
|
68
|
+
@controller.stream_csv('abc.csv', @header) do |rows|
|
69
|
+
rows << row_1
|
70
|
+
rows << row_2
|
71
|
+
end
|
72
|
+
@controller.response.status.should == 200
|
73
|
+
@controller.response_body.is_a?(Enumerator).should == true
|
74
|
+
@controller.response_body.take(1)[0].to_s[4].bytes == '\\'.bytes
|
75
|
+
@controller.response_body.take(1)[0].to_s[5].bytes == '|'.bytes
|
76
|
+
@controller.response_body.take(3)[2].to_s[4].bytes == '\\'.bytes
|
77
|
+
@controller.response_body.take(3)[2].to_s[5].bytes == '|'.bytes
|
78
|
+
end
|
79
|
+
it 'does not sanitize the csv if the option provided' do
|
80
|
+
row_1 = CSV::Row.new([:name, :title], ['AB', 'Mr'])
|
81
|
+
row_2 = CSV::Row.new([:name, :title], ["=cmd|' /C", 'Pres'])
|
82
|
+
header = [:name, "=cmd|' /C"]
|
83
|
+
rows = [header, row_1]
|
84
|
+
|
85
|
+
@controller.stream_csv('abc.csv', @header, false) do |rows|
|
86
|
+
rows << row_1
|
87
|
+
rows << row_2
|
88
|
+
end
|
89
|
+
@controller.response.status.should == 200
|
90
|
+
@controller.response_body.is_a?(Enumerator).should == true
|
91
|
+
@controller.response_body.take(1)[0].to_s[4].bytes == 'd'.bytes
|
92
|
+
@controller.response_body.take(1)[0].to_s[5].bytes == '|'.bytes
|
93
|
+
@controller.response_body.take(3)[2].to_s[4].bytes == 'd'.bytes
|
94
|
+
@controller.response_body.take(3)[2].to_s[5].bytes == '|'.bytes
|
95
|
+
end
|
62
96
|
end
|
63
97
|
|
64
98
|
end
|
metadata
CHANGED
@@ -1,30 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: streamy_csv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- smsohan
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2017-08-02 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
description: Streamy CSV lets you stream live generated CSV files
|
@@ -34,8 +31,9 @@ executables: []
|
|
34
31
|
extensions: []
|
35
32
|
extra_rdoc_files: []
|
36
33
|
files:
|
37
|
-
- .gitignore
|
38
|
-
- .rspec
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- ".travis.yml"
|
39
37
|
- Gemfile
|
40
38
|
- LICENSE.txt
|
41
39
|
- README.md
|
@@ -46,27 +44,26 @@ files:
|
|
46
44
|
- streamy_csv.gemspec
|
47
45
|
homepage: https://github.com/smsohan/streamy_csv
|
48
46
|
licenses: []
|
47
|
+
metadata: {}
|
49
48
|
post_install_message:
|
50
49
|
rdoc_options: []
|
51
50
|
require_paths:
|
52
51
|
- lib
|
53
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
53
|
requirements:
|
56
|
-
- -
|
54
|
+
- - ">="
|
57
55
|
- !ruby/object:Gem::Version
|
58
56
|
version: '0'
|
59
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
58
|
requirements:
|
62
|
-
- -
|
59
|
+
- - ">="
|
63
60
|
- !ruby/object:Gem::Version
|
64
61
|
version: '0'
|
65
62
|
requirements: []
|
66
63
|
rubyforge_project:
|
67
|
-
rubygems_version:
|
64
|
+
rubygems_version: 2.4.5
|
68
65
|
signing_key:
|
69
|
-
specification_version:
|
66
|
+
specification_version: 4
|
70
67
|
summary: Provides a simple API for your controllers to stream CSV files one row at
|
71
68
|
a time
|
72
69
|
test_files:
|