streamy_csv 0.3.0 → 0.4.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 +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
|
+
[](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:
|