conciergelive-simple_xlsx_writer 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 29c8bb8aa7b33bd3d40a7bb41ebf71243cabeff6
4
+ data.tar.gz: 6d098a00184d6eed601752ea7584a6ecec05bb46
5
+ SHA512:
6
+ metadata.gz: c8b0c5c125cc6d0239a4edf6308afb3d7c558dee2ff63ef8a5577358fa2ef6e67d2b08c0dd8f72b9a784a67299edaea112154e3aa5acccf99ea954f1c9ad1402
7
+ data.tar.gz: 0f3f0e94a4cb15cb849f44efca7762d1143196832cf4a14aa4c8bbdd55b0bcbadfed6bcd4da90f957bcb36d0e1e83dd013d71a59a9a8a513770215f3fecad7bf
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in s.gemspec
4
+ gemspec
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,12 @@
1
+ require 'rake/testtask'
2
+ require 'bundler'
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new do |test|
7
+ test.libs << "test"
8
+ test.test_files = Dir['test/**/*_test.rb']
9
+ test.verbose = true
10
+ end
11
+
12
+ Bundler::GemHelper.install_tasks
@@ -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,20 @@
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
+ stream = @io.open_stream_for_sheet(@sheets.size)
12
+ @sheets << Sheet.new(self, name, stream, &block)
13
+ end
14
+
15
+ def has_shared_strings?
16
+ false
17
+ end
18
+
19
+ end
20
+ 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,182 @@
1
+ module SimpleXlsx
2
+
3
+ class Serializer
4
+
5
+ def initialize file_path
6
+ tempfile = Tempfile.new(File.basename(file_path))
7
+
8
+ Zip::ZipOutputStream.open(tempfile.path) do |zip|
9
+ @zip = zip
10
+ add_doc_props
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
+
20
+ FileUtils.mkdir_p(File.dirname(file_path))
21
+ FileUtils.cp(tempfile.path, file_path)
22
+ end
23
+
24
+ def add_workbook_part
25
+ @zip.put_next_entry("xl/workbook.xml")
26
+ @zip.puts <<-ends
27
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
28
+ <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
29
+ <workbookPr date1904="0" />
30
+ <sheets>
31
+ ends
32
+ @doc.sheets.each_with_index do |sheet, ndx|
33
+ @zip.puts %Q{<sheet name="#{sheet.name}" sheetId="#{ndx + 1}" r:id="#{sheet.rid}"/>}
34
+ end
35
+ @zip.puts "</sheets></workbook>"
36
+ end
37
+
38
+ def open_stream_for_sheet ndx
39
+ @zip.put_next_entry("xl/worksheets/sheet#{ndx + 1}.xml")
40
+ @zip
41
+ end
42
+
43
+ def add_content_types
44
+ @zip.put_next_entry("[Content_Types].xml")
45
+ @zip.puts '<?xml version="1.0" encoding="UTF-8"?>'
46
+ @zip.puts '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
47
+ @zip.puts <<-ends
48
+ <Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
49
+ <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
50
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
51
+ <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
52
+ <Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
53
+ ends
54
+ if @doc.has_shared_strings?
55
+ @zip.puts '<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>'
56
+ end
57
+ @doc.sheets.each_with_index do |sheet, ndx|
58
+ @zip.puts %Q{<Override PartName="/xl/worksheets/sheet#{ndx+1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>}
59
+ end
60
+ @zip.puts '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>'
61
+ @zip.puts "</Types>"
62
+ end
63
+
64
+ def add_workbook_relationship_part
65
+ @zip.put_next_entry("xl/_rels/workbook.xml.rels")
66
+ @zip.puts <<-ends
67
+ <?xml version="1.0" encoding="UTF-8"?>
68
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
69
+ ends
70
+ cnt = 0
71
+ @zip.puts %Q{<Relationship Id="rId#{cnt += 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>}
72
+ @doc.sheets.each_with_index do |sheet, ndx|
73
+ sheet.rid = "rId#{cnt += 1}"
74
+ @zip.puts %Q{<Relationship Id="#{sheet.rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet#{ndx + 1}.xml"/>}
75
+ end
76
+ if @doc.has_shared_strings?
77
+ @zip.puts '<Relationship Id="rId#{cnt += 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="xl/sharedStrings.xml"/>'
78
+ end
79
+ @zip.puts "</Relationships>"
80
+ end
81
+
82
+ def add_relationship_part
83
+ @zip.put_next_entry("_rels/.rels")
84
+ @zip.puts <<-ends
85
+ <?xml version="1.0" encoding="UTF-8"?>
86
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
87
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
88
+ <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
89
+ <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
90
+ ends
91
+ @zip.puts "</Relationships>"
92
+ end
93
+
94
+ def add_doc_props
95
+ @zip.put_next_entry("docProps/core.xml")
96
+ @zip.puts <<-ends
97
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
98
+ <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">
99
+ <dcterms:created xsi:type="dcterms:W3CDTF">#{Time.now.utc.xmlschema}</dcterms:created>
100
+ <cp:revision>0</cp:revision>
101
+ </cp:coreProperties>
102
+ ends
103
+ @zip.put_next_entry("docProps/app.xml")
104
+ @zip.puts <<-ends
105
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
106
+ <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
107
+ <TotalTime>0</TotalTime>
108
+ </Properties>
109
+ ends
110
+ end
111
+
112
+ def add_styles
113
+ @zip.put_next_entry("xl/styles.xml")
114
+ @zip.puts <<-ends
115
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
116
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
117
+ <numFmts count="7">
118
+ <numFmt formatCode="GENERAL" numFmtId="164"/>
119
+ <numFmt formatCode="&quot;TRUE&quot;;&quot;TRUE&quot;;&quot;FALSE&quot;" numFmtId="170"/>
120
+ </numFmts>
121
+ <fonts count="5">
122
+ <font><name val="Mangal"/><family val="2"/><sz val="10"/></font>
123
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
124
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
125
+ <font><name val="Arial"/><family val="0"/><sz val="10"/></font>
126
+ <font><name val="Arial"/><family val="2"/><sz val="10"/></font>
127
+ </fonts>
128
+ <fills count="2">
129
+ <fill><patternFill patternType="none"/></fill>
130
+ <fill><patternFill patternType="gray125"/></fill>
131
+ </fills>
132
+ <borders count="1">
133
+ <border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>
134
+ </borders>
135
+ <cellStyleXfs count="20">
136
+ <xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">
137
+ <alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>
138
+ <protection hidden="false" locked="true"/>
139
+ </xf>
140
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"></xf>
141
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"></xf>
142
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"></xf>
143
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"></xf>
144
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
145
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
146
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
147
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
148
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
149
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
150
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
151
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
152
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
153
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
154
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"></xf>
155
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"></xf>
156
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"></xf>
157
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"></xf>
158
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"></xf>
159
+ </cellStyleXfs>
160
+ <cellXfs count="7">
161
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="164" xfId="0"></xf>
162
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="22" xfId="0"></xf>
163
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="15" xfId="0"></xf>
164
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="1" xfId="0"></xf>
165
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="2" xfId="0"></xf>
166
+ <xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="49" xfId="0"></xf>
167
+ <xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="4" numFmtId="170" xfId="0"></xf>
168
+ </cellXfs>
169
+ <cellStyles count="6"><cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>
170
+ <cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>
171
+ <cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>
172
+ <cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>
173
+ <cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>
174
+ <cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>
175
+ </cellStyles>
176
+ </styleSheet>
177
+ ends
178
+ end
179
+
180
+ end
181
+
182
+ end
@@ -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 = [%Q{<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 << %Q{<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,3 @@
1
+ module SimpleXlsx
2
+ VERSION = "0.5.4"
3
+ 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
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "simple_xlsx/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "conciergelive-simple_xlsx_writer"
7
+ s.version = SimpleXlsx::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dee Zsombor", "Justin Beck"]
10
+ s.email = ["zsombor@primalgrasp.com"]
11
+ s.homepage = "http://github.com/conciergelive/simple_xlsx_writer"
12
+ s.summary = "Just as the name says, simple writer for Office 2007+ Excel files"
13
+ s.description = "Writes XLSX files"
14
+
15
+ s.rubyforge_project = "conciergelive-simple_xlsx_writer"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("rubyzip", ">= 0.9.4")
23
+ s.add_dependency("fast_xs", ">= 0.7.3")
24
+ 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
+ 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 %w[r s t], row.elements.to_a[0].attributes.keys.sort
76
+ end
77
+
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'fileutils'
3
+
4
+ class SimpleXlsxTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ FileUtils.rm_f "test.xlsx"
8
+ end
9
+
10
+ def teardown
11
+ FileUtils.rm_f "test.xlsx"
12
+ end
13
+
14
+ def test_top_level
15
+ o = SimpleXlsx::Serializer.new("test.xlsx") do |doc|
16
+ doc.add_sheet "First" do |sheet|
17
+ sheet.add_row ["Hello", "World", 3.14, 7]
18
+ sheet.add_row ["Another", "Row", Date.today, Time.parse('2010-Jul-24 12:00 UTC')]
19
+ end
20
+ end
21
+ end
22
+
23
+ if false
24
+ def test_top_level_stream
25
+ File.open "test_stream.xlsx", "wb" do |stream|
26
+ o = SimpleXlsx::Serializer.new(stream) do |doc|
27
+ doc.add_sheet "First" do |sheet|
28
+ sheet.add_row ["Hello", "World", 3.14]
29
+ sheet.add_row ["Another", "Row", Date.today]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ 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,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conciergelive-simple_xlsx_writer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.4
5
+ platform: ruby
6
+ authors:
7
+ - Dee Zsombor
8
+ - Justin Beck
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyzip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.9.4
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.9.4
28
+ - !ruby/object:Gem::Dependency
29
+ name: fast_xs
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.7.3
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.7.3
42
+ description: Writes XLSX files
43
+ email:
44
+ - zsombor@primalgrasp.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE
52
+ - README
53
+ - Rakefile
54
+ - lib/simple_xlsx.rb
55
+ - lib/simple_xlsx/document.rb
56
+ - lib/simple_xlsx/monkey_patches_for_true_zip_stream.rb
57
+ - lib/simple_xlsx/serializer.rb
58
+ - lib/simple_xlsx/sheet.rb
59
+ - lib/simple_xlsx/version.rb
60
+ - lib/simple_xlsx/xml_escape.rb
61
+ - simple_xlsx_writer.gemspec
62
+ - test/simple_xlsx/document_test.rb
63
+ - test/simple_xlsx/sheet_test.rb
64
+ - test/simple_xlsx_test.rb
65
+ - test/test_helper.rb
66
+ homepage: http://github.com/conciergelive/simple_xlsx_writer
67
+ licenses: []
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project: conciergelive-simple_xlsx_writer
85
+ rubygems_version: 2.2.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Just as the name says, simple writer for Office 2007+ Excel files
89
+ test_files:
90
+ - test/simple_xlsx/document_test.rb
91
+ - test/simple_xlsx/sheet_test.rb
92
+ - test/simple_xlsx_test.rb
93
+ - test/test_helper.rb