simple_xlsx_reader 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 0.9.2
2
+
3
+ * Support reading files written by ex. simple_xlsx_writer that don't
4
+ specify sheet dimensions explicitly (which Excel does).
5
+
1
6
  ### 0.9.1
2
7
 
3
8
  * Fixed an important parse bug that ignored empty 'Generic' cells
@@ -1,3 +1,3 @@
1
1
  module SimpleXlsxReader
2
- VERSION = "0.9.1"
2
+ VERSION = "0.9.2"
3
3
  end
@@ -109,9 +109,7 @@ module SimpleXlsxReader
109
109
  def parse_sheet(sheet_name, xsheet)
110
110
  sheet = Sheet.new(sheet_name)
111
111
 
112
- last_column = xsheet.xpath('/xmlns:worksheet/xmlns:dimension').first.
113
- attributes['ref'].value.match(/:([A-Z]*)[1-9]*/).captures.first
114
-
112
+ last_column = last_column(xsheet)
115
113
  rownum = -1
116
114
  sheet.rows =
117
115
  xsheet.xpath("/xmlns:worksheet/xmlns:sheetData/xmlns:row").map do |xrow|
@@ -158,6 +156,28 @@ module SimpleXlsxReader
158
156
  sheet
159
157
  end
160
158
 
159
+ ##
160
+ # Returns the last column name, ex. 'E'
161
+ #
162
+ # Note that excel writes a '/worksheet/dimension' node we can get the
163
+ # last cell from, but some libs (ex. simple_xlsx_writer) don't record
164
+ # this. In that case, we assume the data is of uniform column length
165
+ # and check the column name of the last header row. Obviously this isn't
166
+ # the most robust strategy, but it likely fits 99% of use cases
167
+ # considering it's not a problem with actual excel docs.
168
+ def last_column(xsheet)
169
+ dimension = xsheet.xpath('/xmlns:worksheet/xmlns:dimension').first
170
+ if dimension
171
+ dimension.attributes['ref'].value.
172
+ match(/:([A-Z]*)[1-9]*/).captures.first
173
+ else
174
+ xsheet.at_xpath("/xmlns:worksheet/xmlns:sheetData/xmlns:row/xmlns:c[last()]").
175
+ attributes['r'].value.
176
+ match(/([A-Z]*)[1-9]*/).captures.first
177
+ end
178
+ end
179
+
180
+
161
181
  # Excel doesn't record types for some cells, only its display style, so
162
182
  # we have to back out the type from that style.
163
183
  #
@@ -56,6 +56,64 @@ describe SimpleXlsxReader do
56
56
  end
57
57
  end
58
58
 
59
+ describe '#last_column' do
60
+
61
+ let(:generic_style) do
62
+ Nokogiri::XML(
63
+ <<-XML
64
+ <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
65
+ <cellXfs count="1">
66
+ <xf numFmtId="0" />
67
+ </cellXfs>
68
+ </styleSheet>
69
+ XML
70
+ )
71
+ end
72
+
73
+ # Note, this is not a valid sheet, since the last cell is actually D1 but
74
+ # the dimension specifies C1. This is just for testing.
75
+ let(:sheet) do
76
+ Nokogiri::XML(
77
+ <<-XML
78
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
79
+ <dimension ref="A1:C1" />
80
+ <sheetData>
81
+ <row>
82
+ <c r='A1' s='0'>
83
+ <v>Cell A</v>
84
+ </c>
85
+ <c r='C1' s='0'>
86
+ <v>Cell C</v>
87
+ </c>
88
+ <c r='D1' s='0'>
89
+ <v>Cell D</v>
90
+ </c>
91
+ </row>
92
+ </sheetData>
93
+ </worksheet>
94
+ XML
95
+ )
96
+ end
97
+
98
+ let(:xml) do
99
+ SimpleXlsxReader::Document::Xml.new.tap do |xml|
100
+ xml.sheets = [sheet]
101
+ xml.styles = generic_style
102
+ end
103
+ end
104
+
105
+ subject { described_class.new(xml) }
106
+
107
+ it 'uses /worksheet/dimension if available' do
108
+ subject.last_column(sheet).must_equal 'C'
109
+ end
110
+
111
+ it 'uses the last header cell if /worksheet/dimension is missing' do
112
+ sheet.xpath('/xmlns:worksheet/xmlns:dimension').remove
113
+ subject.last_column(sheet).must_equal 'D'
114
+ end
115
+ end
116
+
59
117
  describe "parse errors" do
60
118
  after do
61
119
  SimpleXlsxReader.configuration.catch_cell_load_errors = false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_xlsx_reader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: