xlsxtream 0.2.0 → 0.3.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 +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
|