workbook 0.4 → 0.4.1
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.
- 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
|