xlsxtream 0.2.0 → 0.3.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 +13 -2
- data/CHANGELOG.md +14 -0
- data/README.md +44 -27
- data/lib/xlsxtream/io/rubyzip.rb +2 -1
- data/lib/xlsxtream/row.rb +56 -19
- data/lib/xlsxtream/version.rb +1 -1
- data/lib/xlsxtream/workbook.rb +6 -1
- data/lib/xlsxtream/worksheet.rb +4 -3
- data/xlsxtream.gemspec +2 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f6d507fcaf88af59b20094f992ba4e921ca610b
|
4
|
+
data.tar.gz: 81f6f7d9a0312be08a7e394ef2e27be1f5f51a09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2890495ed08670099470e514836ee399481bdd261e12207a3df454be1e4e7ef6a8489e2b2b662f7cb3286dc5cb82af7c5fa5354b4e801847a95b0307812e008e
|
7
|
+
data.tar.gz: 19f3812cdb16465b87e86c1aa2ff78605092729cd1fdbd767ae64abe3ba4cbea57d1ad63e5b1760c4a2bf5015a1629a5cb3834c9cafee6c33836a058f004a803
|
data/.travis.yml
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
language: ruby
|
2
|
+
|
3
|
+
sudo: false
|
4
|
+
cache: bundler
|
5
|
+
|
6
|
+
before_install: gem install bundler -v '~> 1.15' --conservative --minimal-deps
|
7
|
+
|
2
8
|
rvm:
|
3
|
-
-
|
4
|
-
|
9
|
+
- 1.9.2
|
10
|
+
- 1.9.3
|
11
|
+
- 2.0.0
|
12
|
+
- 2.1.10
|
13
|
+
- 2.2.7
|
14
|
+
- 2.3.4
|
15
|
+
- 2.4.1
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# Xlsxtream
|
2
2
|
|
3
|
-
Xlsxtream is a streaming writer for XLSX spreadsheets. It supports multiple worksheets and optional string
|
3
|
+
Xlsxtream is a streaming writer for XLSX spreadsheets. It supports multiple worksheets and optional string
|
4
|
+
deduplication via a shared string table (SST). Its purpose is to replace CSV for large exports, because using
|
5
|
+
CSV in Excel is very buggy and error prone. It's very efficient and can quickly write millions of rows with
|
6
|
+
low memory usage.
|
4
7
|
|
5
|
-
Xlsxtream does not support formatting, charts, comments and a myriad of
|
8
|
+
Xlsxtream does not support formatting, charts, comments and a myriad of
|
9
|
+
other [OOXML](https://en.wikipedia.org/wiki/Office_Open_XML) features. If you are looking for a
|
10
|
+
fully featured solution take a look at [axslx](https://github.com/randym/axlsx).
|
6
11
|
|
7
12
|
Xlsxtream supports writing to files or IO-like objects, data is flushed as the ZIP compressor sees fit.
|
8
13
|
|
@@ -25,38 +30,51 @@ Or install it yourself as:
|
|
25
30
|
## Usage
|
26
31
|
|
27
32
|
```ruby
|
28
|
-
# Creates a new workbook and closes it at the end of the block
|
29
|
-
Xlsxtream::Workbook.open(
|
30
|
-
xlsx.write_worksheet
|
33
|
+
# Creates a new workbook and closes it at the end of the block
|
34
|
+
Xlsxtream::Workbook.open('my_data.xlsx') do |xlsx|
|
35
|
+
xlsx.write_worksheet 'Sheet1' do |sheet|
|
31
36
|
# Date, Time, DateTime and Numeric are properly mapped
|
32
|
-
sheet << [Date.today,
|
37
|
+
sheet << [Date.today, 'hello', 'world', 42, 3.14159265359, 42**13]
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
36
|
-
io = StringIO.new
|
41
|
+
io = StringIO.new
|
37
42
|
xlsx = Xlsxtream::Workbook.new(io)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
sheet <<
|
43
|
+
|
44
|
+
# Number of columns doesn't have to match
|
45
|
+
xlsx.write_worksheet 'Sheet1' do |sheet|
|
46
|
+
sheet << ['first', 'row']
|
47
|
+
sheet << ['second', 'row', 'with', 'more colums']
|
42
48
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
sheet.add_row [
|
47
|
-
sheet.add_row [Time.now,
|
49
|
+
|
50
|
+
# Write multiple worksheets with custom names
|
51
|
+
xlsx.write_worksheet 'AppendixSheet' do |sheet|
|
52
|
+
sheet.add_row ['Timestamp', 'Comment']
|
53
|
+
sheet.add_row [Time.now, 'Good times']
|
54
|
+
sheet.add_row [Time.now, 'Time-machine']
|
48
55
|
end
|
49
|
-
|
50
|
-
#
|
51
|
-
# The SST has to be kept in memory,
|
52
|
-
# have a huge amount of rows or a little duplication
|
53
|
-
#
|
54
|
-
xlsx.write_worksheet(
|
55
|
-
sheet <<
|
56
|
-
sheet <<
|
57
|
-
sheet <<
|
56
|
+
|
57
|
+
# If you have highly repetitive data, you can enable Shared String Tables (SST)
|
58
|
+
# for the workbook or a single worksheet. The SST has to be kept in memory,
|
59
|
+
# so do not use it if you have a huge amount of rows or a little duplication
|
60
|
+
# of content across cells. A single SST is used for the whole workbook.
|
61
|
+
xlsx.write_worksheet('SheetWithSST', :use_shared_strings => true) do |sheet|
|
62
|
+
sheet << ['the', 'same', 'old', 'story']
|
63
|
+
sheet << ['the', 'old', 'same', 'story']
|
64
|
+
sheet << ['old', 'the', 'same', 'story']
|
58
65
|
end
|
59
|
-
|
66
|
+
|
67
|
+
# Strings in numeric or date/time format can be auto-detected and formatted
|
68
|
+
# appropriately. This is a convenient way to avoid an Excel-warning about
|
69
|
+
# "Number stored as text". Dates and times must be in the ISO-8601 format and
|
70
|
+
# numeric values must contain only numbers and an optional decimal separator.
|
71
|
+
xlsx.write_worksheet('SheetWithAutoFormat', :auto_format => true) do |sheet|
|
72
|
+
# these two rows will be identical in the xlsx-output
|
73
|
+
sheet << [11.85, DateTime.parse('2050-01-01T12:00'), Date.parse('1984-01-01')]
|
74
|
+
sheet << ['11.85', '2050-01-01T12:00', '1984-01-01']
|
75
|
+
end
|
76
|
+
|
77
|
+
# Writes metadata and ZIP archive central directory
|
60
78
|
xlsx.close
|
61
79
|
```
|
62
80
|
|
@@ -74,4 +92,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/felixb
|
|
74
92
|
## License
|
75
93
|
|
76
94
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
77
|
-
|
data/lib/xlsxtream/io/rubyzip.rb
CHANGED
@@ -25,7 +25,8 @@ module Xlsxtream
|
|
25
25
|
|
26
26
|
# Extend get_compressor to hook our custom deflater.
|
27
27
|
class UnbufferedZipOutputStream < ::Zip::OutputStream
|
28
|
-
private
|
28
|
+
private
|
29
|
+
def get_compressor(entry, level)
|
29
30
|
case entry.compression_method
|
30
31
|
when ::Zip::Entry::DEFLATED then
|
31
32
|
StreamingDeflater.new(@output_stream, level, @encrypter)
|
data/lib/xlsxtream/row.rb
CHANGED
@@ -4,45 +4,82 @@ require "xlsxtream/xml"
|
|
4
4
|
|
5
5
|
module Xlsxtream
|
6
6
|
class Row
|
7
|
-
|
7
|
+
|
8
|
+
ENCODING = Encoding.find('UTF-8')
|
9
|
+
|
10
|
+
NUMBER_PATTERN = /\A-?[0-9]+(\.[0-9]+)?\z/.freeze
|
11
|
+
# ISO 8601 yyyy-mm-dd
|
12
|
+
DATE_PATTERN = /\A[0-9]{4}-[0-9]{2}-[0-9]{2}\z/.freeze
|
13
|
+
# ISO 8601 yyyy-mm-ddThh:mm:ss(.s)(Z|+hh:mm|-hh:mm)
|
14
|
+
TIME_PATTERN = /\A[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}(?::[0-9]{2}(?:\.[0-9]{1,9})?)?(?:Z|[+-][0-9]{2}:[0-9]{2})?\z/.freeze
|
15
|
+
|
16
|
+
DATE_STYLE = 1
|
17
|
+
TIME_STYLE = 2
|
18
|
+
|
19
|
+
def initialize(row, rownum, options = {})
|
8
20
|
@row = row
|
9
21
|
@rownum = rownum
|
10
|
-
@sst = sst
|
11
|
-
@
|
22
|
+
@sst = options[:sst]
|
23
|
+
@auto_format = options[:auto_format]
|
12
24
|
end
|
13
25
|
|
14
26
|
def to_xml
|
15
27
|
column = 'A'
|
16
|
-
|
17
|
-
|
28
|
+
xml = %Q{<row r="#{@rownum}">}
|
29
|
+
|
30
|
+
@row.each do |value|
|
31
|
+
cid = "#{column}#{@rownum}"
|
18
32
|
column.next!
|
19
|
-
|
33
|
+
|
34
|
+
if @auto_format && value.is_a?(String)
|
35
|
+
value = auto_format(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
case value
|
20
39
|
when Numeric
|
21
|
-
%
|
22
|
-
when
|
23
|
-
|
24
|
-
|
40
|
+
xml << %Q{<c r="#{cid}" t="n"><v>#{value}</v></c>}
|
41
|
+
when Time, DateTime
|
42
|
+
xml << %Q{<c r="#{cid}" s="#{TIME_STYLE}"><v>#{time_to_oa_date(value)}</v></c>}
|
43
|
+
when Date
|
44
|
+
xml << %Q{<c r="#{cid}" s="#{DATE_STYLE}"><v>#{time_to_oa_date(value)}</v></c>}
|
25
45
|
else
|
26
|
-
value = value.to_s
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
46
|
+
value = value.to_s
|
47
|
+
|
48
|
+
unless value.empty? # no xml output for for empty strings
|
49
|
+
value = value.encode(ENCODING) if value.encoding != ENCODING
|
50
|
+
|
31
51
|
if @sst
|
32
|
-
%
|
52
|
+
xml << %Q{<c r="#{cid}" t="s"><v>#{@sst[value]}</v></c>}
|
33
53
|
else
|
34
|
-
%
|
54
|
+
xml << %Q{<c r="#{cid}" t="inlineStr"><is><t>#{XML.escape_value(value)}</t></is></c>}
|
35
55
|
end
|
36
56
|
end
|
37
57
|
end
|
38
|
-
end
|
58
|
+
end
|
59
|
+
|
60
|
+
xml << '</row>'
|
39
61
|
end
|
40
62
|
|
41
63
|
private
|
42
64
|
|
65
|
+
# Detects and casts numbers, date, time in text
|
66
|
+
def auto_format(value)
|
67
|
+
case value
|
68
|
+
when NUMBER_PATTERN
|
69
|
+
value.include?('.') ? value.to_f : value.to_i
|
70
|
+
when DATE_PATTERN
|
71
|
+
Date.parse(value)
|
72
|
+
when TIME_PATTERN
|
73
|
+
DateTime.parse(value)
|
74
|
+
else
|
75
|
+
value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
43
79
|
# Converts Time objects to OLE Automation Date
|
44
80
|
def time_to_oa_date(time)
|
45
|
-
time = time.respond_to?(:to_time)
|
81
|
+
time = time.to_time if time.respond_to?(:to_time)
|
82
|
+
|
46
83
|
# Local dates are stored as UTC by truncating the offset:
|
47
84
|
# 1970-01-01 00:00:00 +0200 => 1970-01-01 00:00:00 UTC
|
48
85
|
# This is done because SpreadsheetML is not timezone aware.
|
data/lib/xlsxtream/version.rb
CHANGED
data/lib/xlsxtream/workbook.rb
CHANGED
@@ -37,12 +37,17 @@ module Xlsxtream
|
|
37
37
|
|
38
38
|
def write_worksheet(name = nil, options = {})
|
39
39
|
use_sst = options.fetch(:use_shared_strings, @options[:use_shared_strings])
|
40
|
+
auto_format = options.fetch(:auto_format, @options[:auto_format])
|
41
|
+
sst = use_sst ? @sst : nil
|
42
|
+
|
40
43
|
name ||= "Sheet#{@worksheets.size + 1}"
|
41
44
|
sheet_id = @worksheets[name]
|
42
45
|
@io.add_file "xl/worksheets/sheet#{sheet_id}.xml"
|
43
|
-
|
46
|
+
|
47
|
+
worksheet = Worksheet.new(@io, :sst => sst, :auto_format => auto_format)
|
44
48
|
yield worksheet if block_given?
|
45
49
|
worksheet.close
|
50
|
+
|
46
51
|
nil
|
47
52
|
end
|
48
53
|
alias_method :add_worksheet, :write_worksheet
|
data/lib/xlsxtream/worksheet.rb
CHANGED
@@ -4,15 +4,16 @@ require "xlsxtream/row"
|
|
4
4
|
|
5
5
|
module Xlsxtream
|
6
6
|
class Worksheet
|
7
|
-
def initialize(io,
|
7
|
+
def initialize(io, options = {})
|
8
8
|
@io = io
|
9
9
|
@rownum = 1
|
10
|
-
@
|
10
|
+
@options = options
|
11
|
+
|
11
12
|
write_header
|
12
13
|
end
|
13
14
|
|
14
15
|
def <<(row)
|
15
|
-
@io << Row.new(row, @rownum, @
|
16
|
+
@io << Row.new(row, @rownum, @options).to_xml
|
16
17
|
@rownum += 1
|
17
18
|
end
|
18
19
|
alias_method :add_row, :<<
|
data/xlsxtream.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.bindir = "exe"
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
|
+
spec.required_ruby_version = '>= 1.9.1'
|
21
22
|
|
22
23
|
spec.add_dependency "rubyzip", ">= 1.0.0"
|
23
24
|
|
24
|
-
spec.add_development_dependency "bundler", "~> 1.
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
27
|
spec.add_development_dependency "minitest"
|
27
28
|
spec.add_development_dependency "pry"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xlsxtream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix Bünemann
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.7'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.7'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,6 +90,7 @@ extra_rdoc_files: []
|
|
90
90
|
files:
|
91
91
|
- ".gitignore"
|
92
92
|
- ".travis.yml"
|
93
|
+
- CHANGELOG.md
|
93
94
|
- Gemfile
|
94
95
|
- LICENSE.txt
|
95
96
|
- README.md
|
@@ -119,7 +120,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
120
|
requirements:
|
120
121
|
- - ">="
|
121
122
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
123
|
+
version: 1.9.1
|
123
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
125
|
requirements:
|
125
126
|
- - ">="
|
@@ -127,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
128
|
version: '0'
|
128
129
|
requirements: []
|
129
130
|
rubyforge_project:
|
130
|
-
rubygems_version: 2.
|
131
|
+
rubygems_version: 2.6.13
|
131
132
|
signing_key:
|
132
133
|
specification_version: 4
|
133
134
|
summary: Xlsxtream is a streaming XLSX spreadsheet writer
|