workbook 0.4 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -6
- data/lib/workbook.rb +1 -0
- data/lib/workbook/cell.rb +9 -1
- data/lib/workbook/format.rb +0 -1
- data/lib/workbook/nil_value.rb +33 -0
- data/lib/workbook/readers/ods_reader.rb +33 -17
- data/lib/workbook/table.rb +34 -0
- data/lib/workbook/version.rb +1 -1
- data/lib/workbook/writers/html_writer.rb +10 -3
- data/test/artifacts/complex_types.ods +0 -0
- data/test/artifacts/excel_different_types.ods +0 -0
- data/test/artifacts/sheet_with_combined_cells.ods +0 -0
- data/test/test_cell.rb +22 -0
- data/test/test_readers_ods_reader.rb +15 -3
- data/test/test_table.rb +33 -0
- data/test/test_writers_html_writer.rb +16 -0
- metadata +4 -1
data/README.md
CHANGED
@@ -29,19 +29,19 @@ or
|
|
29
29
|
Calling
|
30
30
|
|
31
31
|
s = b.sheet
|
32
|
-
|
32
|
+
t = s.table
|
33
33
|
|
34
34
|
will give you an the first Sheet and Table (if one doesn't exist it is created on the fly).
|
35
35
|
|
36
36
|
You can initialize with simple 2-d array like this:
|
37
37
|
|
38
38
|
b = Workbook::Book.new [['a','b'],[1,2],[3,4],[5,6]]
|
39
|
-
|
39
|
+
t = s.sheet.table
|
40
40
|
|
41
41
|
Subsequently you look up values in the table like this:
|
42
42
|
|
43
43
|
t[1][:b]
|
44
|
-
|
44
|
+
# returns <Workbook::Cel @value=2>
|
45
45
|
|
46
46
|
which is equivalent to
|
47
47
|
|
@@ -51,6 +51,10 @@ Of course you'll be able to write a new value back to it. If you just enter a va
|
|
51
51
|
|
52
52
|
t[1][:b] = 5
|
53
53
|
|
54
|
+
Alternatively (more spreadsheet like) you can read cells like this (writing to be supported, not implemented yet)
|
55
|
+
|
56
|
+
t['A2']
|
57
|
+
|
54
58
|
If you want to use an existing file as a template (which you can create in Excel to create nice looking templates),
|
55
59
|
simply clone the row, and add it back:
|
56
60
|
|
@@ -70,10 +74,9 @@ simply clone the row, and add it back:
|
|
70
74
|
# in the endresult
|
71
75
|
b.write("result.xls") # write it!
|
72
76
|
|
73
|
-
<!-- Feature *to implement*:
|
74
77
|
|
75
|
-
|
76
|
-
|
78
|
+
|
79
|
+
<!-- Feature *to implement*:
|
77
80
|
|
78
81
|
Feature *to implement*, get a single column:
|
79
82
|
|
data/lib/workbook.rb
CHANGED
data/lib/workbook/cell.rb
CHANGED
@@ -9,9 +9,11 @@ module Workbook
|
|
9
9
|
attr_accessor :value
|
10
10
|
attr_accessor :format
|
11
11
|
attr_accessor :formula
|
12
|
+
attr_accessor :colspan
|
13
|
+
attr_accessor :rowspan
|
12
14
|
|
13
15
|
# Note that these types are sorted by 'importance'
|
14
|
-
VALID_TYPES = [Numeric,String,Time,Date,TrueClass,FalseClass,NilClass]
|
16
|
+
VALID_TYPES = [Numeric,String,Time,Date,TrueClass,FalseClass,NilClass,Workbook::NilValue]
|
15
17
|
|
16
18
|
# Evaluates a value for class-validity
|
17
19
|
#
|
@@ -170,5 +172,11 @@ module Workbook
|
|
170
172
|
end
|
171
173
|
end
|
172
174
|
|
175
|
+
def colspan
|
176
|
+
@colspan.to_i if @colspan.to_i > 1
|
177
|
+
end
|
178
|
+
def rowspan
|
179
|
+
@rowspan.to_i if @rowspan.to_i > 1
|
180
|
+
end
|
173
181
|
end
|
174
182
|
end
|
data/lib/workbook/format.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Workbook
|
2
|
+
|
3
|
+
# Used in cases col or rowspans are used
|
4
|
+
class NilValue
|
5
|
+
attr_accessor :reason #:covered
|
6
|
+
|
7
|
+
# initialize this special nilvalue with a reason
|
8
|
+
# @params [String] reason (currently only :covered, in case this cell is coverd because an adjecant cell spans over it)
|
9
|
+
def initialize reason
|
10
|
+
self.reason= reason
|
11
|
+
end
|
12
|
+
|
13
|
+
# returns the value of itself (nil)
|
14
|
+
# @return [NilClass] nil
|
15
|
+
def value
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def <=> v
|
20
|
+
value <=> v
|
21
|
+
end
|
22
|
+
|
23
|
+
# set the reason why this value is nil
|
24
|
+
def reason= reason
|
25
|
+
if reason == :covered
|
26
|
+
@reason = reason
|
27
|
+
else
|
28
|
+
raise "invalid reason given"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Workbook
|
4
4
|
module Readers
|
5
5
|
module OdsReader
|
6
|
-
|
7
6
|
# reads self with and ods-type content.xml
|
8
7
|
# @param [String,File] file_obj a file or file reference
|
9
8
|
# @return [Workbook::Book] self
|
@@ -53,6 +52,8 @@ module Workbook
|
|
53
52
|
# @param [Nokogiri::XML::Document] ods_spreadsheet nokogirified content.xml
|
54
53
|
# @return [Workbook::Book] self
|
55
54
|
def parse_ods ods_spreadsheet=template.raws[Nokogiri::XML::Document], options={}
|
55
|
+
require 'cgi'
|
56
|
+
|
56
57
|
options = {:additional_type_parsing=>false}.merge options
|
57
58
|
# styles
|
58
59
|
#puts ods_spreadsheet
|
@@ -63,26 +64,41 @@ module Workbook
|
|
63
64
|
s = self.create_or_open_sheet_at(sheetindex)
|
64
65
|
sheet.xpath("table:table").each_with_index do |table,tableindex|
|
65
66
|
t = s.create_or_open_table_at(tableindex)
|
67
|
+
t.name = table.xpath("@table:name").to_s
|
68
|
+
columns = table.xpath("table:table-column").count
|
66
69
|
table.xpath("table:table-row").each_with_index do |row,rowindex|
|
67
|
-
|
70
|
+
cells = row.xpath("table:table-cell|table:covered-table-cell")
|
71
|
+
r = Workbook::Row.new
|
72
|
+
columns.times do |column_index|
|
73
|
+
cell = cells[column_index]
|
68
74
|
c = Workbook::Cell.new()
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
value = nil
|
76
|
+
if cell
|
77
|
+
if cell.name == "covered-table-cell"
|
78
|
+
value = Workbook::NilValue.new(:covered)
|
79
|
+
else
|
80
|
+
cell_style_name = cell.xpath('@table:style-name').to_s
|
81
|
+
c.format = self.template.formats[cell_style_name]
|
82
|
+
valuetype = cell.xpath('@office:value-type').to_s
|
83
|
+
c.colspan= cell.xpath('@table:number-columns-spanned').to_s
|
84
|
+
c.rowspan= cell.xpath('@table:number-rows-spanned').to_s
|
85
|
+
|
86
|
+
value = CGI.unescapeHTML(cell.xpath("text:p//text()").to_s)
|
87
|
+
value = (value == "") ? nil : value
|
88
|
+
case valuetype
|
89
|
+
when 'float'
|
90
|
+
value = cell.xpath("@office:value").to_s.to_f
|
91
|
+
when 'integer'
|
92
|
+
value = cell.xpath("@office:value").to_s.to_i
|
93
|
+
when 'date'
|
94
|
+
value = DateTime.parse(cell.xpath("@office:date-value").to_s)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
82
98
|
c.value = value
|
83
|
-
c
|
99
|
+
r << c
|
84
100
|
end
|
85
|
-
t <<
|
101
|
+
t << r
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
data/lib/workbook/table.rb
CHANGED
@@ -9,6 +9,7 @@ module Workbook
|
|
9
9
|
include Workbook::Modules::TableDiffSort
|
10
10
|
include Workbook::Writers::CsvTableWriter
|
11
11
|
attr_accessor :sheet
|
12
|
+
attr_accessor :name
|
12
13
|
attr_accessor :header
|
13
14
|
|
14
15
|
def initialize row_cel_values=[], sheet=nil, options={}
|
@@ -113,5 +114,38 @@ module Workbook
|
|
113
114
|
return c
|
114
115
|
end
|
115
116
|
|
117
|
+
# Overrides normal Array's []-function with support for symbols that identify a column based on the header-values
|
118
|
+
#
|
119
|
+
# @example Lookup using fixnum or header value encoded as symbol
|
120
|
+
# table[0] #=> <Row [a,2,3,4]> (first row)
|
121
|
+
# table["A1"] #=> <Cell value="a"> (first cell of first row)
|
122
|
+
#
|
123
|
+
# @param [Fixnum, String] index_or_string to reference to either the row, or the cell
|
124
|
+
# @return [Workbook::Row, Workbook::Cell, nil]
|
125
|
+
def [](index_or_string)
|
126
|
+
if index_or_string.is_a? String
|
127
|
+
match = index_or_string.upcase.match(/([A-Z]*)([0-9]*)/)
|
128
|
+
cell_index = alpha_index_to_number_index(match[1])
|
129
|
+
row_index = match[2].to_i - 1
|
130
|
+
return self[row_index][cell_index]
|
131
|
+
else
|
132
|
+
if index_or_string
|
133
|
+
return to_a[index_or_string]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Helps to convert from e.g. "AA" to 26
|
139
|
+
# @param [String] string that typically identifies a column
|
140
|
+
# @return [Integer]
|
141
|
+
def alpha_index_to_number_index string
|
142
|
+
string.upcase!
|
143
|
+
sum = 0
|
144
|
+
string.chars.each_with_index do | char, char_index|
|
145
|
+
sum = sum * 26 + char.ord-64
|
146
|
+
end
|
147
|
+
return sum-1
|
148
|
+
end
|
149
|
+
|
116
150
|
end
|
117
151
|
end
|
data/lib/workbook/version.rb
CHANGED
@@ -19,6 +19,9 @@ module Workbook
|
|
19
19
|
doc.text sheet.name
|
20
20
|
}
|
21
21
|
sheet.each{|table|
|
22
|
+
doc.h2 {
|
23
|
+
doc.text table.name
|
24
|
+
}
|
22
25
|
doc.table {
|
23
26
|
table.each{|row|
|
24
27
|
doc.tr {
|
@@ -26,9 +29,13 @@ module Workbook
|
|
26
29
|
classnames = cell.format.all_names.join(" ").strip
|
27
30
|
td_options = classnames != "" ? {:class=>classnames} : {}
|
28
31
|
td_options = td_options.merge({:style=>cell.format.to_css}) if options[:style_with_inline_css] and cell.format.to_css != ""
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
td_options = td_options.merge({:colspan=>cell.colspan}) if cell.colspan
|
33
|
+
td_options = td_options.merge({:rowspan=>cell.rowspan}) if cell.rowspan
|
34
|
+
unless cell.value.class == Workbook::NilValue
|
35
|
+
doc.td(td_options) {
|
36
|
+
doc.text cell.value
|
37
|
+
}
|
38
|
+
end
|
32
39
|
}
|
33
40
|
}
|
34
41
|
}
|
Binary file
|
Binary file
|
Binary file
|
data/test/test_cell.rb
CHANGED
@@ -80,4 +80,26 @@ class TestCell < Test::Unit::TestCase
|
|
80
80
|
c = Workbook::Cell.new nil
|
81
81
|
assert_equal(true,c.nil?)
|
82
82
|
end
|
83
|
+
|
84
|
+
def test_colspan_rowspan
|
85
|
+
c = Workbook::Cell.new
|
86
|
+
c.colspan = 1
|
87
|
+
c.rowspan = 1
|
88
|
+
assert_equal(nil,c.colspan)
|
89
|
+
assert_equal(nil,c.rowspan)
|
90
|
+
c.colspan = nil
|
91
|
+
c.rowspan = ""
|
92
|
+
assert_equal(nil,c.colspan)
|
93
|
+
assert_equal(nil,c.rowspan)
|
94
|
+
c.colspan = 3
|
95
|
+
c.rowspan = "4"
|
96
|
+
assert_equal(3,c.colspan)
|
97
|
+
c.rowspan = 0
|
98
|
+
assert_equal(nil,c.rowspan)
|
99
|
+
assert_equal(3,c.colspan)
|
100
|
+
c.colspan = 0
|
101
|
+
c.rowspan = 3
|
102
|
+
assert_equal(3,c.rowspan)
|
103
|
+
assert_equal(nil,c.colspan)
|
104
|
+
end
|
83
105
|
end
|
@@ -6,7 +6,7 @@ module Readers
|
|
6
6
|
|
7
7
|
w = Workbook::Book.new
|
8
8
|
w.open 'test/artifacts/book_with_tabs_and_colours.ods'
|
9
|
-
assert_equal([:a, :b, :c, :d, :e],w.sheet.table.header.to_symbols
|
9
|
+
assert_equal([:a, :b, :c, :d, :e],w.sheet.table.header.to_symbols)
|
10
10
|
assert_equal(90588,w.sheet.table[2][:b].value)
|
11
11
|
end
|
12
12
|
|
@@ -26,7 +26,7 @@ module Readers
|
|
26
26
|
w.open 'test/artifacts/complex_types.ods'
|
27
27
|
assert_equal(Date.new(2011,11,15), w.sheet.table[2][3].value)
|
28
28
|
assert_equal("http://murb.nl", w.sheet.table[3][2].value)
|
29
|
-
assert_equal("
|
29
|
+
assert_equal("Sadfasdfsd > 2", w.sheet.table[4][2].value)
|
30
30
|
assert_equal(1.2, w.sheet.table[3][1].value)
|
31
31
|
end
|
32
32
|
|
@@ -46,6 +46,18 @@ module Readers
|
|
46
46
|
assert_equal(42000,w.sheet.table[3][:b].value)
|
47
47
|
assert_equal(nil,w.sheet.table[2][:c].value)
|
48
48
|
end
|
49
|
-
|
49
|
+
def test_sheet_with_combined_cells
|
50
|
+
w = Workbook::Book.new
|
51
|
+
w.open("test/artifacts/sheet_with_combined_cells.ods")
|
52
|
+
t = w.sheet.table
|
53
|
+
assert_equal("14 90589",t[1][:a].value)
|
54
|
+
assert_equal(Workbook::NilValue,t[1][:b].value.class)
|
55
|
+
assert_equal(:covered,t[1][:b].value.reason)
|
56
|
+
assert_equal(2,t[1][:a].colspan)
|
57
|
+
assert_equal(nil,t[1][:c].colspan)
|
58
|
+
assert_equal(2,t["D3"].rowspan)
|
59
|
+
assert_equal(2,t["D5"].rowspan)
|
60
|
+
assert_equal(2,t["D5"].colspan)
|
61
|
+
end
|
50
62
|
end
|
51
63
|
end
|
data/test/test_table.rb
CHANGED
@@ -60,6 +60,12 @@ class TestTable< Test::Unit::TestCase
|
|
60
60
|
assert_equal(t.sheet, s)
|
61
61
|
end
|
62
62
|
|
63
|
+
def test_name
|
64
|
+
t = Workbook::Table.new
|
65
|
+
t.name = "test naam"
|
66
|
+
assert_equal("test naam", t.name)
|
67
|
+
end
|
68
|
+
|
63
69
|
def test_delete_all
|
64
70
|
w = Workbook::Book.new [["a","b"],[1,2],[3,4]]
|
65
71
|
t = w.sheet.table
|
@@ -88,5 +94,32 @@ class TestTable< Test::Unit::TestCase
|
|
88
94
|
assert_equal(3,t[3][:a])
|
89
95
|
assert_equal(5,t2[3][:a])
|
90
96
|
end
|
97
|
+
|
98
|
+
def test_spreadsheet_style_cell_addressing
|
99
|
+
w = Workbook::Book.new [[nil, nil],["a","b"],[1,2],[3,4]]
|
100
|
+
t = w.sheet.table
|
101
|
+
assert_equal(nil,t["A1"].value)
|
102
|
+
assert_equal(nil,t["B1"].value)
|
103
|
+
assert_equal("a",t["A2"].value)
|
104
|
+
assert_equal("b",t["B2"].value)
|
105
|
+
assert_equal(1,t["A3"].value)
|
106
|
+
assert_equal(2,t["B3"].value)
|
107
|
+
assert_equal(3,t["A4"].value)
|
108
|
+
assert_equal(4,t["B4"].value)
|
109
|
+
# t["B4"]="asdf"
|
110
|
+
# assert_equal("asdf",t["B4"].value)
|
111
|
+
|
112
|
+
end
|
91
113
|
|
114
|
+
def test_alpha_index_to_number_index
|
115
|
+
w = Workbook::Book.new
|
116
|
+
t = w.sheet.table
|
117
|
+
assert_equal(0,t.alpha_index_to_number_index("A"))
|
118
|
+
assert_equal(2,t.alpha_index_to_number_index("C"))
|
119
|
+
assert_equal(25,t.alpha_index_to_number_index("Z"))
|
120
|
+
assert_equal(26,t.alpha_index_to_number_index("AA"))
|
121
|
+
assert_equal(27,t.alpha_index_to_number_index("AB"))
|
122
|
+
assert_equal(51,t.alpha_index_to_number_index("AZ"))
|
123
|
+
assert_equal(52,t.alpha_index_to_number_index("BA"))
|
124
|
+
end
|
92
125
|
end
|
@@ -33,5 +33,21 @@ module Writers
|
|
33
33
|
match = html.match(/<td style="background: #f00">a<\/td>/) ? true : false
|
34
34
|
assert_equal(true, match)
|
35
35
|
end
|
36
|
+
def test_sheet_and_table_names
|
37
|
+
b = Workbook::Book.new([['a','b'],[1,2],[3,4]])
|
38
|
+
b.sheet.name = "Sheet name"
|
39
|
+
b.sheet.table.name = "Table name"
|
40
|
+
html = b.to_html
|
41
|
+
assert_equal(true, (html.match(/<h1>Sheet name<\/h1>/) ? true : false) )
|
42
|
+
assert_equal(true, (html.match(/<h2>Table name<\/h2>/) ? true : false) )
|
43
|
+
end
|
44
|
+
def test_col_and_rowspans
|
45
|
+
w = Workbook::Book.new
|
46
|
+
w.open("test/artifacts/sheet_with_combined_cells.ods")
|
47
|
+
html = w.to_html
|
48
|
+
assert_equal(true, (html.match(/rowspan="2">15 nov 11 15 nov 11/) ? true : false) )
|
49
|
+
assert_equal(true, (html.match(/colspan="2" rowspan="2">13 mrt 12 15 mrt 12 13 mrt 12 15 mrt 12/) ? true : false) )
|
50
|
+
assert_equal(true, (html.match(/colspan="2">14 90589/) ? true : false) )
|
51
|
+
end
|
36
52
|
end
|
37
53
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -174,6 +174,7 @@ files:
|
|
174
174
|
- lib/workbook/modules/raw_objects_storage.rb
|
175
175
|
- lib/workbook/modules/table_diff_sort.rb
|
176
176
|
- lib/workbook/modules/type_parser.rb
|
177
|
+
- lib/workbook/nil_value.rb
|
177
178
|
- lib/workbook/readers/csv_reader.rb
|
178
179
|
- lib/workbook/readers/ods_reader.rb
|
179
180
|
- lib/workbook/readers/txt_reader.rb
|
@@ -200,6 +201,7 @@ files:
|
|
200
201
|
- test/artifacts/excel_different_types.xlsx
|
201
202
|
- test/artifacts/failing_import1.xls
|
202
203
|
- test/artifacts/native_xlsx.xlsx
|
204
|
+
- test/artifacts/sheet_with_combined_cells.ods
|
203
205
|
- test/artifacts/sheetduplication.xls
|
204
206
|
- test/artifacts/simple_csv.csv
|
205
207
|
- test/artifacts/simple_excel_csv.csv
|
@@ -265,6 +267,7 @@ test_files:
|
|
265
267
|
- test/artifacts/excel_different_types.xlsx
|
266
268
|
- test/artifacts/failing_import1.xls
|
267
269
|
- test/artifacts/native_xlsx.xlsx
|
270
|
+
- test/artifacts/sheet_with_combined_cells.ods
|
268
271
|
- test/artifacts/sheetduplication.xls
|
269
272
|
- test/artifacts/simple_csv.csv
|
270
273
|
- test/artifacts/simple_excel_csv.csv
|