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 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
@@ -0,0 +1,4 @@
1
+ dist: trusty
2
+ sudo: false
3
+ rvm: 2.2.7
4
+ script: "rake"
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # StreamyCsv
2
2
 
3
- 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.
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
@@ -1,3 +1,3 @@
1
1
  module StreamyCsv
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
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
- Enumerator.new do |rows|
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
@@ -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.3.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: 2013-05-10 00:00:00.000000000 Z
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: 1.8.24
64
+ rubygems_version: 2.4.5
68
65
  signing_key:
69
- specification_version: 3
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: