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 CHANGED
@@ -29,19 +29,19 @@ or
29
29
  Calling
30
30
 
31
31
  s = b.sheet
32
- t = s.table
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
- t = s.sheet.table
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
- # returns <Workbook::Cel @value=2>
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
- t['A2']
76
- # returns <Workbook::Cel @value=1>
78
+
79
+ <!-- Feature *to implement*:
77
80
 
78
81
  Feature *to implement*, get a single column:
79
82
 
data/lib/workbook.rb CHANGED
@@ -3,6 +3,7 @@ $KCODE="u" if RUBY_VERSION < "1.9"
3
3
  require 'workbook/book'
4
4
  require 'workbook/sheet'
5
5
  require 'workbook/table'
6
+ require 'workbook/nil_value'
6
7
  require 'workbook/row'
7
8
  require 'workbook/cell'
8
9
  require 'workbook/format'
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
@@ -7,7 +7,6 @@ module Workbook
7
7
  include Workbook::Modules::RawObjectsStorage
8
8
  alias_method :merge_hash, :merge
9
9
  alias_method :merge_hash!, :merge!
10
-
11
10
  attr_accessor :name, :parent
12
11
 
13
12
  # Initialize
@@ -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
- row = row.xpath("table:table-cell").collect do |cell|
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
- cell_style_name = cell.xpath('@table:style-name').to_s
70
- c.format = self.template.formats[cell_style_name]
71
- valuetype = cell.xpath('@office:value-type').to_s
72
- value = cell.xpath("text:p//text()").to_s
73
- value = value == "" ? nil : value
74
- case valuetype
75
- when 'float'
76
- value = cell.xpath("@office:value").to_s.to_f
77
- when 'integer'
78
- value = cell.xpath("@office:value").to_s.to_i
79
- when 'date'
80
- value = DateTime.parse(cell.xpath("@office:date-value").to_s)
81
- end
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 << Workbook::Row.new(row)
101
+ t << r
86
102
  end
87
103
  end
88
104
  end
@@ -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
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Workbook
3
- VERSION = '0.4'
3
+ VERSION = '0.4.1'
4
4
  end
@@ -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
- doc.td(td_options) {
30
- doc.text cell.value
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
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[0..4])
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("sadfasdfsd", w.sheet.table[4][2].value)
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: '0.4'
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