simple_xlsx_writer 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Dee Zsombor (zsombor@primalgrasp.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,44 @@
1
+ ** Description **
2
+
3
+
4
+ This is a simple no fuss generator for OpenXML aka XLSX files. No
5
+ formatting, styles just raw content with a few basic datatypes
6
+ supported. Produced output is tested to be compatible with
7
+
8
+ - Open Office 3.2 series (Linux, Mac, Windows)
9
+ - Neo Office 3.2 series (Mac)
10
+ - Microsoft Office 2007 (Windows)
11
+ - Microsoft Office 2010 (Windows)
12
+ - Microsoft Office 2008 for Mac (versions 12.2.5 or above)
13
+ - Microsoft Excel Viewer (Windows)
14
+
15
+ Numbers of iWork '09 does not appear to support the inline string
16
+ storage model prefered by this gem. Apple may release a fix for this
17
+ eventually, I have avoided the more common shared string table
18
+ method as it cannot be implemented in linear time.
19
+
20
+
21
+ ** Sample **
22
+
23
+
24
+ serializer = SimpleXlsx::Serializer.new("test.xlsx") do |doc|
25
+ doc.add_sheet("People") do |sheet|
26
+ sheet.add_row(%w{DoB Name Occupation})
27
+ sheet.add_row([Date.parse("July 31, 1912"),
28
+ "Milton Friedman",
29
+ "Economist / Statistician"])
30
+ end
31
+ end
32
+
33
+
34
+ ** License **
35
+
36
+
37
+ See attached LICENSE for details.
38
+
39
+
40
+ ** Credits **
41
+
42
+
43
+ Written by Dee Zsombor: http://primalgrasp.com
44
+ Funded by Harvest: http://www.getharvest.com
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/gempackagetask'
5
+
6
+ task :default => [:test]
7
+
8
+ Rake::TestTask.new do |test|
9
+ test.libs << "test"
10
+ test.test_files = Dir['test/**/*_test.rb'].sort
11
+ test.verbose = true
12
+ end
13
+
14
+ desc "generate tags for emacs"
15
+ task :tags do
16
+ sh "ctags -Re lib/ "
17
+ end
18
+
19
+
20
+ spec = Gem::Specification.new do |s|
21
+ s.name = "simple_xlsx_writer"
22
+ s.version = "0.5.3"
23
+ s.author = "Dee Zsombor"
24
+ s.email = "zsombor@primalgrasp.com"
25
+ s.homepage = "http://simplxlsxwriter.rubyforge.org"
26
+ s.rubyforge_project = "simple_xlsx_writer"
27
+ s.platform = Gem::Platform::RUBY
28
+ s.summary = "Just as the name says, simple writter for Office 2007+ Excel files"
29
+ s.files = [FileList["{bin,lib}/**/*"].to_a, "LICENSE", "Rakefile"].flatten
30
+ s.require_path = "lib"
31
+ s.test_files = [FileList["{test}/**/*test.rb"].to_a, "test/test_helper.rb"].flatten
32
+ s.has_rdoc = true
33
+ s.extra_rdoc_files = ["README"]
34
+ s.add_dependency("rubyzip", ">= 0.9.4")
35
+ s.add_dependency("fast_xs", ">= 0.7.3")
36
+ end
37
+
38
+ Rake::GemPackageTask.new(spec) do |pkg|
39
+ pkg.need_tar = true
40
+ end
@@ -0,0 +1,11 @@
1
+ require 'tempfile'
2
+ require 'rubygems'
3
+
4
+ $:.unshift(File.dirname(__FILE__))
5
+ require 'simple_xlsx/xml_escape'
6
+ require 'simple_xlsx/monkey_patches_for_true_zip_stream'
7
+ require 'simple_xlsx/serializer'
8
+ require 'simple_xlsx/document'
9
+ require 'simple_xlsx/sheet'
10
+
11
+
@@ -0,0 +1,21 @@
1
+ module SimpleXlsx
2
+ class Document
3
+ def initialize(io)
4
+ @sheets = []
5
+ @io = io
6
+ end
7
+
8
+ attr_reader :sheets
9
+
10
+ def add_sheet name, &block
11
+ @io.open_stream_for_sheet(@sheets.size) do |stream|
12
+ @sheets << Sheet.new(self, name, stream, &block)
13
+ end
14
+ end
15
+
16
+ def has_shared_strings?
17
+ false
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'zip/zip' #dep
2
+
3
+ __END__
4
+
5
+ module Zip
6
+ class ZipOutputStream
7
+ def initialize(fileName)
8
+ super()
9
+ if fileName.is_a?(String) && !fileName.empty?
10
+ @fileName = fileName
11
+ @outputStream = File.new(@fileName, "wb")
12
+ else
13
+ @outputStream = fileName
14
+ @fileName = ''
15
+ end
16
+ @entrySet = ZipEntrySet.new
17
+ @compressor = NullCompressor.instance
18
+ @closed = false
19
+ @currentEntry = nil
20
+ @comment = nil
21
+ end
22
+ end
23
+
24
+ class ZipFile < ZipCentralDirectory
25
+ def initialize(stream, create = nil)
26
+ super()
27
+ @name = stream.is_a?(String) ? stream : ''
28
+ @comment = ""
29
+ if stream.is_a?(String) && File.exists?(stream)
30
+ File.open(name, "rb") { |f| read_from_stream(f) }
31
+ elsif (create)
32
+ @entrySet = ZipEntrySet.new
33
+ elsif !stream.is_a?(String) && !create && !stream.respond_to(:path)
34
+ # do nothing here
35
+ elsif !stream.is_a?(String) && !create
36
+ File.open(stream.path, "rb") { |f| read_from_stream(f) }
37
+ else
38
+ raise ZipError, "File #{stream} not found"
39
+ end
40
+ @create = create
41
+ @storedEntries = @entrySet.dup
42
+
43
+ @restore_ownership = false
44
+ @restore_permissions = false
45
+ @restore_times = true
46
+ end
47
+
48
+ def on_success_replace arg
49
+ if arg.is_a?(String) && !arg.empty?
50
+ tmpfile = get_tempfile
51
+ tmpFilename = tmpfile.path
52
+ tmpfile.close
53
+ if yield tmpFilename
54
+ File.rename(tmpFilename, name)
55
+ end
56
+ else
57
+ yield arg
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,196 @@
1
+ module SimpleXlsx
2
+
3
+ class Serializer
4
+
5
+ def initialize to
6
+ @to = to
7
+ Zip::ZipFile.open(to, Zip::ZipFile::CREATE) do |zip|
8
+ @zip = zip
9
+ add_doc_props
10
+ add_worksheets_directory
11
+ add_relationship_part
12
+ add_styles
13
+ @doc = Document.new(self)
14
+ yield @doc
15
+ add_workbook_relationship_part
16
+ add_content_types
17
+ add_workbook_part
18
+ end
19
+ end
20
+
21
+ def add_workbook_part
22
+ @zip.get_output_stream "xl/workbook.xml" do |f|
23
+ f.puts <<-ends
24
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
25
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
26
+ <workbookPr date1904="0" />
27
+ <sheets>
28
+ ends
29
+ @doc.sheets.each_with_index do |sheet, ndx|
30
+ f.puts "<sheet name=\"#{sheet.name}\" sheetId=\"#{ndx + 1}\" r:id=\"#{sheet.rid}\"/>"
31
+ end
32
+ f.puts "</sheets></workbook>"
33
+ end
34
+ end
35
+
36
+ def add_worksheets_directory
37
+ @zip.mkdir "xl"
38
+ @zip.mkdir "xl/worksheets"
39
+ end
40
+
41
+ def open_stream_for_sheet ndx
42
+ @zip.get_output_stream "xl/worksheets/sheet#{ndx + 1}.xml" do |f|
43
+ yield f
44
+ end
45
+ end
46
+
47
+ def add_content_types
48
+ @zip.get_output_stream "[Content_Types].xml" do |f|
49
+ f.puts '<?xml version="1.0" encoding="UTF-8"?>'
50
+ f.puts '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
51
+ f.puts <<-ends
52
+ <Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
53
+ <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
54
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
55
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
56
+ <Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
57
+ ends
58
+ if @doc.has_shared_strings?
59
+ f.puts '<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>'
60
+ end
61
+ @doc.sheets.each_with_index do |sheet, ndx|
62
+ f.puts "<Override PartName=\"/xl/worksheets/sheet#{ndx+1}.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>"
63
+ end
64
+ f.puts '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>'
65
+ f.puts "</Types>"
66
+ end
67
+ end
68
+
69
+ def add_workbook_relationship_part
70
+ @zip.mkdir "xl/_rels"
71
+ @zip.get_output_stream "xl/_rels/workbook.xml.rels" do |f|
72
+ f.puts <<-ends
73
+ <?xml version="1.0" encoding="UTF-8"?>
74
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
75
+ ends
76
+ cnt = 0
77
+ f.puts "<Relationship Id=\"rId#{cnt += 1}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>"
78
+ @doc.sheets.each_with_index do |sheet, ndx|
79
+ sheet.rid = "rId#{cnt += 1}"
80
+ f.puts "<Relationship Id=\"#{sheet.rid}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet#{ndx + 1}.xml\"/>"
81
+ end
82
+ if @doc.has_shared_strings?
83
+ f.puts '<Relationship Id="rId#{cnt += 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="xl/sharedStrings.xml"/>'
84
+ end
85
+ f.puts "</Relationships>"
86
+ end
87
+ end
88
+
89
+ def add_relationship_part
90
+ @zip.mkdir "_rels"
91
+ @zip.get_output_stream "_rels/.rels" do |f|
92
+ f.puts <<-ends
93
+ <?xml version="1.0" encoding="UTF-8"?>
94
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
95
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
96
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
97
+ <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
98
+ ends
99
+ f.puts "</Relationships>"
100
+ end
101
+ end
102
+
103
+ def add_doc_props
104
+ @zip.mkdir "docProps"
105
+ @zip.get_output_stream "docProps/core.xml" do |f|
106
+ f.puts <<-ends
107
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
108
+ <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
109
+ <dcterms:created xsi:type="dcterms:W3CDTF">2010-07-20T14:30:58.00Z</dcterms:created>
110
+ <cp:revision>0</cp:revision>
111
+ </cp:coreProperties>
112
+ ends
113
+ end
114
+ @zip.get_output_stream "docProps/app.xml" do |f|
115
+ f.puts <<-ends
116
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
117
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
118
+ <TotalTime>0</TotalTime>
119
+ </Properties>
120
+ ends
121
+ end
122
+ end
123
+
124
+ def add_styles
125
+ @zip.get_output_stream "xl/styles.xml" do |f|
126
+ f.puts <<-ends
127
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
128
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
129
+ <numFmts count="7">
130
+ <numFmt formatCode="GENERAL" numFmtId="164"/>
131
+ <numFmt formatCode="&quot;TRUE&quot;;&quot;TRUE&quot;;&quot;FALSE&quot;" numFmtId="170"/>
132
+ </numFmts>
133
+ <fonts count="5">
134
+ <font><name val="Mangal"/><family val="2"/><sz val="10"/></font>
135
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
136
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
137
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
138
+ <font><name val="Arial"/><family val="2"/><sz val="10"/></font>
139
+ </fonts>
140
+ <fills count="2">
141
+ <fill><patternFill patternType="none"/></fill>
142
+ <fill><patternFill patternType="gray125"/></fill>
143
+ </fills>
144
+ <borders count="1">
145
+ <border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>
146
+ </borders>
147
+ <cellStyleXfs count="20">
148
+ <xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">
149
+ <alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>
150
+ <protection hidden="false" locked="true"/>
151
+ </xf>
152
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"></xf>
153
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"></xf>
154
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"></xf>
155
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"></xf>
156
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
157
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
158
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
159
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
160
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
161
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
162
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
163
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
164
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
165
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
166
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"></xf>
167
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"></xf>
168
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"></xf>
169
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"></xf>
170
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"></xf>
171
+ </cellStyleXfs>
172
+ <cellXfs count="7">
173
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="164" xfId="0"></xf>
174
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="22" xfId="0"></xf>
175
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="15" xfId="0"></xf>
176
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="1" xfId="0"></xf>
177
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="2" xfId="0"></xf>
178
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="49" xfId="0"></xf>
179
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="170" xfId="0"></xf>
180
+ </cellXfs>
181
+ <cellStyles count="6"><cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>
182
+ <cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>
183
+ <cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>
184
+ <cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>
185
+ <cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>
186
+ <cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>
187
+ </cellStyles>
188
+ </styleSheet>
189
+ ends
190
+ end
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+
@@ -0,0 +1,83 @@
1
+ require 'bigdecimal'
2
+ require 'time'
3
+
4
+ module SimpleXlsx
5
+
6
+ class Sheet
7
+ attr_reader :name
8
+ attr_accessor :rid
9
+
10
+ def initialize document, name, stream, &block
11
+ @document = document
12
+ @stream = stream
13
+ @name = name
14
+ @row_ndx = 1
15
+ @stream.write <<-ends
16
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
17
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
18
+ <sheetData>
19
+ ends
20
+ if block_given?
21
+ yield self
22
+ end
23
+ @stream.write "</sheetData></worksheet>"
24
+ end
25
+
26
+ def add_row arry
27
+ row = ["<row r=\"#{@row_ndx}\">"]
28
+ arry.each_with_index do |value, col_ndx|
29
+ kind, ccontent, cstyle = Sheet.format_field_and_type_and_style value
30
+ row << "<c r=\"#{Sheet.column_index(col_ndx)}#{@row_ndx}\" t=\"#{kind.to_s}\" s=\"#{cstyle}\">#{ccontent}</c>"
31
+ end
32
+ row << "</row>"
33
+ @row_ndx += 1
34
+ @stream.write(row.join())
35
+ end
36
+
37
+ def self.format_field_and_type_and_style value
38
+ if value.is_a?(String)
39
+ [:inlineStr, "<is><t>#{value.to_xs}</t></is>", 5]
40
+ elsif value.is_a?(BigDecimal)
41
+ [:n, "<v>#{value.to_s('f')}</v>", 4]
42
+ elsif value.is_a?(Float)
43
+ [:n, "<v>#{value.to_s}</v>", 4]
44
+ elsif value.is_a?(Numeric)
45
+ [:n, "<v>#{value.to_s}</v>", 3]
46
+ elsif value.is_a?(Date)
47
+ [:n, "<v>#{days_since_jan_1_1900(value)}</v>", 2]
48
+ elsif value.is_a?(Time)
49
+ [:n, "<v>#{fractional_days_since_jan_1_1900(value)}</v>", 1]
50
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
51
+ [:b, "<v>#{value ? '1' : '0'}</v>", 6]
52
+ else
53
+ [:inlineStr, "<is><t>#{value.to_s.to_xs}</t></is>", 5]
54
+ end
55
+ end
56
+
57
+ def self.days_since_jan_1_1900 date
58
+ @@jan_1_1904 ||= Date.parse("1904 Jan 1")
59
+ (date - @@jan_1_1904).to_i + 1462 # http://support.microsoft.com/kb/180162
60
+ end
61
+
62
+ def self.fractional_days_since_jan_1_1900 value
63
+ @@jan_1_1904_midnight ||= ::Time.utc(1904, 1, 1)
64
+ ((value - @@jan_1_1904_midnight) / 86400.0) + #24*60*60
65
+ 1462 # http://support.microsoft.com/kb/180162
66
+ end
67
+
68
+ def self.abc
69
+ @@abc ||= ('A'..'Z').to_a
70
+ end
71
+
72
+ def self.column_index n
73
+ result = []
74
+ while n >= 26 do
75
+ result << abc[n % 26]
76
+ n /= 26
77
+ end
78
+ result << abc[result.empty? ? n : n - 1]
79
+ result.reverse.join
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,6 @@
1
+ unless String.method_defined? :to_xs
2
+ require 'fast_xs' #dep
3
+ class String
4
+ alias_method :to_xs, :fast_xs
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ module SimpleXlsx
4
+
5
+ class DocumentTest < Test::Unit::TestCase
6
+
7
+ def open_stream_for_sheet sheets_size
8
+ assert_equal sheets_size, @doc.sheets.size
9
+ yield self
10
+ end
11
+
12
+ def write arg
13
+ end
14
+
15
+ def test_add_sheet
16
+ @doc = Document.new self
17
+ assert_equal [], @doc.sheets
18
+ @doc.add_sheet "new sheet"
19
+ assert_equal 1, @doc.sheets.size
20
+ assert_equal 'new sheet', @doc.sheets.first.name
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+ require "rexml/document"
3
+ require 'time'
4
+
5
+ module SimpleXlsx
6
+
7
+ class SheetTest < Test::Unit::TestCase
8
+
9
+ def test_column_index
10
+ assert_equal 'A', Sheet.column_index(0)
11
+ assert_equal 'B', Sheet.column_index(1)
12
+ assert_equal 'C', Sheet.column_index(2)
13
+ assert_equal 'D', Sheet.column_index(3)
14
+ assert_equal 'Y', Sheet.column_index(24)
15
+ assert_equal 'Z', Sheet.column_index(25)
16
+ end
17
+
18
+ def test_column_index_two_digits
19
+ assert_equal 'AA', Sheet.column_index(0+26)
20
+ assert_equal 'AB', Sheet.column_index(1+26)
21
+ assert_equal 'AC', Sheet.column_index(2+26)
22
+ assert_equal 'AD', Sheet.column_index(3+26)
23
+ assert_equal 'AZ', Sheet.column_index(25+26)
24
+ assert_equal 'BA', Sheet.column_index(25+26+1)
25
+ assert_equal 'BB', Sheet.column_index(25+26+2)
26
+ assert_equal 'BC', Sheet.column_index(25+26+3)
27
+ end
28
+
29
+ def test_format_field_for_strings
30
+ v = Sheet.format_field_and_type_and_style "<escape this>"
31
+ assert_equal [:inlineStr, "<is><t>&lt;escape this&gt;</t></is>", 5], v
32
+ end
33
+
34
+ def test_format_field_for_numbers
35
+ v = Sheet.format_field_and_type_and_style 3
36
+ assert_equal [:n, "<v>3</v>", 3], v
37
+ v = Sheet.format_field_and_type_and_style(BigDecimal.new("45"))
38
+ assert_equal [:n, "<v>45.0</v>", 4], v
39
+ v = Sheet.format_field_and_type_and_style(9.32)
40
+ assert_equal [:n, "<v>9.32</v>", 4], v
41
+ end
42
+
43
+ def test_format_field_for_date
44
+ v = Sheet.format_field_and_type_and_style(Date.parse('2010-Jul-24'))
45
+ assert_equal [:n, "<v>#{38921+1462}</v>", 2], v
46
+ end
47
+
48
+ def test_format_field_for_datetime
49
+ v = Sheet.format_field_and_type_and_style(Time.parse('2010-Jul-24 12:00 UTC'))
50
+ assert_equal [:n, "<v>#{38921.5+1462}</v>", 1], v
51
+ end
52
+
53
+
54
+ def test_format_field_for_boolean
55
+ v = Sheet.format_field_and_type_and_style(false)
56
+ assert_equal [:b, "<v>0</v>", 6], v
57
+ v = Sheet.format_field_and_type_and_style(true)
58
+ assert_equal [:b, "<v>1</v>", 6], v
59
+ end
60
+
61
+ def test_add_row
62
+ str = ""
63
+ io = StringIO.new(str)
64
+ Sheet.new(nil, 'name', io) do |sheet|
65
+ sheet.add_row ['this is ', 'a new row']
66
+ end
67
+ doc = REXML::Document.new str
68
+ assert_equal 'worksheet', doc.root.name
69
+ sheetdata = doc.root.elements['sheetData']
70
+ assert sheetdata
71
+ row = sheetdata.elements['row']
72
+ assert row
73
+ assert_equal '1', row.attributes['r']
74
+ assert_equal 2, row.elements.to_a.size
75
+ assert_equal ["r", "s", "t"], row.elements.to_a[0].attributes.keys
76
+ end
77
+
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'fileutils'
3
+
4
+ class SimpleXlsxTest < Test::Unit::TestCase
5
+
6
+ def test_top_level
7
+ FileUtils.rm_f "test.xlsx"
8
+ o = SimpleXlsx::Serializer.new("test.xlsx") do |doc|
9
+ doc.add_sheet "First" do |sheet|
10
+ sheet.add_row ["Hello", "World", 3.14, 7]
11
+ sheet.add_row ["Another", "Row", Date.today, Time.parse('2010-Jul-24 12:00 UTC')]
12
+ end
13
+ end
14
+ end
15
+
16
+ if false
17
+ def test_top_level_stream
18
+ File.open "test_stream.xlsx", "wb" do |stream|
19
+ o = SimpleXlsx::Serializer.new(stream) do |doc|
20
+ doc.add_sheet "First" do |sheet|
21
+ sheet.add_row ["Hello", "World", 3.14]
22
+ sheet.add_row ["Another", "Row", Date.today]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "rubygems"
3
+ require File.dirname(__FILE__) + '/../lib/simple_xlsx' unless defined?(SimpleXlsx)
4
+
5
+ require 'ruby-debug'
6
+ Debugger.settings[:autoeval] = true
7
+ Debugger.settings[:autolist] = 1
8
+ Debugger.start
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_xlsx_writer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 13
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 3
10
+ version: 0.5.3
11
+ platform: ruby
12
+ authors:
13
+ - Dee Zsombor
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-20 00:00:00 +03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubyzip
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 51
30
+ segments:
31
+ - 0
32
+ - 9
33
+ - 4
34
+ version: 0.9.4
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: fast_xs
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 5
46
+ segments:
47
+ - 0
48
+ - 7
49
+ - 3
50
+ version: 0.7.3
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description:
54
+ email: zsombor@primalgrasp.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files:
60
+ - README
61
+ files:
62
+ - lib/simple_xlsx.rb
63
+ - lib/simple_xlsx/xml_escape.rb
64
+ - lib/simple_xlsx/serializer.rb
65
+ - lib/simple_xlsx/monkey_patches_for_true_zip_stream.rb
66
+ - lib/simple_xlsx/document.rb
67
+ - lib/simple_xlsx/sheet.rb
68
+ - LICENSE
69
+ - Rakefile
70
+ - test/simple_xlsx_test.rb
71
+ - test/simple_xlsx/document_test.rb
72
+ - test/simple_xlsx/sheet_test.rb
73
+ - test/test_helper.rb
74
+ - README
75
+ has_rdoc: true
76
+ homepage: http://simplxlsxwriter.rubyforge.org
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project: simple_xlsx_writer
105
+ rubygems_version: 1.3.7
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Just as the name says, simple writter for Office 2007+ Excel files
109
+ test_files:
110
+ - test/simple_xlsx_test.rb
111
+ - test/simple_xlsx/document_test.rb
112
+ - test/simple_xlsx/sheet_test.rb
113
+ - test/test_helper.rb