jruby-poi 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +18 -4
  4. data/Gemfile +3 -6
  5. data/Gemfile.lock +20 -27
  6. data/README.markdown +49 -47
  7. data/Rakefile +20 -27
  8. data/jruby-poi.gemspec +7 -70
  9. data/lib/poi-jars/lib/commons-codec-1.10.jar +0 -0
  10. data/lib/poi-jars/lib/commons-collections4-4.1.jar +0 -0
  11. data/lib/poi-jars/lib/commons-logging-1.2.jar +0 -0
  12. data/lib/poi-jars/lib/junit-4.12.jar +0 -0
  13. data/lib/poi-jars/lib/log4j-1.2.17.jar +0 -0
  14. data/lib/poi-jars/ooxml-lib/curvesapi-1.04.jar +0 -0
  15. data/lib/{ooxml-lib/xmlbeans-2.3.0.jar → poi-jars/ooxml-lib/xmlbeans-2.6.0.jar} +0 -0
  16. data/lib/poi-jars/poi-3.15.jar +0 -0
  17. data/lib/poi-jars/poi-examples-3.15.jar +0 -0
  18. data/lib/poi-jars/poi-excelant-3.15.jar +0 -0
  19. data/lib/poi-jars/poi-ooxml-3.15.jar +0 -0
  20. data/lib/poi-jars/poi-ooxml-schemas-3.15.jar +0 -0
  21. data/lib/poi-jars/poi-scratchpad-3.15.jar +0 -0
  22. data/lib/poi.rb +13 -7
  23. data/lib/poi/version.rb +11 -0
  24. data/lib/poi/workbook/area.rb +20 -17
  25. data/lib/poi/workbook/cell.rb +28 -42
  26. data/lib/poi/workbook/row.rb +1 -1
  27. data/lib/poi/workbook/workbook.rb +3 -7
  28. data/lib/poi/workbook/worksheet.rb +2 -2
  29. data/spec/data/1904_window_dates.xls +0 -0
  30. data/spec/data/various_samples.xlsx +0 -0
  31. data/spec/facade_spec.rb +35 -35
  32. data/spec/spec_helper.rb +10 -0
  33. data/spec/support/matchers/cell_matcher.rb +3 -3
  34. data/spec/workbook_spec.rb +368 -385
  35. data/spec/writing_spec.rb +144 -144
  36. metadata +111 -121
  37. data/VERSION +0 -1
  38. data/lib/ooxml-lib/dom4j-1.6.1.jar +0 -0
  39. data/lib/ooxml-lib/stax-api-1.0.1.jar +0 -0
  40. data/lib/poi-3.8-20120326.jar +0 -0
  41. data/lib/poi-examples-3.8-20120326.jar +0 -0
  42. data/lib/poi-excelant-3.8-20120326.jar +0 -0
  43. data/lib/poi-ooxml-3.8-20120326.jar +0 -0
  44. data/lib/poi-ooxml-schemas-3.8-20120326.jar +0 -0
  45. data/lib/poi-scratchpad-3.8-20120326.jar +0 -0
@@ -1,3 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'date'
1
3
  module POI
2
4
  class Cells
3
5
  include Enumerable
@@ -9,7 +11,7 @@ module POI
9
11
  end
10
12
 
11
13
  def [](index)
12
- @cells[index] ||= Cell.new(@poi_row.cell(index) || @poi_row.create_cell(index), @row)
14
+ @cells[index] ||= Cell.new(@poi_row.get_cell(index) || @poi_row.create_cell(index), @row)
13
15
  end
14
16
 
15
17
  def size
@@ -52,8 +54,8 @@ module POI
52
54
  elsif poi_cell.cell_type == CELL_TYPE_FORMULA &&
53
55
  poi_cell.cached_formula_result_type == CELL_TYPE_ERROR
54
56
 
55
- # breaks Law of Demeter by reaching into the Row's Worksheet, but it makes sense to do in this case
56
- value_of(@row.worksheet.workbook.formula_evaluator.evaluate(poi_cell))
57
+ cell_value = formula_evaluator.evaluate(poi_cell)
58
+ cell_value && error_value_from(cell_value.error_value)
57
59
  else
58
60
  nil
59
61
  end
@@ -66,7 +68,7 @@ module POI
66
68
 
67
69
  def value
68
70
  return nil if poi_cell.nil?
69
- value_of(cell_value_for_type(poi_cell.cell_type))
71
+ cast_value
70
72
  end
71
73
 
72
74
  def formula= new_value
@@ -124,51 +126,35 @@ module POI
124
126
  end
125
127
 
126
128
  private
127
- def value_of(cell_value)
128
- return nil if cell_value.nil?
129
-
130
- case cell_value.cell_type
129
+ def cast_value(type = cell_type)
130
+ case type
131
131
  when CELL_TYPE_BLANK then nil
132
- when CELL_TYPE_BOOLEAN then cell_value.boolean_value
133
- when CELL_TYPE_ERROR then error_value_from(cell_value.error_value)
134
- when CELL_TYPE_NUMERIC then numeric_value_from(cell_value)
135
- when CELL_TYPE_STRING then cell_value.string_value
136
- else
137
- raise "unhandled cell type[#{cell_value.cell_type}]"
138
- end
139
- end
140
-
141
- def cell_value_for_type(cell_type)
142
- return nil if cell_type.nil?
143
- begin
144
- case cell_type
145
- when CELL_TYPE_BLANK then nil
146
- when CELL_TYPE_BOOLEAN then CELL_VALUE.value_of(poi_cell.boolean_cell_value)
147
- when CELL_TYPE_FORMULA then cell_value_for_type(poi_cell.cached_formula_result_type)
148
- when CELL_TYPE_STRING then CELL_VALUE.new(poi_cell.string_cell_value)
149
- when CELL_TYPE_ERROR, CELL_TYPE_NUMERIC then CELL_VALUE.new(poi_cell.numeric_cell_value)
132
+ when CELL_TYPE_BOOLEAN then get_boolean_cell_value
133
+ when CELL_TYPE_ERROR then nil
134
+ when CELL_TYPE_FORMULA then cast_value(poi_cell.cached_formula_result_type)
135
+ when CELL_TYPE_STRING then get_string_cell_value
136
+ when CELL_TYPE_NUMERIC
137
+ if DATE_UTIL.cell_date_formatted(poi_cell)
138
+ DateTime.parse(get_date_cell_value.to_s)
150
139
  else
151
- raise "unhandled cell type[#{poi_cell.cell_type}]"
140
+ get_numeric_cell_value
152
141
  end
153
- rescue
154
- nil
142
+ else
143
+ raise "unhandled cell type[#{type}]"
155
144
  end
156
145
  end
157
-
158
- def error_value_from(cell_value)
159
- org.apache.poi.ss.usermodel.ErrorConstants.text(cell_value)
146
+
147
+ def workbook
148
+ @row.worksheet.workbook
160
149
  end
161
-
162
- def formula_evaluator_for(workbook)
163
- workbook.creation_helper.create_formula_evaluator
150
+
151
+ def formula_evaluator
152
+ workbook.formula_evaluator
164
153
  end
165
-
166
- def numeric_value_from(cell_value)
167
- if DATE_UTIL.cell_date_formatted(poi_cell)
168
- Date.parse(DATE_UTIL.get_java_date(cell_value.number_value).to_s)
169
- else
170
- cell_value.number_value
171
- end
154
+
155
+ def error_value_from(cell_value)
156
+ #org.apache.poi.ss.usermodel.ErrorConstants.get_text(cell_value)
157
+ org.apache.poi.ss.usermodel.FormulaError.forInt(cell_value).getString
172
158
  end
173
159
  end
174
160
  end
@@ -9,7 +9,7 @@ module POI
9
9
  end
10
10
 
11
11
  def [](index)
12
- @rows[index] ||= Row.new(@poi_worksheet.row(index) || @poi_worksheet.create_row(index), @worksheet)
12
+ @rows[index] ||= Row.new(@poi_worksheet.get_row(index) || @poi_worksheet.create_row(index), @worksheet)
13
13
  end
14
14
 
15
15
  def size
@@ -132,7 +132,7 @@ module POI
132
132
 
133
133
  def named_ranges
134
134
  @named_ranges ||= (0...@workbook.number_of_names).collect do | idx |
135
- NamedRange.new @workbook.name_at(idx), self
135
+ NamedRange.new @workbook.get_name_at(idx), self
136
136
  end
137
137
  end
138
138
 
@@ -208,7 +208,7 @@ module POI
208
208
  end
209
209
 
210
210
  def cells_in_area reference
211
- area = Area.new(reference)
211
+ area = Area.new(reference, self.get_spreadsheet_version)
212
212
  area.in(self)
213
213
  end
214
214
 
@@ -242,13 +242,9 @@ module POI
242
242
  area_start = "#{sheet_parts.first}!#{area_parts.first}"
243
243
  area_end = area_parts.last
244
244
 
245
- area = org.apache.poi.ss.util.AreaReference.getWholeColumn(area_start, area_end)
245
+ area = AREA_REF.getWholeColumn(get_worksheet_version, area_start, area_end)
246
246
  full_ref = "#{area.first_cell.format_as_string}:#{area.last_cell.format_as_string}"
247
247
  Area.new(full_ref).in(self)
248
- # cell_reference = org.apache.poi.ss.util.CellReference.new( reference + "1" )
249
- # column = cell_reference.get_col
250
- # sheet = cell_reference.get_sheet_name
251
- # worksheets[sheet].rows.collect{|row| row[column]}
252
248
  end
253
249
 
254
250
  private
@@ -10,7 +10,7 @@ module POI
10
10
  def [](index_or_name)
11
11
  worksheet = case
12
12
  when index_or_name.kind_of?(Numeric)
13
- @poi_workbook.sheet_at(index_or_name) || @poi_workbook.create_sheet
13
+ @poi_workbook.get_sheet_at(index_or_name) || @poi_workbook.create_sheet
14
14
  else
15
15
  @poi_workbook.get_sheet(index_or_name) || @poi_workbook.create_sheet(index_or_name)
16
16
  end
@@ -22,7 +22,7 @@ module POI
22
22
  end
23
23
 
24
24
  def each
25
- (0...size).each { |i| yield Worksheet.new(@poi_workbook.sheet_at(i), @workbook) }
25
+ (0...size).each { |i| yield Worksheet.new(@poi_workbook.get_sheet_at(i), @workbook) }
26
26
  end
27
27
  end
28
28
 
@@ -4,45 +4,45 @@ describe "POI.Facade" do
4
4
  it "should create specialized methods for boolean methods, getters, and setters" do
5
5
  book = POI::Workbook.create('foo.xlsx')
6
6
  sheet = book.create_sheet
7
- sheet.respond_to?(:column_broken?).should be_true
8
- sheet.respond_to?(:column_hidden?).should be_true
9
- sheet.respond_to?(:display_formulas?).should be_true
10
- sheet.respond_to?(:display_gridlines?).should be_true
11
- sheet.respond_to?(:selected?).should be_true
12
- sheet.respond_to?(:column_breaks).should be_true
13
- sheet.respond_to?(:column_break=).should be_true
14
- sheet.respond_to?(:autobreaks?).should be_true
15
- sheet.respond_to?(:autobreaks=).should be_true
16
- sheet.respond_to?(:autobreaks!).should be_true
17
- sheet.respond_to?(:column_broken?).should be_true
18
- sheet.respond_to?(:fit_to_page).should be_true
19
- sheet.respond_to?(:fit_to_page?).should be_true
20
- sheet.respond_to?(:fit_to_page=).should be_true
21
- sheet.respond_to?(:fit_to_page!).should be_true
7
+ sheet.respond_to?(:column_broken?).should be true
8
+ sheet.respond_to?(:column_hidden?).should be true
9
+ sheet.respond_to?(:display_formulas?).should be true
10
+ sheet.respond_to?(:display_gridlines?).should be true
11
+ sheet.respond_to?(:selected?).should be true
12
+ sheet.respond_to?(:column_breaks).should be true
13
+ sheet.respond_to?(:column_break=).should be true
14
+ sheet.respond_to?(:autobreaks?).should be true
15
+ sheet.respond_to?(:autobreaks=).should be true
16
+ sheet.respond_to?(:autobreaks!).should be true
17
+ sheet.respond_to?(:column_broken?).should be true
18
+ sheet.respond_to?(:fit_to_page).should be true
19
+ sheet.respond_to?(:fit_to_page?).should be true
20
+ sheet.respond_to?(:fit_to_page=).should be true
21
+ sheet.respond_to?(:fit_to_page!).should be true
22
22
 
23
- sheet.respond_to?(:array_formula).should_not be_true
24
- sheet.respond_to?(:array_formula=).should_not be_true
23
+ sheet.respond_to?(:array_formula).should_not be true
24
+ sheet.respond_to?(:array_formula=).should_not be true
25
25
 
26
26
  row = sheet[0]
27
- row.respond_to?(:first_cell_num).should be_true
28
- row.respond_to?(:height).should be_true
29
- row.respond_to?(:height=).should be_true
30
- row.respond_to?(:height_in_points).should be_true
31
- row.respond_to?(:height_in_points=).should be_true
32
- row.respond_to?(:zero_height?).should be_true
33
- row.respond_to?(:zero_height!).should be_true
34
- row.respond_to?(:zero_height=).should be_true
27
+ row.respond_to?(:first_cell_num).should be true
28
+ row.respond_to?(:height).should be true
29
+ row.respond_to?(:height=).should be true
30
+ row.respond_to?(:height_in_points).should be true
31
+ row.respond_to?(:height_in_points=).should be true
32
+ row.respond_to?(:zero_height?).should be true
33
+ row.respond_to?(:zero_height!).should be true
34
+ row.respond_to?(:zero_height=).should be true
35
35
 
36
36
  cell = row[0]
37
- cell.respond_to?(:boolean_cell_value).should be_true
38
- cell.respond_to?(:boolean_cell_value?).should be_true
39
- cell.respond_to?(:cached_formula_result_type).should be_true
40
- cell.respond_to?(:cell_error_value=).should be_true
41
- cell.respond_to?(:cell_value=).should be_true
42
- cell.respond_to?(:hyperlink=).should be_true
43
- cell.respond_to?(:cell_value!).should be_true
44
- cell.respond_to?(:remove_cell_comment!).should be_true
45
- cell.respond_to?(:cell_style).should be_true
46
- cell.respond_to?(:cell_style=).should be_true
37
+ cell.respond_to?(:boolean_cell_value).should be true
38
+ cell.respond_to?(:boolean_cell_value?).should be true
39
+ cell.respond_to?(:cached_formula_result_type).should be true
40
+ cell.respond_to?(:cell_error_value=).should be true
41
+ cell.respond_to?(:cell_value=).should be true
42
+ cell.respond_to?(:hyperlink=).should be true
43
+ cell.respond_to?(:cell_value!).should be true
44
+ cell.respond_to?(:remove_cell_comment!).should be true
45
+ cell.respond_to?(:cell_style).should be true
46
+ cell.respond_to?(:cell_style=).should be true
47
47
  end
48
48
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.configure do |c|
4
4
  c.filter_run_excluding :unimplemented => true
5
+ c.expect_with(:rspec) { |expect_config| expect_config.syntax = :should }
5
6
  end
6
7
 
7
8
  require File.expand_path('../lib/poi', File.dirname(__FILE__))
@@ -14,3 +15,12 @@ class TestDataFile
14
15
  File.expand_path(File.join(File.dirname(__FILE__), 'data', name))
15
16
  end
16
17
  end
18
+
19
+ if RUBY_VERSION < '1.9'
20
+ class DateTime
21
+ def to_date
22
+ Date.parse(self.strftime('%Y-%m-%d'))
23
+ end
24
+ end
25
+ end
26
+
@@ -3,15 +3,15 @@ RSpec::Matchers.define :equal_at_cell do |expected, row, col|
3
3
  actual == expected
4
4
  end
5
5
 
6
- failure_message_for_should do |actual|
6
+ failure_message do |actual|
7
7
  "expected #{actual} to equal #{expected} (row:#{row}, cell:#{col})"
8
8
  end
9
9
 
10
- failure_message_for_should_not do |actual|
10
+ failure_message_when_negated do |actual|
11
11
  "expected #{actual} not to equal #{expected} (row:#{row}, cell:#{col})"
12
12
  end
13
13
 
14
14
  description do
15
15
  "to equal #{expected}"
16
16
  end
17
- end
17
+ end
@@ -1,385 +1,368 @@
1
- require 'spec_helper'
2
-
3
- require 'date'
4
- require 'stringio'
5
-
6
- describe POI::Workbook do
7
- it "should open a workbook and allow access to its worksheets" do
8
- name = TestDataFile.expand_path("various_samples.xlsx")
9
- book = POI::Workbook.open(name)
10
- book.worksheets.size.should == 5
11
- book.filename.should == name
12
- end
13
-
14
- it "should be able to create a Workbook from an IO object" do
15
- content = StringIO.new(open(TestDataFile.expand_path("various_samples.xlsx"), 'rb'){|f| f.read})
16
- book = POI::Workbook.open(content)
17
- book.worksheets.size.should == 5
18
- book.filename.should =~ /spreadsheet.xlsx$/
19
- end
20
-
21
- it "should be able to create a Workbook from a Java input stream" do
22
- content = java.io.FileInputStream.new(TestDataFile.expand_path("various_samples.xlsx"))
23
- book = POI::Workbook.open(content)
24
- book.worksheets.size.should == 5
25
- book.filename.should =~ /spreadsheet.xlsx$/
26
- end
27
-
28
- it "should create an HSSFWorkbook when passed a :format => :hssf option" do
29
- book = POI::Workbook.create('test.xls', :format => :hssf)
30
- book.poi_workbook.should be_kind_of(org.apache.poi.hssf.usermodel.HSSFWorkbook)
31
- end
32
-
33
- it "should create an XSSFWorkbook when passed a :format => :xssf option" do
34
- book = POI::Workbook.create('test.xlsx', :format => :xssf)
35
- book.poi_workbook.should be_kind_of(org.apache.poi.xssf.usermodel.XSSFWorkbook)
36
- end
37
-
38
- it "should create an XSSFWorkbook by default" do
39
- book = POI::Workbook.create('test.xlsx')
40
- book.poi_workbook.should be_kind_of(org.apache.poi.xssf.usermodel.XSSFWorkbook)
41
- end
42
-
43
- it "should return a column of cells by reference" do
44
- name = TestDataFile.expand_path("various_samples.xlsx")
45
- book = POI::Workbook.open(name)
46
- book["numbers!$A"].should == book['numbers'].rows.collect{|e| e[0].value}
47
- book["numbers!A"].should == book['numbers'].rows.collect{|e| e[0].value}
48
- book["numbers!C"].should == book['numbers'].rows.collect{|e| e[2].value}
49
- book["numbers!$D:$D"].should == book['numbers'].rows.collect{|e| e[3].value}
50
- book["numbers!$c:$D"].should == {"C" => book['numbers'].rows.collect{|e| e[2].value}, "D" => book['numbers'].rows.collect{|e| e[3].value}}
51
- end
52
-
53
- it "should return cells by reference" do
54
- name = TestDataFile.expand_path("various_samples.xlsx")
55
- book = POI::Workbook.open(name)
56
- book.cell("numbers!A1").value.should == 'NUM'
57
- book.cell("numbers!A2").to_s.should == '1.0'
58
- book.cell("numbers!A3").to_s.should == '2.0'
59
- book.cell("numbers!A4").to_s.should == '3.0'
60
-
61
- book.cell("numbers!A10").to_s.should == '9.0'
62
- book.cell("numbers!B10").to_s.should == '81.0'
63
- book.cell("numbers!C10").to_s.should == '729.0'
64
- book.cell("numbers!D10").to_s.should == '3.0'
65
-
66
- book.cell("text & pic!A10").value.should == 'This is an Excel XLSX workbook.'
67
- book.cell("bools & errors!B3").value.should == true
68
- book.cell("high refs!AM619").value.should == 'This is some text'
69
- book.cell("high refs!AO624").value.should == 24.0
70
- book.cell("high refs!AP631").value.should == 13.0
71
-
72
- book.cell(%Q{'text & pic'!A10}).value.should == 'This is an Excel XLSX workbook.'
73
- book.cell(%Q{'bools & errors'!B3}).value.should == true
74
- book.cell(%Q{'high refs'!AM619}).value.should == 'This is some text'
75
- book.cell(%Q{'high refs'!AO624}).value.should == 24.0
76
- book.cell(%Q{'high refs'!AP631}).value.should == 13.0
77
- end
78
-
79
- it "should handle named cell ranges" do
80
- name = TestDataFile.expand_path("various_samples.xlsx")
81
- book = POI::Workbook.open(name)
82
-
83
- book.named_ranges.length.should == 3
84
- book.named_ranges.collect{|e| e.name}.should == %w{four_times_six NAMES nums}
85
- book.named_ranges.collect{|e| e.sheet.name}.should == ['high refs', 'bools & errors', 'high refs']
86
- book.named_ranges.collect{|e| e.formula}.should == ["'high refs'!$AO$624", "'bools & errors'!$D$2:$D$11", "'high refs'!$AP$619:$AP$631"]
87
- book['four_times_six'].should == 24.0
88
- book['nums'].should == (1..13).collect{|e| e * 1.0}
89
-
90
- # NAMES is a range of empty cells
91
- book['NAMES'].should == [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
92
- book.cell('NAMES').each do | cell |
93
- cell.value.should be_nil
94
- cell.poi_cell.should_not be_nil
95
- cell.to_s.should be_empty
96
- end
97
- end
98
-
99
- it "should return an array of cell values by reference" do
100
- name = TestDataFile.expand_path("various_samples.xlsx")
101
- book = POI::Workbook.open(name)
102
- book['dates!A2:A16'].should == (Date.parse('2010-02-28')..Date.parse('2010-03-14')).to_a
103
- end
104
-
105
- it "should return cell values by reference" do
106
- name = TestDataFile.expand_path("various_samples.xlsx")
107
- book = POI::Workbook.open(name)
108
-
109
- book['text & pic!A10'].should == 'This is an Excel XLSX workbook.'
110
- book['bools & errors!B3'].should == true
111
- book['high refs!AM619'].should == 'This is some text'
112
- book['high refs!AO624'].should == 24.0
113
- book['high refs!AP631'].should == 13.0
114
- end
115
- end
116
-
117
- describe POI::Worksheets do
118
- it "should allow indexing worksheets by ordinal" do
119
- name = TestDataFile.expand_path("various_samples.xlsx")
120
- book = POI::Workbook.open(name)
121
-
122
- book.worksheets[0].name.should == "text & pic"
123
- book.worksheets[1].name.should == "numbers"
124
- book.worksheets[2].name.should == "dates"
125
- book.worksheets[3].name.should == "bools & errors"
126
- end
127
-
128
- it "should allow indexing worksheets by name" do
129
- name = TestDataFile.expand_path("various_samples.xlsx")
130
- book = POI::Workbook.open(name)
131
-
132
- book.worksheets["text & pic"].name.should == "text & pic"
133
- book.worksheets["numbers"].name.should == "numbers"
134
- book.worksheets["dates"].name.should == "dates"
135
- end
136
-
137
- it "should be enumerable" do
138
- name = TestDataFile.expand_path("various_samples.xlsx")
139
- book = POI::Workbook.open(name)
140
- book.worksheets.should be_kind_of Enumerable
141
-
142
- book.worksheets.each do |sheet|
143
- sheet.should be_kind_of POI::Worksheet
144
- end
145
-
146
- book.worksheets.count.should == 5
147
- book.worksheets.collect.count.should == 5
148
- end
149
-
150
- it "returns cells when passing a cell reference" do
151
- name = TestDataFile.expand_path("various_samples.xlsx")
152
- book = POI::Workbook.open(name)
153
- book['dates']['A2'].to_s.should == '2010-02-28'
154
- book['dates']['a2'].to_s.should == '2010-02-28'
155
- book['dates']['B2'].to_s.should == '2010-03-14'
156
- book['dates']['b2'].to_s.should == '2010-03-14'
157
- book['dates']['C2'].to_s.should == '2010-03-28'
158
- book['dates']['c2'].to_s.should == '2010-03-28'
159
- end
160
- end
161
-
162
- describe POI::Rows do
163
- it "should be enumerable" do
164
- name = TestDataFile.expand_path("various_samples.xlsx")
165
- book = POI::Workbook.open(name)
166
- sheet = book.worksheets["text & pic"]
167
- sheet.rows.should be_kind_of Enumerable
168
-
169
- sheet.rows.each do |row|
170
- row.should be_kind_of POI::Row
171
- end
172
-
173
- sheet.rows.count.should == 7
174
- sheet.rows.collect.count.should == 7
175
- end
176
- end
177
-
178
- describe POI::Cells do
179
- before :each do
180
- @name = TestDataFile.expand_path("various_samples.xlsx")
181
- @book = POI::Workbook.open(@name)
182
- end
183
-
184
- def book
185
- @book
186
- end
187
-
188
- def name
189
- @name
190
- end
191
-
192
- it "should be enumerable" do
193
- sheet = book.worksheets["text & pic"]
194
- rows = sheet.rows
195
- cells = rows[0].cells
196
-
197
- cells.should be_kind_of Enumerable
198
- cells.count.should == 1
199
- cells.collect.count.should == 1
200
- end
201
- end
202
-
203
- describe POI::Cell do
204
- before :each do
205
- @name = TestDataFile.expand_path("various_samples.xlsx")
206
- @book = POI::Workbook.open(@name)
207
- end
208
-
209
- def book
210
- @book
211
- end
212
-
213
- def name
214
- @name
215
- end
216
-
217
- it "should provide dates for date cells" do
218
- sheet = book.worksheets["dates"]
219
- rows = sheet.rows
220
-
221
- dates_by_column = [
222
- (Date.parse('2010-02-28')..Date.parse('2010-03-14')),
223
- (Date.parse('2010-03-14')..Date.parse('2010-03-28')),
224
- (Date.parse('2010-03-28')..Date.parse('2010-04-11'))]
225
- (0..2).each do |col|
226
- dates_by_column[col].each_with_index do |date, index|
227
- row = index + 1
228
- rows[row][col].value.should equal_at_cell(date, row, col)
229
- end
230
- end
231
- end
232
-
233
- it "should provide numbers for numeric cells" do
234
- sheet = book.worksheets["numbers"]
235
- rows = sheet.rows
236
-
237
- (1..15).each do |number|
238
- row = number
239
- rows[row][0].value.should equal_at_cell(number, row, 0)
240
- rows[row][1].value.should equal_at_cell(number ** 2, row, 1)
241
- rows[row][2].value.should equal_at_cell(number ** 3, row, 2)
242
- rows[row][3].value.should equal_at_cell(Math.sqrt(number), row, 3)
243
- end
244
-
245
- rows[9][0].to_s.should == '9.0'
246
- rows[9][1].to_s.should == '81.0'
247
- rows[9][2].to_s.should == '729.0'
248
- rows[9][3].to_s.should == '3.0'
249
- end
250
-
251
- it "should handle array access from the workbook down to cells" do
252
- book[1][9][0].to_s.should == '9.0'
253
- book[1][9][1].to_s.should == '81.0'
254
- book[1][9][2].to_s.should == '729.0'
255
- book[1][9][3].to_s.should == '3.0'
256
-
257
- book["numbers"][9][0].to_s.should == '9.0'
258
- book["numbers"][9][1].to_s.should == '81.0'
259
- book["numbers"][9][2].to_s.should == '729.0'
260
- book["numbers"][9][3].to_s.should == '3.0'
261
- end
262
-
263
- it "should provide error text for error cells" do
264
- sheet = book.worksheets["bools & errors"]
265
- rows = sheet.rows
266
-
267
- rows[6][0].value.should == 0.0 #'~CIRCULAR~REF~'
268
- rows[6][0].error_value.should be_nil
269
-
270
- rows[7][0].value.should be_nil
271
- rows[7][0].error_value.should == '#DIV/0!'
272
-
273
- rows[8][0].value.should be_nil
274
- rows[8][0].error_value.should == '#N/A'
275
-
276
- rows[9][0].value.should be_nil
277
- rows[9][0].error_value.should == '#NAME?'
278
-
279
- rows[10][0].value.should be_nil
280
- rows[10][0].error_value.should == '#NULL!'
281
-
282
- rows[11][0].value.should be_nil
283
- rows[11][0].error_value.should == '#NUM!'
284
-
285
- rows[12][0].value.should be_nil
286
- rows[12][0].error_value.should == '#REF!'
287
-
288
- rows[13][0].value.should be_nil
289
- rows[13][0].error_value.should == '#VALUE!'
290
-
291
- lambda{ rows[14][0].value }.should_not raise_error(Java::java.lang.RuntimeException)
292
-
293
- rows[6][0].to_s.should == '0.0' #'~CIRCULAR~REF~'
294
- rows[7][0].to_s.should == '' #'#DIV/0!'
295
- rows[8][0].to_s.should == '' #'#N/A'
296
- rows[9][0].to_s.should == '' #'#NAME?'
297
- rows[10][0].to_s.should == '' #'#NULL!'
298
- rows[11][0].to_s.should == '' #'#NUM!'
299
- rows[12][0].to_s.should == '' #'#REF!'
300
- rows[13][0].to_s.should == '' #'#VALUE!'
301
- rows[14][0].to_s.should == ''
302
- end
303
-
304
- it "should provide booleans for boolean cells" do
305
- sheet = book.worksheets["bools & errors"]
306
- rows = sheet.rows
307
- rows[1][0].value.should == false
308
- rows[1][0].to_s.should == 'false'
309
-
310
- rows[1][1].value.should == false
311
- rows[1][1].to_s.should == 'false'
312
-
313
- rows[2][0].value.should == true
314
- rows[2][0].to_s.should == 'true'
315
-
316
- rows[2][1].value.should == true
317
- rows[2][1].to_s.should == 'true'
318
- end
319
-
320
- it "should provide the cell value as a string" do
321
- sheet = book.worksheets["text & pic"]
322
- rows = sheet.rows
323
-
324
- rows[0][0].value.should == "This"
325
- rows[1][0].value.should == "is"
326
- rows[2][0].value.should == "an"
327
- rows[3][0].value.should == "Excel"
328
- rows[4][0].value.should == "XLSX"
329
- rows[5][0].value.should == "workbook"
330
- rows[9][0].value.should == 'This is an Excel XLSX workbook.'
331
-
332
-
333
- rows[0][0].to_s.should == "This"
334
- rows[1][0].to_s.should == "is"
335
- rows[2][0].to_s.should == "an"
336
- rows[3][0].to_s.should == "Excel"
337
- rows[4][0].to_s.should == "XLSX"
338
- rows[5][0].to_s.should == "workbook"
339
- rows[9][0].to_s.should == 'This is an Excel XLSX workbook.'
340
- end
341
-
342
- it "should provide formulas instead of string-ified values" do
343
- sheet = book.worksheets["numbers"]
344
- rows = sheet.rows
345
-
346
- (1..15).each do |number|
347
- row = number
348
- rows[row][0].to_s(false).should == "#{number}.0"
349
- rows[row][1].to_s(false).should == "A#{row + 1}*A#{row + 1}"
350
- rows[row][2].to_s(false).should == "B#{row + 1}*A#{row + 1}"
351
- rows[row][3].to_s(false).should == "SQRT(A#{row + 1})"
352
- end
353
-
354
- sheet = book.worksheets["bools & errors"]
355
- rows = sheet.rows
356
- rows[1][0].to_s(false).should == '1=2'
357
- rows[1][1].to_s(false).should == 'FALSE'
358
- rows[2][0].to_s(false).should == '1=1'
359
- rows[2][1].to_s(false).should == 'TRUE'
360
- rows[14][0].to_s(false).should == 'foobar(1)'
361
-
362
- sheet = book.worksheets["text & pic"]
363
- sheet.rows[9][0].to_s(false).should == 'CONCATENATE(A1," ", A2," ", A3," ", A4," ", A5," ", A6,".")'
364
- end
365
-
366
- it "should handle getting values out of 'non-existent' cells" do
367
- sheet = book.worksheets["bools & errors"]
368
- sheet.rows[14][2].value.should be_nil
369
- end
370
-
371
- it "should notify the workbook that I have been updated" do
372
- book['dates!A10'].to_s.should == '2010-03-08'
373
- book['dates!A16'].to_s.should == '2010-03-14'
374
- book['dates!B2'].to_s.should == '2010-03-14'
375
-
376
- cell = book.cell('dates!B2')
377
- cell.formula.should == 'A16'
378
-
379
- cell.formula = 'A10 + 1'
380
- book.cell('dates!B2').poi_cell.should === cell.poi_cell
381
- book.cell('dates!B2').formula.should == 'A10 + 1'
382
-
383
- book['dates!B2'].to_s.should == '2010-03-09'
384
- end
385
- end
1
+ require 'spec_helper'
2
+
3
+ require 'date'
4
+ require 'stringio'
5
+
6
+ VARIOUS_SAMPLES_PATH = TestDataFile.expand_path("various_samples.xlsx")
7
+ DATES_1904_WINDOW_XLS_PATH = TestDataFile.expand_path("1904_window_dates.xls")
8
+
9
+ describe POI::Workbook do
10
+ it "should open a workbook and allow access to its worksheets" do
11
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
12
+ book.worksheets.size.should == 6
13
+ book.filename.should == VARIOUS_SAMPLES_PATH
14
+ end
15
+
16
+ it "should be able to create a Workbook from an IO object" do
17
+ content = StringIO.new(open(VARIOUS_SAMPLES_PATH, 'rb'){|f| f.read})
18
+ book = POI::Workbook.open(content)
19
+ book.worksheets.size.should == 6
20
+ book.filename.should =~ /spreadsheet.xlsx$/
21
+ end
22
+
23
+ it "should be able to create a Workbook from a Java input stream" do
24
+ content = java.io.FileInputStream.new(VARIOUS_SAMPLES_PATH)
25
+ book = POI::Workbook.open(content)
26
+ book.worksheets.size.should == 6
27
+ book.filename.should =~ /spreadsheet.xlsx$/
28
+ end
29
+
30
+ it "should create an HSSFWorkbook when passed a :format => :hssf option" do
31
+ book = POI::Workbook.create('test.xls', :format => :hssf)
32
+ book.poi_workbook.should be_kind_of(org.apache.poi.hssf.usermodel.HSSFWorkbook)
33
+ end
34
+
35
+ it "should create an XSSFWorkbook when passed a :format => :xssf option" do
36
+ book = POI::Workbook.create('test.xlsx', :format => :xssf)
37
+ book.poi_workbook.should be_kind_of(org.apache.poi.xssf.usermodel.XSSFWorkbook)
38
+ end
39
+
40
+ it "should create an XSSFWorkbook by default" do
41
+ book = POI::Workbook.create('test.xlsx')
42
+ book.poi_workbook.should be_kind_of(org.apache.poi.xssf.usermodel.XSSFWorkbook)
43
+ end
44
+
45
+ it "should return a column of cells by reference" do
46
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
47
+
48
+ book["numbers!$A"].should == book['numbers'].rows.collect{|e| e[0].value}
49
+ book["numbers!A"].should == book['numbers'].rows.collect{|e| e[0].value}
50
+ book["numbers!C"].should == book['numbers'].rows.collect{|e| e[2].value}
51
+ book["numbers!$D:$D"].should == book['numbers'].rows.collect{|e| e[3].value}
52
+ book["numbers!$c:$D"].should == {"C" => book['numbers'].rows.collect{|e| e[2].value}, "D" => book['numbers'].rows.collect{|e| e[3].value}}
53
+ end
54
+
55
+ it "should return cells by reference" do
56
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
57
+ book.cell("numbers!A1").value.should == 'NUM'
58
+ book.cell("numbers!A2").to_s.should == '1.0'
59
+ book.cell("numbers!A3").to_s.should == '2.0'
60
+ book.cell("numbers!A4").to_s.should == '3.0'
61
+
62
+ book.cell("numbers!A10").to_s.should == '9.0'
63
+ book.cell("numbers!B10").to_s.should == '81.0'
64
+ book.cell("numbers!C10").to_s.should == '729.0'
65
+ book.cell("numbers!D10").to_s.should == '3.0'
66
+
67
+ book.cell(%Q{'text & pic'!A10}).value.should == 'This is an Excel XLSX workbook.'
68
+ book.cell(%Q{'bools & errors'!B3}).value.should == true
69
+ book.cell(%Q{'high refs'!AM619}).value.should == 'This is some text'
70
+ book.cell(%Q{'high refs'!AO624}).value.should == 24.0
71
+ book.cell(%Q{'high refs'!AP631}).value.should == 13.0
72
+ end
73
+
74
+ it "should handle named cell ranges" do
75
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
76
+
77
+ book.named_ranges.length.should == 3
78
+ book.named_ranges.collect{|e| e.name}.should == %w{four_times_six NAMES nums}
79
+ book.named_ranges.collect{|e| e.sheet.name}.should == ['high refs', 'bools & errors', 'high refs']
80
+ book.named_ranges.collect{|e| e.formula}.should == ["'high refs'!$AO$624", "'bools & errors'!$D$2:$D$11", "'high refs'!$AP$619:$AP$631"]
81
+ book['four_times_six'].should == 24.0
82
+ book['nums'].should == (1..13).collect{|e| e * 1.0}
83
+
84
+ # NAMES is a range of empty cells
85
+ book['NAMES'].should == [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
86
+ book.cell('NAMES').each do | cell |
87
+ cell.value.should be_nil
88
+ cell.poi_cell.should_not be_nil
89
+ cell.to_s.should be_empty
90
+ end
91
+ end
92
+
93
+ it "should return an array of cell values by reference" do
94
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
95
+ book['dates!A2:A16'].map(&:to_date).should == (Date.parse('2010-02-28')..Date.parse('2010-03-14')).to_a
96
+ end
97
+
98
+ it "should return cell values by reference" do
99
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
100
+
101
+ book[%Q{'text & pic'!A10}].should == 'This is an Excel XLSX workbook.'
102
+ book[%Q{'bools & errors'!B3}].should == true
103
+ book[%Q{'high refs'!AM619}].should == 'This is some text'
104
+ book[%Q{'high refs'!AO624}].should == 24.0
105
+ book[%Q{'high refs'!AP631}].should == 13.0
106
+ end
107
+ end
108
+
109
+ describe POI::Worksheets do
110
+ let(:book) { POI::Workbook.open(VARIOUS_SAMPLES_PATH) }
111
+
112
+ it "should allow indexing worksheets by ordinal" do
113
+ book.worksheets[0].name.should == "text & pic"
114
+ book.worksheets[1].name.should == "numbers"
115
+ book.worksheets[2].name.should == "dates"
116
+ book.worksheets[3].name.should == "bools & errors"
117
+ end
118
+
119
+ it "should allow indexing worksheets by name" do
120
+ book.worksheets["text & pic"].name.should == "text & pic"
121
+ book.worksheets["numbers"].name.should == "numbers"
122
+ book.worksheets["dates"].name.should == "dates"
123
+ end
124
+
125
+ it "should be enumerable" do
126
+ book.worksheets.should be_kind_of Enumerable
127
+
128
+ book.worksheets.each do |sheet|
129
+ sheet.should be_kind_of POI::Worksheet
130
+ end
131
+
132
+ book.worksheets.count.should == 6
133
+ book.worksheets.collect.count.should == 6
134
+ end
135
+
136
+ it "returns cells when passing a cell reference" do
137
+ book['dates']['A2'].class.should == DateTime
138
+ book['dates']['A2'].to_date.should == Date.parse('2010-02-28')
139
+ book['dates']['a2'].to_date.should == Date.parse('2010-02-28')
140
+ book['dates']['B2'].to_date.should == Date.parse('2010-03-14')
141
+ book['dates']['b2'].to_date.should == Date.parse('2010-03-14')
142
+ book['dates']['C2'].to_date.should == Date.parse('2010-03-28')
143
+ book['dates']['c2'].to_date.should == Date.parse('2010-03-28')
144
+ end
145
+ end
146
+
147
+ describe POI::Rows do
148
+ it "should be enumerable" do
149
+ book = POI::Workbook.open(VARIOUS_SAMPLES_PATH)
150
+ sheet = book.worksheets["text & pic"]
151
+ sheet.rows.should be_kind_of Enumerable
152
+
153
+ sheet.rows.each do |row|
154
+ row.should be_kind_of POI::Row
155
+ end
156
+
157
+ sheet.rows.count.should == 7
158
+ sheet.rows.collect.count.should == 7
159
+ end
160
+ end
161
+
162
+ describe POI::Cells do
163
+ let(:book) { POI::Workbook.open(VARIOUS_SAMPLES_PATH) }
164
+
165
+ it "should be enumerable" do
166
+ sheet = book.worksheets["text & pic"]
167
+ rows = sheet.rows
168
+ cells = rows[0].cells
169
+
170
+ cells.should be_kind_of Enumerable
171
+ cells.count.should == 1
172
+ cells.collect.count.should == 1
173
+ end
174
+ end
175
+
176
+ describe POI::Cell do
177
+ let(:book) { POI::Workbook.open(VARIOUS_SAMPLES_PATH) }
178
+
179
+ context "1900 windowed dates" do
180
+ it "should provide dates for date cells" do
181
+ sheet = book.worksheets["dates"]
182
+ rows = sheet.rows
183
+
184
+ dates_by_column = [
185
+ (DateTime.parse('2010-02-28')..DateTime.parse('2010-03-14')),
186
+ (DateTime.parse('2010-03-14')..DateTime.parse('2010-03-28')),
187
+ (DateTime.parse('2010-03-28')..DateTime.parse('2010-04-11'))
188
+ ]
189
+ (0..2).each do |col|
190
+ dates_by_column[col].each_with_index do |date, index|
191
+ row = index + 1
192
+ c = sheet.rows[row].cells[col]
193
+ c.value.to_date.should == date.to_date
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ context "1904 windowed dates" do
200
+ let(:book) { POI::Workbook.open(DATES_1904_WINDOW_XLS_PATH) }
201
+ it "returns entered dates correctly" do
202
+ c = book.worksheets[0].rows[1].cells[0]
203
+ c.value.to_date.should == Date.parse("2012-01-01")
204
+ end
205
+
206
+ it "returns dates computed by a formula correctly" do
207
+ c = book.worksheets[0].rows[1].cells[3]
208
+ c.value.to_date.should == Date.parse("2012-01-02")
209
+ end
210
+ end
211
+
212
+ it "should provide numbers for numeric cells" do
213
+ sheet = book.worksheets["numbers"]
214
+ rows = sheet.rows
215
+
216
+ (1..15).each do |number|
217
+ row = number
218
+ rows[row][0].value.should equal_at_cell(number, row, 0)
219
+ rows[row][1].value.should equal_at_cell(number ** 2, row, 1)
220
+ rows[row][2].value.should equal_at_cell(number ** 3, row, 2)
221
+ rows[row][3].value.should equal_at_cell(Math.sqrt(number), row, 3)
222
+ end
223
+
224
+ rows[9][0].to_s.should == '9.0'
225
+ rows[9][1].to_s.should == '81.0'
226
+ rows[9][2].to_s.should == '729.0'
227
+ rows[9][3].to_s.should == '3.0'
228
+ end
229
+
230
+ it "should handle array access from the workbook down to cells" do
231
+ book[1][9][0].to_s.should == '9.0'
232
+ book[1][9][1].to_s.should == '81.0'
233
+ book[1][9][2].to_s.should == '729.0'
234
+ book[1][9][3].to_s.should == '3.0'
235
+
236
+ book["numbers"][9][0].to_s.should == '9.0'
237
+ book["numbers"][9][1].to_s.should == '81.0'
238
+ book["numbers"][9][2].to_s.should == '729.0'
239
+ book["numbers"][9][3].to_s.should == '3.0'
240
+ end
241
+
242
+ it "should provide error text for error cells" do
243
+ sheet = book.worksheets["bools & errors"]
244
+ rows = sheet.rows
245
+
246
+ rows[6][0].value.should == 0.0 #'~CIRCULAR~REF~'
247
+ rows[6][0].error_value.should be_nil
248
+
249
+ rows[7][0].value.should be_nil
250
+ rows[7][0].error_value.should == '#DIV/0!'
251
+
252
+ rows[8][0].value.should be_nil
253
+ rows[8][0].error_value.should == '#N/A'
254
+
255
+ rows[9][0].value.should be_nil
256
+ rows[9][0].error_value.should == '#NAME?'
257
+
258
+ rows[10][0].value.should be_nil
259
+ rows[10][0].error_value.should == '#NULL!'
260
+
261
+ rows[11][0].value.should be_nil
262
+ rows[11][0].error_value.should == '#NUM!'
263
+
264
+ rows[12][0].value.should be_nil
265
+ rows[12][0].error_value.should == '#REF!'
266
+
267
+ rows[13][0].value.should be_nil
268
+ rows[13][0].error_value.should == '#VALUE!'
269
+
270
+ lambda{ rows[14][0].value }.should_not raise_error
271
+
272
+ rows[6][0].to_s.should == '0.0' #'~CIRCULAR~REF~'
273
+ rows[7][0].to_s.should == '' #'#DIV/0!'
274
+ rows[8][0].to_s.should == '' #'#N/A'
275
+ rows[9][0].to_s.should == '' #'#NAME?'
276
+ rows[10][0].to_s.should == '' #'#NULL!'
277
+ rows[11][0].to_s.should == '' #'#NUM!'
278
+ rows[12][0].to_s.should == '' #'#REF!'
279
+ rows[13][0].to_s.should == '' #'#VALUE!'
280
+ rows[14][0].to_s.should == ''
281
+ end
282
+
283
+ it "should provide booleans for boolean cells" do
284
+ sheet = book.worksheets["bools & errors"]
285
+ rows = sheet.rows
286
+ rows[1][0].value.should == false
287
+ rows[1][0].to_s.should == 'false'
288
+
289
+ rows[1][1].value.should == false
290
+ rows[1][1].to_s.should == 'false'
291
+
292
+ rows[2][0].value.should == true
293
+ rows[2][0].to_s.should == 'true'
294
+
295
+ rows[2][1].value.should == true
296
+ rows[2][1].to_s.should == 'true'
297
+ end
298
+
299
+ it "should provide the cell value as a string" do
300
+ sheet = book.worksheets["text & pic"]
301
+ rows = sheet.rows
302
+
303
+ rows[0][0].value.should == "This"
304
+ rows[1][0].value.should == "is"
305
+ rows[2][0].value.should == "an"
306
+ rows[3][0].value.should == "Excel"
307
+ rows[4][0].value.should == "XLSX"
308
+ rows[5][0].value.should == "workbook"
309
+ rows[9][0].value.should == 'This is an Excel XLSX workbook.'
310
+
311
+
312
+ rows[0][0].to_s.should == "This"
313
+ rows[1][0].to_s.should == "is"
314
+ rows[2][0].to_s.should == "an"
315
+ rows[3][0].to_s.should == "Excel"
316
+ rows[4][0].to_s.should == "XLSX"
317
+ rows[5][0].to_s.should == "workbook"
318
+ rows[9][0].to_s.should == 'This is an Excel XLSX workbook.'
319
+ end
320
+
321
+ it "should provide formulas instead of string-ified values" do
322
+ sheet = book.worksheets["numbers"]
323
+ rows = sheet.rows
324
+
325
+ (1..15).each do |number|
326
+ row = number
327
+ rows[row][0].to_s(false).should == "#{number}.0"
328
+ rows[row][1].to_s(false).should == "A#{row + 1}*A#{row + 1}"
329
+ rows[row][2].to_s(false).should == "B#{row + 1}*A#{row + 1}"
330
+ rows[row][3].to_s(false).should == "SQRT(A#{row + 1})"
331
+ end
332
+
333
+ sheet = book.worksheets["bools & errors"]
334
+ rows = sheet.rows
335
+ rows[1][0].to_s(false).should == '1=2'
336
+ rows[1][1].to_s(false).should == 'FALSE'
337
+ rows[2][0].to_s(false).should == '1=1'
338
+ rows[2][1].to_s(false).should == 'TRUE'
339
+ rows[14][0].to_s(false).should == 'foobar(1)'
340
+
341
+ sheet = book.worksheets["text & pic"]
342
+ sheet.rows[9][0].to_s(false).should == 'CONCATENATE(A1," ", A2," ", A3," ", A4," ", A5," ", A6,".")'
343
+ end
344
+
345
+ it "should handle getting values out of 'non-existent' cells" do
346
+ sheet = book.worksheets["bools & errors"]
347
+ sheet.rows[14][2].value.should be_nil
348
+ end
349
+
350
+ it "should notify the workbook that I have been updated" do
351
+ book['dates!A10'].to_date.should == Date.parse('2010-03-08')
352
+ book['dates!A16'].to_date.should == Date.parse('2010-03-14')
353
+ book['dates!B2'].to_date.should == Date.parse('2010-03-14')
354
+
355
+ cell = book.cell('dates!B2')
356
+ cell.formula.should == 'A16'
357
+
358
+ cell.formula = 'A10 + 1'
359
+ book.cell('dates!B2').poi_cell.should === cell.poi_cell
360
+ book.cell('dates!B2').formula.should == 'A10 + 1'
361
+
362
+ book['dates!B2'].to_date.should == Date.parse('2010-03-09')
363
+ end
364
+
365
+ it "should work with IFERROR" do
366
+ book['newfeatures!F6'].should == 'NOT FOUND'
367
+ end
368
+ end